Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions modules/statics/src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
CANTON_TOKEN_FEATURES,
CELO_TOKEN_FEATURES,
COSMOS_SIDECHAIN_FEATURES,
ERC7984_TOKEN_FEATURES,
TEMPO_FEATURES,
} from './coinFeatures';

Expand Down Expand Up @@ -96,6 +97,10 @@ export interface Erc721ConstructorOptions extends AccountConstructorOptions {
contractAddress: string;
}

export interface Erc7984ConstructorOptions extends AccountConstructorOptions {
contractAddress: string;
}

export interface NFTCollectionIdConstructorOptions extends AccountConstructorOptions {
nftCollectionId: string;
}
Expand Down Expand Up @@ -294,6 +299,19 @@ export class Erc721Coin extends ContractAddressDefinedToken {}
*/
export class Erc1155Coin extends ContractAddressDefinedToken {}

/**
* ERC-7984 is the confidential token standard for fhEVM-enabled blockchains (Zama).
* Token balances are stored as FHE-encrypted ciphertexts; transfers use confidentialTransfer()
* instead of the standard ERC-20 transfer(). Balance reads require delegated decryption via ACL.
*
* {@link https://eips.ethereum.org/EIPS/eip-7984 EIP-7984}
*/
export class Erc7984Coin extends ContractAddressDefinedToken {
constructor(options: Erc7984ConstructorOptions) {
super(options);
}
}

/**
* The TRON blockchain supports tokens of the ERC20 standard similar to ETH ERC20 tokens.
*/
Expand Down Expand Up @@ -1087,6 +1105,99 @@ export function terc20(
return erc20(id, name, fullName, decimalPlaces, contractAddress, asset, features, prefix, suffix, network);
}

/**
* Factory function for ERC-7984 confidential token instances (Zama fhEVM).
*
* ERC-7984 tokens store balances as FHE-encrypted ciphertexts. Transfers use
* confidentialTransfer() and balance reads require ACL delegation to BitGo.
*
* @param id uuid v4
* @param name unique identifier of the token (e.g. 'eth:ctkn')
* @param fullName Complete human-readable name of the token
* @param decimalPlaces Number of decimal places this token supports (divisibility exponent)
* @param contractAddress Contract address of this token
* @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 ERC7984_TOKEN_FEATURES
* @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 Ethereum main network.
* @param primaryKeyCurve The elliptic curve for this chain/token
*/
export function erc7984(
id: string,
name: string,
fullName: string,
decimalPlaces: number,
contractAddress: string,
asset: UnderlyingAsset,
features: CoinFeature[] = ERC7984_TOKEN_FEATURES,
prefix = '',
suffix: string = name.toUpperCase(),
network: EthereumNetwork = Networks.main.ethereum,
primaryKeyCurve: KeyCurve = KeyCurve.Secp256k1
) {
return Object.freeze(
new Erc7984Coin({
id,
name,
fullName,
network,
contractAddress,
prefix,
suffix,
features,
decimalPlaces,
asset,
isToken: true,
primaryKeyCurve,
baseUnit: BaseUnit.ETH,
})
);
}

/**
* Factory function for testnet ERC-7984 confidential token instances (Zama fhEVM).
*
* @param id uuid v4
* @param name unique identifier of the token (e.g. 'hteth:ctkn')
* @param fullName Complete human-readable name of the token
* @param decimalPlaces Number of decimal places this token supports (divisibility exponent)
* @param contractAddress Contract address of this token
* @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 ERC7984_TOKEN_FEATURES
* @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 Hoodi test network.
* @param primaryKeyCurve The elliptic curve for this chain/token
*/
export function terc7984(
id: string,
name: string,
fullName: string,
decimalPlaces: number,
contractAddress: string,
asset: UnderlyingAsset,
features: CoinFeature[] = ERC7984_TOKEN_FEATURES,
prefix = '',
suffix: string = name.toUpperCase(),
network: EthereumNetwork = Networks.test.hoodi,
primaryKeyCurve: KeyCurve = KeyCurve.Secp256k1
) {
return erc7984(
id,
name,
fullName,
decimalPlaces,
contractAddress,
asset,
features,
prefix,
suffix,
network,
primaryKeyCurve
);
}

