From fc531398245bab58ac83f74b33844526ecfc8d28 Mon Sep 17 00:00:00 2001 From: Shunji Zhan Date: Tue, 26 Mar 2024 16:37:48 +0800 Subject: [PATCH] added tests for homa auto route --- .../configs/{acala-fork.yml => acala.yml} | 2 +- src/__tests__/route.test.ts | 94 +++++++++++++++++++ src/__tests__/shouldRoute.test.ts | 30 +++--- src/__tests__/testUtils.ts | 8 ++ src/api/homa.ts | 2 +- src/consts.ts | 4 + src/middlewares/router.ts | 4 +- src/utils/route.ts | 2 +- 8 files changed, 126 insertions(+), 20 deletions(-) rename src/__tests__/configs/{acala-fork.yml => acala.yml} (98%) diff --git a/src/__tests__/configs/acala-fork.yml b/src/__tests__/configs/acala.yml similarity index 98% rename from src/__tests__/configs/acala-fork.yml rename to src/__tests__/configs/acala.yml index 4807865..7aa2d8a 100644 --- a/src/__tests__/configs/acala-fork.yml +++ b/src/__tests__/configs/acala.yml @@ -1,5 +1,5 @@ endpoint: - - wss://crosschain-dev.polkawallet.io:9915 + - wss://acala-rpc.aca-api.network mock-signature-host: true # block: ${env.ACALA_BLOCK_NUMBER} db: ./db.sqlite diff --git a/src/__tests__/route.test.ts b/src/__tests__/route.test.ts index 62abe5a..dda00dd 100644 --- a/src/__tests__/route.test.ts +++ b/src/__tests__/route.test.ts @@ -13,6 +13,7 @@ import { ONE_ACA, almostEq, toHuman } from '@acala-network/asset-router/dist/uti import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { encodeAddress } from '@polkadot/util-crypto'; import { formatEther, parseEther, parseUnits } from 'ethers/lib/utils'; +import assert from 'assert'; import { BASILISK_TESTNET_NODE_URL, @@ -22,6 +23,7 @@ import { TEST_KEY, } from './testConsts'; import { ETH_RPC, FUJI_TOKEN, GOERLI_USDC, PARA_ID } from '../consts'; +import { RouteStatus } from '../api'; import { encodeXcmDest, expectError, @@ -31,6 +33,8 @@ import { relayAndRoute, relayAndRouteBatch, routeHoma, + routeHomaAuto, + routeStatus, routeWormhole, routeXcm, shouldRouteHoma, @@ -421,6 +425,9 @@ describe.skip('/routeHoma', () => { }; const testHomaRouter = async (destAddr: string) => { + const relayerBal = await relayerAcalaFork.getBalance(); + assert(relayerBal.gt(parseEther('10')), `relayer doesn't have enough balance to relay! ${relayerAcalaFork.address}`); + const routeArgs = { destAddr, chain: 'acala', @@ -473,6 +480,80 @@ describe.skip('/routeHoma', () => { expect(bal1.relayerBalDot.sub(bal0.relayerBalDot).toBigInt()).to.eq(routingFee.toBigInt()); }; + const testAutoHomaRouter = async (destAddr: string) => { + const relayerBal = await relayerAcalaFork.getBalance(); + assert(relayerBal.gt(parseEther('10')), `relayer doesn't have enough balance to relay! ${relayerAcalaFork.address}`); + + const routeArgs = { + destAddr, + chain: 'acala', + }; + const res = await shouldRouteHoma(routeArgs); + ({ routerAddr } = res.data); + + // make sure user has enough DOT to transfer to router + const bal = await fetchTokenBalances(); + if (bal.userBalDot.lt(parsedStakeAmount)) { + if (bal.relayerBalDot.lt(parsedStakeAmount)) { + throw new Error('both relayer and user do not have enough DOT to transfer to router!'); + } + + console.log('refilling dot for user ...'); + await (await dot.connect(relayerAcalaFork).transfer(TEST_ADDR_USER, parsedStakeAmount)).wait(); + } + + const bal0 = await fetchTokenBalances(); + + console.log('sending auto routing request ...'); + const routeRes = await routeHomaAuto({ + ...routeArgs, + token: DOT, + }); + const reqId = routeRes.data; + console.log(`auto route submitted! reqId: ${reqId}`); + + const waitForRoute = new Promise((resolve, reject) => { + const pollRouteStatus = setInterval(async () => { + const res = await routeStatus({ id: reqId }); + // console.log(`current status: ${res.data.status}`); + + if (res.data.status === RouteStatus.Complete) { + resolve(); + clearInterval(pollRouteStatus); + } + }, 1000); + + setTimeout(reject, 100 * 1000); + }); + + console.log('xcming to router ...'); + await mockXcmToRouter(routerAddr, userAcalaFork, DOT, stakeAmount); + + console.log('waiting for auto routing ...'); + await waitForRoute; + + console.log('route complete!'); + const bal1 = await fetchTokenBalances(); + + // router should be destroyed + const routerCode = await providerKarura.getCode(routerAddr); + expect(routerCode).to.eq('0x'); + expect(bal1.routerBalDot.toNumber()).to.eq(0); + expect(bal1.routerBalLdot.toNumber()).to.eq(0); + + // user should receive LDOT + const routingFee = await fee.getFee(DOT); + const exchangeRate = parseEther((1 / Number(formatEther(await homa.getExchangeRate()))).toString()); // 10{18} DOT => ? LDOT + const expectedLdot = parsedStakeAmount.sub(routingFee).mul(exchangeRate).div(ONE_ACA); + const ldotReceived = bal1.userBalLdot.sub(bal0.userBalLdot); + + expect(almostEq(expectedLdot, ldotReceived)).to.be.true; + // expect(bal0.userBalDot.sub(bal1.userBalDot)).to.eq(parsedStakeAmount); // TODO: why this has a super slight off? + + // relayer should receive DOT fee + expect(bal1.relayerBalDot.sub(bal0.relayerBalDot).toBigInt()).to.eq(routingFee.toBigInt()); + }; + it('route to evm address', async () => { await testHomaRouter(TEST_ADDR_USER); }); @@ -484,5 +565,18 @@ describe.skip('/routeHoma', () => { await testHomaRouter(userSubstrateAddr); }); + + it('auto route to evm address', async () => { + await testAutoHomaRouter(TEST_ADDR_USER); + }); + + it('auto route to substrate address', async () => { + const ACALA_SS58_PREFIX = 10; + const userAccountId = await evmAccounts.getAccountId(TEST_ADDR_USER); + const userSubstrateAddr = encodeAddress(userAccountId, ACALA_SS58_PREFIX); + + await testAutoHomaRouter(userSubstrateAddr); + }); }); + diff --git a/src/__tests__/shouldRoute.test.ts b/src/__tests__/shouldRoute.test.ts index cb17714..88f6daa 100644 --- a/src/__tests__/shouldRoute.test.ts +++ b/src/__tests__/shouldRoute.test.ts @@ -321,13 +321,13 @@ describe.concurrent.skip('/shouldRouteHoma', () => { }); expect(res).toMatchInlineSnapshot(` - { - "data": { - "routerAddr": "0xa013818BBddc5d2d55ab9cCD50759b3B1953d6cd", - "shouldRoute": true, - }, - } - `); + { + "data": { + "routerAddr": "0x8A4f03B2D615172f0714AaC2E8C399a6f0d9e448", + "shouldRoute": true, + }, + } + `); // should be case insensitive res = await shouldRouteHoma({ @@ -336,13 +336,13 @@ describe.concurrent.skip('/shouldRouteHoma', () => { }); expect(res).toMatchInlineSnapshot(` - { - "data": { - "routerAddr": "0xa013818BBddc5d2d55ab9cCD50759b3B1953d6cd", - "shouldRoute": true, - }, - } - `); + { + "data": { + "routerAddr": "0x8A4f03B2D615172f0714AaC2E8C399a6f0d9e448", + "shouldRoute": true, + }, + } + `); } }); @@ -357,7 +357,7 @@ describe.concurrent.skip('/shouldRouteHoma', () => { expect(res).toMatchInlineSnapshot(` { "data": { - "routerAddr": "0xfD6143c380706912a04230f22cF92c402561820e", + "routerAddr": "0x1140EFc2C45e9307701DA521884F75dDDe28f28f", "shouldRoute": true, }, } diff --git a/src/__tests__/testUtils.ts b/src/__tests__/testUtils.ts index f9d6460..9d14d9c 100644 --- a/src/__tests__/testUtils.ts +++ b/src/__tests__/testUtils.ts @@ -186,6 +186,14 @@ export const routeHoma = process.env.COVERAGE ? _supertestPost(RELAYER_API.ROUTE_HOMA) : _axiosPost(RELAYER_URL.ROUTE_HOMA); +export const routeHomaAuto = process.env.COVERAGE + ? _supertestPost(RELAYER_API.ROUTE_HOMA_AUTO) + : _axiosPost(RELAYER_URL.ROUTE_HOMA_AUTO); + +export const routeStatus = process.env.COVERAGE + ? _supertestGet(RELAYER_API.ROUTE_STATUS) + : _axiosGet(RELAYER_URL.ROUTE_STATUS); + export const shouldRouteEuphrates = process.env.COVERAGE ? _supertestGet(RELAYER_API.SHOULD_ROUTER_EUPHRATES) : _axiosGet(RELAYER_URL.SHOULD_ROUTER_EUPHRATES); diff --git a/src/api/homa.ts b/src/api/homa.ts index efe86a5..9263902 100644 --- a/src/api/homa.ts +++ b/src/api/homa.ts @@ -54,7 +54,7 @@ export const routeHoma = async ({ chain, destAddr }: RouteParamsHoma) => { return receipt.transactionHash; }; -enum RouteStatus { +export enum RouteStatus { Waiting = 0, Routing = 1, Confirming = 2, diff --git a/src/consts.ts b/src/consts.ts index 6c11510..7f8e957 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -55,6 +55,8 @@ export const RELAYER_API = { SHOULD_ROUTER_HOMA: '/shouldRouteHoma', ROUTE_HOMA: '/routeHoma', + ROUTE_HOMA_AUTO: '/routeHomaAuto', + ROUTE_STATUS: '/routeStatus', SHOULD_ROUTER_EUPHRATES: '/shouldRouteEuphrates', ROUTE_EUPHRATES: '/routeEuphrates', @@ -80,6 +82,8 @@ export const RELAYER_URL = { SHOULD_ROUTER_HOMA: `${RELAYER_BASE_URL}${RELAYER_API.SHOULD_ROUTER_HOMA}`, ROUTE_HOMA: `${RELAYER_BASE_URL}${RELAYER_API.ROUTE_HOMA}`, + ROUTE_HOMA_AUTO: `${RELAYER_BASE_URL}${RELAYER_API.ROUTE_HOMA_AUTO}`, + ROUTE_STATUS: `${RELAYER_BASE_URL}${RELAYER_API.ROUTE_STATUS}`, SHOULD_ROUTER_EUPHRATES: `${RELAYER_BASE_URL}${RELAYER_API.SHOULD_ROUTER_EUPHRATES}`, ROUTE_EUPHRATES: `${RELAYER_BASE_URL}${RELAYER_API.ROUTE_EUPHRATES}`, diff --git a/src/middlewares/router.ts b/src/middlewares/router.ts index b6692da..832303c 100644 --- a/src/middlewares/router.ts +++ b/src/middlewares/router.ts @@ -58,11 +58,11 @@ const ROUTER_CONFIGS: { '/health': { handler: healthCheck, }, - '/getRouteStatus': { + '/routeStatus': { schema: routeStatusSchema, handler: getRouteStatus, }, - '/getAllRouteStatus': { + '/allRouteStatus': { handler: getAllRouteStatus, }, }, diff --git a/src/utils/route.ts b/src/utils/route.ts index 0102627..f7dc1dd 100644 --- a/src/utils/route.ts +++ b/src/utils/route.ts @@ -10,8 +10,8 @@ import { ZERO_ADDR, } from '../consts'; import { RelayAndRouteParams, RouteParamsWormhole, RouteParamsXcm } from './validate'; -import { getRouterChainTokenAddr } from './wormhole'; import { checkShouldRelayBeforeRouting } from './relay'; +import { getRouterChainTokenAddr } from './wormhole'; interface RouteProps { routerAddr: string;