From 276a6874028e40a5678018c0a4e5d4cc05004e9f Mon Sep 17 00:00:00 2001 From: Venkat Annavazzala Date: Tue, 3 Feb 2026 13:51:57 +0530 Subject: [PATCH] feat(midnight): added night coin skeleton Ticket: COIN-7475 --- modules/statics/src/account.ts | 112 +++++++++++++++++++++++ modules/statics/src/allCoinsAndTokens.ts | 21 +++++ modules/statics/src/base.ts | 5 + modules/statics/src/coinFeatures.ts | 27 ++++++ modules/statics/src/coins/nightTokens.ts | 28 ++++++ modules/statics/src/index.ts | 2 + modules/statics/src/networks.ts | 35 +++++++ modules/statics/src/night.ts | 92 +++++++++++++++++++ modules/statics/src/tokenConfig.ts | 35 +++++++ 9 files changed, 357 insertions(+) create mode 100644 modules/statics/src/coins/nightTokens.ts create mode 100644 modules/statics/src/night.ts diff --git a/modules/statics/src/account.ts b/modules/statics/src/account.ts index 42e6419c32..859a677997 100644 --- a/modules/statics/src/account.ts +++ b/modules/statics/src/account.ts @@ -169,6 +169,16 @@ export interface VetTokenConstructorOptions extends AccountConstructorOptions { contractAddress: string; gasTankToken?: string; } + +/** + * Midnight Network token (DUST) constructor options + * DUST is the fee token on the Midnight Network + */ +export interface NightTokenConstructorOptions extends AccountConstructorOptions { + // Token identifier (e.g., 'dust' for the fee token) + tokenId: string; +} + export interface CosmosTokenConstructorOptions extends AccountConstructorOptions { denom: string; } @@ -706,6 +716,20 @@ export class VetToken extends AccountCoinToken { } } +/** + * Midnight Network supports the DUST token for transaction fees. + * DUST is a native token on the Midnight Network used to pay for transaction fees. + */ +export class NightToken extends AccountCoinToken { + public tokenId: string; + constructor(options: NightTokenConstructorOptions) { + super({ + ...options, + }); + this.tokenId = options.tokenId; + } +} + /** * Cosmos network supports tokens * Cosmos tokens work similar to native coins, but the token is determined by @@ -3778,6 +3802,94 @@ export function tvetToken( ); } +/** + * Factory function for Night token (DUST) instances. + * + * @param id uuid v4 + * @param name unique identifier of the token + * @param fullName Complete human-readable name of the token + * @param decimalPlaces Number of decimal places this token supports (divisibility exponent) + * @param tokenId Token identifier (e.g., 'dust') + * @param asset Asset which this coin represents + * @param features Features of this coin. Defaults to the DEFAULT_FEATURES defined in `AccountCoin` + * @param prefix Optional token prefix. Defaults to empty string + * @param suffix Optional token suffix. Defaults to token name. + * @param network Optional token network. Defaults to the mainnet Night network. + * @param primaryKeyCurve The elliptic curve for this chain/token + */ +export function nightToken( + id: string, + name: string, + fullName: string, + decimalPlaces: number, + tokenId: string, + asset: UnderlyingAsset, + features: CoinFeature[] = AccountCoin.DEFAULT_FEATURES, + prefix = '', + suffix: string = name.toUpperCase(), + network: AccountNetwork = Networks.main.night, + primaryKeyCurve: KeyCurve = KeyCurve.Ed25519 +) { + return Object.freeze( + new NightToken({ + id, + name, + fullName, + network, + tokenId, + prefix, + suffix, + features, + decimalPlaces, + asset, + isToken: true, + primaryKeyCurve, + baseUnit: BaseUnit.NIGHT, + }) + ); +} + +/** + * Factory function for testnet Night token (DUST) instances. + * + * @param id uuid v4 + * @param name unique identifier of the token + * @param fullName Complete human-readable name of the token + * @param decimalPlaces Number of decimal places this token supports (divisibility exponent) + * @param tokenId Token identifier (e.g., 'tdust') + * @param asset Asset which this coin represents + * @param features Features of this coin. Defaults to the DEFAULT_FEATURES defined in `AccountCoin` + * @param prefix Optional token prefix. Defaults to empty string + * @param suffix Optional token suffix. Defaults to token name. + * @param network Optional token network. Defaults to the testnet Night network. + */ +export function tnightToken( + id: string, + name: string, + fullName: string, + decimalPlaces: number, + tokenId: string, + asset: UnderlyingAsset, + features: CoinFeature[] = AccountCoin.DEFAULT_FEATURES, + prefix = '', + suffix: string = name.toUpperCase(), + network: AccountNetwork = Networks.test.night +) { + return nightToken( + id, + name, + fullName, + decimalPlaces, + tokenId, + asset, + features, + prefix, + suffix, + network, + KeyCurve.Ed25519 + ); +} + /** * Factory function for Vet NFT collections. * diff --git a/modules/statics/src/allCoinsAndTokens.ts b/modules/statics/src/allCoinsAndTokens.ts index 3aee9a3e1a..a9df583677 100644 --- a/modules/statics/src/allCoinsAndTokens.ts +++ b/modules/statics/src/allCoinsAndTokens.ts @@ -55,6 +55,7 @@ import { } from './account'; import { ada } from './ada'; import { avaxp } from './avaxp'; +import { night } from './night'; import { BaseUnit, CoinFeature, KeyCurve, UnderlyingAsset } from './base'; import { canton } from './canton'; import { erc20Coins } from './coins/erc20Coins'; @@ -68,6 +69,7 @@ import { lightningCoins } from './lightning'; import { sip10Tokens } from './coins/sip10Tokens'; import { nep141Tokens } from './coins/nep141Tokens'; import { vetTokens } from './coins/vetTokens'; +import { nightTokens } from './coins/nightTokens'; import { cosmosTokens } from './coins/cosmosTokens'; import { jettonTokens } from './coins/jettonTokens'; import { polyxTokens } from './coins/polyxTokens'; @@ -105,6 +107,7 @@ import { INJECTIVE_FEATURES, IOTA_FEATURES, NEAR_FEATURES, + NIGHT_FEATURES, OAS_FEATURES, OPETH_FEATURES, POLYGON_FEATURES, @@ -155,6 +158,7 @@ export const allCoinsAndTokens = [ ...sip10Tokens, ...nep141Tokens, ...vetTokens, + ...nightTokens, ...cosmosTokens, ...botTokens, ...adaTokens, @@ -199,6 +203,23 @@ export const allCoinsAndTokens = [ UnderlyingAsset.ADA, ADA_FEATURES ), + // Midnight Network - Privacy-first blockchain using zero-knowledge proofs + night( + 'b1c2d3e4-5f6a-7b8c-9d0e-1f2a3b4c5d6e', + 'night', + 'Midnight', + Networks.main.night, + UnderlyingAsset.NIGHT, + NIGHT_FEATURES + ), + night( + 'c2d3e4f5-6a7b-8c9d-0e1f-2a3b4c5d6e7f', + 'tnight', + 'Testnet Midnight', + Networks.test.night, + UnderlyingAsset.NIGHT, + NIGHT_FEATURES + ), account( 'ec41e62a-cc57-4aa0-9b9e-217da1226817', 'algo', diff --git a/modules/statics/src/base.ts b/modules/statics/src/base.ts index 56854a856b..f699c835d0 100644 --- a/modules/statics/src/base.ts +++ b/modules/statics/src/base.ts @@ -87,6 +87,7 @@ export enum CoinFamily { CTC = 'ctc', HYPEEVM = 'hypeevm', NEAR = 'near', + NIGHT = 'night', // Midnight Network OAS = 'oas', OFC = 'ofc', OG = 'og', @@ -613,6 +614,9 @@ export enum UnderlyingAsset { MORPH = 'morph', MORPHETH = 'morpheth', NEAR = 'near', + NIGHT = 'night', // Midnight Network + 'night:dust' = 'night:dust', // Midnight Dust token (fee token) + 'tnight:tdust' = 'tnight:tdust', // Testnet Midnight Dust token OAS = 'oas', OG = 'og', OKBXLAYER = 'okbxlayer', @@ -3609,6 +3613,7 @@ export enum BaseUnit { SUI = 'MIST', TON = 'nanoton', NEAR = 'yocto', + NIGHT = 'dust', // Midnight Network smallest unit OFC = 'ofcCoin', OSMO = 'uosmo', FIAT = 'fiatCoin', diff --git a/modules/statics/src/coinFeatures.ts b/modules/statics/src/coinFeatures.ts index 7edd3fa8d4..99b1c68c45 100644 --- a/modules/statics/src/coinFeatures.ts +++ b/modules/statics/src/coinFeatures.ts @@ -715,6 +715,33 @@ export const VET_FEATURES = [ ]; export const VET_TOKEN_FEATURES = VET_FEATURES.filter((feature) => feature !== CoinFeature.SUPPORTS_TOKENS); +/** + * Midnight Network (NIGHT) features + * Night is a UTXO-based privacy chain using Ed25519 and ZK proofs + * Minimal features enabled for skeleton implementation + */ +export const NIGHT_FEATURES = [ + CoinFeature.UNSPENT_MODEL, + CoinFeature.TSS, + CoinFeature.TSS_COLD, + CoinFeature.TRANSACTION_DATA, + CoinFeature.REQUIRES_BIG_NUMBER, + CoinFeature.SUPPORTS_TOKENS, // Supports DUST token +]; + +/** + * Midnight Network token (DUST) features + * DUST is the fee token on the Midnight Network + * Tokens use account model, not unspent model + */ +export const NIGHT_TOKEN_FEATURES = [ + CoinFeature.ACCOUNT_MODEL, + CoinFeature.TSS, + CoinFeature.TSS_COLD, + CoinFeature.TRANSACTION_DATA, + CoinFeature.REQUIRES_BIG_NUMBER, +]; + export const EVM_NON_EIP1559_FEATURES = [...EVM_FEATURES.filter((feature) => feature !== CoinFeature.EIP1559)]; export const XDC_FEATURES = [...EVM_NON_EIP1559_FEATURES, CoinFeature.ERC20_BULK_TRANSACTION]; diff --git a/modules/statics/src/coins/nightTokens.ts b/modules/statics/src/coins/nightTokens.ts new file mode 100644 index 0000000000..264150e609 --- /dev/null +++ b/modules/statics/src/coins/nightTokens.ts @@ -0,0 +1,28 @@ +import { nightToken, tnightToken } from '../account'; +import { UnderlyingAsset } from '../base'; +import { NIGHT_TOKEN_FEATURES } from '../coinFeatures'; + +/** + * DUST tokens on the Midnight Network + * DUST is the native fee token used to pay for transactions on Midnight + */ +export const nightTokens = [ + nightToken( + 'f8a9b2c1-3d4e-5f6a-7b8c-9d0e1f2a3b4c', // UUID for night:dust + 'night:dust', + 'Midnight Dust', + 8, // 8 decimal places like NIGHT + 'dust', + UnderlyingAsset['night:dust'], + NIGHT_TOKEN_FEATURES + ), + tnightToken( + 'a1b2c3d4-5e6f-7a8b-9c0d-e1f2a3b4c5d6', // UUID for tnight:tdust + 'tnight:tdust', + 'Testnet Midnight Dust', + 8, // 8 decimal places like NIGHT + 'tdust', + UnderlyingAsset['tnight:tdust'], + NIGHT_TOKEN_FEATURES + ), +]; diff --git a/modules/statics/src/index.ts b/modules/statics/src/index.ts index 1289b8858a..9e23fbf6f9 100644 --- a/modules/statics/src/index.ts +++ b/modules/statics/src/index.ts @@ -6,6 +6,7 @@ export * from './tokenConfig'; export { OfcCoin } from './ofc'; export { UtxoCoin } from './utxo'; export { LightningCoin } from './lightning'; +export { Night } from './night'; export { AccountCoin, GasTankAccountCoin, @@ -30,6 +31,7 @@ export { Nep141Token, VetToken, VetNFTCollection, + NightToken, CosmosChainToken, AdaToken, JettonToken, diff --git a/modules/statics/src/networks.ts b/modules/statics/src/networks.ts index 4d871edb69..41d348c4cc 100644 --- a/modules/statics/src/networks.ts +++ b/modules/statics/src/networks.ts @@ -93,6 +93,16 @@ export interface AdaNetwork extends BaseNetwork { coinsPerUtxoWord: number; } +/** + * Midnight Network - Privacy-first blockchain using zero-knowledge proofs + */ +export interface NightNetwork extends BaseNetwork { + // GraphQL indexer endpoint for querying chain state + readonly indexerUrl: string; + // RPC WebSocket endpoint for node communication + readonly rpcUrl: string; +} + export interface AvalancheNetwork extends BaseNetwork { readonly alias: string; readonly blockchainID: string; @@ -236,6 +246,29 @@ class AdaTestnet extends Testnet implements AdaNetwork { stakeKeyDeposit = 2000000; } +/** + * Midnight Network - Privacy-first blockchain using zero-knowledge proofs + * Mainnet configuration + */ +class Night extends Mainnet implements NightNetwork { + name = 'Midnight'; + family = CoinFamily.NIGHT; + explorerUrl = 'https://explorer.midnight.network/'; + indexerUrl = 'https://indexer.midnight.network/api/v3/graphql'; + rpcUrl = 'wss://rpc.midnight.network'; +} + +/** + * Midnight Network Testnet (Preprod) + */ +class NightTestnet extends Testnet implements NightNetwork { + name = 'MidnightTestnet'; + family = CoinFamily.NIGHT; + explorerUrl = 'https://explorer.preprod.midnight.network/'; + indexerUrl = 'https://indexer.preprod.midnight.network/api/v3/graphql'; + rpcUrl = 'wss://rpc.preprod.midnight.network'; +} + class Apt extends Mainnet implements AccountNetwork { name = 'Apt'; family = CoinFamily.APT; @@ -2460,6 +2493,7 @@ export const Networks = { sonic: Object.freeze(new Sonic()), sui: Object.freeze(new Sui()), near: Object.freeze(new Near()), + night: Object.freeze(new Night()), stx: Object.freeze(new Stx()), somi: Object.freeze(new Somi()), soneium: Object.freeze(new Soneium()), @@ -2573,6 +2607,7 @@ export const Networks = { sol: Object.freeze(new SolTestnet()), sui: Object.freeze(new SuiTestnet()), near: Object.freeze(new NearTestnet()), + night: Object.freeze(new NightTestnet()), stx: Object.freeze(new StxTestnet()), stt: Object.freeze(new SomniaTestnet()), soneium: Object.freeze(new SoneiumTestnet()), diff --git a/modules/statics/src/night.ts b/modules/statics/src/night.ts new file mode 100644 index 0000000000..6955815731 --- /dev/null +++ b/modules/statics/src/night.ts @@ -0,0 +1,92 @@ +import { BaseCoin, BaseUnit, CoinFeature, CoinKind, KeyCurve, UnderlyingAsset } from './base'; +import { NightNetwork } from './networks'; + +export interface NightConstructorOptions { + id: string; + fullName: string; + name: string; + network: NightNetwork; + features: CoinFeature[]; + asset: UnderlyingAsset; + prefix?: string; + suffix?: string; + primaryKeyCurve: KeyCurve; +} + +/** + * Midnight Network - Privacy-first blockchain using zero-knowledge proofs + * + * Night is a UTXO-based chain similar to Cardano, using Ed25519 for key derivation. + * It uses zero-knowledge proofs for privacy-preserving transactions. + */ +export class Night extends BaseCoin { + public static readonly DEFAULT_FEATURES = [ + CoinFeature.UNSPENT_MODEL, + CoinFeature.TSS, + CoinFeature.TSS_COLD, + CoinFeature.TRANSACTION_DATA, + CoinFeature.REQUIRES_BIG_NUMBER, + CoinFeature.SUPPORTS_TOKENS, // Supports DUST token + ]; + + public readonly network: NightNetwork; + + constructor(options: NightConstructorOptions) { + super({ + ...options, + kind: CoinKind.CRYPTO, + isToken: false, + decimalPlaces: 8, // NIGHT uses 8 decimal places + baseUnit: BaseUnit.NIGHT, + }); + + this.network = options.network; + } + + protected disallowedFeatures(): Set { + return new Set([CoinFeature.ACCOUNT_MODEL]); + } + + protected requiredFeatures(): Set { + return new Set([CoinFeature.UNSPENT_MODEL]); + } +} + +/** + * Factory function for Night coin instances. + * + * @param id uuid v4 + * @param name unique identifier of the coin + * @param fullName Complete human-readable name of the coin + * @param network Network object for this coin + * @param asset Asset which this coin represents. This is the same for both mainnet and testnet variants of a coin. + * @param features? Features of this coin. Defaults to the DEFAULT_FEATURES defined in `Night` + * @param prefix? Optional coin prefix. Defaults to empty string + * @param suffix? Optional coin suffix. Defaults to coin name. + * @param primaryKeyCurve The elliptic curve for this chain/token + */ +export function night( + id: string, + name: string, + fullName: string, + network: NightNetwork, + asset: UnderlyingAsset, + features: CoinFeature[] = Night.DEFAULT_FEATURES, + prefix = '', + suffix: string = name.toUpperCase(), + primaryKeyCurve: KeyCurve = KeyCurve.Ed25519 +) { + return Object.freeze( + new Night({ + id, + name, + fullName, + network, + prefix, + suffix, + features, + asset, + primaryKeyCurve, + }) + ); +} diff --git a/modules/statics/src/tokenConfig.ts b/modules/statics/src/tokenConfig.ts index eaa96ab8b7..1f37cc9c5a 100644 --- a/modules/statics/src/tokenConfig.ts +++ b/modules/statics/src/tokenConfig.ts @@ -30,6 +30,7 @@ import { PolyxCoin, TronErc20Coin, VetToken, + NightToken, WorldERC20Token, XrpCoin, ZkethERC20Token, @@ -145,6 +146,13 @@ export type VetTokenConfig = BaseNetworkConfig & { contractAddress: string; }; +/** + * Midnight Network token (DUST) configuration + */ +export type NightTokenConfig = BaseNetworkConfig & { + tokenId: string; +}; + export type VetNFTCollectionConfig = BaseNetworkConfig & { nftCollectionId: string; }; @@ -186,6 +194,7 @@ export type TokenConfig = | CosmosTokenConfig | VetTokenConfig | VetNFTCollectionConfig + | NightTokenConfig | TaoTokenConfig | PolyxTokenConfig | JettonTokenConfig @@ -239,6 +248,7 @@ export interface TokenNetwork { tokens: VetTokenConfig[]; nftCollections: VetNFTCollectionConfig[]; }; + night: { tokens: NightTokenConfig[] }; cosmos: { tokens: CosmosTokenConfig[] }; ton: { tokens: JettonTokenConfig[] }; tempo: { tokens: Tip20TokenConfig[] }; @@ -1070,6 +1080,25 @@ const getFormattedVetTokens = (customCoinMap = coins) => return acc; }, []); +function getNightTokenConfig(coin: NightToken): NightTokenConfig { + return { + type: coin.name, + coin: coin.network.type === NetworkType.MAINNET ? 'night' : 'tnight', + network: coin.network.type === NetworkType.MAINNET ? 'Mainnet' : 'Testnet', + name: coin.fullName, + tokenId: coin.tokenId, + decimalPlaces: coin.decimalPlaces, + }; +} + +const getFormattedNightTokens = (customCoinMap = coins) => + customCoinMap.reduce((acc: NightTokenConfig[], coin) => { + if (coin instanceof NightToken) { + acc.push(getNightTokenConfig(coin)); + } + return acc; + }, []); + const getFormattedCosmosChainTokens = (customCoinMap = coins) => customCoinMap.reduce((acc: CosmosTokenConfig[], coin) => { if (coin instanceof CosmosChainToken) { @@ -1365,6 +1394,9 @@ export const getFormattedTokens = (coinMap = coins): Tokens => { (nftCollection: VetNFTCollectionConfig) => nftCollection.network === 'Mainnet' ), }, + night: { + tokens: getFormattedNightTokens(coinMap).filter((token) => token.network === 'Mainnet'), + }, }, testnet: { ...getFormattedTokensByNetwork('Testnet', coinMap), @@ -1380,6 +1412,9 @@ export const getFormattedTokens = (coinMap = coins): Tokens => { (nftCollection: VetNFTCollectionConfig) => nftCollection.network === 'Testnet' ), }, + night: { + tokens: getFormattedNightTokens(coinMap).filter((token) => token.network === 'Testnet'), + }, }, }; };