/**
* Factory function for erc721 token instances.
*
Expand Down
2 changes: 2 additions & 0 deletions modules/statics/src/allCoinsAndTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import { nep141Tokens } from './coins/nep141Tokens';
import { vetTokens } from './coins/vetTokens';
import { cosmosTokens } from './coins/cosmosTokens';
import { jettonTokens } from './coins/jettonTokens';
import { erc7984Tokens } from './coins/erc7984Tokens';
import { polyxTokens } from './coins/polyxTokens';
import { cantonTokens } from './coins/cantonTokens';
import { flrp } from './flrp';
Expand Down Expand Up @@ -164,6 +165,7 @@ export const allCoinsAndTokens = [
...botTokens,
...adaTokens,
...jettonTokens,
...erc7984Tokens,
...polyxTokens,
...cantonTokens,
avaxp(
Expand Down
18 changes: 18 additions & 0 deletions modules/statics/src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,18 @@ export enum CoinFeature {
* This coin allows negative fees in transactions
*/
ALLOWS_NEGATIVE_FEE = 'allows-negative-fee',

/**
* This token uses fully homomorphic encryption (FHE) for confidential transfers (ERC-7984).
* Balances are stored as encrypted ciphertexts; transfers use confidentialTransfer() instead of transfer().
*/
CONFIDENTIAL_TRANSFER = 'confidential-transfer',

/**
* Reading the balance of this token requires the wallet owner to delegate decryption access
* to BitGo via ACL.delegateForUserDecryption() before balances can be displayed.
*/
REQUIRES_DECRYPTION_DELEGATION = 'requires-decryption-delegation',
}

