@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
371 lines • 15.5 kB
JavaScript
/* eslint-disable no-console */
import { SystemProgram } from '@solana/web3.js';
import { expect } from 'chai';
import { ethers } from 'ethers';
import { ProtocolType } from '@hyperlane-xyz/utils';
import { TestChainName, test1, testCosmosChain, testSealevelChain, } from '../consts/testChains.js';
import { MultiProtocolProvider } from '../providers/MultiProtocolProvider.js';
import { stubMultiProtocolProvider } from '../test/multiProviderStubs.js';
import { Token } from './Token.js';
import { TokenStandard } from './TokenStandard.js';
// null values represent TODOs here, ideally all standards should be tested
const STANDARD_TO_TOKEN = {
// EVM
[]: {
chainName: TestChainName.test1,
standard: TokenStandard.ERC20,
addressOrDenom: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
decimals: 6,
symbol: 'USDC',
name: 'USDC',
},
[]: null,
[]: Token.FromChainMetadataNativeToken(test1),
[]: {
chainName: TestChainName.test2,
standard: TokenStandard.EvmHypNative,
addressOrDenom: '0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4',
decimals: 18,
symbol: 'INJ',
name: 'Injective Coin',
},
[]: {
chainName: TestChainName.test3,
standard: TokenStandard.EvmHypCollateral,
addressOrDenom: '0x31b5234A896FbC4b3e2F7237592D054716762131',
collateralAddressOrDenom: '0x64544969ed7ebf5f083679233325356ebe738930',
decimals: 18,
symbol: 'USDC',
name: 'USDC',
},
[]: {
chainName: TestChainName.test3,
standard: TokenStandard.EvmHypRebaseCollateral,
addressOrDenom: '0x31b5234A896FbC4b3e2F7237592D054716762131',
collateralAddressOrDenom: '0x64544969ed7ebf5f083679233325356ebe738930',
decimals: 18,
symbol: 'USDC',
name: 'USDC',
},
[]: {
chainName: TestChainName.test3,
standard: TokenStandard.EvmHypOwnerCollateral,
addressOrDenom: '0x31b5234A896FbC4b3e2F7237592D054716762131',
collateralAddressOrDenom: '0x64544969ed7ebf5f083679233325356ebe738930',
decimals: 18,
symbol: 'USDC',
name: 'USDC',
},
[]: {
chainName: TestChainName.test3,
standard: TokenStandard.EvmHypCollateralFiat,
addressOrDenom: '0x31b5234A896FbC4b3e2F7237592D054716762131',
collateralAddressOrDenom: '0x64544969ed7ebf5f083679233325356ebe738930',
decimals: 18,
symbol: 'USDC',
name: 'USDC',
},
[]: {
chainName: TestChainName.test2,
standard: TokenStandard.EvmHypSynthetic,
addressOrDenom: '0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147',
decimals: 6,
symbol: 'USDC',
name: 'USDC',
},
[]: {
chainName: TestChainName.test2,
standard: TokenStandard.EvmHypSyntheticRebase,
addressOrDenom: '0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147',
decimals: 6,
symbol: 'USDC',
name: 'USDC',
},
[]: {
chainName: TestChainName.test2,
standard: TokenStandard.EvmHypXERC20,
addressOrDenom: '0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147',
decimals: 6,
symbol: 'USDC',
name: 'USDC',
},
[]: {
chainName: TestChainName.test2,
standard: TokenStandard.EvmHypXERC20Lockbox,
addressOrDenom: '0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147',
decimals: 6,
symbol: 'USDC',
name: 'USDC',
},
[]: {
chainName: TestChainName.test2,
standard: TokenStandard.EvmHypVSXERC20,
addressOrDenom: '0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147',
decimals: 6,
symbol: 'USDC',
name: 'USDC',
},
[]: {
chainName: TestChainName.test2,
standard: TokenStandard.EvmHypVSXERC20Lockbox,
addressOrDenom: '0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147',
decimals: 6,
symbol: 'USDC',
name: 'USDC',
},
[]: {
chainName: TestChainName.test2,
standard: TokenStandard.EvmM0PortalLite,
addressOrDenom: '0x36f586A30502AE3afb555b8aA4dCc05d233c2ecE', // Portal address
collateralAddressOrDenom: '0xaca92e438df0b2401ff60da7e4337b687a2435da', // mUSD token address
decimals: 6,
symbol: 'mUSD',
name: 'MetaMask USD',
},
[]: null,
[]: null,
// Sealevel
[]: {
chainName: testSealevelChain.name,
standard: TokenStandard.SealevelSpl,
addressOrDenom: 'So11111111111111111111111111111111111111112',
decimals: 9,
symbol: 'Wrapped SOL',
name: 'SOL',
},
[]: {
chainName: testSealevelChain.name,
standard: TokenStandard.SealevelSpl2022,
addressOrDenom: '21zHSATJqhNkcpoNkhFzPJW9LARSmoinLEeDtdygGuWh',
decimals: 6,
symbol: 'SOLMAX',
name: 'Solana Maxi',
},
[]: Token.FromChainMetadataNativeToken(testSealevelChain),
[]: {
chainName: testSealevelChain.name,
standard: TokenStandard.SealevelHypNative,
addressOrDenom: '4UMNyNWW75zo69hxoJaRX5iXNUa5FdRPZZa9vDVCiESg',
decimals: 9,
symbol: 'SOL',
name: 'SOL',
},
[]: {
chainName: testSealevelChain.name,
standard: TokenStandard.SealevelHypCollateral,
addressOrDenom: 'Fefw54S6NDdwNbPngPePvW4tiFTFQDT7gBPvFoDFeGqg',
collateralAddressOrDenom: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
decimals: 6,
symbol: 'USDC',
name: 'USDC',
},
[]: {
chainName: testSealevelChain.name,
standard: TokenStandard.SealevelHypSynthetic,
addressOrDenom: 'GLpdg3jt6w4eVYiCMhokVZ4mX6hmRvPhcL5RoCjzGr5k',
collateralAddressOrDenom: '8SuhHnSEogAN2udZsoychjTafnaGgM9MCidYZEP8vuVY',
decimals: 9,
symbol: 'SOL',
name: 'SOL',
},
// Cosmos
[]: null,
[]: null,
[]: Token.FromChainMetadataNativeToken(testCosmosChain),
[]: {
chainName: testCosmosChain.name,
standard: TokenStandard.CosmosIbc,
addressOrDenom: 'ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7',
decimals: 6,
symbol: 'TIA',
name: 'TIA',
},
[]: null,
[]: {
chainName: testCosmosChain.name,
standard: TokenStandard.CWNative,
addressOrDenom: 'ibc/5751B8BCDA688FD0A8EC0B292EEF1CDEAB4B766B63EC632778B196D317C40C3A',
decimals: 6,
symbol: 'ASTRO',
name: 'ASTRO',
},
[]: null,
[]: {
chainName: testCosmosChain.name,
standard: TokenStandard.CwHypNative,
addressOrDenom: 'inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k',
igpTokenAddressOrDenom: 'inj',
decimals: 18,
symbol: 'INJ',
name: 'Injective Coin',
},
[]: {
chainName: testCosmosChain.name,
standard: TokenStandard.CwHypCollateral,
addressOrDenom: 'neutron1jyyjd3x0jhgswgm6nnctxvzla8ypx50tew3ayxxwkrjfxhvje6kqzvzudq',
collateralAddressOrDenom: 'ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7',
decimals: 6,
symbol: 'TIA.n',
name: 'TIA.n',
},
[]: null,
[]: null,
[]: null,
//TODO: check this and manage it.
[]: null,
[]: null,
[]: null,
[]: null,
[]: null,
[]: null,
[]: null,
[]: null,
[]: null,
[]: null,
[]: null,
};
const PROTOCOL_TO_ADDRESS_FOR_BALANCE_CHECK = {
[]: ethers.constants.AddressZero,
[]: 'neutron13we0myxwzlpx8l5ark8elw5gj5d59dl6cjkzmt80c5q5cv5rt54qvzkv2a',
[]: 'EK6cs8jNnu2d9pmKTGf1Bvre9oW2xNhcCKNdLKx6t74w',
};
const STANDARD_TO_ADDRESS_FOR_BALANCE_CHECK = {
[]: 'HVSZJ2juJnMxd6yCNarTL56YmgUqzfUiwM7y7LtTXKHR',
[]: 'inj1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3lj7tt0',
};
describe('Token', () => {
for (const tokenArgs of Object.values(STANDARD_TO_TOKEN)) {
if (!tokenArgs)
continue;
it(`Handles ${tokenArgs.standard} standard`, async () => {
const multiProvider = MultiProtocolProvider.createTestMultiProtocolProvider();
// A placeholder mailbox address for the sealevel chain
multiProvider.metadata[testSealevelChain.name].mailbox =
SystemProgram.programId.toBase58();
console.debug('Testing token standard', tokenArgs.standard);
const token = new Token(tokenArgs);
expect(token.standard).to.eql(tokenArgs.standard);
const adapter = token.getAdapter(multiProvider);
const balanceCheckAddress = STANDARD_TO_ADDRESS_FOR_BALANCE_CHECK[token.standard] ??
PROTOCOL_TO_ADDRESS_FOR_BALANCE_CHECK[token.protocol];
if (!balanceCheckAddress)
throw new Error(`No address for standard ${tokenArgs.standard}`);
const sandbox = stubMultiProtocolProvider(multiProvider);
// @ts-ignore simple extra mock for the Ethers V5 token contract call
adapter.contract = {
balanceOf: async () => '100',
};
// @ts-ignore
adapter.getWrappedTokenAdapter = () => ({
getBalance: async () => 100n,
});
const balance = await adapter.getBalance(balanceCheckAddress);
expect(typeof balance).to.eql('bigint');
sandbox.restore();
});
}
describe('isFungibleWith', () => {
const evmNativeToken = Token.FromChainMetadataNativeToken(test1);
const evmHypNativeToken = new Token({
chainName: TestChainName.test1,
standard: TokenStandard.EvmHypNative,
addressOrDenom: '0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4',
decimals: 18,
symbol: 'ETH',
name: 'Ether',
});
const evmHypNativeToken2 = new Token({
chainName: TestChainName.test1,
standard: TokenStandard.EvmHypNative,
addressOrDenom: '0x41b5234A896FbC4b3e2F7237592D054716762131',
decimals: 18,
symbol: 'ETH',
name: 'Ether',
});
const evmHypCollateralToken = new Token({
chainName: TestChainName.test1,
standard: TokenStandard.EvmHypCollateral,
addressOrDenom: '0x31b5234A896FbC4b3e2F7237592D054716762131',
collateralAddressOrDenom: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
decimals: 6,
symbol: 'USDC',
name: 'USD Coin',
});
const evmHypSyntheticToken = new Token({
chainName: TestChainName.test1,
standard: TokenStandard.EvmHypSynthetic,
addressOrDenom: '0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147',
decimals: 6,
symbol: 'USDC',
name: 'USD Coin',
});
const cosmosIbcToken = new Token({
chainName: testCosmosChain.name,
standard: TokenStandard.CosmosIbc,
addressOrDenom: 'ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7',
decimals: 6,
symbol: 'TIA',
name: 'TIA',
});
const cosmosNativeToken = new Token({
chainName: testCosmosChain.name,
standard: TokenStandard.CosmosNative,
addressOrDenom: 'ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7',
decimals: 6,
symbol: 'TIA',
name: 'TIA',
});
it('returns false for undefined token', () => {
expect(evmHypCollateralToken.isFungibleWith(undefined)).to.be.false;
});
it('returns false for tokens on different chains', () => {
const differentChainToken = new Token({
chainName: TestChainName.test2,
standard: TokenStandard.ERC20,
addressOrDenom: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
decimals: 6,
symbol: 'USDC',
name: 'USD Coin',
});
expect(evmHypCollateralToken.isFungibleWith(differentChainToken)).to.be
.false;
});
it('returns true for identical tokens', () => {
expect(evmHypCollateralToken.isFungibleWith(evmHypCollateralToken)).to.be
.true;
});
it('returns false for collateralized token and non-matching token', () => {
expect(evmHypCollateralToken.isFungibleWith(evmHypSyntheticToken)).to.be
.false;
});
it('returns false for non-IBC tokens with different standards', () => {
expect(evmHypCollateralToken.isFungibleWith(cosmosNativeToken)).to.be
.false;
});
it('returns false for HypNative token and different token standards', () => {
expect(evmHypNativeToken.isFungibleWith(evmHypCollateralToken)).to.be
.false;
});
it('returns true for collateralized token without collateral address and native token', () => {
expect(evmHypNativeToken2.isFungibleWith(evmNativeToken)).to.be.true;
});
it('returns true for collateralized token without collateral address and HypNative token', () => {
expect(evmHypNativeToken2.isFungibleWith(evmHypNativeToken)).to.be.true;
});
it('returns true for IBC token and matching native token', () => {
expect(cosmosIbcToken.isFungibleWith(cosmosNativeToken)).to.be.true;
});
it('returns false for IBC token and non-matching native token', () => {
const nonMatchingNativeToken = new Token({
chainName: testCosmosChain.name,
standard: TokenStandard.CosmosNative,
addressOrDenom: 'different_denom',
decimals: 6,
symbol: 'OTHER',
name: 'Other Token',
});
expect(cosmosIbcToken.isFungibleWith(nonMatchingNativeToken)).to.be.false;
});
});
});
//# sourceMappingURL=Token.test.js.map