Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add: tests and fix typos from manual edits #96

Merged
merged 2 commits into from
Oct 15, 2023
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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ jobs:
- run: npm ci
- run: npm run build --if-present
- run: npm test
- run: cd core/tokenRegistry && npx ts-node src/scripts/checkForeignAssetsConfig.ts
- run: cd core/tokenRegistry && npx ts-node src/scripts/checkForeignAssetConfig.ts
4 changes: 3 additions & 1 deletion core/definitions/src/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ export abstract class ChainContext<P extends PlatformName> {
}

// Get the number of decimals for a token
async getDecimals(token: NativeAddress<P> | UniversalAddress | "native"): Promise<bigint> {
async getDecimals(
token: NativeAddress<P> | UniversalAddress | "native",
): Promise<bigint> {
return this.platform.getDecimals(this.chain, this.getRpc(), token);
}

Expand Down
31 changes: 31 additions & 0 deletions core/tokenRegistry/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
}
}
});
})
});
97 changes: 68 additions & 29 deletions core/tokenRegistry/src/foreignAssets.ts
Original file line number Diff line number Diff line change
@@ -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);
}
};
Expand All @@ -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);
Expand All @@ -24,49 +24,71 @@ 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 {
const foreignId = await wh.getWrappedAsset(chain, 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(
Expand All @@ -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}`,
);
Expand All @@ -89,44 +113,59 @@ 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 = {
...suggestedUpdates,
[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];
}
};
35 changes: 17 additions & 18 deletions core/tokenRegistry/src/scripts/checkForeignAssetConfig.ts
Original file line number Diff line number Diff line change
@@ -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);
}
};
Expand All @@ -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);
})();
26 changes: 13 additions & 13 deletions core/tokenRegistry/src/scripts/updateForeignAssetConfig.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
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 { 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;

/**
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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);
})();
await checkEnvConfig("Testnet", TESTNET_TOKENS);
await checkEnvConfig("Mainnet", MAINNET_TOKENS);
})();
Loading