/**
Expand Down Expand Up @@ -3794,6 +3806,12 @@ export enum UnderlyingAsset {
'eth:drv' = 'eth:drv',
'eth:prn' = 'eth:prn',
'eth:zama' = 'eth:zama',
// ERC-7984 confidential tokens (Zama fhEVM - mainnet, contract addresses TBD pending Zama mainnet launch)
'eth:ctkn' = 'eth:ctkn',
'eth:cusdt' = 'eth:cusdt',
// ERC-7984 confidential tokens (Zama fhEVM - testnet / hteth)
'hteth:ctkn' = 'hteth:ctkn',
'hteth:cusdt' = 'hteth:cusdt',
'eth:mony' = 'eth:mony',
'eth:architectgvi' = 'eth:architectgvi',
'eth:zk' = 'eth:zk',
Expand Down
9 changes: 9 additions & 0 deletions modules/statics/src/coinFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -789,3 +789,12 @@ export const CANTON_TOKEN_FEATURES = [
CoinFeature.REQUIRES_DEPOSIT_ACCEPTANCE_TRANSACTION,
CoinFeature.ALPHANUMERIC_MEMO_ID,
];

export const ERC7984_TOKEN_FEATURES = [
...ACCOUNT_COIN_DEFAULT_FEATURES,
CoinFeature.BULK_TRANSACTION,
CoinFeature.TSS,
CoinFeature.TSS_COLD,
CoinFeature.CONFIDENTIAL_TRANSFER,
CoinFeature.REQUIRES_DECRYPTION_DELEGATION,
];
55 changes: 55 additions & 0 deletions modules/statics/src/coins/erc7984Tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { erc7984, terc7984 } from '../account';
import { UnderlyingAsset } from '../base';

/**
* ERC-7984 confidential tokens (Zama fhEVM).
*
* These tokens use fully homomorphic encryption (FHE) for on-chain confidential transfers.
* Balances are stored as encrypted ciphertexts; plaintext amounts require ACL-delegated
* decryption via the Zama Gateway before they can be displayed.
*
* Mainnet contract addresses are TBD pending Zama fhEVM mainnet launch.
* Testnet tokens (hteth:*) are deployed on the BitGo-supported ETH testnet (Hoodi).
*
* Sandbox development contracts (Sepolia):
* CTKN: 0x94167129172A35ab093B44b8b96213DDbc3cD387
* cUSDT: 0x4E7B06D78965594eB5EF5414c357ca21E1554491
*/
export const erc7984Tokens = [
// Mainnet tokens (contract addresses TBD pending Zama fhEVM mainnet launch)
erc7984(
'f47ac10b-58cc-4372-a567-0e02b2c3d479',
'eth:ctkn',
'Confidential Test Token',
6,
'0x0000000000000000000000000000000000000000', // TODO: update with mainnet contract address
UnderlyingAsset['eth:ctkn']
),
erc7984(
'f47ac10b-58cc-4372-a567-0e02b2c3d480',
'eth:cusdt',
'Confidential USDT',
6,
'0x0000000000000000000000000000000000000000', // TODO: update with mainnet contract address
UnderlyingAsset['eth:cusdt']
),

// Testnet tokens (hteth / Hoodi)
// Note: sandbox development contracts are on Ethereum Sepolia; deploy to Hoodi for BitGo testnet support
terc7984(
'f47ac10b-58cc-4372-a567-0e02b2c3d481',
'hteth:ctkn',
'Confidential Test Token',
6,
'0x0000000000000000000000000000000000000000', // TODO: deploy to Hoodi and update address (Sepolia dev: 0x94167129172A35ab093B44b8b96213DDbc3cD387)
UnderlyingAsset['hteth:ctkn']
),
terc7984(
'f47ac10b-58cc-4372-a567-0e02b2c3d482',
'hteth:cusdt',
'Confidential USDT',
6,
'0x0000000000000000000000000000000000000000', // TODO: deploy to Hoodi and update address (Sepolia dev: 0x4E7B06D78965594eB5EF5414c357ca21E1554491)
UnderlyingAsset['hteth:cusdt']
),
];
1 change: 1 addition & 0 deletions modules/statics/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export {
AdaToken,
JettonToken,
CantonToken,
Erc7984Coin,
} from './account';
export { CoinMap } from './map';
export { networkFeatureMapForTokens, registerNetworkFeatures, getNetworkFeatures } from './networkFeatureMapForTokens';
Expand Down
64 changes: 61 additions & 3 deletions modules/statics/src/tokenConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
Erc1155Coin,
Erc20Coin,
Erc721Coin,
Erc7984Coin,
EthLikeERC20Token,
EthLikeERC721Token,
FlrERC20Token,
Expand Down Expand Up @@ -68,6 +69,7 @@ export type EosTokenConfig = BaseContractAddressConfig & {
contractAddress: string;
};
export type Erc20TokenConfig = BaseContractAddressConfig;
export type Erc7984TokenConfig = BaseContractAddressConfig;
export type TrxTokenConfig = BaseContractAddressConfig;
export type StellarTokenConfig = BaseNetworkConfig;

Expand Down Expand Up @@ -191,12 +193,14 @@ export type TokenConfig =
| PolyxTokenConfig
| JettonTokenConfig
| EthLikeERC721TokenConfig
| Tip20TokenConfig;
| Tip20TokenConfig
| Erc7984TokenConfig;

export interface TokenNetwork {
eth: {
tokens: Erc20TokenConfig[];
nfts: EthLikeTokenConfig[];
confidentialTokens: Erc7984TokenConfig[];
};
xlm: { tokens: StellarTokenConfig[] };
algo: { tokens: AlgoTokenConfig[] };
Expand Down Expand Up @@ -342,6 +346,44 @@ export const getFormattedErc20Tokens = (customCoinMap = coins) =>
return acc;
}, []);

function getErc7984TokenConfig(coin: Erc7984Coin): Erc7984TokenConfig {
let baseCoin: string;
switch (coin.network.name) {
case Networks.main.ethereum.name:
baseCoin = 'eth';
break;
case Networks.test.kovan.name:
baseCoin = 'teth';
break;
case Networks.test.goerli.name:
baseCoin = 'gteth';
break;
case Networks.test.holesky.name:
case Networks.test.hoodi.name:
baseCoin = 'hteth';
break;
default:
throw new Error(`ERC-7984 token ${coin.name} has an unsupported network`);
}
return {
type: coin.name,
coin: baseCoin,
network: coin.network.type === NetworkType.MAINNET ? 'Mainnet' : 'Testnet',
name: coin.fullName,
tokenContractAddress: coin.contractAddress.toString().toLowerCase(),
decimalPlaces: coin.decimalPlaces,
};
}

