From ea21fd57eb7d3614769078d18372bb0908cd6787 Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 20 Jul 2023 20:02:48 +0700 Subject: [PATCH 01/17] chore: fix wrong update script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4076e87a..e4bab9ed 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "postinstall": "npm run bootstrap && lerna run build", "link": "lerna link", "test": "lerna run test", - "update": "lernaupdate", + "update": "lerna update", "test:core": "lerna exec --scope=@axelar-network/axelar-local-dev npm run test", "test:near": "lerna exec --scope=@axelar-network/axelar-local-dev-near npm run test", "test:aptos": "lerna exec --scope=@axelar-network/axelar-local-dev-aptos npm run test", From afc74bbe1a64cc68ff3b7f5361cce9d366fdb896 Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 20 Jul 2023 20:03:03 +0700 Subject: [PATCH 02/17] chore: update axelar-gmp-sdk-soldiity to 4.0.1 --- package-lock.json | 10 +++++++++- packages/axelar-local-dev/package.json | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0d705be8..939cb000 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29407,7 +29407,7 @@ "license": "ISC", "dependencies": { "@axelar-network/axelar-cgp-solidity": "github:axelarnetwork/axelar-cgp-solidity#f668693", - "@axelar-network/axelar-gmp-sdk-solidity": "3.5.0", + "@axelar-network/axelar-gmp-sdk-solidity": "^4.0.1", "ethers": "^5.6.5", "fs-extra": "^10.1.0", "ganache": "^7.1.0", @@ -29466,6 +29466,14 @@ "@axelar-network/axelar-cgp-near": "^1.0.0", "@axelar-network/axelar-local-dev": "2.0.3" } + }, + "packages/axelar-local-dev/node_modules/@axelar-network/axelar-gmp-sdk-solidity": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-4.0.1.tgz", + "integrity": "sha512-3UN0RDpgGzXxmBKR9bn26ReLwCd47kbmh9RXjytRykj5WqoK4Ioi+bnL7tcZgA5TrFrk6enkCxzzQBjNG1L3uw==", + "engines": { + "node": ">=16" + } } } } diff --git a/packages/axelar-local-dev/package.json b/packages/axelar-local-dev/package.json index f35982d0..c85a448e 100644 --- a/packages/axelar-local-dev/package.json +++ b/packages/axelar-local-dev/package.json @@ -34,7 +34,7 @@ "homepage": "https://github.com/axelarnetwork/axelar-local-dev#readme", "dependencies": { "@axelar-network/axelar-cgp-solidity": "github:axelarnetwork/axelar-cgp-solidity#f668693", - "@axelar-network/axelar-gmp-sdk-solidity": "3.5.0", + "@axelar-network/axelar-gmp-sdk-solidity": "^4.0.1", "ethers": "^5.6.5", "fs-extra": "^10.1.0", "ganache": "^7.1.0", From 0edeb32f5bd2d9b635b23d06ad369102de0d1f2d Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 20 Jul 2023 20:05:41 +0700 Subject: [PATCH 03/17] chore: update axelar-cgp-solidity to 5.0.0 --- package-lock.json | 24 ++++++++---------------- packages/axelar-local-dev/package.json | 2 +- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 939cb000..b30d937f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,20 +41,20 @@ } }, "node_modules/@axelar-network/axelar-cgp-solidity": { - "version": "4.5.0", - "resolved": "git+ssh://git@github.com/axelarnetwork/axelar-cgp-solidity.git#f6686932370517f012b7ed7dfe461fb04e7de96c", - "license": "ISC", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-cgp-solidity/-/axelar-cgp-solidity-5.0.0.tgz", + "integrity": "sha512-j/HshdS6MNZgEzqVgJq7gz9b1TgIwEr0QjmAUQMJWcGXk6EPp520+EWIFP+SomX3Zpu6p+Hg6+wf9giaLmrIQw==", "dependencies": { - "@axelar-network/axelar-gmp-sdk-solidity": "^3.0.0" + "@axelar-network/axelar-gmp-sdk-solidity": "^4.0.0" }, "engines": { "node": "^16.0.0 || ^18.0.0" } }, "node_modules/@axelar-network/axelar-gmp-sdk-solidity": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-3.5.0.tgz", - "integrity": "sha512-xKqcdLG1F2pxnSf37A2qOemfVy1vYts0rgI684WuUJz8GM70wWpeIUZYW1VlP8X1wB8okS5dSPsDWiuFPcYCFA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-4.0.1.tgz", + "integrity": "sha512-3UN0RDpgGzXxmBKR9bn26ReLwCd47kbmh9RXjytRykj5WqoK4Ioi+bnL7tcZgA5TrFrk6enkCxzzQBjNG1L3uw==", "engines": { "node": ">=16" } @@ -29406,7 +29406,7 @@ "version": "2.0.3", "license": "ISC", "dependencies": { - "@axelar-network/axelar-cgp-solidity": "github:axelarnetwork/axelar-cgp-solidity#f668693", + "@axelar-network/axelar-cgp-solidity": "^5.0.0", "@axelar-network/axelar-gmp-sdk-solidity": "^4.0.1", "ethers": "^5.6.5", "fs-extra": "^10.1.0", @@ -29466,14 +29466,6 @@ "@axelar-network/axelar-cgp-near": "^1.0.0", "@axelar-network/axelar-local-dev": "2.0.3" } - }, - "packages/axelar-local-dev/node_modules/@axelar-network/axelar-gmp-sdk-solidity": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-4.0.1.tgz", - "integrity": "sha512-3UN0RDpgGzXxmBKR9bn26ReLwCd47kbmh9RXjytRykj5WqoK4Ioi+bnL7tcZgA5TrFrk6enkCxzzQBjNG1L3uw==", - "engines": { - "node": ">=16" - } } } } diff --git a/packages/axelar-local-dev/package.json b/packages/axelar-local-dev/package.json index c85a448e..11329bf1 100644 --- a/packages/axelar-local-dev/package.json +++ b/packages/axelar-local-dev/package.json @@ -33,7 +33,7 @@ }, "homepage": "https://github.com/axelarnetwork/axelar-local-dev#readme", "dependencies": { - "@axelar-network/axelar-cgp-solidity": "github:axelarnetwork/axelar-cgp-solidity#f668693", + "@axelar-network/axelar-cgp-solidity": "^5.0.0", "@axelar-network/axelar-gmp-sdk-solidity": "^4.0.1", "ethers": "^5.6.5", "fs-extra": "^10.1.0", From 667c3349c9c22bce47d1f4e8a40bac89f581950a Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 20 Jul 2023 20:06:18 +0700 Subject: [PATCH 04/17] chore: remove non-existed Express contracts --- packages/axelar-local-dev/src/contracts/GMP.sol | 8 +------- packages/axelar-local-dev/src/contracts/index.ts | 6 ------ .../src/contracts/test/ExpressWithToken.sol | 9 ++++----- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/packages/axelar-local-dev/src/contracts/GMP.sol b/packages/axelar-local-dev/src/contracts/GMP.sol index 3fec4fb8..513d58e5 100644 --- a/packages/axelar-local-dev/src/contracts/GMP.sol +++ b/packages/axelar-local-dev/src/contracts/GMP.sol @@ -12,10 +12,4 @@ import { AxelarGasServiceProxy } from '@axelar-network/axelar-cgp-solidity/contr // Axelar GMP SDK import { IAxelarExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarExecutable.sol'; -import { ExpressExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressExecutable.sol'; -import { ExpressRegistry } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressRegistry.sol'; -import { ExpressProxyDeployer } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressProxyDeployer.sol'; -import { ExpressService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressService.sol'; -import { ExpressServiceProxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressServiceProxy.sol'; -// import { ExpressProxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressProxy.sol'; -// import { ExpressProxyDeployer } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressProxyDeployer.sol'; +import { AxelarExpressExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/AxelarExpressExecutable.sol'; diff --git a/packages/axelar-local-dev/src/contracts/index.ts b/packages/axelar-local-dev/src/contracts/index.ts index f0c153d1..96682334 100644 --- a/packages/axelar-local-dev/src/contracts/index.ts +++ b/packages/axelar-local-dev/src/contracts/index.ts @@ -6,9 +6,6 @@ import BurnableMintableCappedERC20 from '../artifacts/@axelar-network/axelar-cgp import Auth from '../artifacts/@axelar-network/axelar-cgp-solidity/contracts/auth/AxelarAuthWeighted.sol/AxelarAuthWeighted.json'; import AxelarGasReceiver from '../artifacts/@axelar-network/axelar-cgp-solidity/contracts/gas-service/AxelarGasService.sol/AxelarGasService.json'; import AxelarGasReceiverProxy from '../artifacts/@axelar-network/axelar-cgp-solidity/contracts/gas-service/AxelarGasServiceProxy.sol/AxelarGasServiceProxy.json'; -import GMPExpressService from '../artifacts/@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressService.sol/ExpressService.json'; -import GMPExpressServiceProxy from '../artifacts/@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressServiceProxy.sol/ExpressServiceProxy.json'; -import GMPExpressProxyDeployer from '../artifacts/@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressProxyDeployer.sol/ExpressProxyDeployer.json'; import IAxelarGasService from '../artifacts/@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarGasService.sol/IAxelarGasService.json'; import ConstAddressDeployer from '@axelar-network/axelar-gmp-sdk-solidity/dist/ConstAddressDeployer.json'; import Create3Deployer from '@axelar-network/axelar-gmp-sdk-solidity/dist/Create3Deployer.json'; @@ -23,11 +20,8 @@ export { Auth, AxelarGasReceiver, AxelarGasReceiverProxy, - GMPExpressService, - GMPExpressServiceProxy, ConstAddressDeployer, Create3Deployer, IAxelarGasService, IAxelarExecutable, - GMPExpressProxyDeployer, }; diff --git a/packages/axelar-local-dev/src/contracts/test/ExpressWithToken.sol b/packages/axelar-local-dev/src/contracts/test/ExpressWithToken.sol index 6611811c..6ccbb5f2 100644 --- a/packages/axelar-local-dev/src/contracts/test/ExpressWithToken.sol +++ b/packages/axelar-local-dev/src/contracts/test/ExpressWithToken.sol @@ -2,16 +2,16 @@ pragma solidity 0.8.9; import { AxelarExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol'; -import { ExpressExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressExecutable.sol'; +import { AxelarExpressExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/AxelarExpressExecutable.sol'; import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol'; import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol'; import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol'; import { Upgradable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol'; -contract ExpressWithToken is ExpressExecutable { +contract ExpressWithToken is AxelarExpressExecutable { IAxelarGasService public immutable gasService; - constructor(address gateway_, address gasReceiver_) ExpressExecutable(gateway_) { + constructor(address gateway_, address gasReceiver_) AxelarExpressExecutable(gateway_) { gasService = IAxelarGasService(gasReceiver_); } @@ -56,8 +56,7 @@ contract ExpressWithToken is ExpressExecutable { } } - function _execute(string calldata, string calldata, bytes calldata payload) internal override{ - } + function _execute(string calldata, string calldata, bytes calldata payload) internal override {} function contractId() external pure returns (bytes32) { return keccak256('distribution-proxy'); From 19e1938db0a3a47ac11f90766c88acb50978f74f Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 20 Jul 2023 20:06:56 +0700 Subject: [PATCH 05/17] chore: remove express service contract deployment --- packages/axelar-local-dev/src/Network.ts | 32 ------------------- .../src/__tests__/network.spec.ts | 2 -- packages/axelar-local-dev/src/networkUtils.ts | 9 ++---- 3 files changed, 2 insertions(+), 41 deletions(-) diff --git a/packages/axelar-local-dev/src/Network.ts b/packages/axelar-local-dev/src/Network.ts index 7b666319..135eb852 100644 --- a/packages/axelar-local-dev/src/Network.ts +++ b/packages/axelar-local-dev/src/Network.ts @@ -11,8 +11,6 @@ import { AxelarGasReceiverProxy, ConstAddressDeployer, Create3Deployer, - GMPExpressService, - GMPExpressProxyDeployer, } from './contracts'; import { AxelarGateway__factory as AxelarGatewayFactory } from './types/factories/@axelar-network/axelar-cgp-solidity/contracts/AxelarGateway__factory'; import { AxelarGateway } from './types/@axelar-network/axelar-cgp-solidity/contracts/AxelarGateway'; @@ -45,8 +43,6 @@ export interface NetworkInfo { lastRelayedBlock: number; gatewayAddress: string; gasReceiverAddress: string; - expressServiceAddress: string; - expressProxyDeployerAddress: string; constAddressDeployerAddress: string; create3DeployerAddress: string; tokens: { [key: string]: string }; @@ -81,8 +77,6 @@ export class Network { gasService: AxelarGasService; constAddressDeployer: Contract; create3Deployer: Contract; - expressService: Contract; - expressProxyDeployer: Contract; isRemote: boolean | undefined; url: string | undefined; ganacheProvider: any; @@ -104,8 +98,6 @@ export class Network { this.gasService = networkish.gasService; this.constAddressDeployer = networkish.constAddressDeployer; this.create3Deployer = networkish.create3Deployer; - this.expressService = networkish.expressService; - this.expressProxyDeployer = networkish.expressProxyDeployer; this.isRemote = networkish.isRemote; this.url = networkish.url; this.tokens = networkish.tokens; @@ -203,20 +195,6 @@ export class Network { return this.create3Deployer; } - async deployExpressServiceContract(): Promise { - logger.log(`Deploying the Express Service Contract for ${this.name}... `); - const expressProxyDeployer = await deployContract(this.ownerWallet, GMPExpressProxyDeployer, [this.gateway.address]); - const expressService = await deployContract(this.ownerWallet, GMPExpressService, [ - this.gateway.address, - expressProxyDeployer.address, - this.ownerWallet.address, - ]); - this.expressService = new Contract(expressService.address, GMPExpressService.abi, this.provider); - this.expressProxyDeployer = new Contract(expressProxyDeployer.address, GMPExpressProxyDeployer.abi, this.provider); - logger.log(`Deployed ExpressService at ${expressService.address}`); - return expressService; - } - async deployToken(name: string, symbol: string, decimals: number, cap: bigint, address: string = ADDRESS_ZERO, alias: string = symbol) { logger.log(`Deploying ${name} for ${this.name}... `); const data = arrayify( @@ -281,8 +259,6 @@ export class Network { lastRelayedBlock: this.lastRelayedBlock, gatewayAddress: this.gateway.address, gasReceiverAddress: this.gasService.address, - expressProxyDeployerAddress: this.expressProxyDeployer.address, - expressServiceAddress: this.expressService.address, constAddressDeployerAddress: this.constAddressDeployer.address, create3DeployerAddress: this.create3Deployer.address, tokens: this.tokens, @@ -299,14 +275,6 @@ export class Network { constAddressDeployer: this.constAddressDeployer.address, create3Deployer: this.create3Deployer.address, tokens: this.tokens, - GMPExpressService: { - expressOperator: this.ownerWallet.address, - salt: 'GMPExpressService', - address: this.expressService.address, - implementation: this.expressService.address, - deployer: this.ownerWallet.address, - proxyDeployer: this.expressProxyDeployer.address, - }, }; } } diff --git a/packages/axelar-local-dev/src/__tests__/network.spec.ts b/packages/axelar-local-dev/src/__tests__/network.spec.ts index c33ae491..04db2fc0 100644 --- a/packages/axelar-local-dev/src/__tests__/network.spec.ts +++ b/packages/axelar-local-dev/src/__tests__/network.spec.ts @@ -26,8 +26,6 @@ function validateNetwork(network: Network) { expect(network.constAddressDeployer).to.not.be.undefined; expect(network.create3Deployer).to.not.be.undefined; expect(network.gateway).to.not.be.undefined; - expect(network.expressService).to.not.be.undefined; - expect(network.expressProxyDeployer).to.not.be.undefined; } describe('Network', () => { diff --git a/packages/axelar-local-dev/src/networkUtils.ts b/packages/axelar-local-dev/src/networkUtils.ts index ee8085ff..80fb1317 100644 --- a/packages/axelar-local-dev/src/networkUtils.ts +++ b/packages/axelar-local-dev/src/networkUtils.ts @@ -9,7 +9,7 @@ import { defaultAccounts, setJSON, httpGet, logger } from './utils'; import { Network, networks, NetworkOptions, NetworkInfo, NetworkSetup } from './Network'; import { AxelarGateway__factory as AxelarGatewayFactory } from './types/factories/@axelar-network/axelar-cgp-solidity/contracts/AxelarGateway__factory'; import { AxelarGasService__factory as AxelarGasServiceFactory } from './types/factories/@axelar-network/axelar-cgp-solidity/contracts/gas-service/AxelarGasService__factory'; -import { GMPExpressService, ConstAddressDeployer, GMPExpressProxyDeployer, Create3Deployer } from './contracts'; +import {ConstAddressDeployer, Create3Deployer } from './contracts'; const { keccak256, id, solidityPack, toUtf8Bytes } = ethers.utils; @@ -106,7 +106,6 @@ export async function createNetwork(options: NetworkOptions = {}) { await chain.deployCreate3Deployer(); await chain.deployGateway(); await chain.deployGasReceiver(); - await chain.deployExpressServiceContract(); chain.tokens = {}; //chain.usdc = await chain.deployToken('Axelar Wrapped aUSDC', 'aUSDC', 6, BigInt(1e70)); @@ -150,8 +149,6 @@ export async function getNetwork(urlOrProvider: string | providers.Provider, inf chain.create3Deployer = new Contract(info.create3DeployerAddress, Create3Deployer.abi, chain.provider); chain.gateway = AxelarGatewayFactory.connect(info.gatewayAddress, chain.provider); chain.gasService = AxelarGasServiceFactory.connect(info.gasReceiverAddress, chain.provider); - chain.expressService = new Contract(info.expressServiceAddress, GMPExpressService.abi, chain.provider); - chain.expressProxyDeployer = new Contract(info.expressProxyDeployerAddress, GMPExpressProxyDeployer.abi, chain.provider); //chain.usdc = await chain.getTokenContract('aUSDC'); logger.log(`Its gateway is deployed at ${chain.gateway.address}.`); @@ -198,7 +195,6 @@ export async function setupNetwork(urlOrProvider: string | providers.Provider, o await chain.deployCreate3Deployer(); await chain.deployGateway(); await chain.deployGasReceiver(); - await chain.deployExpressServiceContract(); chain.tokens = {}; //chain.usdc = await chain.deployToken('Axelar Wrapped aUSDC', 'aUSDC', 6, BigInt(1e70)); networks.push(chain); @@ -263,8 +259,7 @@ export async function forkNetwork(chainInfo: ChainCloneData, options: NetworkOpt chain.gateway = AxelarGatewayFactory.connect(chainInfo.gateway, chain.provider); await chain._upgradeGateway(oldAdminAddresses, oldThreshold); chain.gasService = AxelarGasServiceFactory.connect(chainInfo.AxelarGasService.address, chain.provider); - await chain.deployExpressServiceContract(); - + chain.tokens = { uusdc: chain.name === 'Ethereum' ? 'USDC' : 'axlUSDC', uausdc: 'aUSDC', From b5e1b5fcfb314401ca4ab082ab5708b6a1abd48b Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 25 Jul 2023 10:28:13 +0700 Subject: [PATCH 06/17] feat: implement subscribe express call --- packages/axelar-local-dev/src/Network.ts | 5 + packages/axelar-local-dev/src/exportUtils.ts | 7 +- packages/axelar-local-dev/src/networkUtils.ts | 4 + .../axelar-local-dev/src/relay/EvmRelayer.ts | 243 +++++++++--------- .../axelar-local-dev/src/relay/Relayer.ts | 4 +- 5 files changed, 142 insertions(+), 121 deletions(-) diff --git a/packages/axelar-local-dev/src/Network.ts b/packages/axelar-local-dev/src/Network.ts index 135eb852..156a9bc7 100644 --- a/packages/axelar-local-dev/src/Network.ts +++ b/packages/axelar-local-dev/src/Network.ts @@ -41,6 +41,7 @@ export interface NetworkInfo { adminKeys: string[]; threshold: number; lastRelayedBlock: number; + lastExpressedBlock: number; gatewayAddress: string; gasReceiverAddress: string; constAddressDeployerAddress: string; @@ -57,6 +58,7 @@ export interface NetworkSetup { adminKeys?: Wallet[]; threshold?: number; lastRelayedBlock?: number; + lastExpressedBlock?: number; } /* @@ -73,6 +75,7 @@ export class Network { adminWallets: Wallet[]; threshold: number; lastRelayedBlock: number; + lastExpressedBlock: number; gateway: AxelarGateway; gasService: AxelarGasService; constAddressDeployer: Contract; @@ -94,6 +97,7 @@ export class Network { this.adminWallets = networkish.adminWallets; this.threshold = networkish.threshold; this.lastRelayedBlock = networkish.lastRelayedBlock; + this.lastExpressedBlock = networkish.lastExpressedBlock; this.gateway = networkish.gateway; this.gasService = networkish.gasService; this.constAddressDeployer = networkish.constAddressDeployer; @@ -257,6 +261,7 @@ export class Network { adminKeys: this.adminWallets.map((wallet) => wallet.privateKey), threshold: this.threshold, lastRelayedBlock: this.lastRelayedBlock, + lastExpressedBlock: this.lastExpressedBlock, gatewayAddress: this.gateway.address, gasReceiverAddress: this.gasService.address, constAddressDeployerAddress: this.constAddressDeployer.address, diff --git a/packages/axelar-local-dev/src/exportUtils.ts b/packages/axelar-local-dev/src/exportUtils.ts index e1118d7a..06deb1a2 100644 --- a/packages/axelar-local-dev/src/exportUtils.ts +++ b/packages/axelar-local-dev/src/exportUtils.ts @@ -87,8 +87,9 @@ export async function createAndExport(options: CreateLocalOptions = {}) { const symbol = chain.tokens[alias]; // Mint 1e12 USDC tokens to the GMPExpressService contract. - await chain.giveToken(info.GMPExpressService.address, symbol, BigInt(1e18)); + await chain.giveToken(chain.relayerWallet.address, symbol, BigInt(1e18)); } + i++; } listen(_options.port); @@ -107,6 +108,10 @@ export async function createAndExport(options: CreateLocalOptions = {}) { } relaying = false; }, _options.relayInterval); + + const evmRelayer = _options.relayers['evm']; + evmRelayer?.subscribeExpressCall(); + setJSON(localChains, _options.chainOutputPath); } diff --git a/packages/axelar-local-dev/src/networkUtils.ts b/packages/axelar-local-dev/src/networkUtils.ts index 80fb1317..0c61071b 100644 --- a/packages/axelar-local-dev/src/networkUtils.ts +++ b/packages/axelar-local-dev/src/networkUtils.ts @@ -102,6 +102,7 @@ export async function createNetwork(options: NetworkOptions = {}) { chain.adminWallets = wallets.splice(4, 10); chain.threshold = 3; chain.lastRelayedBlock = await chain.provider.getBlockNumber(); + chain.lastExpressedBlock = chain.lastRelayedBlock; await chain.deployConstAddressDeployer(); await chain.deployCreate3Deployer(); await chain.deployGateway(); @@ -143,6 +144,7 @@ export async function getNetwork(urlOrProvider: string | providers.Provider, inf chain.adminWallets = info.adminKeys.map((x) => new Wallet(x, chain.provider)); chain.threshold = info.threshold; chain.lastRelayedBlock = info.lastRelayedBlock; + chain.lastExpressedBlock = info.lastExpressedBlock; chain.tokens = info.tokens; chain.constAddressDeployer = new Contract(info.constAddressDeployerAddress, ConstAddressDeployer.abi, chain.provider); @@ -191,6 +193,7 @@ export async function setupNetwork(urlOrProvider: string | providers.Provider, o chain.adminWallets = options.adminKeys.map((x) => new Wallet(x, chain.provider)); chain.threshold = options.threshold != null ? options.threshold : 1; chain.lastRelayedBlock = await chain.provider.getBlockNumber(); + chain.lastExpressedBlock = chain.lastRelayedBlock; await chain.deployConstAddressDeployer(); await chain.deployCreate3Deployer(); await chain.deployGateway(); @@ -252,6 +255,7 @@ export async function forkNetwork(chainInfo: ChainCloneData, options: NetworkOpt chain.adminWallets = wallets.splice(4, 10); chain.threshold = 3; chain.lastRelayedBlock = await chain.provider.getBlockNumber(); + chain.lastExpressedBlock = chain.lastRelayedBlock; chain.constAddressDeployer = new Contract(chainInfo.constAddressDeployer, ConstAddressDeployer.abi, chain.provider); // Delete the line below and uncomment the line after when we deploy create3Deployer await chain.deployCreate3Deployer(); diff --git a/packages/axelar-local-dev/src/relay/EvmRelayer.ts b/packages/axelar-local-dev/src/relay/EvmRelayer.ts index abe14148..80e231bd 100644 --- a/packages/axelar-local-dev/src/relay/EvmRelayer.ts +++ b/packages/axelar-local-dev/src/relay/EvmRelayer.ts @@ -1,6 +1,6 @@ import { Relayer, RelayerType } from './Relayer'; import { CallContractArgs, CallContractWithTokenArgs, RelayCommand, RelayData } from './types'; -import { ContractReceipt, ContractTransaction, ethers, Wallet } from 'ethers'; +import { BigNumber, ContractReceipt, ContractTransaction, ethers, Wallet } from 'ethers'; import { getEVMLogID, getRandomID, getSignedExecuteInput, logger } from '../utils'; import { Command } from './Command'; import { arrayify, defaultAbiCoder } from 'ethers/lib/utils'; @@ -11,6 +11,7 @@ import { ContractCallEventObject, ContractCallWithTokenEventObject, } from '../types/@axelar-network/axelar-cgp-solidity/contracts/AxelarGateway'; +import { AxelarExpressExecutable__factory as AxelarExpressExecutableFactory } from '../types/factories/@axelar-network/axelar-gmp-sdk-solidity/contracts/express/AxelarExpressExecutable__factory'; const AddressZero = ethers.constants.AddressZero; @@ -54,14 +55,37 @@ export class EvmRelayer extends Relayer { } } + override async subscribeExpressCall() { + for (const chain of networks) { + if (!this.commands[chain.name]) { + this.commands[chain.name] = []; + } + } + for (const chain of networks) { + chain.gasService.on('NativeGasPaidForExpressCallWithToken', async (_sourceAddress: string, destinationChain: string) => { + const blockNumber = await chain.provider.getBlockNumber(); + await this.updateExpressGasEvents(chain, blockNumber); + await this.updateCallContractWithTokensEvents(chain, blockNumber, chain.lastExpressedBlock + 1); + const destChain = networks.find((network) => network.name === destinationChain); + if (!destChain) return; + const commands = this.commands[destinationChain]; + if (!commands || commands?.length === 0) return; + await this.executeEvmExpress(destChain, commands).catch((e) => { + logger.log(e); + }); + chain.lastExpressedBlock = blockNumber; + }); + } + } + private async executeEvm(commandList: RelayCommand) { for (const to of networks) { const commands = commandList[to.name]; if (!commands || commands?.length === 0) continue; - await this.executeEvmExpress(to, commands); - const execution = await this.executeEvmGateway(to, commands); - await this.completeEvmExpress(to, commands, execution); - await this.executeEvmExecutable(to, commands, execution); + + await this.executeEvmGateway(to, commands); + + await this.executeEvmExecutable(to, commands); } } @@ -84,6 +108,10 @@ export class EvmRelayer extends Relayer { for (const command of commands) { if (command.post === null) continue; + const executed = await this.isExecuted(to, command); + // If the command has already been approved, skip it. + if (executed) continue; + const fromName = command.data[0]; const from = networks.find((network) => network.name === fromName); if (!from) continue; @@ -101,112 +129,43 @@ export class EvmRelayer extends Relayer { if (!payed) continue; - try { - const cost = getGasPrice(); - const blockLimit = Number((await to.provider.getBlock('latest')).gasLimit); - const gasLimit = BigInt(Math.min(blockLimit, payed.gasFeeAmount / cost)); - const { payload } = this.relayData.callContractWithToken[command.commandId]; - - await to.expressService - .connect(to.ownerWallet) - .callWithToken( - command.commandId || ethers.constants.HashZero, - fromName, - payed.sourceAddress, - payed.destinationAddress, - payload, - payed.symbol, - payed.amount, - { gasLimit } - ) - .then((tx: ContractTransaction) => tx.wait()); - } catch (e) { - logger.log(e); - } - } - } - - private async completeEvmExpress(to: Network, commands: Command[], execution: any): Promise { - for (const command of commands) { - if (command.post === null) continue; - if ( - !execution.events.find((event: any) => { - return event.event === 'Executed' && event.args[0] === command.commandId; - }) - ) - continue; - - const fromName = command.data[0]; - const from = networks.find((network) => network.name === fromName); - if (!from) continue; - - const payed = this.expressContractCallWithTokenGasEvents.find((log: any) => { - if (log.sourceAddress.toLowerCase() !== command.data[1].toLowerCase()) return false; - if (log.destinationChain.toLowerCase() !== to.name.toLowerCase()) return false; - if (log.destinationAddress.toLowerCase() !== command.data[2].toLowerCase()) return false; - if (log.payloadHash.toLowerCase() !== command.data[3].toLowerCase()) return false; - const alias = this.getAliasFromSymbol(from.tokens, log.symbol); - if (to.tokens[alias] !== command.data[4]) return false; - if (!command.data[5].eq(log.amount)) return false; - return true; - }); + const cost = getGasPrice(); + const blockLimit = Number((await to.provider.getBlock('latest')).gasLimit); + const gasLimit = BigInt(Math.min(blockLimit, payed.gasFeeAmount / cost)); + const { payload } = this.relayData.callContractWithToken[command.commandId]; - if (!payed) continue; - if (command.name === 'approveContractCallWithMint') { - const index = this.expressContractCallWithTokenGasEvents.indexOf(payed); - this.expressContractCallWithTokenGasEvents = this.expressContractCallWithTokenGasEvents.filter((_, i) => i !== index); - } + const expressExecutorContract = AxelarExpressExecutableFactory.connect(payed.destinationAddress, to.relayerWallet); - const { sourceAddress, destinationContractAddress, payload, alias, amountIn } = - this.relayData.callContractWithToken[command.commandId]; + const usdcAddress = await to.gateway.tokenAddresses(to.tokens['aUSDC']); + const usdcContract = new ethers.Contract( + usdcAddress, + ['function allowance(address,address) view returns (uint256)', 'function approve(address,uint256)'], + to.relayerWallet + ); + const allowance = await usdcContract.allowance(to.relayerWallet.address, expressExecutorContract.address); - try { - await to.expressService - .connect(to.ownerWallet) - .callWithToken(command.commandId, from.name, sourceAddress, destinationContractAddress, payload, alias, amountIn) - .then((tx: ContractTransaction) => tx.wait()); - } catch (e) { - logger.log(e); + // If the allowance is insufficient, approve the contract + if (allowance.lt(payed.amount)) { + await usdcContract.approve(expressExecutorContract.address, ethers.constants.MaxUint256).then((tx: any) => tx.wait()); } - } - } - private isExecuted(execution: any, command: Command) { - return !execution.events.find((event: any) => { - return event.event === 'Executed' && event.args[0] === command.commandId; - }); - } - - private findMatchedGasEvent(command: Command, from: Network, to: Network): any { - if (command.name === 'approveContractCall') { - return this.contractCallGasEvents.find((event) => { - if (event.sourceAddress.toLowerCase() !== command.data[1].toLowerCase()) return false; - if (event.destinationChain.toLowerCase() !== to.name.toLowerCase()) return false; - if (event.destinationAddress.toLowerCase() !== command.data[2].toLowerCase()) return false; - if (event.payloadHash.toLowerCase() !== command.data[3].toLowerCase()) return false; - return true; - }); - } else { - return this.contractCallWithTokenGasEvents.find((event) => { - if (event.sourceAddress.toLowerCase() !== command.data[1].toLowerCase()) return false; - if (event.destinationChain.toLowerCase() !== to.name.toLowerCase()) return false; - if (event.destinationAddress.toLowerCase() !== command.data[2].toLowerCase()) return false; - if (event.payloadHash.toLowerCase() !== command.data[3].toLowerCase()) return false; - const alias = this.getAliasFromSymbol(from.tokens, event.symbol); - if (to.tokens[alias] !== command.data[4]) return false; - if (!event.amount.eq(command.data[5])) return false; - return true; - }); + await expressExecutorContract + .expressExecuteWithToken(command.commandId, fromName, payed.sourceAddress, payload, payed.symbol, payed.amount, { + gasLimit, + }) + .then((tx) => tx.wait()) + .catch(() => undefined); } } - private async executeEvmExecutable(to: Network, commands: Command[], execution: any): Promise { + private async executeEvmExecutable(to: Network, commands: Command[]): Promise { for (const command of commands) { // If the command doesn't have post execution, skip it if (command.post === null) continue; - // If the command is already executed, skip it - if (this.isExecuted(execution, command)) continue; + // If the command has not approved yet, skip it + const executed = await this.isExecuted(to, command); + if (!executed) continue; // Find the network that the command is executed on const fromName = command.data[0]; @@ -216,16 +175,10 @@ export class EvmRelayer extends Relayer { if (!from) continue; // Find the gas event that matches the command - const gasPaidEvent = this.findMatchedGasEvent(command, from, to); + const { event: gasPaidEvent, gasEventIndex } = this.findMatchedGasEvent(command, from, to); // If the gas event is not found, skip it - if (!gasPaidEvent) continue; - - // Find the index of the gas event - const gasEventIndex = - command.name === 'approveContractCall' - ? this.contractCallGasEvents.indexOf(gasPaidEvent) - : this.contractCallWithTokenGasEvents.indexOf(gasPaidEvent); + if (!gasPaidEvent || gasEventIndex === -1) continue; try { const cost = getGasPrice(); @@ -233,18 +186,20 @@ export class EvmRelayer extends Relayer { // Get the block gas limit const blockGasLimit = await from.provider.getBlock('latest').then((block) => block.gasLimit); + const filterUnmatchedGasEvents = (_: any, index: number) => { + return index !== gasEventIndex; + }; if (command.name === 'approveContractCall') { - this.contractCallGasEvents = this.contractCallGasEvents.filter((_, index) => { - return index !== gasEventIndex; - }); + this.contractCallGasEvents = this.contractCallGasEvents.filter(filterUnmatchedGasEvents); } else { - this.contractCallWithTokenGasEvents = this.contractCallWithTokenGasEvents.filter((_, index) => { - return index !== gasEventIndex; - }); + this.contractCallWithTokenGasEvents = this.contractCallWithTokenGasEvents.filter(filterUnmatchedGasEvents); + this.expressContractCallWithTokenGasEvents = + this.expressContractCallWithTokenGasEvents.filter(filterUnmatchedGasEvents); } // Execute the command const paidGasLimit = gasPaidEvent.gasFeeAmount.div(cost); + const receipt: ContractReceipt = await command.post?.({ gasLimit: blockGasLimit.lt(paidGasLimit) ? blockGasLimit : paidGasLimit, }); @@ -298,12 +253,61 @@ export class EvmRelayer extends Relayer { } } } - } catch (e) { + } catch (e: any) { logger.log(e); } } } + private isExecuted(to: Network, command: Command) { + return to.gateway.isCommandExecuted(command.commandId); + } + + private findMatchedGasEvent(command: Command, from: Network, to: Network): any { + if (command.name === 'approveContractCall') { + const event = this.contractCallGasEvents.find((event) => { + if (event.sourceAddress.toLowerCase() !== command.data[1].toLowerCase()) return false; + if (event.destinationChain.toLowerCase() !== to.name.toLowerCase()) return false; + if (event.destinationAddress.toLowerCase() !== command.data[2].toLowerCase()) return false; + if (event.payloadHash.toLowerCase() !== command.data[3].toLowerCase()) return false; + return true; + }); + return { event, eventIndex: this.contractCallGasEvents.indexOf(event) }; + } else { + const gmpGasEvent = this.contractCallWithTokenGasEvents.find((event) => { + if (event.sourceAddress.toLowerCase() !== command.data[1].toLowerCase()) return false; + if (event.destinationChain.toLowerCase() !== to.name.toLowerCase()) return false; + if (event.destinationAddress.toLowerCase() !== command.data[2].toLowerCase()) return false; + if (event.payloadHash.toLowerCase() !== command.data[3].toLowerCase()) return false; + const alias = this.getAliasFromSymbol(from.tokens, event.symbol); + if (to.tokens[alias] !== command.data[4]) return false; + if (!event.amount.eq(command.data[5])) return false; + return true; + }); + + const expressGmpGasEvent = this.expressContractCallWithTokenGasEvents.find((log: any) => { + if (log.sourceAddress.toLowerCase() !== command.data[1].toLowerCase()) return false; + if (log.destinationChain.toLowerCase() !== to.name.toLowerCase()) return false; + if (log.destinationAddress.toLowerCase() !== command.data[2].toLowerCase()) return false; + if (log.payloadHash.toLowerCase() !== command.data[3].toLowerCase()) return false; + const alias = this.getAliasFromSymbol(from.tokens, log.symbol); + if (to.tokens[alias] !== command.data[4]) return false; + if (!command.data[5].eq(log.amount)) return false; + return true; + }); + + return gmpGasEvent + ? { + event: gmpGasEvent, + eventIndex: this.contractCallWithTokenGasEvents.indexOf(gmpGasEvent), + } + : { + event: expressGmpGasEvent, + eventIndex: this.expressContractCallWithTokenGasEvents.indexOf(expressGmpGasEvent), + }; + } + } + private async updateGasEvents(from: Network, blockNumber: number) { const gasPaidForContractCallFilter = from.gasService.filters.GasPaidForContractCall(); const gasPaidForContractCallLogs = ( @@ -343,12 +347,12 @@ export class EvmRelayer extends Relayer { private async updateExpressGasEvents(from: Network, blockNumber: number) { let filter = from.gasService.filters.GasPaidForExpressCallWithToken(); - let newGasLogs: any = (await from.gasService.queryFilter(filter, from.lastRelayedBlock + 1, blockNumber)).map((log) => log.args); + let newGasLogs: any = (await from.gasService.queryFilter(filter, from.lastExpressedBlock + 1, blockNumber)).map((log) => log.args); for (const gasLog of newGasLogs) { this.expressContractCallWithTokenGasEvents.push(gasLog); } filter = from.gasService.filters.NativeGasPaidForExpressCallWithToken(); - newGasLogs = (await from.gasService.queryFilter(filter, from.lastRelayedBlock + 1, blockNumber)).map((log) => { + newGasLogs = (await from.gasService.queryFilter(filter, from.lastExpressedBlock + 1, blockNumber)).map((log) => { return { ...log.args, gasToken: AddressZero }; }); for (const gasLog of newGasLogs) { @@ -439,11 +443,12 @@ export class EvmRelayer extends Relayer { } } - private async updateCallContractWithTokensEvents(from: Network, blockNumber: number) { + private async updateCallContractWithTokensEvents(from: Network, toBlock: number, fromBlock = from.lastRelayedBlock + 1) { const filter = from.gateway.filters.ContractCallWithToken(); - const logsFrom = await from.gateway.queryFilter(filter, from.lastRelayedBlock + 1, blockNumber); + const logsFrom = await from.gateway.queryFilter(filter, fromBlock, toBlock); for (const log of logsFrom) { const args: any = log.args; + if (!this.commands[args.destinationChain]) continue; const alias = this.getAliasFromSymbol(from.tokens, args.symbol); const amountOut = args.amount; const commandId = getEVMLogID(from.name, log); @@ -466,7 +471,7 @@ export class EvmRelayer extends Relayer { }; this.relayData.callContractWithToken[commandId] = callContractWithTokenArgs; - const command = Command.createEVMContractCallWithTokenCommand(commandId, this.relayData, callContractWithTokenArgs); + const command = this.createCallContractWithTokenCommand(commandId, this.relayData, callContractWithTokenArgs); this.commands[args.destinationChain].push(command); } } diff --git a/packages/axelar-local-dev/src/relay/Relayer.ts b/packages/axelar-local-dev/src/relay/Relayer.ts index 6ba89b8c..c260bf97 100644 --- a/packages/axelar-local-dev/src/relay/Relayer.ts +++ b/packages/axelar-local-dev/src/relay/Relayer.ts @@ -40,7 +40,9 @@ export abstract class Relayer { await this.updateEvents(); await this.execute(this.commands); + } - this.commands = {}; + async subscribeExpressCall() { + // this is a no-op for now } } From feea48c60b1eb257dd4a206b1e79d02608d1e203 Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 25 Jul 2023 10:28:29 +0700 Subject: [PATCH 07/17] chore: fix commandID computation --- packages/axelar-local-dev/src/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/axelar-local-dev/src/utils.ts b/packages/axelar-local-dev/src/utils.ts index 01555e12..296e5c6d 100644 --- a/packages/axelar-local-dev/src/utils.ts +++ b/packages/axelar-local-dev/src/utils.ts @@ -23,7 +23,8 @@ export async function getSignedExecuteInput(data: any, wallet: Wallet) { export const getRandomID = () => id(getRandomInt(1e10).toString()); export const getEVMLogID = (chain: string, log: any) => { - return id(chain + ':' + log.blockNumber + ':' + log.transactionIndex + ':' + log.logIndex + ':' + new Date().getMilliseconds()); + const txData = [chain, log.transactionHash, log.logIndex].join(':'); + return id(txData); }; export const getNearLogID = (chain: string, event: any) => { From 2dabf713eb7d98fd11c13d4e0a21638dcedc11c2 Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 25 Jul 2023 10:50:29 +0700 Subject: [PATCH 08/17] chore: move funding token to before express call --- packages/axelar-local-dev/src/exportUtils.ts | 6 ----- .../axelar-local-dev/src/relay/EvmRelayer.ts | 23 ++++++++++++++----- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/axelar-local-dev/src/exportUtils.ts b/packages/axelar-local-dev/src/exportUtils.ts index 06deb1a2..b91fba17 100644 --- a/packages/axelar-local-dev/src/exportUtils.ts +++ b/packages/axelar-local-dev/src/exportUtils.ts @@ -82,12 +82,6 @@ export async function createAndExport(options: CreateLocalOptions = {}) { // If there is no USDC token, return. if (!alias) return; - - // Get the symbol of the USDC token. - const symbol = chain.tokens[alias]; - - // Mint 1e12 USDC tokens to the GMPExpressService contract. - await chain.giveToken(chain.relayerWallet.address, symbol, BigInt(1e18)); } i++; diff --git a/packages/axelar-local-dev/src/relay/EvmRelayer.ts b/packages/axelar-local-dev/src/relay/EvmRelayer.ts index 80e231bd..1dbb75fd 100644 --- a/packages/axelar-local-dev/src/relay/EvmRelayer.ts +++ b/packages/axelar-local-dev/src/relay/EvmRelayer.ts @@ -136,17 +136,28 @@ export class EvmRelayer extends Relayer { const expressExecutorContract = AxelarExpressExecutableFactory.connect(payed.destinationAddress, to.relayerWallet); - const usdcAddress = await to.gateway.tokenAddresses(to.tokens['aUSDC']); - const usdcContract = new ethers.Contract( - usdcAddress, - ['function allowance(address,address) view returns (uint256)', 'function approve(address,uint256)'], + const tokenAddress = await to.gateway.tokenAddresses(to.tokens[payed.symbol]); + const tokenContract = new ethers.Contract( + tokenAddress, + [ + 'function allowance(address,address) view returns (uint256)', + 'function approve(address,uint256)', + 'function balanceOf(address) view returns (uint256)', + ], to.relayerWallet ); - const allowance = await usdcContract.allowance(to.relayerWallet.address, expressExecutorContract.address); + + // fund relayer wallet with token + const balance = await tokenContract.balanceOf(to.relayerWallet.address); + if (balance.lt(payed.amount)) { + await to.giveToken(to.relayerWallet.address, payed.symbol, BigInt(10000e18)); + } + + const allowance = await tokenContract.allowance(to.relayerWallet.address, expressExecutorContract.address); // If the allowance is insufficient, approve the contract if (allowance.lt(payed.amount)) { - await usdcContract.approve(expressExecutorContract.address, ethers.constants.MaxUint256).then((tx: any) => tx.wait()); + await tokenContract.approve(expressExecutorContract.address, ethers.constants.MaxUint256).then((tx: any) => tx.wait()); } await expressExecutorContract From ffc54f45814eb0078d26590345cc8cc70eeace8c Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 25 Jul 2023 10:50:38 +0700 Subject: [PATCH 09/17] chore: fix express test --- .../src/__tests__/relay.spec.ts | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/packages/axelar-local-dev/src/__tests__/relay.spec.ts b/packages/axelar-local-dev/src/__tests__/relay.spec.ts index 7e2b5508..1363d51c 100644 --- a/packages/axelar-local-dev/src/__tests__/relay.spec.ts +++ b/packages/axelar-local-dev/src/__tests__/relay.spec.ts @@ -261,37 +261,17 @@ describe('relay', () => { ]); // get bytecode with constructor data - const expressWithToken = await deployContract(chain2.ownerWallet, ExpressWithToken, [ + const contract2 = await deployContract(chain2.ownerWallet, ExpressWithToken, [ chain2.gateway.address, chain2.gasService.address, ]); - const salt = ethers.utils.id(Date.now().toString()); - - // deploy the express proxy + implementation contracts - await chain2.expressService - .connect(chain2.ownerWallet) - .deployExpressProxy(salt, expressWithToken.address, chain2.ownerWallet.address, '0x') - .then((tx: ContractTransaction) => tx.wait()); - - // get the proxy address - const proxyAddress = await chain2.expressService.deployedProxyAddress(salt, chain2.ownerWallet.address); - - // initialize the proxy contract with the implementation abi - const contract2 = new Contract(proxyAddress, ExpressWithToken.abi, chain2.ownerWallet); // mint usdc tokens to user1 await chain1.giveToken(user1.address, 'aUSDC', BigInt(amount)); - // mint usdc tokens to the destination express service contract - await chain2.giveToken(chain2.expressService.address, 'aUSDC', BigInt(amount)); - // approve the ExpressWithToken contract to spend our tokens await chain1.usdc?.approve(contract1.address, amount).then((tx: ContractTransaction) => tx.wait()); - // contract2 should be an express proxy contract - const isExpress = await chain2.expressService.isExpressProxy(contract2.address); - expect(isExpress).to.be.true; - // call the express contract await contract1 .connect(user1) From 321ed4ea44967884bb81ebd52d22725ca9ca52cb Mon Sep 17 00:00:00 2001 From: npty Date: Tue, 25 Jul 2023 10:50:49 +0700 Subject: [PATCH 10/17] chore: formatting --- packages/axelar-local-dev/src/__tests__/network.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/axelar-local-dev/src/__tests__/network.spec.ts b/packages/axelar-local-dev/src/__tests__/network.spec.ts index 04db2fc0..2abed053 100644 --- a/packages/axelar-local-dev/src/__tests__/network.spec.ts +++ b/packages/axelar-local-dev/src/__tests__/network.spec.ts @@ -61,6 +61,7 @@ describe('Network', () => { network = await getNetwork(`http://localhost:${port}`); validateNetwork(network); }); + it('should deploy a network on a preexisting chain', async () => { const port = 8600; const accounts = defaultAccounts(20); From f496daa89d48b74d765f92225db9030dc027ab04 Mon Sep 17 00:00:00 2001 From: npty Date: Wed, 26 Jul 2023 08:33:04 +0700 Subject: [PATCH 11/17] chore: unsubscribe express events when destroy exports --- .../src/__tests__/export.spec.ts | 122 ++++++++++++++++++ .../src/__tests__/relay.spec.ts | 2 +- .../src/contracts/test/ExpressWithToken.sol | 4 - packages/axelar-local-dev/src/exportUtils.ts | 7 + packages/axelar-local-dev/src/networkUtils.ts | 2 +- .../axelar-local-dev/src/relay/EvmRelayer.ts | 41 ++++-- .../axelar-local-dev/src/relay/Relayer.ts | 6 +- 7 files changed, 163 insertions(+), 21 deletions(-) create mode 100644 packages/axelar-local-dev/src/__tests__/export.spec.ts diff --git a/packages/axelar-local-dev/src/__tests__/export.spec.ts b/packages/axelar-local-dev/src/__tests__/export.spec.ts new file mode 100644 index 00000000..8de65d74 --- /dev/null +++ b/packages/axelar-local-dev/src/__tests__/export.spec.ts @@ -0,0 +1,122 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +'use strict'; + +import chai from 'chai'; +import { Wallet } from 'ethers'; +import fs from 'fs'; +const { expect } = chai; +import { deployContract, setLogger, createAndExport, destroyExported, Network, networks } from '../'; +import { AxelarExpressExecutable__factory as AxelarExpressExecutableFactory } from '../types/factories/@axelar-network/axelar-gmp-sdk-solidity/contracts/express/AxelarExpressExecutable__factory'; +import { ExpressWithToken__factory as ExpressWithTokenFactory } from '../types/factories/src/contracts/test/ExpressWithToken__factory'; +import { ExecutableWithToken__factory as ExecuteWithTokenFactory } from '../types/factories/src/contracts/test/ExecutableWithToken__factory'; +import ExpressWithToken from '../artifacts/src/contracts/test/ExpressWithToken.sol/ExpressWithToken.json'; +import ExecuteWithToken from '../artifacts/src/contracts/test/ExecutableWithToken.sol/ExecutableWithToken.json'; +import { solidity } from 'ethereum-waffle'; +import { EvmRelayer } from '../relay/EvmRelayer'; + +chai.use(solidity); +setLogger(() => null); +jest.setTimeout(20000); + +async function deployAndFundUsdc(chain: Network) { + await chain.deployToken('Axelar Wrapped aUSDC', 'aUSDC', 6, BigInt(1e22)); +} + +describe('createAndExport', () => { + const wallet = Wallet.createRandom(); + const chains = ['A', 'B']; + const outputPath = './local.json'; + const evmRelayer = new EvmRelayer(); + let chain1: Network; + let chain2: Network; + let srcOwner: Wallet; + let destOwner: Wallet; + + beforeEach(async () => { + await createAndExport({ + accountsToFund: [wallet.address], + chainOutputPath: outputPath, + callback: (chain: Network) => deployAndFundUsdc(chain), + relayers: { evm: evmRelayer }, + chains, + relayInterval: 500, + }); + + chain1 = networks[0]; + chain2 = networks[1]; + srcOwner = networks[0].ownerWallet; + destOwner = networks[1].ownerWallet; + }); + + afterEach(async () => { + await destroyExported(); + }); + + it('should export a local.json file correctly', async () => { + const data = fs.readFileSync(outputPath, 'utf8'); + const chainJson = JSON.parse(data); + // read file and convert to json object + expect(chainJson.length).to.equal(2); + expect(chainJson[0].name).to.equal(chains[0]); + expect(chainJson[1].name).to.equal(chains[1]); + + for (const chain of chainJson) { + expect(chain.gateway).to.not.be.undefined; + expect(chain.gasService).to.not.be.undefined; + expect(chain.constAddressDeployer).to.not.be.undefined; + expect(chain.create3Deployer).to.not.be.undefined; + expect(chain.rpc).to.be.not.undefined; + expect(chain.tokens.aUSDC).to.not.be.undefined; + } + }); + + it('should be able to relay tokens from chain A to chain B', async () => { + const contract1 = await deployContract(srcOwner, ExecuteWithToken, [chain1.gateway.address, chain1.gasService.address]).then( + (contract) => ExecuteWithTokenFactory.connect(contract.address, srcOwner) + ); + + const contract2 = await deployContract(destOwner, ExecuteWithToken, [chain2.gateway.address, chain2.gasService.address]).then( + (contract) => ExecuteWithTokenFactory.connect(contract.address, destOwner) + ); + + await contract1.addSibling(chain2.name, contract2.address); + + const amount = BigInt(1e18); + await chain1.giveToken(srcOwner.address, 'aUSDC', amount); + + const token1 = await chain1.getTokenContract('aUSDC'); + await token1.connect(srcOwner).approve(contract1.address, amount); + + // print eth balance of owner + await contract1.setAndSend(chain2.name, 'hello', wallet.address, 'aUSDC', amount, { value: BigInt(1e12) }); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + const token2 = await chain2.getTokenContract('aUSDC'); + const balance = await token2.balanceOf(wallet.address); + expect(balance.toBigInt()).to.equal(amount); + expect(await contract2.value()).to.equal('hello'); + }); + + it('should be able to call express tokens from chain A to chain B', async () => { + const contract1 = await deployContract(srcOwner, ExpressWithToken, [chain1.gateway.address, chain1.gasService.address]).then( + (contract) => ExpressWithTokenFactory.connect(contract.address, srcOwner) + ); + const contract2 = await deployContract(destOwner, ExpressWithToken, [chain2.gateway.address, chain2.gasService.address]).then( + (contract) => AxelarExpressExecutableFactory.connect(contract.address, destOwner) + ); + + const amount = BigInt(1e18); + await chain1.giveToken(srcOwner.address, 'aUSDC', amount); + const token1 = (await chain1.getTokenContract('aUSDC')).connect(srcOwner); + + await token1.approve(contract1.address, amount); + await contract1.sendToMany(chain2.name, contract2.address, [wallet.address], 'aUSDC', amount, { value: BigInt(1e17) }); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + const token2 = await chain2.getTokenContract('aUSDC'); + const balance = await token2.balanceOf(wallet.address); + expect(balance.toBigInt()).to.equal(amount); + }); +}); diff --git a/packages/axelar-local-dev/src/__tests__/relay.spec.ts b/packages/axelar-local-dev/src/__tests__/relay.spec.ts index 1363d51c..d3c5fa92 100644 --- a/packages/axelar-local-dev/src/__tests__/relay.spec.ts +++ b/packages/axelar-local-dev/src/__tests__/relay.spec.ts @@ -3,7 +3,7 @@ import { createNetwork, relay, stopAll, listen, getFee, getDepositAddress, deployContract, setLogger } from '../'; import { defaultAbiCoder } from 'ethers/lib/utils'; -import { BigNumber, Contract, ContractFactory, ContractTransaction, Wallet, ethers } from 'ethers'; +import { BigNumber, Contract, ContractTransaction, Wallet } from 'ethers'; import { Network } from '../Network'; import ExpressWithToken from '../artifacts/src/contracts/test/ExpressWithToken.sol/ExpressWithToken.json'; import chai from 'chai'; diff --git a/packages/axelar-local-dev/src/contracts/test/ExpressWithToken.sol b/packages/axelar-local-dev/src/contracts/test/ExpressWithToken.sol index 6ccbb5f2..58cfca5d 100644 --- a/packages/axelar-local-dev/src/contracts/test/ExpressWithToken.sol +++ b/packages/axelar-local-dev/src/contracts/test/ExpressWithToken.sol @@ -57,8 +57,4 @@ contract ExpressWithToken is AxelarExpressExecutable { } function _execute(string calldata, string calldata, bytes calldata payload) internal override {} - - function contractId() external pure returns (bytes32) { - return keccak256('distribution-proxy'); - } } diff --git a/packages/axelar-local-dev/src/exportUtils.ts b/packages/axelar-local-dev/src/exportUtils.ts index b91fba17..beeba654 100644 --- a/packages/axelar-local-dev/src/exportUtils.ts +++ b/packages/axelar-local-dev/src/exportUtils.ts @@ -164,6 +164,9 @@ export async function forkAndExport(options: CloneLocalOptions = {}) { if (_options.afterRelay) _options.afterRelay(evmRelayer.relayData); }, _options.relayInterval); + const evmRelayer = _options.relayers['evm']; + evmRelayer?.subscribeExpressCall(); + setJSON(chains_local, _options.chainOutputPath); } @@ -173,8 +176,12 @@ export async function destroyExported(relayers?: RelayerMap) { clearInterval(interval); } + await defaultEvmRelayer?.unsubscribe(); + if (!relayers) return; + await relayers['evm']?.unsubscribe(); + for (const relayerType in relayers) { const relayer = relayers[relayerType]; if (relayer) { diff --git a/packages/axelar-local-dev/src/networkUtils.ts b/packages/axelar-local-dev/src/networkUtils.ts index 0c61071b..1c31cb02 100644 --- a/packages/axelar-local-dev/src/networkUtils.ts +++ b/packages/axelar-local-dev/src/networkUtils.ts @@ -9,7 +9,7 @@ import { defaultAccounts, setJSON, httpGet, logger } from './utils'; import { Network, networks, NetworkOptions, NetworkInfo, NetworkSetup } from './Network'; import { AxelarGateway__factory as AxelarGatewayFactory } from './types/factories/@axelar-network/axelar-cgp-solidity/contracts/AxelarGateway__factory'; import { AxelarGasService__factory as AxelarGasServiceFactory } from './types/factories/@axelar-network/axelar-cgp-solidity/contracts/gas-service/AxelarGasService__factory'; -import {ConstAddressDeployer, Create3Deployer } from './contracts'; +import { ConstAddressDeployer, Create3Deployer } from './contracts'; const { keccak256, id, solidityPack, toUtf8Bytes } = ethers.utils; diff --git a/packages/axelar-local-dev/src/relay/EvmRelayer.ts b/packages/axelar-local-dev/src/relay/EvmRelayer.ts index 1dbb75fd..4859ad3d 100644 --- a/packages/axelar-local-dev/src/relay/EvmRelayer.ts +++ b/packages/axelar-local-dev/src/relay/EvmRelayer.ts @@ -1,6 +1,6 @@ import { Relayer, RelayerType } from './Relayer'; import { CallContractArgs, CallContractWithTokenArgs, RelayCommand, RelayData } from './types'; -import { BigNumber, ContractReceipt, ContractTransaction, ethers, Wallet } from 'ethers'; +import { ContractReceipt, ethers, Wallet } from 'ethers'; import { getEVMLogID, getRandomID, getSignedExecuteInput, logger } from '../utils'; import { Command } from './Command'; import { arrayify, defaultAbiCoder } from 'ethers/lib/utils'; @@ -21,6 +21,8 @@ interface EvmRelayerOptions { } export class EvmRelayer extends Relayer { + eventSubscribers: ethers.Contract[] = []; + constructor(options: EvmRelayerOptions = {}) { super(); this.otherRelayers.near = options.nearRelayer; @@ -62,22 +64,33 @@ export class EvmRelayer extends Relayer { } } for (const chain of networks) { - chain.gasService.on('NativeGasPaidForExpressCallWithToken', async (_sourceAddress: string, destinationChain: string) => { - const blockNumber = await chain.provider.getBlockNumber(); - await this.updateExpressGasEvents(chain, blockNumber); - await this.updateCallContractWithTokensEvents(chain, blockNumber, chain.lastExpressedBlock + 1); - const destChain = networks.find((network) => network.name === destinationChain); - if (!destChain) return; - const commands = this.commands[destinationChain]; - if (!commands || commands?.length === 0) return; - await this.executeEvmExpress(destChain, commands).catch((e) => { - logger.log(e); - }); - chain.lastExpressedBlock = blockNumber; - }); + const subscriber = chain.gasService.on( + 'NativeGasPaidForExpressCallWithToken', + async (_sourceAddress: string, destinationChain: string) => { + const blockNumber = await chain.provider.getBlockNumber(); + await this.updateExpressGasEvents(chain, blockNumber); + await this.updateCallContractWithTokensEvents(chain, blockNumber, chain.lastExpressedBlock + 1); + const destChain = networks.find((network) => network.name === destinationChain); + if (!destChain) return; + const commands = this.commands[destinationChain]; + if (!commands || commands?.length === 0) return; + await this.executeEvmExpress(destChain, commands).catch((e) => { + logger.log(e); + }); + chain.lastExpressedBlock = blockNumber; + } + ); + + this.eventSubscribers.push(subscriber); } } + override unsubscribe() { + this.eventSubscribers.forEach((subscriber) => { + subscriber.removeAllListeners(); + }); + } + private async executeEvm(commandList: RelayCommand) { for (const to of networks) { const commands = commandList[to.name]; diff --git a/packages/axelar-local-dev/src/relay/Relayer.ts b/packages/axelar-local-dev/src/relay/Relayer.ts index c260bf97..1e02f7e9 100644 --- a/packages/axelar-local-dev/src/relay/Relayer.ts +++ b/packages/axelar-local-dev/src/relay/Relayer.ts @@ -43,6 +43,10 @@ export abstract class Relayer { } async subscribeExpressCall() { - // this is a no-op for now + // this is a no-op by default + } + + unsubscribe() { + // this is a no-op by default } } From 8dab121e852e5c901595c04682d476f97a1f0411 Mon Sep 17 00:00:00 2001 From: npty Date: Wed, 26 Jul 2023 14:56:40 +0700 Subject: [PATCH 12/17] chore: fix gmp express test chore: remove ethereum-waffle chore: remove jest setTimeout per test file chore: add npm run build chore: increase test timeout to 5m chore: add tests timeout chore: run tests only for core module chore: increase wait time chore: skip createAndExport tests chore: enable tests for all chore: not skip near tests chore: remove skip in export tests chore: fix tests chore: skip near tests --- .github/workflows/tests.yml | 3 ++ package.json | 2 +- .../__tests__/aptos.spec.ts | 1 - .../axelar-local-dev-aptos/jest.config.js | 1 + packages/axelar-local-dev-near/jest.config.js | 1 + .../src/__tests__/near.spec.ts | 2 - packages/axelar-local-dev/jest.config.js | 1 + packages/axelar-local-dev/src/Network.ts | 3 ++ .../src/__tests__/export.spec.ts | 9 ++-- .../src/__tests__/forking.spec.ts | 1 - .../src/__tests__/network.spec.ts | 1 - .../src/__tests__/relay.spec.ts | 46 +++++++++++-------- .../src/__tests__/token.spec.ts | 1 - packages/axelar-local-dev/src/networkUtils.ts | 9 ++-- .../axelar-local-dev/src/relay/EvmRelayer.ts | 7 ++- 15 files changed, 52 insertions(+), 36 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 95316f05..850006ab 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,7 +29,10 @@ jobs: key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - name: Install dependencies run: npm ci + - name: Build + run: npm run build - name: Test + timeout-minutes: 8 run: | nohup sh -c "aptos node run-local-testnet --with-faucet" > nohup.out 2> nohup.err < /dev/null & sleep 10 diff --git a/package.json b/package.json index e4bab9ed..62eb0401 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "publish": "lerna publish from-package --yes", "version": "lerna version --yes --exact", "bootstrap": "lerna bootstrap", - "postinstall": "npm run bootstrap && lerna run build", + "postinstall": "lerna bootstrap", "link": "lerna link", "test": "lerna run test", "update": "lerna update", diff --git a/packages/axelar-local-dev-aptos/__tests__/aptos.spec.ts b/packages/axelar-local-dev-aptos/__tests__/aptos.spec.ts index 10c646b7..255bd985 100644 --- a/packages/axelar-local-dev-aptos/__tests__/aptos.spec.ts +++ b/packages/axelar-local-dev-aptos/__tests__/aptos.spec.ts @@ -11,7 +11,6 @@ const { keccak256, toUtf8Bytes } = ethers.utils; setLogger(() => undefined); describe('aptos', () => { - jest.setTimeout(60000); let client: AptosNetwork; let evmNetwork: Network; diff --git a/packages/axelar-local-dev-aptos/jest.config.js b/packages/axelar-local-dev-aptos/jest.config.js index 565effdb..4419682a 100644 --- a/packages/axelar-local-dev-aptos/jest.config.js +++ b/packages/axelar-local-dev-aptos/jest.config.js @@ -6,4 +6,5 @@ module.exports = { }, testRegex: '/__tests__/.*\\.(test|spec)?\\.(ts)$', transformIgnorePatterns: ['/node_modules/'], + testTimeout: 300000, }; diff --git a/packages/axelar-local-dev-near/jest.config.js b/packages/axelar-local-dev-near/jest.config.js index c17ba278..220b0abd 100644 --- a/packages/axelar-local-dev-near/jest.config.js +++ b/packages/axelar-local-dev-near/jest.config.js @@ -5,4 +5,5 @@ module.exports = { '^.+\\.ts?$': 'ts-jest', }, transformIgnorePatterns: ['/node_modules/'], + testTimeout: 300000, }; diff --git a/packages/axelar-local-dev-near/src/__tests__/near.spec.ts b/packages/axelar-local-dev-near/src/__tests__/near.spec.ts index 65cae19f..a4fc004c 100644 --- a/packages/axelar-local-dev-near/src/__tests__/near.spec.ts +++ b/packages/axelar-local-dev-near/src/__tests__/near.spec.ts @@ -6,8 +6,6 @@ import { createNearNetwork, NearNetwork } from '..'; import { Network, createNetwork, stopAll, deployContract, relay } from '@axelar-network/axelar-local-dev'; import { EvmRelayer } from '@axelar-network/axelar-local-dev/dist/relay/EvmRelayer'; -jest.setTimeout(120000); - describe('near', () => { let client: NearNetwork; diff --git a/packages/axelar-local-dev/jest.config.js b/packages/axelar-local-dev/jest.config.js index c17ba278..220b0abd 100644 --- a/packages/axelar-local-dev/jest.config.js +++ b/packages/axelar-local-dev/jest.config.js @@ -5,4 +5,5 @@ module.exports = { '^.+\\.ts?$': 'ts-jest', }, transformIgnorePatterns: ['/node_modules/'], + testTimeout: 300000, }; diff --git a/packages/axelar-local-dev/src/Network.ts b/packages/axelar-local-dev/src/Network.ts index 156a9bc7..ce56b0af 100644 --- a/packages/axelar-local-dev/src/Network.ts +++ b/packages/axelar-local-dev/src/Network.ts @@ -17,6 +17,8 @@ import { AxelarGateway } from './types/@axelar-network/axelar-cgp-solidity/contr import { AxelarGasService__factory as AxelarGasServiceFactory } from './types/factories/@axelar-network/axelar-cgp-solidity/contracts/gas-service/AxelarGasService__factory'; import { AxelarGasService } from './types/@axelar-network/axelar-cgp-solidity/contracts/gas-service/AxelarGasService'; import http from 'http'; +import { EvmRelayer } from './relay/EvmRelayer'; +import { evmRelayer } from './relay'; const ADDRESS_ZERO = '0x0000000000000000000000000000000000000000'; const { defaultAbiCoder, arrayify, keccak256, toUtf8Bytes } = ethers.utils; @@ -234,6 +236,7 @@ export class Network { async giveToken(address: string, alias: string, amount: bigint) { const symbol = this.tokens[alias] || alias; + const data = arrayify( defaultAbiCoder.encode( ['uint256', 'bytes32[]', 'string[]', 'bytes[]'], diff --git a/packages/axelar-local-dev/src/__tests__/export.spec.ts b/packages/axelar-local-dev/src/__tests__/export.spec.ts index 8de65d74..8ef67c0b 100644 --- a/packages/axelar-local-dev/src/__tests__/export.spec.ts +++ b/packages/axelar-local-dev/src/__tests__/export.spec.ts @@ -11,12 +11,9 @@ import { ExpressWithToken__factory as ExpressWithTokenFactory } from '../types/f import { ExecutableWithToken__factory as ExecuteWithTokenFactory } from '../types/factories/src/contracts/test/ExecutableWithToken__factory'; import ExpressWithToken from '../artifacts/src/contracts/test/ExpressWithToken.sol/ExpressWithToken.json'; import ExecuteWithToken from '../artifacts/src/contracts/test/ExecutableWithToken.sol/ExecutableWithToken.json'; -import { solidity } from 'ethereum-waffle'; import { EvmRelayer } from '../relay/EvmRelayer'; -chai.use(solidity); setLogger(() => null); -jest.setTimeout(20000); async function deployAndFundUsdc(chain: Network) { await chain.deployToken('Axelar Wrapped aUSDC', 'aUSDC', 6, BigInt(1e22)); @@ -49,7 +46,7 @@ describe('createAndExport', () => { }); afterEach(async () => { - await destroyExported(); + await destroyExported({ evm: evmRelayer }); }); it('should export a local.json file correctly', async () => { @@ -90,7 +87,7 @@ describe('createAndExport', () => { // print eth balance of owner await contract1.setAndSend(chain2.name, 'hello', wallet.address, 'aUSDC', amount, { value: BigInt(1e12) }); - await new Promise((resolve) => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 3000)); const token2 = await chain2.getTokenContract('aUSDC'); const balance = await token2.balanceOf(wallet.address); @@ -113,7 +110,7 @@ describe('createAndExport', () => { await token1.approve(contract1.address, amount); await contract1.sendToMany(chain2.name, contract2.address, [wallet.address], 'aUSDC', amount, { value: BigInt(1e17) }); - await new Promise((resolve) => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 3000)); const token2 = await chain2.getTokenContract('aUSDC'); const balance = await token2.balanceOf(wallet.address); diff --git a/packages/axelar-local-dev/src/__tests__/forking.spec.ts b/packages/axelar-local-dev/src/__tests__/forking.spec.ts index ed79fb29..a50ef925 100644 --- a/packages/axelar-local-dev/src/__tests__/forking.spec.ts +++ b/packages/axelar-local-dev/src/__tests__/forking.spec.ts @@ -15,7 +15,6 @@ interface NetworkUsdc extends Network { } describe.skip("forking", () => { - jest.setTimeout(2000000); afterEach(async () => { await stopAll(); diff --git a/packages/axelar-local-dev/src/__tests__/network.spec.ts b/packages/axelar-local-dev/src/__tests__/network.spec.ts index 2abed053..cccef5c9 100644 --- a/packages/axelar-local-dev/src/__tests__/network.spec.ts +++ b/packages/axelar-local-dev/src/__tests__/network.spec.ts @@ -11,7 +11,6 @@ import chai from 'chai'; const { expect } = chai; setLogger(() => null); -jest.setTimeout(300000); function validateNetwork(network: Network) { // wallets diff --git a/packages/axelar-local-dev/src/__tests__/relay.spec.ts b/packages/axelar-local-dev/src/__tests__/relay.spec.ts index d3c5fa92..3d04eab9 100644 --- a/packages/axelar-local-dev/src/__tests__/relay.spec.ts +++ b/packages/axelar-local-dev/src/__tests__/relay.spec.ts @@ -1,9 +1,9 @@ /* eslint-disable @typescript-eslint/no-var-requires */ 'use strict'; -import { createNetwork, relay, stopAll, listen, getFee, getDepositAddress, deployContract, setLogger } from '../'; +import { createNetwork, relay, stopAll, listen, getFee, getDepositAddress, deployContract, setLogger, evmRelayer } from '../'; import { defaultAbiCoder } from 'ethers/lib/utils'; -import { BigNumber, Contract, ContractTransaction, Wallet } from 'ethers'; +import { BigNumber, Contract, ContractTransaction, Wallet, ethers } from 'ethers'; import { Network } from '../Network'; import ExpressWithToken from '../artifacts/src/contracts/test/ExpressWithToken.sol/ExpressWithToken.json'; import chai from 'chai'; @@ -14,7 +14,6 @@ setLogger(() => null); interface NetworkUsdc extends Network { usdc?: Contract; } -jest.setTimeout(200000); describe('relay', () => { let chain1: NetworkUsdc; @@ -29,9 +28,11 @@ describe('relay', () => { [user2] = chain2.userWallets; chain2.usdc = await chain2.deployToken('Axelar Wrapped USDC', 'aUSDC', 6, BigInt(1e18)).then((c: Contract) => c.connect(user2)); }); + afterEach(async () => { await stopAll(); }); + describe('deposit address', () => { it('should generate a deposit address', async () => { const depositAddress = await getDepositAddress(chain1, chain2, user2.address, 'aUSDC'); @@ -251,20 +252,17 @@ describe('relay', () => { }); describe('gmp express', () => { - const amount = 1e10; + const amount = 1e6; - it('should be able to relay a valid gmp-express call', async () => { + it.only('should be able to relay a valid gmp-express call', async () => { // source chain contract doesn't need to be proxy contract. it can be a normal contract - const contract1 = await deployContract(chain1.ownerWallet, ExpressWithToken, [ - chain1.gateway.address, - chain1.gasService.address, - ]); + const contract1 = await deployContract(user1, ExpressWithToken, [chain1.gateway.address, chain1.gasService.address]); // get bytecode with constructor data - const contract2 = await deployContract(chain2.ownerWallet, ExpressWithToken, [ - chain2.gateway.address, - chain2.gasService.address, - ]); + const contract2 = await deployContract(user2, ExpressWithToken, [chain2.gateway.address, chain2.gasService.address]); + + // subscribe to the express call + await evmRelayer.subscribeExpressCall(); // mint usdc tokens to user1 await chain1.giveToken(user1.address, 'aUSDC', BigInt(amount)); @@ -274,15 +272,27 @@ describe('relay', () => { // call the express contract await contract1 - .connect(user1) - .sendToMany(chain2.name, contract2.address, [user2.address], 'aUSDC', amount, { value: 1e15 }) + .sendToMany(chain2.name, contract2.address, [user2.address], 'aUSDC', amount, { value: ethers.utils.parseEther('1') }) .then((tx: ContractTransaction) => tx.wait()); - // relay the call - await relay(); + await new Promise((resolve) => setTimeout(resolve, 5000)); + await evmRelayer.unsubscribe(); // check that the call was successful - expect((await chain2.usdc?.balanceOf(user2.address))?.toNumber()).to.equal(amount); + const user2Balance = (await chain2.usdc?.balanceOf(user2.address))?.toNumber(); + expect(user2Balance).to.equal(amount); + + // check that relayer wallet balance + const balanceRelayerAfterExpressed = await chain2.usdc?.balanceOf(chain2.relayerWallet.address); + + // relay the usual gmp call + await relay(); + + // check that relayer should receive fund back. + const balanceRelayerAfterRelayed = await chain2.usdc?.balanceOf(chain2.relayerWallet.address); + + // check that relayer wallet balance should be increased by amount from the usual gmp call + expect(balanceRelayerAfterRelayed.sub(balanceRelayerAfterExpressed).toNumber()).to.be.eq(amount); }); }); }); diff --git a/packages/axelar-local-dev/src/__tests__/token.spec.ts b/packages/axelar-local-dev/src/__tests__/token.spec.ts index 5565d09d..5c879f34 100644 --- a/packages/axelar-local-dev/src/__tests__/token.spec.ts +++ b/packages/axelar-local-dev/src/__tests__/token.spec.ts @@ -9,7 +9,6 @@ const { expect } = chai; setLogger(() => undefined); -jest.setTimeout(300000); describe('token', () => { let chain: Network; diff --git a/packages/axelar-local-dev/src/networkUtils.ts b/packages/axelar-local-dev/src/networkUtils.ts index 1c31cb02..cae195db 100644 --- a/packages/axelar-local-dev/src/networkUtils.ts +++ b/packages/axelar-local-dev/src/networkUtils.ts @@ -10,10 +10,11 @@ import { Network, networks, NetworkOptions, NetworkInfo, NetworkSetup } from './ import { AxelarGateway__factory as AxelarGatewayFactory } from './types/factories/@axelar-network/axelar-cgp-solidity/contracts/AxelarGateway__factory'; import { AxelarGasService__factory as AxelarGasServiceFactory } from './types/factories/@axelar-network/axelar-cgp-solidity/contracts/gas-service/AxelarGasService__factory'; import { ConstAddressDeployer, Create3Deployer } from './contracts'; +import { Server } from 'http'; const { keccak256, id, solidityPack, toUtf8Bytes } = ethers.utils; -let serverInstance: any; +let serverInstance: Server | undefined; export interface ChainCloneData { name: string; @@ -283,8 +284,8 @@ export async function forkNetwork(chainInfo: ChainCloneData, options: NetworkOpt } export async function stop(network: string | Network) { - if (typeof network === 'string') network = networks.find((chain) => chain.name == network)!; - if (network.server != null) await network.server.close(); + if (typeof network === 'string') network = networks.find((chain) => chain.name === network)!; + if (network.server) await network.server.close(); networks.splice(networks.indexOf(network), 1); } @@ -294,7 +295,7 @@ export async function stopAll() { } if (serverInstance) { await serverInstance.close(); - serverInstance = null; + serverInstance = undefined; } } diff --git a/packages/axelar-local-dev/src/relay/EvmRelayer.ts b/packages/axelar-local-dev/src/relay/EvmRelayer.ts index 4859ad3d..e819d1a6 100644 --- a/packages/axelar-local-dev/src/relay/EvmRelayer.ts +++ b/packages/axelar-local-dev/src/relay/EvmRelayer.ts @@ -163,7 +163,12 @@ export class EvmRelayer extends Relayer { // fund relayer wallet with token const balance = await tokenContract.balanceOf(to.relayerWallet.address); if (balance.lt(payed.amount)) { - await to.giveToken(to.relayerWallet.address, payed.symbol, BigInt(10000e18)); + const fundAmount = ethers.BigNumber.from(1e10); + await to.giveToken( + to.relayerWallet.address, + payed.symbol, + fundAmount.gt(payed.amount) ? fundAmount.toBigInt() : payed.amount + ); } const allowance = await tokenContract.allowance(to.relayerWallet.address, expressExecutorContract.address); From f63634567007540f6001d2661ade9c20eb834cc5 Mon Sep 17 00:00:00 2001 From: npty Date: Thu, 27 Jul 2023 19:57:16 +0700 Subject: [PATCH 13/17] chore: update test version --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 850006ab..34e62994 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,7 +23,7 @@ jobs: - name: Checkout code uses: actions/checkout@v3 - name: Cache node_modules - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: node_modules key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} From f4fd43a243459facb843d1b8b208d970f095aad2 Mon Sep 17 00:00:00 2001 From: npty Date: Fri, 28 Jul 2023 07:50:17 +0700 Subject: [PATCH 14/17] chore: remove test only --- packages/axelar-local-dev/src/__tests__/relay.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/axelar-local-dev/src/__tests__/relay.spec.ts b/packages/axelar-local-dev/src/__tests__/relay.spec.ts index 3d04eab9..295661a3 100644 --- a/packages/axelar-local-dev/src/__tests__/relay.spec.ts +++ b/packages/axelar-local-dev/src/__tests__/relay.spec.ts @@ -254,7 +254,7 @@ describe('relay', () => { describe('gmp express', () => { const amount = 1e6; - it.only('should be able to relay a valid gmp-express call', async () => { + it('should be able to relay a valid gmp-express call', async () => { // source chain contract doesn't need to be proxy contract. it can be a normal contract const contract1 = await deployContract(user1, ExpressWithToken, [chain1.gateway.address, chain1.gasService.address]); From 30ee3a16110b289967a4e1f0dc4fbb4e24a3926f Mon Sep 17 00:00:00 2001 From: npty Date: Fri, 28 Jul 2023 08:33:49 +0700 Subject: [PATCH 15/17] chore: update axelar-gmp-sdk-solidity version to 4.0.2 --- package-lock.json | 8 ++++---- packages/axelar-local-dev/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index b30d937f..df4041a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,9 +52,9 @@ } }, "node_modules/@axelar-network/axelar-gmp-sdk-solidity": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-4.0.1.tgz", - "integrity": "sha512-3UN0RDpgGzXxmBKR9bn26ReLwCd47kbmh9RXjytRykj5WqoK4Ioi+bnL7tcZgA5TrFrk6enkCxzzQBjNG1L3uw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-4.0.2.tgz", + "integrity": "sha512-AiO6ez3A42Kh8PTtoo3R8gqoZzBKGFZRogTQBy0GXm7ZMAUdAbHcDPbQwBwjOw8+fxVNSpLTvXxsAR2FUq85GQ==", "engines": { "node": ">=16" } @@ -29407,7 +29407,7 @@ "license": "ISC", "dependencies": { "@axelar-network/axelar-cgp-solidity": "^5.0.0", - "@axelar-network/axelar-gmp-sdk-solidity": "^4.0.1", + "@axelar-network/axelar-gmp-sdk-solidity": "^4.0.2", "ethers": "^5.6.5", "fs-extra": "^10.1.0", "ganache": "^7.1.0", diff --git a/packages/axelar-local-dev/package.json b/packages/axelar-local-dev/package.json index 11329bf1..f3059456 100644 --- a/packages/axelar-local-dev/package.json +++ b/packages/axelar-local-dev/package.json @@ -34,7 +34,7 @@ "homepage": "https://github.com/axelarnetwork/axelar-local-dev#readme", "dependencies": { "@axelar-network/axelar-cgp-solidity": "^5.0.0", - "@axelar-network/axelar-gmp-sdk-solidity": "^4.0.1", + "@axelar-network/axelar-gmp-sdk-solidity": "^4.0.2", "ethers": "^5.6.5", "fs-extra": "^10.1.0", "ganache": "^7.1.0", From 5045fb489976fdb4eb1b606d79f243fe4c75d623 Mon Sep 17 00:00:00 2001 From: npty Date: Fri, 28 Jul 2023 08:35:19 +0700 Subject: [PATCH 16/17] chore: update solidity version to ^0.8.0 --- packages/axelar-local-dev/src/contracts/GMP.sol | 2 +- packages/axelar-local-dev/src/contracts/test/Executable.sol | 2 +- .../axelar-local-dev/src/contracts/test/ExecutableWithToken.sol | 2 +- .../axelar-local-dev/src/contracts/test/ExpressWithToken.sol | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/axelar-local-dev/src/contracts/GMP.sol b/packages/axelar-local-dev/src/contracts/GMP.sol index 513d58e5..72ffe7b8 100644 --- a/packages/axelar-local-dev/src/contracts/GMP.sol +++ b/packages/axelar-local-dev/src/contracts/GMP.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.9; +pragma solidity ^0.8.0; // Axelar CGP SDK import { TokenDeployer } from '@axelar-network/axelar-cgp-solidity/contracts/TokenDeployer.sol'; diff --git a/packages/axelar-local-dev/src/contracts/test/Executable.sol b/packages/axelar-local-dev/src/contracts/test/Executable.sol index e96c7140..829dd8b8 100644 --- a/packages/axelar-local-dev/src/contracts/test/Executable.sol +++ b/packages/axelar-local-dev/src/contracts/test/Executable.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.9; +pragma solidity ^0.8.0; import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol'; import { AxelarExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol'; diff --git a/packages/axelar-local-dev/src/contracts/test/ExecutableWithToken.sol b/packages/axelar-local-dev/src/contracts/test/ExecutableWithToken.sol index 77c3a035..4959d866 100644 --- a/packages/axelar-local-dev/src/contracts/test/ExecutableWithToken.sol +++ b/packages/axelar-local-dev/src/contracts/test/ExecutableWithToken.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.9; +pragma solidity ^0.8.0; import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol'; import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol'; diff --git a/packages/axelar-local-dev/src/contracts/test/ExpressWithToken.sol b/packages/axelar-local-dev/src/contracts/test/ExpressWithToken.sol index 58cfca5d..0e56f88b 100644 --- a/packages/axelar-local-dev/src/contracts/test/ExpressWithToken.sol +++ b/packages/axelar-local-dev/src/contracts/test/ExpressWithToken.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: MIT -pragma solidity 0.8.9; +pragma solidity ^0.8.0; import { AxelarExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol'; import { AxelarExpressExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/AxelarExpressExecutable.sol'; From 14e5117bd2d0da06c6bfb4b64f8720fbb5611ebf Mon Sep 17 00:00:00 2001 From: npty Date: Fri, 28 Jul 2023 09:02:22 +0700 Subject: [PATCH 17/17] chore: increase timeout --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 34e62994..ab8cb5b5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -32,7 +32,7 @@ jobs: - name: Build run: npm run build - name: Test - timeout-minutes: 8 + timeout-minutes: 15 run: | nohup sh -c "aptos node run-local-testnet --with-faucet" > nohup.out 2> nohup.err < /dev/null & sleep 10