Create new CollateralToCollateral route type Refactor route computation into separate file Add unit tests for route computationpull/51/head
parent
0faea83ca8
commit
1014966351
@ -0,0 +1,16 @@ |
|||||||
|
const nextJest = require('next/jest') |
||||||
|
|
||||||
|
const createJestConfig = nextJest({ |
||||||
|
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
|
||||||
|
dir: './', |
||||||
|
}) |
||||||
|
|
||||||
|
// Add any custom config to be passed to Jest
|
||||||
|
/** @type {import('jest').Config} */ |
||||||
|
const customJestConfig = { |
||||||
|
// Add more setup options before each test is run
|
||||||
|
// setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
||||||
|
} |
||||||
|
|
||||||
|
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
|
||||||
|
module.exports = createJestConfig(customJestConfig) |
@ -0,0 +1,252 @@ |
|||||||
|
import { TokenType } from '@hyperlane-xyz/hyperlane-token'; |
||||||
|
|
||||||
|
import { SOL_ZERO_ADDRESS } from '../../../consts/values'; |
||||||
|
|
||||||
|
import { computeTokenRoutes } from './fetch'; |
||||||
|
|
||||||
|
describe('computeTokenRoutes', () => { |
||||||
|
it('Handles empty list', () => { |
||||||
|
const routesMap = computeTokenRoutes([]); |
||||||
|
expect(routesMap).toBeTruthy(); |
||||||
|
expect(Object.values(routesMap).length).toBe(0); |
||||||
|
}); |
||||||
|
|
||||||
|
it('Handles basic 3-node route', () => { |
||||||
|
const routesMap = computeTokenRoutes([ |
||||||
|
{ |
||||||
|
type: TokenType.collateral, |
||||||
|
tokenCaip19Id: 'ethereum:5/erc20:0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', |
||||||
|
routerAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
name: 'Weth', |
||||||
|
symbol: 'WETH', |
||||||
|
decimals: 18, |
||||||
|
hypTokens: [ |
||||||
|
{ |
||||||
|
decimals: 18, |
||||||
|
tokenCaip19Id: 'ethereum:11155111/erc20:0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
}, |
||||||
|
{ |
||||||
|
decimals: 18, |
||||||
|
tokenCaip19Id: 'ethereum:44787/erc20:0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
]); |
||||||
|
expect(routesMap).toEqual({ |
||||||
|
'ethereum:5': { |
||||||
|
'ethereum:11155111': [ |
||||||
|
{ |
||||||
|
type: 'collateralToSynthetic', |
||||||
|
baseTokenCaip19Id: 'ethereum:5/erc20:0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', |
||||||
|
baseRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originCaip2Id: 'ethereum:5', |
||||||
|
originRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originDecimals: 18, |
||||||
|
destCaip2Id: 'ethereum:11155111', |
||||||
|
destRouterAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
destDecimals: 18, |
||||||
|
}, |
||||||
|
], |
||||||
|
'ethereum:44787': [ |
||||||
|
{ |
||||||
|
type: 'collateralToSynthetic', |
||||||
|
baseTokenCaip19Id: 'ethereum:5/erc20:0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', |
||||||
|
baseRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originCaip2Id: 'ethereum:5', |
||||||
|
originRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originDecimals: 18, |
||||||
|
destCaip2Id: 'ethereum:44787', |
||||||
|
destRouterAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
destDecimals: 18, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
'ethereum:11155111': { |
||||||
|
'ethereum:5': [ |
||||||
|
{ |
||||||
|
type: 'syntheticToCollateral', |
||||||
|
baseTokenCaip19Id: 'ethereum:5/erc20:0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', |
||||||
|
baseRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originCaip2Id: 'ethereum:11155111', |
||||||
|
originRouterAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
originDecimals: 18, |
||||||
|
destCaip2Id: 'ethereum:5', |
||||||
|
destRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
destDecimals: 18, |
||||||
|
}, |
||||||
|
], |
||||||
|
'ethereum:44787': [ |
||||||
|
{ |
||||||
|
type: 'syntheticToSynthetic', |
||||||
|
baseTokenCaip19Id: 'ethereum:5/erc20:0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', |
||||||
|
baseRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originCaip2Id: 'ethereum:11155111', |
||||||
|
originRouterAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
originDecimals: 18, |
||||||
|
destCaip2Id: 'ethereum:44787', |
||||||
|
destRouterAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
destDecimals: 18, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
'ethereum:44787': { |
||||||
|
'ethereum:5': [ |
||||||
|
{ |
||||||
|
type: 'syntheticToCollateral', |
||||||
|
baseTokenCaip19Id: 'ethereum:5/erc20:0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', |
||||||
|
baseRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originCaip2Id: 'ethereum:44787', |
||||||
|
originRouterAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
originDecimals: 18, |
||||||
|
destCaip2Id: 'ethereum:5', |
||||||
|
destRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
destDecimals: 18, |
||||||
|
}, |
||||||
|
], |
||||||
|
'ethereum:11155111': [ |
||||||
|
{ |
||||||
|
type: 'syntheticToSynthetic', |
||||||
|
baseTokenCaip19Id: 'ethereum:5/erc20:0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', |
||||||
|
baseRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originCaip2Id: 'ethereum:44787', |
||||||
|
originRouterAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
originDecimals: 18, |
||||||
|
destCaip2Id: 'ethereum:11155111', |
||||||
|
destRouterAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
destDecimals: 18, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('Handles multi-collateral route', () => { |
||||||
|
const routesMap = computeTokenRoutes([ |
||||||
|
{ |
||||||
|
type: TokenType.collateral, |
||||||
|
tokenCaip19Id: 'ethereum:5/erc20:0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', |
||||||
|
routerAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
name: 'Weth', |
||||||
|
symbol: 'WETH', |
||||||
|
decimals: 18, |
||||||
|
hypTokens: [ |
||||||
|
{ |
||||||
|
decimals: 18, |
||||||
|
tokenCaip19Id: 'ethereum:11155111/erc20:0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
}, |
||||||
|
{ |
||||||
|
decimals: 6, |
||||||
|
tokenCaip19Id: 'sealevel:1399811151/native:PJH5QAbxAqrrnSXfH3GHR8icua8CDFZmo97z91xmpvx', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: TokenType.native, |
||||||
|
tokenCaip19Id: `sealevel:1399811151/native:${SOL_ZERO_ADDRESS}`, |
||||||
|
routerAddress: 'PJH5QAbxAqrrnSXfH3GHR8icua8CDFZmo97z91xmpvx', |
||||||
|
name: 'Zebec', |
||||||
|
symbol: 'ZBC', |
||||||
|
decimals: 6, |
||||||
|
hypTokens: [ |
||||||
|
{ |
||||||
|
decimals: 18, |
||||||
|
tokenCaip19Id: 'ethereum:11155111/erc20:0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
}, |
||||||
|
{ |
||||||
|
decimals: 18, |
||||||
|
tokenCaip19Id: 'ethereum:5/erc20:0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
]); |
||||||
|
expect(routesMap).toEqual({ |
||||||
|
'ethereum:5': { |
||||||
|
'ethereum:11155111': [ |
||||||
|
{ |
||||||
|
type: 'collateralToSynthetic', |
||||||
|
baseTokenCaip19Id: 'ethereum:5/erc20:0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', |
||||||
|
baseRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originCaip2Id: 'ethereum:5', |
||||||
|
originRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originDecimals: 18, |
||||||
|
destCaip2Id: 'ethereum:11155111', |
||||||
|
destRouterAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
destDecimals: 18, |
||||||
|
}, |
||||||
|
], |
||||||
|
'sealevel:1399811151': [ |
||||||
|
{ |
||||||
|
type: 'collateralToCollateral', |
||||||
|
baseTokenCaip19Id: 'ethereum:5/erc20:0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', |
||||||
|
baseRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originCaip2Id: 'ethereum:5', |
||||||
|
originRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originDecimals: 18, |
||||||
|
destCaip2Id: 'sealevel:1399811151', |
||||||
|
destRouterAddress: 'PJH5QAbxAqrrnSXfH3GHR8icua8CDFZmo97z91xmpvx', |
||||||
|
destDecimals: 6, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
'ethereum:11155111': { |
||||||
|
'ethereum:5': [ |
||||||
|
{ |
||||||
|
type: 'syntheticToCollateral', |
||||||
|
baseTokenCaip19Id: 'ethereum:5/erc20:0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6', |
||||||
|
baseRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
originCaip2Id: 'ethereum:11155111', |
||||||
|
originRouterAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
originDecimals: 18, |
||||||
|
destCaip2Id: 'ethereum:5', |
||||||
|
destRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
destDecimals: 18, |
||||||
|
}, |
||||||
|
], |
||||||
|
'sealevel:1399811151': [ |
||||||
|
{ |
||||||
|
type: 'syntheticToCollateral', |
||||||
|
baseTokenCaip19Id: |
||||||
|
'sealevel:1399811151/native:00000000000000000000000000000000000000000000', |
||||||
|
baseRouterAddress: 'PJH5QAbxAqrrnSXfH3GHR8icua8CDFZmo97z91xmpvx', |
||||||
|
originCaip2Id: 'ethereum:11155111', |
||||||
|
originRouterAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
originDecimals: 18, |
||||||
|
destCaip2Id: 'sealevel:1399811151', |
||||||
|
destRouterAddress: 'PJH5QAbxAqrrnSXfH3GHR8icua8CDFZmo97z91xmpvx', |
||||||
|
destDecimals: 6, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
'sealevel:1399811151': { |
||||||
|
'ethereum:5': [ |
||||||
|
{ |
||||||
|
type: 'collateralToCollateral', |
||||||
|
baseTokenCaip19Id: |
||||||
|
'sealevel:1399811151/native:00000000000000000000000000000000000000000000', |
||||||
|
baseRouterAddress: 'PJH5QAbxAqrrnSXfH3GHR8icua8CDFZmo97z91xmpvx', |
||||||
|
originCaip2Id: 'sealevel:1399811151', |
||||||
|
originRouterAddress: 'PJH5QAbxAqrrnSXfH3GHR8icua8CDFZmo97z91xmpvx', |
||||||
|
originDecimals: 6, |
||||||
|
destCaip2Id: 'ethereum:5', |
||||||
|
destRouterAddress: '0x145de8760021c4ac6676376691b78038d3DE9097', |
||||||
|
destDecimals: 18, |
||||||
|
}, |
||||||
|
], |
||||||
|
'ethereum:11155111': [ |
||||||
|
{ |
||||||
|
type: 'collateralToSynthetic', |
||||||
|
baseTokenCaip19Id: |
||||||
|
'sealevel:1399811151/native:00000000000000000000000000000000000000000000', |
||||||
|
baseRouterAddress: 'PJH5QAbxAqrrnSXfH3GHR8icua8CDFZmo97z91xmpvx', |
||||||
|
originCaip2Id: 'sealevel:1399811151', |
||||||
|
originRouterAddress: 'PJH5QAbxAqrrnSXfH3GHR8icua8CDFZmo97z91xmpvx', |
||||||
|
originDecimals: 6, |
||||||
|
destCaip2Id: 'ethereum:11155111', |
||||||
|
destRouterAddress: '0xDcbc0faAA269Cf649AC8950838664BB7B355BD6B', |
||||||
|
destDecimals: 18, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,166 @@ |
|||||||
|
import { ProtocolType } from '@hyperlane-xyz/sdk'; |
||||||
|
|
||||||
|
import { areAddressesEqual, bytesToProtocolAddress } from '../../../utils/addresses'; |
||||||
|
import { logger } from '../../../utils/logger'; |
||||||
|
import { getCaip2Id } from '../../caip/chains'; |
||||||
|
import { |
||||||
|
getCaip19Id, |
||||||
|
getChainIdFromToken, |
||||||
|
isNonFungibleToken, |
||||||
|
parseCaip19Id, |
||||||
|
resolveAssetNamespace, |
||||||
|
} from '../../caip/tokens'; |
||||||
|
import { getMultiProvider } from '../../multiProvider'; |
||||||
|
import { AdapterFactory } from '../adapters/AdapterFactory'; |
||||||
|
import { TokenMetadata, TokenMetadataWithHypTokens } from '../types'; |
||||||
|
|
||||||
|
import { RouteType, RoutesMap } from './types'; |
||||||
|
|
||||||
|
export async function fetchRemoteHypTokens( |
||||||
|
baseToken: TokenMetadata, |
||||||
|
allTokens: TokenMetadata[], |
||||||
|
): Promise<TokenMetadataWithHypTokens> { |
||||||
|
const { |
||||||
|
symbol: baseSymbol, |
||||||
|
tokenCaip19Id: baseTokenCaip19Id, |
||||||
|
routerAddress: baseRouter, |
||||||
|
} = baseToken; |
||||||
|
const isNft = isNonFungibleToken(baseTokenCaip19Id); |
||||||
|
logger.info(`Fetching remote tokens for symbol ${baseSymbol} (${baseTokenCaip19Id})`); |
||||||
|
|
||||||
|
const baseAdapter = AdapterFactory.HypCollateralAdapterFromAddress(baseTokenCaip19Id, baseRouter); |
||||||
|
|
||||||
|
const remoteRouters = await baseAdapter.getAllRouters(); |
||||||
|
logger.info(`Router addresses found:`, remoteRouters); |
||||||
|
|
||||||
|
const multiProvider = getMultiProvider(); |
||||||
|
const hypTokens = await Promise.all( |
||||||
|
remoteRouters.map(async (router) => { |
||||||
|
const destMetadata = multiProvider.getChainMetadata(router.domain); |
||||||
|
const protocol = destMetadata.protocol || ProtocolType.Ethereum; |
||||||
|
const chainCaip2Id = getCaip2Id(protocol, multiProvider.getChainId(router.domain)); |
||||||
|
const namespace = resolveAssetNamespace(protocol, false, isNft, true); |
||||||
|
const formattedAddress = bytesToProtocolAddress(router.address, protocol); |
||||||
|
const tokenCaip19Id = getCaip19Id(chainCaip2Id, namespace, formattedAddress); |
||||||
|
if (isNft) return { tokenCaip19Id, decimals: 0 }; |
||||||
|
// Attempt to find the decimals from the token list
|
||||||
|
const routerMetadata = allTokens.find((token) => |
||||||
|
areAddressesEqual(formattedAddress, token.routerAddress), |
||||||
|
); |
||||||
|
if (routerMetadata) return { tokenCaip19Id, decimals: routerMetadata.decimals }; |
||||||
|
// Otherwise try to query the contract
|
||||||
|
const remoteAdapter = AdapterFactory.HypSyntheticTokenAdapterFromAddress( |
||||||
|
baseTokenCaip19Id, |
||||||
|
chainCaip2Id, |
||||||
|
formattedAddress, |
||||||
|
); |
||||||
|
const metadata = await remoteAdapter.getMetadata(); |
||||||
|
return { tokenCaip19Id, decimals: metadata.decimals }; |
||||||
|
}), |
||||||
|
); |
||||||
|
return { ...baseToken, hypTokens }; |
||||||
|
} |
||||||
|
|
||||||
|
// Process token list to populates routesCache with all possible token routes (e.g. router pairs)
|
||||||
|
export function computeTokenRoutes(tokens: TokenMetadataWithHypTokens[]) { |
||||||
|
const tokenRoutes: RoutesMap = {}; |
||||||
|
|
||||||
|
// Instantiate map structure
|
||||||
|
const allChainIds = getChainsFromTokens(tokens); |
||||||
|
for (const origin of allChainIds) { |
||||||
|
tokenRoutes[origin] = {}; |
||||||
|
for (const dest of allChainIds) { |
||||||
|
if (origin === dest) continue; |
||||||
|
tokenRoutes[origin][dest] = []; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Compute all possible routes, in both directions
|
||||||
|
for (const token of tokens) { |
||||||
|
for (const remoteHypToken of token.hypTokens) { |
||||||
|
const { |
||||||
|
tokenCaip19Id: baseTokenCaip19Id, |
||||||
|
routerAddress: baseRouterAddress, |
||||||
|
decimals: baseDecimals, |
||||||
|
} = token; |
||||||
|
const baseChainCaip2Id = getChainIdFromToken(baseTokenCaip19Id); |
||||||
|
const { chainCaip2Id: remoteCaip2Id, address: remoteRouterAddress } = parseCaip19Id( |
||||||
|
remoteHypToken.tokenCaip19Id, |
||||||
|
); |
||||||
|
const remoteDecimals = remoteHypToken.decimals; |
||||||
|
// Check if the token list contains the dest router address, meaning it's also a base collateral token
|
||||||
|
const isRemoteCollateral = tokensHasRouter(tokens, remoteRouterAddress); |
||||||
|
const commonRouteProps = { baseTokenCaip19Id, baseRouterAddress }; |
||||||
|
|
||||||
|
// Register a route from the base to the remote
|
||||||
|
tokenRoutes[baseChainCaip2Id][remoteCaip2Id]?.push({ |
||||||
|
type: isRemoteCollateral |
||||||
|
? RouteType.CollateralToCollateral |
||||||
|
: RouteType.CollateralToSynthetic, |
||||||
|
...commonRouteProps, |
||||||
|
originCaip2Id: baseChainCaip2Id, |
||||||
|
originRouterAddress: baseRouterAddress, |
||||||
|
originDecimals: baseDecimals, |
||||||
|
destCaip2Id: remoteCaip2Id, |
||||||
|
destRouterAddress: remoteRouterAddress, |
||||||
|
destDecimals: remoteDecimals, |
||||||
|
}); |
||||||
|
|
||||||
|
// If the remote is not a synthetic (i.e. it's a native/collateral token with it's own config)
|
||||||
|
// then stop here to avoid duplicate route entries.
|
||||||
|
if (isRemoteCollateral) continue; |
||||||
|
|
||||||
|
// Register a route back from the synthetic remote to the base
|
||||||
|
tokenRoutes[remoteCaip2Id][baseChainCaip2Id]?.push({ |
||||||
|
type: RouteType.SyntheticToCollateral, |
||||||
|
...commonRouteProps, |
||||||
|
originCaip2Id: remoteCaip2Id, |
||||||
|
originRouterAddress: remoteRouterAddress, |
||||||
|
originDecimals: remoteDecimals, |
||||||
|
destCaip2Id: baseChainCaip2Id, |
||||||
|
destRouterAddress: baseRouterAddress, |
||||||
|
destDecimals: baseDecimals, |
||||||
|
}); |
||||||
|
|
||||||
|
// Now create routes from the remote synthetic token to all other hypTokens
|
||||||
|
// This assumes the synthetics were all enrolled to connect to each other
|
||||||
|
// which is the deployer's default behavior
|
||||||
|
for (const otherHypToken of token.hypTokens) { |
||||||
|
const { chainCaip2Id: otherSynCaip2Id, address: otherHypTokenAddress } = parseCaip19Id( |
||||||
|
otherHypToken.tokenCaip19Id, |
||||||
|
); |
||||||
|
// Skip if it's same hypToken as parent loop (no route to self)
|
||||||
|
// or if if remote isn't a synthetic
|
||||||
|
if (otherHypToken === remoteHypToken || tokensHasRouter(tokens, otherHypTokenAddress)) |
||||||
|
continue; |
||||||
|
|
||||||
|
tokenRoutes[remoteCaip2Id][otherSynCaip2Id]?.push({ |
||||||
|
type: RouteType.SyntheticToSynthetic, |
||||||
|
...commonRouteProps, |
||||||
|
originCaip2Id: remoteCaip2Id, |
||||||
|
originRouterAddress: remoteRouterAddress, |
||||||
|
originDecimals: remoteDecimals, |
||||||
|
destCaip2Id: otherSynCaip2Id, |
||||||
|
destRouterAddress: otherHypTokenAddress, |
||||||
|
destDecimals: otherHypToken.decimals, |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return tokenRoutes; |
||||||
|
} |
||||||
|
|
||||||
|
function getChainsFromTokens(tokens: TokenMetadataWithHypTokens[]): ChainCaip2Id[] { |
||||||
|
const chains = new Set<ChainCaip2Id>(); |
||||||
|
for (const token of tokens) { |
||||||
|
chains.add(getChainIdFromToken(token.tokenCaip19Id)); |
||||||
|
for (const hypToken of token.hypTokens) { |
||||||
|
chains.add(getChainIdFromToken(hypToken.tokenCaip19Id)); |
||||||
|
} |
||||||
|
} |
||||||
|
return Array.from(chains); |
||||||
|
} |
||||||
|
|
||||||
|
function tokensHasRouter(tokens: TokenMetadataWithHypTokens[], router: Address) { |
||||||
|
return !!tokens.find((t) => areAddressesEqual(t.routerAddress, router)); |
||||||
|
} |
Loading…
Reference in new issue