// Get the list of ERC-7984 confidential tokens from statics and format it properly
export const getFormattedErc7984Tokens = (customCoinMap = coins) =>
customCoinMap.reduce((acc: Erc7984TokenConfig[], coin) => {
if (coin instanceof Erc7984Coin) {
acc.push(getErc7984TokenConfig(coin));
}
return acc;
}, []);

export const ethGasConfigs = {
minimumGasPrice: 1000000000, // minimum gas price a user can provide (1 Gwei)
defaultGasPrice: 20000000000, // default gas price if estimation fails (20 Gwei)
Expand Down Expand Up @@ -1152,6 +1194,7 @@ type EthLikeTokenMap = {
export enum TokenTypeEnum {
ERC20 = 'erc20',
ERC721 = 'erc721',
ERC7984 = 'erc7984',
}

function getEthLikeTokenConfig(coin: EthLikeERC20Token): EthLikeTokenConfig {
Expand Down Expand Up @@ -1257,6 +1300,7 @@ export const getFormattedTokensByNetwork = (network: 'Mainnet' | 'Testnet', coin
eth: {
tokens: getFormattedErc20Tokens(coinMap).filter((token) => token.network === network),
nfts: getFormattedErc721Tokens(coinMap).filter((token) => token.network === network),
confidentialTokens: getFormattedErc7984Tokens(coinMap).filter((token) => token.network === network),
},
xlm: {
tokens: getFormattedStellarTokens(coinMap).filter((token) => token.network === network),
Expand Down Expand Up @@ -1443,13 +1487,25 @@ export const formattedAlgoTokens = getFormattedAlgoTokens();

const mainnetErc20Tokens = verifyTokens(tokens.bitcoin.eth.tokens);
const mainnetErc721Tokens = verifyTokens(tokens.bitcoin.eth.nfts);
const mainnetErc7984Tokens = verifyTokens(tokens.bitcoin.eth.confidentialTokens);
const mainnetStellarTokens = verifyTokens(tokens.bitcoin.xlm.tokens);
export const mainnetTokens = { ...mainnetErc20Tokens, ...mainnetErc721Tokens, ...mainnetStellarTokens };
export const mainnetTokens = {
...mainnetErc20Tokens,
...mainnetErc721Tokens,
...mainnetErc7984Tokens,
...mainnetStellarTokens,
};

const testnetErc20Tokens = verifyTokens(tokens.testnet.eth.tokens);
const testnetErc721Tokens = verifyTokens(tokens.testnet.eth.nfts);
const testnetErc7984Tokens = verifyTokens(tokens.testnet.eth.confidentialTokens);
const testnetStellarTokens = verifyTokens(tokens.testnet.xlm.tokens);
export const testnetTokens = { ...testnetErc20Tokens, ...testnetErc721Tokens, ...testnetStellarTokens };
export const testnetTokens = {
...testnetErc20Tokens,
...testnetErc721Tokens,
...testnetErc7984Tokens,
...testnetStellarTokens,
};

/**
* Get formatted token configuration for a single coin
Expand All @@ -1459,6 +1515,8 @@ export const testnetTokens = { ...testnetErc20Tokens, ...testnetErc721Tokens, ..
export function getFormattedTokenConfigForCoin(coin: Readonly<BaseCoin>): TokenConfig | undefined {
if (coin instanceof Erc20Coin) {
return getErc20TokenConfig(coin);
} else if (coin instanceof Erc7984Coin) {
return getErc7984TokenConfig(coin);
} else if (coin instanceof StellarCoin) {
return getStellarTokenConfig(coin);
} else if (coin instanceof OfcCoin) {
Expand Down
Loading