diff --git a/core/definitions/src/chain.ts b/core/definitions/src/chain.ts index 072dc97ca..cfe34caa8 100644 --- a/core/definitions/src/chain.ts +++ b/core/definitions/src/chain.ts @@ -39,7 +39,9 @@ export abstract class ChainContext

{ } // Get the number of decimals for a token - async getDecimals(token: NativeAddress

| UniversalAddress | "native"): Promise { + async getDecimals( + token: NativeAddress

| UniversalAddress | "native", + ): Promise { return this.platform.getDecimals(this.chain, this.getRpc(), token); } diff --git a/core/tokenRegistry/__tests__/index.test.ts b/core/tokenRegistry/__tests__/index.test.ts new file mode 100644 index 000000000..258ebd712 --- /dev/null +++ b/core/tokenRegistry/__tests__/index.test.ts @@ -0,0 +1,31 @@ +import * as fs from 'fs'; +import { TokensConfig } from '../src/types'; +import { Network } from '@wormhole-foundation/connect-sdk'; + +const testnetTokens = fs.readFileSync('src/tokens/testnetTokens.json', 'utf-8'); +const TESTNET_TOKENS = JSON.parse(testnetTokens) as TokensConfig; +const mainnetTokens = fs.readFileSync('src/tokens/mainnetTokens.json', 'utf-8'); +const MAINNET_TOKENS = JSON.parse(mainnetTokens) as TokensConfig; + +const getTokens = (network: Network) => { + return network === 'Mainnet' ? MAINNET_TOKENS : TESTNET_TOKENS; +} + +describe('token config format', () => { + const networks: Network[] = ['Mainnet', 'Testnet']; + networks.forEach(network => { + const tokens = getTokens(network); + describe(`All ${network} token details are set`, () => { + for (const [chain, chainTokens] of Object.entries(tokens)) { + for (const [address, tokenConfig] of Object.entries(chainTokens)) { + test(`${chain} ${address} details are set`, () => { + expect(tokenConfig.name).toBeTruthy(); + expect(tokenConfig.symbol).toBeTruthy(); + expect(tokenConfig.decimals).toBeTruthy(); + expect(tokenConfig.nativeChain).toBe(chain); + }); + } + } + }); + }) +}); diff --git a/core/tokenRegistry/src/foreignAssets.ts b/core/tokenRegistry/src/foreignAssets.ts index 9af7c2749..643391277 100644 --- a/core/tokenRegistry/src/foreignAssets.ts +++ b/core/tokenRegistry/src/foreignAssets.ts @@ -1,7 +1,7 @@ // patch out annoying logs const info = console.info; console.info = function (x: any, ...rest: any) { - if (x !== 'secp256k1 unavailable, reverting to browser version') { + if (x !== "secp256k1 unavailable, reverting to browser version") { info(x, ...rest); } }; @@ -11,7 +11,7 @@ console.warn = function (x: any, ...rest: any) { !x .toString() .startsWith( - 'Error: Error: RPC Validation Error: The response returned from RPC server does not match the TypeScript definition. This is likely because the SDK version is not compatible with the RPC server.', + "Error: Error: RPC Validation Error: The response returned from RPC server does not match the TypeScript definition. This is likely because the SDK version is not compatible with the RPC server.", ) ) { warn(x, ...rest); @@ -24,21 +24,36 @@ import { TokenId, Wormhole, toNative } from "@wormhole-foundation/connect-sdk"; // TODO: Question: How do we handle if a user tries to perform an action for a chain/platform which isn't installed?? // const supportedPlatforms: PlatformName[] = ['Evm', 'Solana']; -const supportedChains: ChainName[] = ['Ethereum', 'Polygon', 'Celo', 'Moonbeam', 'Fantom', 'Avalanche', 'Bsc', 'Optimism', 'Arbitrum', 'Solana'] +const supportedChains: ChainName[] = [ + "Ethereum", + "Polygon", + "Celo", + "Moonbeam", + "Fantom", + "Avalanche", + "Bsc", + "Optimism", + "Arbitrum", + "Solana", +]; export const isSupportedChain = (chain: ChainName) => { return supportedChains.includes(chain); -} +}; export const createTokenId = (chain: ChainName, address: string) => { if (!isSupportedChain(chain)) return; return { chain, address: toNative(chain, address), - } -} + }; +}; -export const getForeignAddress = async (wh: Wormhole, chain: ChainName, tokenId: TokenId) => { +export const getForeignAddress = async ( + wh: Wormhole, + chain: ChainName, + tokenId: TokenId, +) => { if (!isSupportedChain(chain)) return; let foreignAddress: string | null = null; try { @@ -46,27 +61,34 @@ export const getForeignAddress = async (wh: Wormhole, chain: ChainName, tokenId: foreignAddress = foreignId.address.toString(); } catch (e: any) { if ( - e?.message === '3104 RPC not configured' || - e?.message === 'wormchain RPC not configured' + e?.message === "3104 RPC not configured" || + e?.message === "wormchain RPC not configured" ) { // do not throw on wormchain errors - } else if (e?.message.includes('is not a wrapped asset')) { + } else if (e?.message.includes("is not a wrapped asset")) { // do not throw if wrapped asset does not exist } else { // log error but keep going - console.error(e) + console.error(e); } } return foreignAddress; -} +}; -export const getForeignAssetsData = async (wh: Wormhole, chain: ChainName, tokenId: TokenId | undefined, foreignAssetsCache: ForeignAssetsCache | undefined) => { +export const getForeignAssetsData = async ( + wh: Wormhole, + chain: ChainName, + tokenId: TokenId | undefined, + foreignAssetsCache: ForeignAssetsCache | undefined, +) => { if (!tokenId) return; let updates: ForeignAssetsCache = {}; for (const foreignChain of chains) { const isSupported = isSupportedChain(foreignChain); if (foreignChain !== tokenId.chain && isSupported) { - const configForeignAddress = foreignAssetsCache ? foreignAssetsCache[foreignChain] : undefined; + const configForeignAddress = foreignAssetsCache + ? foreignAssetsCache[foreignChain] + : undefined; const foreignAddress = await getForeignAddress(wh, foreignChain, tokenId); if (foreignAddress) { const foreignDecimals = await wh.getDecimals( @@ -78,7 +100,9 @@ export const getForeignAssetsData = async (wh: Wormhole, chain: ChainName, token throw new Error( `❌ Invalid foreign address detected! Env: ${wh.conf.network}, Existing Address: ${configForeignAddress.address}, Chain: ${chain}, Expected: ${foreignAddress}, Received: ${configForeignAddress.address}`, ); - } else if (configForeignAddress.decimals !== Number(foreignDecimals)) { + } else if ( + configForeignAddress.decimals !== Number(foreignDecimals) + ) { throw new Error( `❌ Invalid foreign decimals detected! Env: ${wh.conf.network}, Existing Address: ${configForeignAddress.address}, Chain: ${chain}, Expected: ${foreignDecimals}, Received: ${configForeignAddress.decimals}`, ); @@ -89,25 +113,33 @@ export const getForeignAssetsData = async (wh: Wormhole, chain: ChainName, token const update = { [foreignChain]: { address: foreignAddress, - decimals: Number(foreignDecimals) - } - } - updates = { ...updates, ...update } + decimals: Number(foreignDecimals), + }, + }; + updates = { ...updates, ...update }; } } } } return updates; -} +}; -export const getSuggestedUpdates = async (wh: Wormhole, tokensConfig: TokensConfig) => { +export const getSuggestedUpdates = async ( + wh: Wormhole, + tokensConfig: TokensConfig, +) => { let suggestedUpdates: TokensConfig = {}; let numUpdates = 0; for (const [chain, chainTokensConfig] of Object.entries(tokensConfig)) { for (const [token, config] of Object.entries(chainTokensConfig)) { const tokenId = createTokenId(chain as ChainName, token); - const updates = await getForeignAssetsData(wh, chain as ChainName, tokenId, config.foreignAssets); + const updates = await getForeignAssetsData( + wh, + chain as ChainName, + tokenId, + config.foreignAssets, + ); if (updates && Object.values(updates).length > 0) { numUpdates += Object.values(updates).length; suggestedUpdates = { @@ -115,18 +147,25 @@ export const getSuggestedUpdates = async (wh: Wormhole, tokensConfig: TokensConf [chain]: { ...(suggestedUpdates[chain as ChainName] || {}), [token]: { - ...(suggestedUpdates[chain as ChainName] ? suggestedUpdates[chain as ChainName]![token] || {} : {}), + ...(suggestedUpdates[chain as ChainName] + ? suggestedUpdates[chain as ChainName]![token] || {} + : {}), foreignAssets: { - ...(suggestedUpdates[chain as ChainName] ? suggestedUpdates[chain as ChainName]![token] ? suggestedUpdates[chain as ChainName]![token]!.foreignAssets : {} : {}), + ...(suggestedUpdates[chain as ChainName] + ? suggestedUpdates[chain as ChainName]![token] + ? suggestedUpdates[chain as ChainName]![token]! + .foreignAssets + : {} + : {}), ...updates, - } - } - } - } + }, + }, + }, + }; } } } // console.log(`${numUpdates} updates available`); // console.log(JSON.stringify(suggestedUpdates, null, 4)); return [numUpdates, suggestedUpdates]; -} +}; diff --git a/core/tokenRegistry/src/scripts/checkForeignAssetConfig.ts b/core/tokenRegistry/src/scripts/checkForeignAssetConfig.ts index 72ba443f9..693f1ccc9 100644 --- a/core/tokenRegistry/src/scripts/checkForeignAssetConfig.ts +++ b/core/tokenRegistry/src/scripts/checkForeignAssetConfig.ts @@ -1,7 +1,7 @@ // patch out annoying logs const info = console.info; console.info = function (x: any, ...rest: any) { - if (x !== 'secp256k1 unavailable, reverting to browser version') { + if (x !== "secp256k1 unavailable, reverting to browser version") { info(x, ...rest); } }; @@ -11,47 +11,46 @@ console.warn = function (x: any, ...rest: any) { !x .toString() .startsWith( - 'Error: Error: RPC Validation Error: The response returned from RPC server does not match the TypeScript definition. This is likely because the SDK version is not compatible with the RPC server.', + "Error: Error: RPC Validation Error: The response returned from RPC server does not match the TypeScript definition. This is likely because the SDK version is not compatible with the RPC server.", ) ) { warn(x, ...rest); } }; -import * as fs from 'fs'; +import * as fs from "fs"; import { Network } from "@wormhole-foundation/sdk-base"; import { Wormhole } from "@wormhole-foundation/connect-sdk"; import { EvmPlatform } from "@wormhole-foundation/connect-sdk-evm"; import { SolanaPlatform } from "@wormhole-foundation/connect-sdk-solana"; -import { getSuggestedUpdates } from '../foreignAssets'; +import { getSuggestedUpdates } from "../foreignAssets"; import { TokensConfig } from "../types"; -const testnetTokens = fs.readFileSync('src/tokens/testnetTokens.json', 'utf-8'); +const testnetTokens = fs.readFileSync("src/tokens/testnetTokens.json", "utf-8"); const TESTNET_TOKENS = JSON.parse(testnetTokens) as TokensConfig; -const mainnetTokens = fs.readFileSync('src/tokens/mainnetTokens.json', 'utf-8'); +const mainnetTokens = fs.readFileSync("src/tokens/mainnetTokens.json", "utf-8"); const MAINNET_TOKENS = JSON.parse(mainnetTokens) as TokensConfig; // warning: be careful optimizing the RPC calls in this script, you may 429 yourself // slow and steady, or something like that -const checkEnvConfig = async ( - env: Network, - tokensConfig: TokensConfig, -) => { +const checkEnvConfig = async (env: Network, tokensConfig: TokensConfig) => { const wh = new Wormhole(env, [EvmPlatform, SolanaPlatform]); - const [numUpdates, suggestedUpdates] = await getSuggestedUpdates(wh, tokensConfig); - if (numUpdates as number > 0) { + const [numUpdates, suggestedUpdates] = await getSuggestedUpdates( + wh, + tokensConfig, + ); + if ((numUpdates as number) > 0) { console.log(` ${numUpdates} updates available. To update, run:\n - npm run updateForeignAssets` - ); + npm run updateForeignAssets`); console.log(JSON.stringify(suggestedUpdates, null, 4)); } else { - console.log('Up to date') + console.log("Up to date"); } -} +}; (async () => { - await checkEnvConfig('Testnet', TESTNET_TOKENS); - await checkEnvConfig('Mainnet', MAINNET_TOKENS); + await checkEnvConfig("Testnet", TESTNET_TOKENS); + await checkEnvConfig("Mainnet", MAINNET_TOKENS); })(); diff --git a/core/tokenRegistry/src/scripts/updateForeignAssetConfig.ts b/core/tokenRegistry/src/scripts/updateForeignAssetConfig.ts index e446fca92..9c51d4391 100644 --- a/core/tokenRegistry/src/scripts/updateForeignAssetConfig.ts +++ b/core/tokenRegistry/src/scripts/updateForeignAssetConfig.ts @@ -1,4 +1,4 @@ -import * as fs from 'fs'; +import * as fs from "fs"; import { Network } from "@wormhole-foundation/sdk-base"; import { Wormhole } from "@wormhole-foundation/connect-sdk"; import { EvmPlatform } from "@wormhole-foundation/connect-sdk-evm"; @@ -6,9 +6,9 @@ import { SolanaPlatform } from "@wormhole-foundation/connect-sdk-solana"; import { getSuggestedUpdates } from "../foreignAssets"; import { TokensConfig } from "../types"; -const testnetTokens = fs.readFileSync('src/tokens/testnetTokens.json', 'utf-8'); +const testnetTokens = fs.readFileSync("src/tokens/testnetTokens.json", "utf-8"); const TESTNET_TOKENS = JSON.parse(testnetTokens) as TokensConfig; -const mainnetTokens = fs.readFileSync('src/tokens/mainnetTokens.json', 'utf-8'); +const mainnetTokens = fs.readFileSync("src/tokens/mainnetTokens.json", "utf-8"); const MAINNET_TOKENS = JSON.parse(mainnetTokens) as TokensConfig; /** @@ -17,7 +17,7 @@ const MAINNET_TOKENS = JSON.parse(mainnetTokens) as TokensConfig; * @returns {boolean} */ export function isObject(item: any) { - return (item && typeof item === 'object' && !Array.isArray(item)); + return item && typeof item === "object" && !Array.isArray(item); } /** @@ -45,20 +45,20 @@ export function mergeDeep(target: any, ...sources: any) { // warning: be careful optimizing the RPC calls in this script, you may 429 yourself // slow and steady, or something like that -const checkEnvConfig = async ( - env: Network, - tokensConfig: TokensConfig, -) => { +const checkEnvConfig = async (env: Network, tokensConfig: TokensConfig) => { const wh = new Wormhole(env, [EvmPlatform, SolanaPlatform]); const data = await getSuggestedUpdates(wh, tokensConfig); const suggestedUpdates = data[1] as TokensConfig; const newConfig = mergeDeep(tokensConfig, suggestedUpdates); - const filePath = env === 'Mainnet' ? 'src/tokens/mainnetTokens.json' : 'src/tokens/testnetTokens.json'; + const filePath = + env === "Mainnet" + ? "src/tokens/mainnetTokens.json" + : "src/tokens/testnetTokens.json"; fs.writeFileSync(filePath, JSON.stringify(newConfig, null, 2)); -} +}; (async () => { - await checkEnvConfig('Testnet', TESTNET_TOKENS); - await checkEnvConfig('Mainnet', MAINNET_TOKENS); -})(); \ No newline at end of file + await checkEnvConfig("Testnet", TESTNET_TOKENS); + await checkEnvConfig("Mainnet", MAINNET_TOKENS); +})(); diff --git a/core/tokenRegistry/src/tokens/mainnetTokens.json b/core/tokenRegistry/src/tokens/mainnetTokens.json index 41741e58c..43fa1a776 100644 --- a/core/tokenRegistry/src/tokens/mainnetTokens.json +++ b/core/tokenRegistry/src/tokens/mainnetTokens.json @@ -1,9 +1,10 @@ { "Ethereum": { "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0": { - "name:": "wstETH", + "name": "wstETH", "symbol": "wstETH", "nativeChain": "Ethereum", + "decimals": 18, "foreignAssets": { "Arbitrum": { "address": "0xf2717122Dfdbe988ae811E7eFB157aAa07Ff9D0F", @@ -22,7 +23,7 @@ "0x18084fbA666a33d37592fA2633fD49a74DD93a88": { "name": "tBTC", "symbol": "tBTC", - "nativeChain": "ethereum", + "nativeChain": "Ethereum", "decimals": 18, "foreignAssets": { "Polygon": { @@ -119,7 +120,7 @@ "name": "USDC (Ethereum)", "symbol": "USDC", "nativeChain": "Ethereum", - "deimals": 6, + "decimals": 6, "foreignAssets": { "Bsc": { "address": "0xB04906e95AB5D797aDA81508115611fee694c2b3", @@ -175,7 +176,7 @@ "name": "WBTC", "symbol": "WBTC", "nativeChain": "Ethereum", - "deimals": 8, + "decimals": 8, "foreignAssets": { "Bsc": { "address": "0x43359676E1A3F9FbB5de095333f8e9c1B46dFA44", @@ -231,7 +232,7 @@ "name": "USDT", "symbol": "USDT", "nativeChain": "Ethereum", - "deimals": 6, + "decimals": 6, "foreignAssets": { "Bsc": { "address": "0x524bC91Dc82d6b90EF29F76A3ECAaBAffFD490Bc", @@ -283,7 +284,7 @@ "name": "DAI", "symbol": "DAI", "nativeChain": "Ethereum", - "deimals": 18, + "decimals": 18, "foreignAssets": { "Bsc": { "address": "0x3413a030EF81a3dD5a302F4B4D11d911e12ed337", @@ -335,7 +336,7 @@ "name": "BUSD", "symbol": "BUSD", "nativeChain": "Ethereum", - "deimals": 18, + "decimals": 18, "foreignAssets": { "Bsc": { "address": "0x035de3679E692C471072d1A09bEb9298fBB2BD31", @@ -1344,4 +1345,4 @@ } } } -} \ No newline at end of file +} diff --git a/core/tokenRegistry/src/tokens/testnetTokens.json b/core/tokenRegistry/src/tokens/testnetTokens.json index 3e50e0266..f3b1db5e4 100644 --- a/core/tokenRegistry/src/tokens/testnetTokens.json +++ b/core/tokenRegistry/src/tokens/testnetTokens.json @@ -1013,4 +1013,4 @@ } } } -} \ No newline at end of file +} diff --git a/core/tokenRegistry/src/types.ts b/core/tokenRegistry/src/types.ts index ad2e9ee88..3b6e777d5 100644 --- a/core/tokenRegistry/src/types.ts +++ b/core/tokenRegistry/src/types.ts @@ -14,7 +14,7 @@ export type TokenConfig = { nativeChain: ChainName; decimals: number; foreignAssets?: ForeignAssetsCache; -} +}; export type TokensConfig = { - [chain in ChainName]?: { [key: string]: TokenConfig } + [chain in ChainName]?: { [key: string]: TokenConfig }; }; diff --git a/platforms/cosmwasm/src/platformUtils.ts b/platforms/cosmwasm/src/platformUtils.ts index d5e251e02..33a28cb29 100644 --- a/platforms/cosmwasm/src/platformUtils.ts +++ b/platforms/cosmwasm/src/platformUtils.ts @@ -55,17 +55,13 @@ export module CosmwasmUtils { export async function getDecimals( chain: ChainName, rpc: CosmWasmClient, - token: UniversalOrNative<'Cosmwasm'> | "native", + token: UniversalOrNative<"Cosmwasm"> | "native", ): Promise { - if (token === "native") - return nativeDecimals(CosmwasmPlatform.platform); - - const { decimals } = await rpc.queryContractSmart( - token.toString(), - { - token_info: {}, - }, - ); + if (token === "native") return nativeDecimals(CosmwasmPlatform.platform); + + const { decimals } = await rpc.queryContractSmart(token.toString(), { + token_info: {}, + }); return decimals; } @@ -73,7 +69,7 @@ export module CosmwasmUtils { chain: ChainName, rpc: CosmWasmClient, walletAddress: string, - token: UniversalOrNative<'Cosmwasm'> | "native", + token: UniversalOrNative<"Cosmwasm"> | "native", ): Promise { if (token === "native") { const { amount } = await rpc.getBalance( @@ -83,10 +79,7 @@ export module CosmwasmUtils { return BigInt(amount); } - const { amount } = await rpc.getBalance( - walletAddress, - token.toString(), - ); + const { amount } = await rpc.getBalance(walletAddress, token.toString()); return BigInt(amount); }