From 929013906e424e54de7e855302708636c72a858c Mon Sep 17 00:00:00 2001 From: Foivos Date: Tue, 31 Oct 2023 15:25:57 +0200 Subject: [PATCH] feat: support for axelar-cgp-sui, deployment and relaying (#91) * added a dummy its to showcase how it works. * made some headway but still lots to do. * Added relayer functionality * prettier and update packages * update package.lock * Fixed relaying and tests, but there are errors when running in parralel * update sui binary * update sui binary * update sui binary --- .github/workflows/tests.yml | 4 +- package-lock.json | 156 ++++++++++++++++-- .../__tests__/deploy.spec.ts | 25 +-- .../__tests__/e2e.spec.ts | 37 ++++- .../__tests__/relayer.spec.ts | 10 +- .../axelar-local-dev-sui/docs/evm_to_sui.md | 2 +- .../move/axelar/Move.lock | 22 --- .../move/axelar/Move.toml | 10 -- .../move/axelar/sources/gateway.move | 23 --- .../move/sample/Move.lock | 26 +-- .../move/sample/Move.toml | 9 +- .../move/sample/sources/hello_world.move | 51 ------ .../move/sample/sources/test/test.move | 91 ++++++++++ packages/axelar-local-dev-sui/package.json | 3 +- packages/axelar-local-dev-sui/src/Command.ts | 35 +++- .../axelar-local-dev-sui/src/SuiNetwork.ts | 46 +++++- .../axelar-local-dev-sui/src/SuiRelayer.ts | 71 ++++++-- packages/axelar-local-dev-sui/src/setup.ts | 3 +- packages/axelar-local-dev-sui/src/utils.ts | 102 ++++++++++++ .../axelar-local-dev/src/relay/Command.ts | 1 + .../axelar-local-dev/src/relay/Relayer.ts | 2 +- 21 files changed, 535 insertions(+), 194 deletions(-) delete mode 100644 packages/axelar-local-dev-sui/move/axelar/Move.lock delete mode 100644 packages/axelar-local-dev-sui/move/axelar/Move.toml delete mode 100644 packages/axelar-local-dev-sui/move/axelar/sources/gateway.move delete mode 100644 packages/axelar-local-dev-sui/move/sample/sources/hello_world.move create mode 100644 packages/axelar-local-dev-sui/move/sample/sources/test/test.move diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b365680d..3c528fe7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,8 +21,8 @@ jobs: - name: Download and Install Sui Binary run: | - wget https://github.com/MystenLabs/sui/releases/download/devnet-v1.7.0/sui-devnet-v1.7.0-ubuntu-x86_64.tgz - tar -xvf sui-devnet-v1.7.0-ubuntu-x86_64.tgz + wget https://github.com/MystenLabs/sui/releases/download/mainnet-v1.11.2/sui-mainnet-v1.11.2-ubuntu-x86_64.tgz + tar -xvf sui-mainnet-v1.11.2-ubuntu-x86_64.tgz sudo mv ./target/release/sui-test-validator-ubuntu-x86_64 /usr/local/bin/sui-test-validator sudo mv ./target/release/sui-ubuntu-x86_64 /usr/local/bin/sui diff --git a/package-lock.json b/package-lock.json index c91917e4..45497131 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,130 @@ "node": ">=16" } }, + "node_modules/@axelar-network/axelar-cgp-sui": { + "version": "0.0.0", + "resolved": "git+ssh://git@github.com/axelarnetwork/axelar-cgp-sui.git#5f00c0b3cfde39998de40884b64a0f2a3d14755a", + "license": "MIT", + "dependencies": { + "@mysten/sui.js": "^0.42.0", + "child_process": "^1.0.2", + "dotenv": "^16.3.1", + "ethers": "^5.0.0", + "fs": "^0.0.1-security", + "secp256k1": "^5.0.0", + "tmp": "^0.2.1" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@axelar-network/axelar-cgp-sui/node_modules/@mysten/bcs": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@mysten/bcs/-/bcs-0.7.4.tgz", + "integrity": "sha512-6DKzM4L10Au3Og5EJRBqJZmXWZ7hS/clVjbVUH4sA0aFtS3AZo2xc+r5fUFfdJbaWZUxVaDiQ8BNiEZWkAnEOw==", + "dependencies": { + "bs58": "^5.0.0" + } + }, + "node_modules/@axelar-network/axelar-cgp-sui/node_modules/@mysten/sui.js": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@mysten/sui.js/-/sui.js-0.42.0.tgz", + "integrity": "sha512-khYpfrWTRNk7WTuDWJx/KCbleqY1B40gVRt1DttqlNkD2lvg134xZn7F/r94jxgnUETbK+hVoQvBY7F36MsfRw==", + "dependencies": { + "@mysten/bcs": "0.7.4", + "@noble/curves": "^1.1.0", + "@noble/hashes": "^1.3.1", + "@open-rpc/client-js": "^1.8.1", + "@scure/bip32": "^1.3.1", + "@scure/bip39": "^1.2.1", + "@suchipi/femver": "^1.0.0", + "events": "^3.3.0", + "superstruct": "^1.0.3", + "tweetnacl": "^1.0.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@axelar-network/axelar-cgp-sui/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@axelar-network/axelar-cgp-sui/node_modules/@scure/bip32": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", + "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", + "dependencies": { + "@noble/curves": "~1.2.0", + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@axelar-network/axelar-cgp-sui/node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@axelar-network/axelar-cgp-sui/node_modules/base-x": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==" + }, + "node_modules/@axelar-network/axelar-cgp-sui/node_modules/bs58": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "dependencies": { + "base-x": "^4.0.0" + } + }, + "node_modules/@axelar-network/axelar-cgp-sui/node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/@axelar-network/axelar-cgp-sui/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/@axelar-network/axelar-cgp-sui/node_modules/secp256k1": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.0.tgz", + "integrity": "sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==", + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^5.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@axelar-network/axelar-chains-config": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@axelar-network/axelar-chains-config/-/axelar-chains-config-0.1.0.tgz", @@ -7606,8 +7730,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "devOptional": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base-x": { "version": "3.0.9", @@ -7904,7 +8027,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "devOptional": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8520,6 +8642,11 @@ "node": "*" } }, + "node_modules/child_process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", + "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==" + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -8981,8 +9108,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "devOptional": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/concat-stream": { "version": "2.0.0", @@ -12333,6 +12459,11 @@ "node": ">= 0.6" } }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -12373,8 +12504,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "devOptional": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.2", @@ -13361,7 +13491,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "devOptional": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -14329,7 +14458,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "devOptional": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -19092,7 +19220,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "devOptional": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -19967,7 +20094,6 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", - "devOptional": true, "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -21157,7 +21283,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "devOptional": true, "dependencies": { "wrappy": "1" } @@ -21853,7 +21978,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -23273,7 +23397,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "devOptional": true, "dependencies": { "glob": "^7.1.3" }, @@ -25597,7 +25720,6 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, "dependencies": { "rimraf": "^3.0.0" }, @@ -29368,8 +29490,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "devOptional": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "4.0.2", @@ -29812,6 +29933,7 @@ "version": "2.1.1", "license": "ISC", "dependencies": { + "@axelar-network/axelar-cgp-sui": "https://github.com/axelarnetwork/axelar-cgp-sui.git", "@axelar-network/axelar-local-dev": "2.1.1", "@mysten/sui.js": "^0.41.0" } diff --git a/packages/axelar-local-dev-sui/__tests__/deploy.spec.ts b/packages/axelar-local-dev-sui/__tests__/deploy.spec.ts index b2cb92e3..24d7fb1a 100644 --- a/packages/axelar-local-dev-sui/__tests__/deploy.spec.ts +++ b/packages/axelar-local-dev-sui/__tests__/deploy.spec.ts @@ -1,6 +1,5 @@ import { SuiNetwork } from '../src/SuiNetwork'; import { TransactionBlock } from '@mysten/sui.js/transactions'; -import { toHEX } from '@mysten/bcs'; import path from 'path'; describe('Sui Network', () => { @@ -13,36 +12,38 @@ describe('Sui Network', () => { it('should deploy a sample module', async () => { const response = await client.deploy(path.join(__dirname, '../move/sample')); - expect(response.packages.length).toBe(client.gatewayObjects.length); - expect(response.packages[0].packageId).toBe(client.gatewayObjects[0].packageId); + + expect(response.packages.length).toBe(1); }); it('should deploy and execute a function', async () => { const response = await client.deploy(path.join(__dirname, '../move/sample')); - + const packageId = response.packages[0].packageId; + const singleton: any = response.publishTxn.objectChanges?.find((change) => (change as any).objectType === `${packageId}::test::Singleton` ) + const tx = new TransactionBlock(); const msg = 'hello from test'; - const msgBytes = new Uint8Array(Buffer.from(msg, 'utf8')); tx.moveCall({ - target: `${response.packages[0].packageId}::hello_world::execute`, - arguments: [tx.pure('0x0'), tx.pure('Avalanche'), tx.pure('0x0'), tx.pure(toHEX(msgBytes))], + target: `${response.packages[0].packageId}::test::send_call`, + arguments: [tx.object(singleton.objectId), tx.pure('Avalanche'), tx.pure('0x0'), tx.pure(msg)], }); await client.execute(tx); const { data } = await client.queryEvents({ query: { MoveModule: { - module: `hello_world`, - package: response.packages[0].packageId, + module: `test`, + package: packageId, }, }, limit: 1, }); - const updatedMessage = (data[0].parsedJson as any).updated_message; + const event = (data[0].parsedJson as any); - // query the value - expect(updatedMessage).toEqual(msg); + expect(event.destination_chain).toEqual('Avalanche'); + expect(event.destination_address).toEqual('0x0'); + expect(String.fromCharCode(...event.payload)).toEqual(msg); }); }); diff --git a/packages/axelar-local-dev-sui/__tests__/e2e.spec.ts b/packages/axelar-local-dev-sui/__tests__/e2e.spec.ts index 5038d8b0..4949f0c4 100644 --- a/packages/axelar-local-dev-sui/__tests__/e2e.spec.ts +++ b/packages/axelar-local-dev-sui/__tests__/e2e.spec.ts @@ -1,8 +1,9 @@ import { Contract, ethers } from 'ethers'; import { TransactionBlock } from '@mysten/sui.js/transactions'; -import { Network, createNetwork, deployContract, EvmRelayer, RelayerType } from '@axelar-network/axelar-local-dev'; +import { Network, createNetwork, deployContract, EvmRelayer, RelayerType, evmRelayer } from '@axelar-network/axelar-local-dev'; import { SuiNetwork, SuiRelayer, initSui } from '@axelar-network/axelar-local-dev-sui'; import path from 'path'; +const { arrayify } = ethers.utils; describe('e2e', () => { let client: SuiNetwork; @@ -33,6 +34,9 @@ describe('e2e', () => { // Deploy a sample module const response = await client.deploy(path.join(__dirname, '../move/sample')); + const packageId = response.packages[0].packageId; + const singleton: any = response.publishTxn.objectChanges?.find((change) => (change as any).objectType === `${packageId}::test::Singleton` ) + const msg = 'hello from sui'; const payload = ethers.utils.defaultAbiCoder.encode(['string'], [msg]); @@ -40,8 +44,8 @@ describe('e2e', () => { // Send a callContract transaction const tx = new TransactionBlock(); tx.moveCall({ - target: `${response.packages[0].packageId}::hello_world::call`, - arguments: [tx.pure(evmChainName), tx.pure(evmContract.address), tx.pure(payload), tx.pure(1)], + target: `${response.packages[0].packageId}::test::send_call`, + arguments: [tx.object(singleton.objectId), tx.pure(evmChainName), tx.pure(evmContract.address), tx.pure(String.fromCharCode(...arrayify(payload)))], }); await client.execute(tx); @@ -63,9 +67,26 @@ describe('e2e', () => { // Deploy a sample module const response = await client.deploy(path.join(__dirname, '../move/sample')); + const packageId = response.packages[0].packageId; + const singleton: any = response.publishTxn.objectChanges?.find((change) => (change as any).objectType === `${packageId}::test::Singleton` ) + + const singletonFields: any = await client.getObject({ + id: singleton.objectId, + options: { + showContent: true, + } + + }); + + let tx = new TransactionBlock(); + tx.moveCall({ + target: `${packageId}::test::register_transaction`, + arguments: [tx.object(client.axelarDiscoveryId), tx.object(singleton.objectId)], + }); + await client.execute(tx); // add sibling - await evmContract.addSibling('sui', `${response.packages[0].packageId}::hello_world`); + await evmContract.addSibling('sui', singletonFields.data.content.fields.channel.fields.id.id); await evmContract.set('sui', 'hello from evm', { value: 10000000, }); @@ -75,15 +96,15 @@ describe('e2e', () => { const { data } = await client.queryEvents({ query: { MoveModule: { - module: `hello_world`, - package: response.packages[0].packageId, + module: `test`, + package: packageId, }, }, limit: 1, }); - const updatedMessage = (data[0].parsedJson as any).updated_message; + const updatedMessage = (data[0].parsedJson as any).data; - expect(updatedMessage).toEqual('hello from evm'); + expect(String.fromCharCode(...updatedMessage)).toEqual('hello from evm'); }); }); diff --git a/packages/axelar-local-dev-sui/__tests__/relayer.spec.ts b/packages/axelar-local-dev-sui/__tests__/relayer.spec.ts index d6d1fcfd..b2d45833 100644 --- a/packages/axelar-local-dev-sui/__tests__/relayer.spec.ts +++ b/packages/axelar-local-dev-sui/__tests__/relayer.spec.ts @@ -4,6 +4,8 @@ import path from 'path'; import { ethers } from 'ethers'; import { TransactionBlock } from '@mysten/sui.js/transactions'; +const { utils: { arrayify } } = ethers; + describe('relayer', () => { let client: SuiNetwork; let relayer: SuiRelayer; @@ -22,15 +24,17 @@ describe('relayer', () => { // Deploy a sample module const response = await client.deploy(path.join(__dirname, '../move/sample')); - + const packageId = response.packages[0].packageId; + const singleton: any = response.publishTxn.objectChanges?.find((change) => (change as any).objectType === `${packageId}::test::Singleton` ) + const msg = 'hello from sui'; const payload = ethers.utils.defaultAbiCoder.encode(['string'], [msg]); // Send a callContract transaction const tx = new TransactionBlock(); tx.moveCall({ - target: `${response.packages[0].packageId}::hello_world::call`, - arguments: [tx.pure('Avalanche'), tx.pure('0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789'), tx.pure(payload), tx.pure(1)], + target: `${packageId}::test::send_call`, + arguments: [tx.object(singleton.objectId), tx.pure('Avalanche'), tx.pure('0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789'), tx.pure(String.fromCharCode(...arrayify(payload)))], }); await client.execute(tx); diff --git a/packages/axelar-local-dev-sui/docs/evm_to_sui.md b/packages/axelar-local-dev-sui/docs/evm_to_sui.md index a9999c29..62316512 100644 --- a/packages/axelar-local-dev-sui/docs/evm_to_sui.md +++ b/packages/axelar-local-dev-sui/docs/evm_to_sui.md @@ -6,7 +6,7 @@ In this guide, we demonstrate how to facilitate a transaction relay from an EVM ```ts import { createSuiRelayer, RelayerType, initSui } from '@axelar-network/axelar-local-dev-sui'; -import { EvmRelayer, createNetwork, deployContract } from '@axelar-network/axelar-local-dev +import { EvmRelayer, createNetwork, deployContract } from '@axelar-network/axelar-local-dev'; import { ethers } from 'ethers'; import path from 'path'; ``` diff --git a/packages/axelar-local-dev-sui/move/axelar/Move.lock b/packages/axelar-local-dev-sui/move/axelar/Move.lock deleted file mode 100644 index 5f3046db..00000000 --- a/packages/axelar-local-dev-sui/move/axelar/Move.lock +++ /dev/null @@ -1,22 +0,0 @@ -# @generated by Move, please check-in and do not edit manually. - -[move] -version = 0 -manifest_digest = "41863A8E1B55929B6713FF8B6C3AA6096DF15222639F12D05BC7DAB5FB4EC4C5" -deps_digest = "112928C94A84031C09CD9B9D1D44B149B73FC0EEA5FA8D8B2D7CA4D91936335A" - -dependencies = [ - { name = "Sui" }, -] - -[[move.package]] -name = "MoveStdlib" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "77a9e0d", subdir = "crates/sui-framework/packages/move-stdlib" } - -[[move.package]] -name = "Sui" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "77a9e0d", subdir = "crates/sui-framework/packages/sui-framework" } - -dependencies = [ - { name = "MoveStdlib" }, -] diff --git a/packages/axelar-local-dev-sui/move/axelar/Move.toml b/packages/axelar-local-dev-sui/move/axelar/Move.toml deleted file mode 100644 index 4bf43836..00000000 --- a/packages/axelar-local-dev-sui/move/axelar/Move.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "axelar" -version = "0.0.1" - -[dependencies] -Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "77a9e0d"} - -[addresses] -axelar="0x0" -sui = "0x2" diff --git a/packages/axelar-local-dev-sui/move/axelar/sources/gateway.move b/packages/axelar-local-dev-sui/move/axelar/sources/gateway.move deleted file mode 100644 index 9a52b612..00000000 --- a/packages/axelar-local-dev-sui/move/axelar/sources/gateway.move +++ /dev/null @@ -1,23 +0,0 @@ -// Todo: remove this file once we have a proper gateway module -module axelar::gateway { - use sui::event::emit; - use sui::hash::keccak256; - - struct ContractCall has copy, drop { - source: vector, - destination_chain: vector, - destination_address: vector, - payload: vector, - payload_hash: vector, - } - - public fun call_contract(destination_chain: vector, destination_address: vector, payload: vector) { - emit(ContractCall { - source: b"sui", - destination_chain, - destination_address, - payload, - payload_hash: keccak256(&payload) - }); - } -} diff --git a/packages/axelar-local-dev-sui/move/sample/Move.lock b/packages/axelar-local-dev-sui/move/sample/Move.lock index e2199033..d805a92c 100644 --- a/packages/axelar-local-dev-sui/move/sample/Move.lock +++ b/packages/axelar-local-dev-sui/move/sample/Move.lock @@ -2,30 +2,30 @@ [move] version = 0 -manifest_digest = "40E7CC48C373DB6DD2C189111F63B77B78F93EE3EA2E1256D46DF2BD70069755" -deps_digest = "C6C93A018C0B43F8AA8899B23B42E757DF9300785637696481F59EBFA35625D2" +manifest_digest = "B9756E20D89B038C4DA36DD158F55936F14526859C71CDB9AFEA61AE4AAD0903" +deps_digest = "3C4103934B1E040BB6B23F1D610B4EF9F2F1166A50A104EADCF77467C004C600" dependencies = [ + { name = "Axelar" }, { name = "Sui" }, - { name = "axelar" }, ] [[move.package]] -name = "MoveStdlib" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "77a9e0d", subdir = "crates/sui-framework/packages/move-stdlib" } - -[[move.package]] -name = "Sui" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "77a9e0d", subdir = "crates/sui-framework/packages/sui-framework" } +name = "Axelar" +source = { local = "../../../../node_modules/@axelar-network/axelar-cgp-sui/move/axelar" } dependencies = [ - { name = "MoveStdlib" }, + { name = "Sui" }, ] [[move.package]] -name = "axelar" -source = { local = "../axelar" } +name = "MoveStdlib" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "framework/devnet", subdir = "crates/sui-framework/packages/move-stdlib" } + +[[move.package]] +name = "Sui" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "framework/devnet", subdir = "crates/sui-framework/packages/sui-framework" } dependencies = [ - { name = "Sui" }, + { name = "MoveStdlib" }, ] diff --git a/packages/axelar-local-dev-sui/move/sample/Move.toml b/packages/axelar-local-dev-sui/move/sample/Move.toml index e2a43fdd..2ec656d6 100644 --- a/packages/axelar-local-dev-sui/move/sample/Move.toml +++ b/packages/axelar-local-dev-sui/move/sample/Move.toml @@ -1,11 +1,10 @@ [package] -name = "axelar_sui_sample" +name = "Test" version = "0.0.1" [dependencies] -Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "77a9e0d"} -axelar = { local = "../axelar" } +Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/devnet" } +Axelar = { local = "../../../../node_modules/@axelar-network/axelar-cgp-sui/move/axelar" } [addresses] -axelar_sui_sample = "0x0" -sui = "0x2" +test = "0x0" \ No newline at end of file diff --git a/packages/axelar-local-dev-sui/move/sample/sources/hello_world.move b/packages/axelar-local-dev-sui/move/sample/sources/hello_world.move deleted file mode 100644 index b9f92000..00000000 --- a/packages/axelar-local-dev-sui/move/sample/sources/hello_world.move +++ /dev/null @@ -1,51 +0,0 @@ -module axelar_sui_sample::hello_world { - use std::string::{utf8, String}; - use sui::object::{Self, ID, UID}; - use sui::transfer; - use sui::hex::{decode}; - use sui::event::emit; - use axelar::gateway; - use sui::tx_context::{TxContext}; - - struct MessageChangeEvent has copy, drop { - id: ID, - updated_message: String, - } - - struct MessageHolder has key { - id: UID, - message: String, - } - - fun init(tx: &mut TxContext) { - transfer::share_object(MessageHolder { - id: object::new(tx), - message: utf8(b"init"), - }); - } - - public fun get_message(messageHolder: &MessageHolder): String { - messageHolder.message - } - - public entry fun call(destination_chain: vector, destination_address: vector, payload: vector, _fee_amount: u64) { - gateway::call_contract(destination_chain, destination_address, payload); - } - - public entry fun execute(_command_id: vector, _source_chain: String, _source_address: String, payload: vector, ctx: &mut TxContext) { - // TODO: skip checking command_id with gateway module for now - - let message = utf8(decode(payload)); - let event = MessageHolder { - id: object::new(ctx), - message - }; - - emit(MessageChangeEvent { - id: object::uid_to_inner(&event.id), - updated_message: event.message, - }); - - transfer::share_object(event); - } -} diff --git a/packages/axelar-local-dev-sui/move/sample/sources/test/test.move b/packages/axelar-local-dev-sui/move/sample/sources/test/test.move new file mode 100644 index 00000000..7aff6387 --- /dev/null +++ b/packages/axelar-local-dev-sui/move/sample/sources/test/test.move @@ -0,0 +1,91 @@ +module test::test { + use std::ascii; + use std::vector; + use std::string::{String}; + use std::type_name; + use std::option; + + use sui::object::{Self, UID}; + use sui::transfer; + use sui::tx_context::{TxContext}; + use sui::event; + use sui::address; + use sui::hex; + + use axelar::channel::{Self, Channel, ApprovedCall}; + use axelar::discovery::{Self, RelayerDiscovery, Transaction}; + + use axelar::gateway; + + struct Singleton has key { + id: UID, + channel: Channel, + } + + struct Executed has copy, drop { + data: vector, + } + + struct ChannelType has store { + } + + fun init(ctx: &mut TxContext) { + let singletonId = object::new(ctx); + let channel = channel::create_channel(option::none(), ctx); + transfer::share_object(Singleton { + id: singletonId, + channel, + }); + } + + public fun register_transaction(discovery: &mut RelayerDiscovery, singleton: &Singleton) { + let arguments = vector::empty>(); + let arg = vector::singleton(0); + vector::append(&mut arg, address::to_bytes(object::id_address(singleton))); + vector::push_back(&mut arguments, arg); + let tx = discovery::new_transaction( + discovery::new_description( + address::from_bytes(hex::decode(*ascii::as_bytes(&type_name::get_address(&type_name::get())))), + ascii::string(b"test"), + ascii::string(b"get_call_info") + ), + arguments, + vector[], + ); + discovery::register_transaction(discovery, &singleton.channel, tx); + } + + public fun send_call(singleton: &mut Singleton, destination_chain: String, destination_address: String, payload: vector) { + gateway::call_contract(&mut singleton.channel, destination_chain, destination_address, payload); + } + + public fun get_call_info(singleton: &Singleton): Transaction { + let arguments = vector::empty>(); + let arg = vector::singleton(2); + vector::push_back(&mut arguments, arg); + arg = vector::singleton(0); + vector::append(&mut arg, address::to_bytes(object::id_address(singleton))); + vector::push_back(&mut arguments, arg); + discovery::new_transaction( + discovery::new_description( + address::from_bytes(hex::decode(*ascii::as_bytes(&type_name::get_address(&type_name::get())))), + ascii::string(b"test"), + ascii::string(b"execute") + ), + arguments, + vector[], + ) + } + + public fun execute(call: ApprovedCall, singleton: &mut Singleton) { + let ( + _, + _, + payload, + ) = channel::consume_approved_call( + &mut singleton.channel, + call, + ); + event::emit(Executed { data: payload }); + } + } \ No newline at end of file diff --git a/packages/axelar-local-dev-sui/package.json b/packages/axelar-local-dev-sui/package.json index 9a20b5a8..a663e7f7 100644 --- a/packages/axelar-local-dev-sui/package.json +++ b/packages/axelar-local-dev-sui/package.json @@ -18,7 +18,8 @@ }, "dependencies": { "@axelar-network/axelar-local-dev": "2.1.1", - "@mysten/sui.js": "^0.41.0" + "@mysten/sui.js": "^0.41.0", + "@axelar-network/axelar-cgp-sui": "https://github.com/axelarnetwork/axelar-cgp-sui.git" }, "author": "euro@axelar.network", "license": "ISC", diff --git a/packages/axelar-local-dev-sui/src/Command.ts b/packages/axelar-local-dev-sui/src/Command.ts index 6cea6d35..8ba9eb4f 100644 --- a/packages/axelar-local-dev-sui/src/Command.ts +++ b/packages/axelar-local-dev-sui/src/Command.ts @@ -2,8 +2,10 @@ import { ethers } from 'ethers'; import { CallContractArgs, RelayData } from '@axelar-network/axelar-local-dev'; import { SuiNetwork } from './SuiNetwork'; import { TransactionBlock } from '@mysten/sui.js/transactions'; +import { BCS, getSuiMoveConfig } from '@mysten/bcs'; +import { getMoveCallFromTx } from './utils'; -const { defaultAbiCoder } = ethers.utils; +const { defaultAbiCoder, arrayify } = ethers.utils; export class Command { commandId: string; @@ -34,11 +36,36 @@ export class Command { [args.from, args.sourceAddress, args.destinationContractAddress, args.payloadHash, args.payload], [], async () => { - const tx = new TransactionBlock(); + + + let tx = new TransactionBlock(); tx.moveCall({ - target: `${args.destinationContractAddress}::execute` as any, - arguments: [tx.pure(commandId), tx.pure(args.from), tx.pure(args.sourceAddress), tx.pure(args.payload.slice(2))], + target: `${suiNetwork.axelarPackageId}::discovery::get_transaction`, + arguments: [tx.object(suiNetwork.axelarDiscoveryId), tx.pure(args.destinationContractAddress)] }); + let resp = await suiNetwork.devInspect(tx) as any; + + tx = new TransactionBlock(); + tx.moveCall(getMoveCallFromTx(tx, resp.results[0].returnValues[0][0], args.payload)); + resp = await suiNetwork.devInspect(tx) as any; + + tx = new TransactionBlock(); + + const approvedCall = tx.moveCall({ + target: `${suiNetwork.axelarPackageId}::gateway::take_approved_call`, + arguments: [ + tx.object(suiNetwork.axelarValidators as string), + tx.pure(commandId), + tx.pure(args.from), + tx.pure(args.sourceAddress), + tx.pure(args.destinationContractAddress), + tx.pure(String.fromCharCode(...arrayify(args.payload))), + ], + typeArguments: [], + }); + + tx.moveCall(getMoveCallFromTx(tx, resp.results[0].returnValues[0][0], '', approvedCall)); + return suiNetwork.execute(tx); }, 'sui', diff --git a/packages/axelar-local-dev-sui/src/SuiNetwork.ts b/packages/axelar-local-dev-sui/src/SuiNetwork.ts index 997d8f05..8bb3b559 100644 --- a/packages/axelar-local-dev-sui/src/SuiNetwork.ts +++ b/packages/axelar-local-dev-sui/src/SuiNetwork.ts @@ -5,6 +5,7 @@ import { requestSuiFromFaucetV0, getFaucetHost } from '@mysten/sui.js/faucet'; import { TransactionBlock } from '@mysten/sui.js/transactions'; import { execSync, exec } from 'child_process'; import { PublishedPackage } from './types'; +const { publishPackage, updateMoveToml } = require('@axelar-network/axelar-cgp-sui/scripts/publish-package'); /** * `SuiNetwork` class provides methods and functionalities to interact with the Sui network. @@ -16,7 +17,6 @@ import { PublishedPackage } from './types'; * @property {Ed25519Keypair} executor - Represents the keypair of the executor. * @property {string} faucetUrl - URL of the faucet for fetching tokens. * @property {string} nodeUrl - URL of the node to connect to the Sui network. - * @property {PublishedPackage[]} gatewayObjects - Array to store packages compatible with gateway. * * Main Features: * - Initialize and fund the executor account. @@ -28,7 +28,9 @@ export class SuiNetwork extends SuiClient { private executor: Ed25519Keypair; private faucetUrl: string; public nodeUrl: string; - public gatewayObjects: PublishedPackage[] = []; + public axelarValidators: string = ''; + public axelarPackageId: string = ''; + public axelarDiscoveryId: string = ''; /** * Constructs an instance of SuiNetwork @@ -37,6 +39,7 @@ export class SuiNetwork extends SuiClient { * @param faucetUrl - Optional faucet URL; defaults to localnet if not provided */ constructor(nodeUrl?: string, faucetUrl?: string) { + super({ url: nodeUrl || getFullnodeUrl('localnet') }); this.nodeUrl = nodeUrl || getFullnodeUrl('localnet'); this.faucetUrl = faucetUrl || getFaucetHost('localnet'); @@ -49,6 +52,22 @@ export class SuiNetwork extends SuiClient { async init() { // Fund executor account await this.fundWallet(this.getExecutorAddress()); + + updateMoveToml('axelar', '0x0'); + const {packageId, publishTxn } = await publishPackage('../move/axelar', this, this.executor); + updateMoveToml('axelar', packageId); + + const validators = publishTxn.objectChanges?.find((obj: any) => { + return obj.objectType && obj.objectType.endsWith('validators::AxelarValidators'); + }) as any; + this.axelarValidators = validators.objectId; + + const discovery = publishTxn.objectChanges?.find((obj: any) => { + return obj.objectType && obj.objectType.endsWith('discovery::RelayerDiscovery'); + }) as any; + this.axelarDiscoveryId = discovery.objectId; + + this.axelarPackageId = validators.objectType.slice(0, 66); } /** @@ -77,7 +96,7 @@ export class SuiNetwork extends SuiClient { } const { modules, dependencies } = JSON.parse( - execSync(`sui move build --dump-bytecode-as-base64 --path ${modulePath} --with-unpublished-dependencies`, { + execSync(`sui move build --dump-bytecode-as-base64 --path ${modulePath}`, { encoding: 'utf-8', stdio: 'pipe', // silent the output }), @@ -106,12 +125,10 @@ export class SuiNetwork extends SuiClient { throw new Error('No published packages'); } - // add gateway compatible modules - this.gatewayObjects.push(...publishedPackages.filter((p: any) => p.modules.includes('gateway'))); - return { digest: result.digest, packages: publishedPackages, + publishTxn: result, }; } @@ -124,7 +141,7 @@ export class SuiNetwork extends SuiClient { * @returns A Promise with details of the transaction execution */ public async execute(tx: TransactionBlock, keypair: Keypair = this.executor, options?: SuiTransactionBlockResponseOptions) { - // todo: add check for sui command + // todo: add check for sui command return this.signAndExecuteTransactionBlock({ signer: keypair || this.executor, transactionBlock: tx, @@ -140,6 +157,21 @@ export class SuiNetwork extends SuiClient { }); } + /** + * Signs and executes a transaction block + * + * @param tx - The transaction block to execute + * @param sender - Optional sender address to execute as, defaults to the executor address + * @returns A Promise with details of the transaction dev inspect + */ + public async devInspect(tx: TransactionBlock, sender: string = this.executor.getPublicKey().toSuiAddress()) { + // todo: add check for sui command + return this.devInspectTransactionBlock({ + sender: sender || this.executor.getPublicKey().toSuiAddress(), + transactionBlock: tx, + }); + } + /** * Queries for gateway events within a specified time range * diff --git a/packages/axelar-local-dev-sui/src/SuiRelayer.ts b/packages/axelar-local-dev-sui/src/SuiRelayer.ts index b8a23dd2..e4f251ac 100644 --- a/packages/axelar-local-dev-sui/src/SuiRelayer.ts +++ b/packages/axelar-local-dev-sui/src/SuiRelayer.ts @@ -10,10 +10,12 @@ import { RelayerType, CallContractArgs, RelayData, + getRandomID, } from '@axelar-network/axelar-local-dev'; import { Command as SuiCommand } from './Command'; import { SuiNetwork } from './SuiNetwork'; -import { getCommandId } from './utils'; +import { getBcsForGateway, getCommandId, getInputForMessage } from './utils'; +import { TransactionBlock } from '@mysten/sui.js/transactions'; const DEFAULT_GAS_LIMIT = BigInt(8e6); @@ -64,8 +66,55 @@ export class SuiRelayer extends Relayer { await this.executeSuiExecutable(toExecute); } + private approveContractCallInput( + sourceChain: string, + sourceAddress: string, + destinationAddress: string, + payloadHash: string, + commandId = getRandomID(), + ) { + const bcs = getBcsForGateway(); + const params = bcs + .ser('GenericMessage', { + source_chain: sourceChain, + source_address: sourceAddress, + payload_hash: payloadHash, + target_id: destinationAddress, + }) + .toBytes(); + const message = bcs + .ser('AxelarMessage', { + chain_id: 1, + command_ids: [commandId], + commands: ['approveContractCall'], + params: [params], + }) + .toBytes(); + + return getInputForMessage(message); + } + private async executeSuiGateway(commands: Command[]) { - // TODO: Send approve_contract_call tx to Axelar Gateway + for (const command of commands) { + const input = this.approveContractCallInput( + command.data[0], + command.data[1], + command.data[2], + command.data[3], + command.commandId, + ); + + const packageId = this.suiNetwork.axelarPackageId; + const validators = this.suiNetwork.axelarValidators; + + const tx = new TransactionBlock(); + tx.moveCall({ + target: `${packageId}::gateway::process_commands`, + arguments: [tx.object(validators), tx.pure(String.fromCharCode(...input))], + typeArguments: [], + }); + await this.suiNetwork.execute(tx); + } } private async executeSuiExecutable(commands: Command[]) { @@ -132,22 +181,18 @@ export class SuiRelayer extends Relayer { destination_address: destinationAddress, destination_chain: destinationChain, payload, - payload_hash: _payloadHash, + payload_hash, + source_id, } = eventParams; - // TODO: Investigate why using the payload hash directly causes relay failure. - // Current workaround: use keccak256 hash of the payload. - const payloadHash = keccak256(this.convertUint8ArrayToUtf8String(payload)); - const contractCallArgs: CallContractArgs = { from: 'sui', - to: this.convertUint8ArrayToUtf8String(destinationChain), - destinationContractAddress: this.convertUint8ArrayToUtf8String(destinationAddress), - payload: this.convertUint8ArrayToUtf8String(payload), - sourceAddress: event.packageId, + to: destinationChain, + destinationContractAddress: destinationAddress, + payload: hexlify(payload), + sourceAddress: source_id, transactionHash: event.id.txDigest, - // payloadHash: hexlify(_payloadHash), - payloadHash, + payloadHash: payload_hash, sourceEventIndex: parseInt(event.id.eventSeq), }; diff --git a/packages/axelar-local-dev-sui/src/setup.ts b/packages/axelar-local-dev-sui/src/setup.ts index f18b25e0..8e6282eb 100644 --- a/packages/axelar-local-dev-sui/src/setup.ts +++ b/packages/axelar-local-dev-sui/src/setup.ts @@ -1,3 +1,4 @@ +import { evmRelayer } from '@axelar-network/axelar-local-dev'; import { SuiNetwork } from './SuiNetwork'; import { SuiRelayer } from './SuiRelayer'; @@ -23,10 +24,10 @@ export async function initSui( }> { try { suiNetwork = new SuiNetwork(nodeUrl, faucetUrl); - await suiNetwork.init(); suiRelayer = new SuiRelayer(suiNetwork); + evmRelayer.otherRelayers.sui = suiRelayer; return { suiNetwork, suiRelayer }; } catch (error) { diff --git a/packages/axelar-local-dev-sui/src/utils.ts b/packages/axelar-local-dev-sui/src/utils.ts index 157f70e0..1f38c5ec 100644 --- a/packages/axelar-local-dev-sui/src/utils.ts +++ b/packages/axelar-local-dev-sui/src/utils.ts @@ -1,6 +1,108 @@ import { ethers } from 'ethers'; import { EventId } from '@mysten/sui.js/client'; +import { BCS, getSuiMoveConfig } from '@mysten/bcs'; +import { TransactionBlock } from '@mysten/sui.js'; + +const { utils: { hexlify, arrayify }} = ethers; export const getCommandId = (event: EventId) => { return ethers.utils.id([event.txDigest, event.eventSeq].join(':')); }; + +export const getBcsForGateway = () => { + const bcs = new BCS(getSuiMoveConfig()); + + // input argument for the tx + bcs.registerStructType('Input', { + data: 'vector', + proof: 'vector', + }); + + bcs.registerStructType('Proof', { + // operators is a 33 byte / for now at least + operators: 'vector>', + weights: 'vector', + threshold: 'u128', + signatures: 'vector>', + }); + + // internals of the message + bcs.registerStructType('AxelarMessage', { + chain_id: 'u64', + command_ids: 'vector
', + commands: 'vector', + params: 'vector>', + }); + + // internals of the message + bcs.registerStructType('TransferOperatorshipMessage', { + operators: 'vector>', + weights: 'vector', + threshold: 'u128', + }); + bcs.registerStructType('GenericMessage', { + source_chain: 'string', + source_address: 'string', + target_id: 'address', + payload_hash: 'address', + }); + + return bcs; +}; + +export const getInputForMessage = (message: Uint8Array) => { + const bcs = getBcsForGateway(); + const proof = bcs + .ser('Proof', { + operators: [], + weights: [], + threshold: 0, + signatures: [], + }) + .toBytes(); + + const input = bcs + .ser('Input', { + data: message, + proof: proof, + }) + .toBytes(); + return input; +}; + +export const getMoveCallFromTx = (tx: TransactionBlock, txData: any, payload: string, callContractObj: any = null) => { + const bcs = new BCS(getSuiMoveConfig()); + + // input argument for the tx + bcs.registerStructType("Description", { + packageId: "address", + module_name: "string", + name: "string", + }); + bcs.registerStructType("Transaction", { + function: "Description", + arguments: "vector>", + type_arguments: "vector", + }); + let txInfo = bcs.de('Transaction', new Uint8Array(txData)); + const decodeArgs = (args: Uint8Array[]) => args.map(arg => { + if(arg[0] === 0) { + return tx.object(hexlify(arg.slice(1))); + } else if (arg[0] === 1) { + return tx.pure(arg.slice(1)); + } else if (arg[0] === 2) { + return callContractObj + } else if (arg[0] === 3) { + return tx.pure(String.fromCharCode(...arrayify(payload))); + } else { + throw new Error(`Invalid argument prefix: ${arg[0]}`); + } + }); + const decodeDescription = (description: any) => `${description.packageId}::${description.module_name}::${description.name}`; + + return{ + target: decodeDescription(txInfo.function), + arguments: decodeArgs(txInfo.arguments), + typeArguments: txInfo.type_arguments.map(decodeDescription), + } as any; +} \ No newline at end of file diff --git a/packages/axelar-local-dev/src/relay/Command.ts b/packages/axelar-local-dev/src/relay/Command.ts index bb2c6252..344550e8 100644 --- a/packages/axelar-local-dev/src/relay/Command.ts +++ b/packages/axelar-local-dev/src/relay/Command.ts @@ -25,6 +25,7 @@ export class Command { this.commandId = commandId; this.name = name; this.data = data; + this.encodedData = (chain === 'aptos' || chain === 'sui') && name === 'approve_contract_call' ? '' : defaultAbiCoder.encode(dataSignature, data); this.post = post; diff --git a/packages/axelar-local-dev/src/relay/Relayer.ts b/packages/axelar-local-dev/src/relay/Relayer.ts index c89deea8..19ca0b89 100644 --- a/packages/axelar-local-dev/src/relay/Relayer.ts +++ b/packages/axelar-local-dev/src/relay/Relayer.ts @@ -44,7 +44,7 @@ export abstract class Relayer { this.commands['near'] = []; // Update all events at the source chains await this.updateEvents(); - + await this.execute(this.commands); }