From 5788ac483331624678939bc8a8d3e09d0431cd38 Mon Sep 17 00:00:00 2001 From: Foivos Date: Tue, 9 Jul 2024 16:37:08 +0300 Subject: [PATCH] feat: convert all scripts to ts and removed unused scripts (#60) * saving progress * added the transaction builder * lint and prettier * added object cache * fixed some bugs * added some structs, adding squid structs next * added all the move types * fix an error * prettier * adding publish options in TxBuilder * fixed some issues after testing * add ts of relevant utils * use npx tsc instead * run compile properly * remove previous scripts * add an index.ts * fix imports * update bcs pure types * remove last change as it does not work * add address to bcs * shorten typeName calculation as per euro's request * prettier * add run.sh back * add coverage.sh back * get eslint to work * prettier * allow execution of scripts * stash * stash * ue correct test script --------- Co-authored-by: Milap Sheth --- .eslintrc | 2 +- .github/workflows/test.yaml | 2 +- .gitignore | 2 + .prettierrc | 6 + package-lock.json | 413 +++++++++++++++- package.json | 17 +- scripts/gateway.js | 257 ---------- scripts/governance.js | 66 --- scripts/its/discovery.js | 65 --- scripts/its/interchain_token.move | 20 - scripts/its/publish-interchain-token.js | 99 ---- scripts/its/receive-token.js | 103 ---- scripts/its/register-token.js | 140 ------ scripts/its/send-token.js | 125 ----- scripts/its/set-trusted-address.js | 110 ----- scripts/publish-all.js | 73 --- scripts/publish-package.js | 84 ---- scripts/squid/discovery.js | 57 --- scripts/squid/squid.js | 586 ----------------------- scripts/test-receive-call.js | 190 -------- scripts/test-send-call.js | 60 --- scripts/test-transfer-operatorship.js | 25 - scripts/transfer-genesis-operatorship.js | 27 -- src/bcs.ts | 118 +++++ src/index.ts | 3 + src/tx-builder.ts | 314 ++++++++++++ scripts/utils.js => src/utils.ts | 50 +- test/test.js | 17 + tsconfig.json | 34 ++ 29 files changed, 930 insertions(+), 2135 deletions(-) delete mode 100644 scripts/gateway.js delete mode 100644 scripts/governance.js delete mode 100644 scripts/its/discovery.js delete mode 100644 scripts/its/interchain_token.move delete mode 100644 scripts/its/publish-interchain-token.js delete mode 100644 scripts/its/receive-token.js delete mode 100644 scripts/its/register-token.js delete mode 100644 scripts/its/send-token.js delete mode 100644 scripts/its/set-trusted-address.js delete mode 100644 scripts/publish-all.js delete mode 100644 scripts/publish-package.js delete mode 100644 scripts/squid/discovery.js delete mode 100644 scripts/squid/squid.js delete mode 100644 scripts/test-receive-call.js delete mode 100644 scripts/test-send-call.js delete mode 100644 scripts/test-transfer-operatorship.js delete mode 100644 scripts/transfer-genesis-operatorship.js create mode 100644 src/bcs.ts create mode 100644 src/index.ts create mode 100644 src/tx-builder.ts rename scripts/utils.js => src/utils.ts (53%) create mode 100644 test/test.js create mode 100644 tsconfig.json diff --git a/.eslintrc b/.eslintrc index 0b44142f..5943b6a7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,5 @@ { - "extends": "richardpringle", + "extends": ["richardpringle", "plugin:@typescript-eslint/recommended"], "env": { "es2020": true, "mocha": true diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 91af4f9f..661729d9 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -70,4 +70,4 @@ jobs: run: npm run build - name: Test - run: npm run test + run: npm run test-move diff --git a/.gitignore b/.gitignore index 769703ba..15e5d424 100644 --- a/.gitignore +++ b/.gitignore @@ -65,4 +65,6 @@ target/ # MSVC Windows builds of rustc generate these, which store debugging information *.pdb +# dist and move_compile +dist move_comile diff --git a/.prettierrc b/.prettierrc index ee79893b..70ea16e0 100644 --- a/.prettierrc +++ b/.prettierrc @@ -11,6 +11,12 @@ "trailingComma": "all" } }, + { + "files": "*.ts", + "options": { + "trailingComma": "all" + } + }, { "files": "*.json", "options": { diff --git a/package-lock.json b/package-lock.json index 83a65f0c..35e1c859 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "@axelar-network/axelar-cgp-sui", "version": "0.1.0", + "hasInstallScript": true, "license": "MIT", "dependencies": { "@cosmjs/cosmwasm-stargate": "^0.32.2", @@ -15,10 +16,14 @@ "ethers": "^5.0.0", "fs": "^0.0.1-security", "secp256k1": "^5.0.0", - "tmp": "^0.2.1" + "tmp": "^0.2.1", + "typescript": "^5.3.3" }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.2.1", + "@types/tmp": "^0.2.6", + "@typescript-eslint/eslint-plugin": "^7.13.1", + "@typescript-eslint/parser": "^7.13.1", "dotenv": "^16.3.1", "eslint": "^8.57.0", "eslint-config-richardpringle": "^2.0.0", @@ -1699,6 +1704,233 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/tmp": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz", + "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz", + "integrity": "sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.13.1", + "@typescript-eslint/type-utils": "7.13.1", + "@typescript-eslint/utils": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.1.tgz", + "integrity": "sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.13.1", + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/typescript-estree": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", + "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz", + "integrity": "sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.13.1", + "@typescript-eslint/utils": "7.13.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", + "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", + "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.1.tgz", + "integrity": "sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.13.1", + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/typescript-estree": "7.13.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz", + "integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -1907,6 +2139,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/array.prototype.findlastindex": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", @@ -2072,6 +2313,18 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -2418,6 +2671,18 @@ "node": ">=6" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3082,6 +3347,34 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3115,6 +3408,18 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3350,6 +3655,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -3689,6 +4014,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-number-object": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", @@ -3994,6 +4328,28 @@ "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "peer": true }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -4292,6 +4648,15 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/periscopic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", @@ -4318,6 +4683,18 @@ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -4685,6 +5062,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", @@ -4888,6 +5274,30 @@ "node": ">=4" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -5011,7 +5421,6 @@ "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index e18a409f..2b8e9d94 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,16 @@ "type": "git", "url": "https://github.com/axelarnetwork/axelar-cgp-sui" }, - "main": "index.js", + "main": "dist/index.js", "scripts": { "build": "./scripts/run.sh build", - "test": "./scripts/run.sh test", + "test-move": "./scripts/run.sh test", + "test-js": "", "coverage": "./scripts/coverage.sh", - "lint": "eslint --fix './scripts/*.js'", - "prettier": "prettier --write './scripts/*.js'" + "lint": "eslint --fix './src/*.ts'", + "prettier": "prettier --write './src/*.ts'", + "compile": "npx tsc", + "postinstall": "npm run compile" }, "keywords": [ "axelar", @@ -29,10 +32,14 @@ "ethers": "^5.0.0", "fs": "^0.0.1-security", "secp256k1": "^5.0.0", - "tmp": "^0.2.1" + "tmp": "^0.2.1", + "typescript": "^5.3.3" }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.2.1", + "@types/tmp": "^0.2.6", + "@typescript-eslint/eslint-plugin": "^7.13.1", + "@typescript-eslint/parser": "^7.13.1", "dotenv": "^16.3.1", "eslint": "^8.57.0", "eslint-config-richardpringle": "^2.0.0", diff --git a/scripts/gateway.js b/scripts/gateway.js deleted file mode 100644 index ae54fd74..00000000 --- a/scripts/gateway.js +++ /dev/null @@ -1,257 +0,0 @@ -require('dotenv').config(); -const { TransactionBlock } = require('@mysten/sui.js/transactions'); -const { BCS, fromHEX, getSuiMoveConfig } = require('@mysten/bcs'); -const { CosmWasmClient } = require('@cosmjs/cosmwasm-stargate'); -const { - utils: { keccak256 }, -} = require('ethers'); -const secp256k1 = require('secp256k1'); - -function hashMessage(data) { - // sorry for putting it here... - const messagePrefix = new Uint8Array(Buffer.from('\x19Sui Signed Message:\n', 'ascii')); - const hashed = new Uint8Array(messagePrefix.length + data.length); - hashed.set(messagePrefix); - hashed.set(data, messagePrefix.length); - - return keccak256(hashed); -} - -function 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; -} - -function getOperators(axelarInfo) { - if (!axelarInfo.activeOperators) { - return { - privKeys: [], - weights: [], - threshold: 0, - }; - } - - return axelarInfo.activeOperators; -} - -function getRandomOperators(n = 5) { - let privKeys = []; - - for (let i = 0; i < n; i++) { - privKeys.push(keccak256(Math.floor(Math.random() * 10000000)).slice(2)); - } - - let pubKeys = privKeys.map((privKey) => secp256k1.publicKeyCreate(Buffer.from(privKey, 'hex'))); - const indices = Array.from(pubKeys.keys()); - const pubKeyLength = 33; - - indices.sort((a, b) => { - for (let i = 0; i < pubKeyLength; i++) { - const aByte = pubKeys[a][i]; - const bByte = pubKeys[b][i]; - if (aByte !== bByte) return aByte - bByte; - } - - return 0; - }); - pubKeys = indices.map((i) => pubKeys[i]); - privKeys = indices.map((i) => privKeys[i]); - const weights = privKeys.map((privKey) => 3); - const threshold = privKeys.length * 2; - - return { - privKeys, - pubKeys, - weights, - threshold, - }; -} - -function getInputForMessage(info, message) { - const operators = getOperators(info); - // get the public key in a compressed format - const pubKeys = operators.privKeys.map((privKey) => secp256k1.publicKeyCreate(Buffer.from(privKey, 'hex'))); - - const hashed = fromHEX(hashMessage(message)); - const signatures = operators.privKeys.map((privKey) => { - const { signature, recid } = secp256k1.ecdsaSign(hashed, Buffer.from(privKey, 'hex')); - return new Uint8Array([...signature, recid]); - }); - - const bcs = getBcsForGateway(); - const proof = bcs - .ser('Proof', { - operators: pubKeys, - weights: operators.weights, - threshold: operators.threshold, - signatures, - }) - .toBytes(); - - const input = bcs - .ser('Input', { - data: message, - proof, - }) - .toBytes(); - return input; -} - -function approveContractCallInput( - axelarInfo, - sourceChain, - sourceAddress, - destinationAddress, - payloadHash, - commandId = keccak256(new Date().getTime()), -) { - const bcs = getBcsForGateway(); - - const message = bcs - .ser('AxelarMessage', { - chain_id: 1, - command_ids: [commandId], - commands: ['approveContractCall'], - params: [ - bcs - .ser('GenericMessage', { - source_chain: sourceChain, - source_address: sourceAddress, - payload_hash: payloadHash, - target_id: destinationAddress, - }) - .toBytes(), - ], - }) - .toBytes(); - - return getInputForMessage(axelarInfo, message); -} - -function TransferOperatorshipInput(info, newOperators, newWeights, newThreshold, commandId = keccak256(new Date().getTime())) { - const bcs = getBcsForGateway(); - const message = bcs - .ser('AxelarMessage', { - chain_id: 1, - command_ids: [commandId], - commands: ['transferOperatorship'], - params: [ - bcs - .ser('TransferOperatorshipMessage', { - operators: newOperators, - weights: newWeights, - threshold: newThreshold, - }) - .toBytes(), - ], - }) - .toBytes(); - - return getInputForMessage(info, message); -} - -async function approveContractCall(client, keypair, axelarInfo, sourceChain, sourceAddress, destinationAddress, payloadHash) { - const commandId = keccak256(new Date().getTime()); - const input = approveContractCallInput(axelarInfo, sourceChain, sourceAddress, destinationAddress, payloadHash, commandId); - const packageId = axelarInfo.packageId; - const validators = axelarInfo['gateway::Gateway']; - - const tx = new TransactionBlock(); - tx.moveCall({ - target: `${packageId}::gateway::process_commands`, - arguments: [tx.object(validators.objectId), tx.pure(String.fromCharCode(...input))], - typeArguments: [], - }); - await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - }, - requestType: 'WaitForLocalExecution', - }); - return commandId; -} - -async function getAmplifierWorkers(rpc, proverAddr) { - const client = await CosmWasmClient.connect(rpc); - const workerSet = await client.queryContractSmart(proverAddr, 'get_worker_set'); - const signers = Object.values(workerSet.signers).sort((a, b) => - a.pub_key.ecdsa.toLowerCase().localeCompare(b.pub_key.ecdsa.toLowerCase()), - ); - - const pubKeys = signers.map((signer) => Buffer.from(signer.pub_key.ecdsa, 'hex')); - const weights = signers.map((signer) => Number(signer.weight)); - const threshold = Number(workerSet.threshold); - - return { pubKeys, weights, threshold }; -} - -async function transferOperatorship(info, client, keypair, newOperators, newWeights, newThreshold) { - const input = TransferOperatorshipInput(info, newOperators, newWeights, newThreshold); - const packageId = info.packageId; - const gateway = info['gateway::Gateway']; - - const tx = new TransactionBlock(); - tx.moveCall({ - target: `${packageId}::gateway::process_commands`, - arguments: [tx.object(gateway.objectId), tx.pure(String.fromCharCode(...input))], - typeArguments: [], - }); - const approveTxn = await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - }, - requestType: 'WaitForLocalExecution', - }); - console.log(approveTxn.effects.status); -} - -module.exports = { - approveContractCall, - transferOperatorship, - getRandomOperators, - getAmplifierWorkers, - getBcsForGateway, - hashMessage, -}; diff --git a/scripts/governance.js b/scripts/governance.js deleted file mode 100644 index f52c162d..00000000 --- a/scripts/governance.js +++ /dev/null @@ -1,66 +0,0 @@ -const { TransactionBlock } = require('@mysten/sui.js/transactions'); -const { setConfig, getConfig, getFullObject } = require('./utils'); - -async function initializeGovernance(upgradeCap, client, keypair, env) { - const governanceConfig = getConfig('governance', env.alias); - const packageId = governanceConfig.packageId; - - const tx = new TransactionBlock(); - tx.moveCall({ - target: `${packageId}::governance::new`, - arguments: [ - tx.pure.string('Axelar'), - tx.pure.string('the governance source addresss'), - tx.pure.u256(0), - tx.object(upgradeCap.objectId), - ], - typeArguments: [], - }); - const publishTxn = await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - showContent: true, - }, - requestType: 'WaitForLocalExecution', - }); - - const governance = publishTxn.objectChanges.find((obj) => obj.objectType === `${packageId}::governance::Governance`); - - governanceConfig['governance::Governance'] = await getFullObject(governance, client); - setConfig('governance', env.alias, governanceConfig); - - return governance; -} - -async function takeUpgradeCaps(upgradeCaps, client, keypair, env) { - const governanceConfig = getConfig('governance', env.alias); - const packageId = governanceConfig.packageId; - const tx = new TransactionBlock(); - - for (const upgradeCap of upgradeCaps) { - tx.moveCall({ - target: `${packageId}::governance::take_upgrade_cap`, - arguments: [tx.object(governanceConfig['governance::Governance'].objectId), tx.object(upgradeCap.objectId)], - typeArguments: [], - }); - } - - return await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - showContent: true, - }, - requestType: 'WaitForLocalExecution', - }); -} - -module.exports = { - initializeGovernance, - takeUpgradeCaps, -}; diff --git a/scripts/its/discovery.js b/scripts/its/discovery.js deleted file mode 100644 index 823aece2..00000000 --- a/scripts/its/discovery.js +++ /dev/null @@ -1,65 +0,0 @@ -require('dotenv').config(); -const { requestSuiFromFaucetV0, getFaucetHost } = require('@mysten/sui.js/faucet'); -const { SuiClient, getFullnodeUrl } = require('@mysten/sui.js/client'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { TransactionBlock } = require('@mysten/sui.js/transactions'); - -const { getConfig } = require('../utils'); - -async function setItsDiscovery(client, keypair, envAlias) { - const axelarInfo = getConfig('axelar', envAlias); - const itsInfo = getConfig('its', envAlias); - const itsPackageId = itsInfo.packageId; - const itsObjectId = itsInfo['its::ITS'].objectId; - const relayerDecovery = axelarInfo['discovery::RelayerDiscovery'].objectId; - - let tx = new TransactionBlock(); - - tx.moveCall({ - target: `${itsPackageId}::discovery::register_transaction`, - arguments: [tx.object(itsObjectId), tx.object(relayerDecovery)], - typeArguments: [], - }); - - await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - showContent: true, - }, - requestType: 'WaitForLocalExecution', - }); -} - -module.exports = { - setItsDiscovery, -}; - -if (require.main === module) { - const env = process.argv[2] || 'localnet'; - const chainName = process.argv[3] || 'Ethereum'; - const trustedAddress = process.argv[4] || '0x1234'; - - (async () => { - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - const keypair = Ed25519Keypair.fromSecretKey(privKey); - const address = keypair.getPublicKey().toSuiAddress(); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: getFullnodeUrl(env) }); - - try { - await requestSuiFromFaucetV0({ - // use getFaucetHost to make sure you're using correct faucet address - // you can also just use the address (see Sui Typescript SDK Quick Start for values) - host: getFaucetHost(env), - recipient: address, - }); - } catch (e) { - console.log(e); - } - - await setItsDiscovery(client, keypair, env); - })(); -} diff --git a/scripts/its/interchain_token.move b/scripts/its/interchain_token.move deleted file mode 100644 index 6fbd6a47..00000000 --- a/scripts/its/interchain_token.move +++ /dev/null @@ -1,20 +0,0 @@ -module interchain_token::$module_name { - use sui::coin::{Self}; - use sui::url::{Url}; - - public struct $witness has drop {} - - fun init(witness: $witness, ctx: &mut TxContext) { - let (treasury, metadata) = coin::create_currency<$witness>( - witness, - $decimals, - b"$symbol", - b"$name", - b"", - option::none(), - ctx - ); - transfer::public_transfer(treasury, tx_context::sender(ctx)); - transfer::public_transfer(metadata, tx_context::sender(ctx)); - } -} \ No newline at end of file diff --git a/scripts/its/publish-interchain-token.js b/scripts/its/publish-interchain-token.js deleted file mode 100644 index 99f06c29..00000000 --- a/scripts/its/publish-interchain-token.js +++ /dev/null @@ -1,99 +0,0 @@ -require('dotenv').config(); -const { requestSuiFromFaucetV0, getFaucetHost } = require('@mysten/sui.js/faucet'); -const { SuiClient, getFullnodeUrl } = require('@mysten/sui.js/client'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { TransactionBlock } = require('@mysten/sui.js/transactions'); -const fs = require('fs'); - -const { getModuleNameFromSymbol, getConfig } = require('../utils'); -const { publishPackage } = require('../publish-package'); - -const packagePath = 'interchain_token'; - -async function publishInterchainToken(client, keypair, itsInfo, name, symbol, decimals, skipRegister = false) { - let file = fs.readFileSync(`scripts/its/interchain_token.move`, 'utf8'); - let moduleName = getModuleNameFromSymbol(symbol); - let witness = moduleName.toUpperCase(); - file = file.replaceAll('$module_name', moduleName); - file = file.replaceAll('$witness', witness); - file = file.replaceAll('$name', name); - file = file.replaceAll('$symbol', symbol); - file = file.replaceAll('$decimals', decimals); - fs.writeFileSync(`move/${packagePath}/sources/interchain_token.move`, file); - - const { packageId, publishTxn } = await publishPackage(`../move/${packagePath}`, client, keypair); - - const treasuryCap = publishTxn.objectChanges.find((object) => { - return object.objectType && object.objectType.startsWith('0x2::coin::TreasuryCap'); - }); - const coinMetadata = publishTxn.objectChanges.find((object) => { - return object.objectType && object.objectType.startsWith('0x2::coin::CoinMetadata'); - }); - - coinType = `${packageId}::${moduleName}::${witness}`; - - if (skipRegister) { - return { - coinType, - treasuryCap, - coinMetadata, - }; - } - - const itsPackageId = itsInfo.packageId; - const itsObjectId = itsInfo['its::ITS'].objectId; - - const tx = new TransactionBlock(); - - tx.moveCall({ - target: `${itsPackageId}::service::give_unregistered_coin`, - arguments: [tx.object(itsObjectId), tx.object(treasuryCap.objectId), tx.object(coinMetadata.objectId)], - typeArguments: [`${packageId}::${moduleName}::${witness}`], - }); - - await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - showContent: true, - }, - requestType: 'WaitForLocalExecution', - }); - - return { - coinType, - }; -} - -module.exports = { - publishInterchainToken, -}; - -if (require.main === module) { - const symbol = process.argv[2] || 'TT'; - const decimals = process.argv[3] || 6; - const skipRegister = process.argv[4] ? process.argv[4] != 'false' : false; - const env = process.argv[5] || 'localnet'; - - (async () => { - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - const keypair = Ed25519Keypair.fromSecretKey(privKey); - const address = keypair.getPublicKey().toSuiAddress(); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: getFullnodeUrl(env) }); - - try { - await requestSuiFromFaucetV0({ - // use getFaucetHost to make sure you're using correct faucet address - // you can also just use the address (see Sui Typescript SDK Quick Start for values) - host: getFaucetHost(env), - recipient: address, - }); - } catch (e) { - console.log(e); - } - await publishInterchainToken(client, keypair, getConfig('its', env), '', symbol, decimals, skipRegister); - })(); -} diff --git a/scripts/its/receive-token.js b/scripts/its/receive-token.js deleted file mode 100644 index 4f530284..00000000 --- a/scripts/its/receive-token.js +++ /dev/null @@ -1,103 +0,0 @@ -require('dotenv').config(); -const { requestSuiFromFaucetV0, getFaucetHost } = require('@mysten/sui.js/faucet'); -const { SuiClient, getFullnodeUrl } = require('@mysten/sui.js/client'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { TransactionBlock } = require('@mysten/sui.js/transactions'); -const { BCS, getSuiMoveConfig } = require('@mysten/bcs'); - -const { arrayify, defaultAbiCoder } = require('ethers/lib/utils'); -const { registerInterchainToken } = require('./register-token'); -const { receiveCall } = require('../test-receive-call'); -const { getConfig } = require('../utils'); - -async function receiveInterchainToken( - client, - keypair, - axelarInfo, - itsInfo, - tokenId, - sourceChain, - sourceAddress, - destinationAddress, - amount, -) { - const itsPackageId = itsInfo.packageId; - const itsObjectId = itsInfo['its::ITS'].objectId; - const channelId = itsInfo['its::ITS'].channel; - - const selector = 0; - const payload = defaultAbiCoder.encode( - ['uint256', 'bytes32', 'bytes', 'bytes', 'uint256', 'bytes'], - [selector, tokenId, sourceAddress, destinationAddress, amount, '0x'], - ); - - const tx = new TransactionBlock(); - - tx.moveCall({ - target: `${itsPackageId}::its::get_trusted_address`, - arguments: [tx.object(itsObjectId), tx.pure.string(sourceChain)], - }); - - const resp = await client.devInspectTransactionBlock({ - sender: keypair.getPublicKey().toSuiAddress(), - transactionBlock: tx, - }); - - const bcs = new BCS(getSuiMoveConfig()); - - const trustedAddress = bcs.de('string', new Uint8Array(resp.results[0].returnValues[0][0])); - await receiveCall(client, keypair, axelarInfo, sourceChain, trustedAddress, channelId, payload); -} - -if (require.main === module) { - const env = process.argv[2] || 'localnet'; - - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - const keypair = Ed25519Keypair.fromSecretKey(privKey); - const address = keypair.getPublicKey().toSuiAddress(); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: getFullnodeUrl(env) }); - - const sourceChain = process.argv[3] || 'Ethereum'; - const sourceAddress = process.argv[4] || '0x1234'; - const destinationAddress = process.argv[5] || address; - const amount = process.argv[6] || 123467; - const name = process.argv[7] || 'Test Token'; - const symbol = process.argv[8] || 'TT'; - const decimals = process.argv[9] || 6; - - (async () => { - try { - await requestSuiFromFaucetV0({ - // use getFaucetHost to make sure you're using correct faucet address - // you can also just use the address (see Sui Typescript SDK Quick Start for values) - host: getFaucetHost(env), - recipient: address, - }); - } catch (e) { - console.log(e); - } - - const [tokenId, coinType] = await registerInterchainToken(client, keypair, getConfig('its', env), name, symbol, decimals); - - await receiveInterchainToken( - client, - keypair, - getConfig('axelar', env), - getConfig('its', env), - tokenId, - sourceChain, - sourceAddress, - destinationAddress, - amount, - ); - - const coins = await client.getCoins({ - owner: address, - coinType: coinType, - }); - const balance = coins.data[0].balance; - - console.log(balance); - })(); -} diff --git a/scripts/its/register-token.js b/scripts/its/register-token.js deleted file mode 100644 index 7065ca50..00000000 --- a/scripts/its/register-token.js +++ /dev/null @@ -1,140 +0,0 @@ -require('dotenv').config(); -const { requestSuiFromFaucetV0, getFaucetHost } = require('@mysten/sui.js/faucet'); -const { publishInterchainToken } = require('./publish-interchain-token'); -const { SuiClient, getFullnodeUrl } = require('@mysten/sui.js/client'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { TransactionBlock } = require('@mysten/sui.js/transactions'); -const { BCS, getSuiMoveConfig } = require('@mysten/bcs'); -const { getConfig } = require('../utils'); - -async function registerInterchainToken(client, keypair, itsInfo, name, symbol, decimals, mintAmount = false) { - const { coinType, coinMetadata, treasuryCap } = await publishInterchainToken(client, keypair, itsInfo, name, symbol, decimals, true); - - const itsPackageId = itsInfo.packageId; - const itsObjectId = itsInfo['its::ITS'].objectId; - let tx = new TransactionBlock(); - - if (mintAmount) { - tx.moveCall({ - target: `0x2::coin::mint_and_transfer`, - arguments: [tx.object(treasuryCap.objectId), tx.pure(mintAmount), tx.pure.address(keypair.getPublicKey().toSuiAddress())], - typeArguments: [coinType], - }); - } - - const coinInfo = tx.moveCall({ - target: `${itsPackageId}::coin_info::from_metadata`, - arguments: [tx.object(coinMetadata.objectId)], - typeArguments: [coinType], - }); - - const coinManagement = tx.moveCall({ - target: `${itsPackageId}::coin_management::new_with_cap`, - arguments: [tx.object(treasuryCap.objectId)], - typeArguments: [coinType], - }); - - tx.moveCall({ - target: `${itsPackageId}::service::register_coin`, - arguments: [tx.object(itsObjectId), coinInfo, coinManagement], - typeArguments: [coinType], - }); - - const result = await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - showContent: true, - }, - requestType: 'WaitForLocalExecution', - }); - const coinObjectId = mintAmount - ? result.objectChanges.find((object) => object.objectType === `0x2::coin::Coin<${coinType}>`).objectId - : null; - - const eventData = await client.queryEvents({ - query: { - MoveEventType: `${itsPackageId}::service::CoinRegistered<${coinType}>`, - }, - }); - const tokenId = eventData.data[0].parsedJson.token_id.id; - - return [tokenId, coinType, coinObjectId]; -} - -module.exports = { - registerInterchainToken, -}; - -if (require.main === module) { - const name = process.argv[2] || 'Test Token'; - const symbol = process.argv[3] || 'TT'; - const decimals = process.argv[4] || 6; - const env = process.argv[5] || 'localnet'; - - (async () => { - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - const keypair = Ed25519Keypair.fromSecretKey(privKey); - const address = keypair.getPublicKey().toSuiAddress(); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: getFullnodeUrl(env) }); - - try { - await requestSuiFromFaucetV0({ - // use getFaucetHost to make sure you're using correct faucet address - // you can also just use the address (see Sui Typescript SDK Quick Start for values) - host: getFaucetHost(env), - recipient: address, - }); - } catch (e) { - console.log(e); - } - const itsInfo = getConfig('its', env); - const [tokenId, coinType] = await registerInterchainToken(client, keypair, itsInfo, name, symbol, decimals); - - const itsPackageId = itsInfo.packageId; - const itsObjectId = itsInfo['its::ITS'].objectId; - - tx = new TransactionBlock(); - - const tokenIdObj = tx.moveCall({ - target: `${itsPackageId}::token_id::from_address`, - arguments: [tx.pure.address(tokenId)], - typeArguments: [], - }); - - tx.moveCall({ - target: `${itsPackageId}::its::token_name`, - arguments: [tx.object(itsObjectId), tokenIdObj], - typeArguments: [coinType], - }); - - tx.moveCall({ - target: `${itsPackageId}::its::token_symbol`, - arguments: [tx.object(itsObjectId), tokenIdObj], - typeArguments: [coinType], - }); - - tx.moveCall({ - target: `${itsPackageId}::its::token_decimals`, - arguments: [tx.object(itsObjectId), tokenIdObj], - typeArguments: [coinType], - }); - - let resp = await client.devInspectTransactionBlock({ - sender: keypair.getPublicKey().toSuiAddress(), - transactionBlock: tx, - }); - - const bcs = new BCS(getSuiMoveConfig()); - - { - const name = bcs.de('string', new Uint8Array(resp.results[1].returnValues[0][0])); - const symbol = bcs.de('string', new Uint8Array(resp.results[2].returnValues[0][0])); - const decimals = bcs.de('u8', new Uint8Array(resp.results[3].returnValues[0][0])); - console.log(name, symbol, decimals); - } - })(); -} diff --git a/scripts/its/send-token.js b/scripts/its/send-token.js deleted file mode 100644 index dcdbc85a..00000000 --- a/scripts/its/send-token.js +++ /dev/null @@ -1,125 +0,0 @@ -require('dotenv').config(); -const { requestSuiFromFaucetV0, getFaucetHost } = require('@mysten/sui.js/faucet'); -const { SuiClient, getFullnodeUrl } = require('@mysten/sui.js/client'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { TransactionBlock } = require('@mysten/sui.js/transactions'); -const { BCS, getSuiMoveConfig } = require('@mysten/bcs'); - -const { arrayify, defaultAbiCoder } = require('ethers/lib/utils'); -const { registerInterchainToken } = require('./register-token'); -const { getConfig } = require('../utils'); - -async function sendInterchainToken(client, keypair, itsInfo, tokenId, coin, destinationChain, destiantionAddress) { - const itsPackageId = itsInfo.packageId; - const itsObjectId = itsInfo['its::ITS'].objectId; - - let tx = new TransactionBlock(); - - let tokenIdObj = tx.moveCall({ - target: `${itsPackageId}::token_id::from_address`, - arguments: [tx.pure(tokenId)], - typeArguments: [], - }); - - tx.moveCall({ - target: `${itsPackageId}::its::get_registered_coin_type`, - arguments: [tx.object(itsObjectId), tokenIdObj], - typeArguments: [], - }); - - let resp = await client.devInspectTransactionBlock({ - sender: keypair.getPublicKey().toSuiAddress(), - transactionBlock: tx, - }); - - const bcs = new BCS(getSuiMoveConfig()); - - const coinType = bcs.de('string', new Uint8Array(resp.results[1].returnValues[0][0])); - - tx = new TransactionBlock(); - - tokenIdObj = tx.moveCall({ - target: `${itsPackageId}::token_id::from_address`, - arguments: [tx.pure(tokenId)], - typeArguments: [], - }); - - tx.moveCall({ - target: `${itsPackageId}::service::interchain_transfer`, - arguments: [ - tx.object(itsObjectId), - tokenIdObj, - tx.object(coin), - tx.pure.string(destinationChain), - tx.pure(String.fromCharCode(...arrayify(destiantionAddress))), - tx.pure(''), - ], - typeArguments: [coinType], - }); - - await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - showContent: true, - }, - requestType: 'WaitForLocalExecution', - }); -} - -if (require.main === module) { - const env = process.argv[2] || 'localnet'; - const destinationChain = process.argv[3] || 'Ethereum'; - const destiantionAddress = process.argv[4] || '0x1234'; - const amount = process.argv[5] || 1234; - const name = process.argv[6] || 'Test Token'; - const symbol = process.argv[7] || 'TT'; - const decimals = process.argv[8] || 6; - - (async () => { - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - const keypair = Ed25519Keypair.fromSecretKey(privKey); - const address = keypair.getPublicKey().toSuiAddress(); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: getFullnodeUrl(env) }); - - try { - await requestSuiFromFaucetV0({ - // use getFaucetHost to make sure you're using correct faucet address - // you can also just use the address (see Sui Typescript SDK Quick Start for values) - host: getFaucetHost(env), - recipient: address, - }); - } catch (e) { - console.log(e); - } - const itsInfo = getConfig('its', env); - const axelarInfo = getConfig('axelar', env); - - const [tokenId, coinType] = await registerInterchainToken(client, keypair, itsInfo, name, symbol, decimals, amount); - - let resp = await client.getCoins({ - owner: keypair.getPublicKey().toSuiAddress(), - coinType: coinType, - }); - coin = resp.data[0].coinObjectId; - - await sendInterchainToken(client, keypair, itsInfo, tokenId, coin, destinationChain, destiantionAddress); - - const eventData = await client.queryEvents({ - query: { - MoveEventType: `${axelarInfo.packageId}::gateway::ContractCall`, - }, - }); - const payload = eventData.data[0].parsedJson.payload; - { - const [, tokenId, sourceAddress, destinationAddress, amount, data] = defaultAbiCoder.decode( - ['uint256', 'bytes32', 'bytes', 'bytes', 'uint256', 'bytes'], - payload, - ); - console.log([tokenId, sourceAddress, destinationAddress, Number(amount), data]); - } - })(); -} diff --git a/scripts/its/set-trusted-address.js b/scripts/its/set-trusted-address.js deleted file mode 100644 index 2dadfafb..00000000 --- a/scripts/its/set-trusted-address.js +++ /dev/null @@ -1,110 +0,0 @@ -require('dotenv').config(); -const { requestSuiFromFaucetV0, getFaucetHost } = require('@mysten/sui.js/faucet'); -const { defaultAbiCoder, keccak256, arrayify } = require('ethers/lib/utils'); -const { getBcsForGateway, approveContractCall } = require('../gateway'); -const { SuiClient, getFullnodeUrl } = require('@mysten/sui.js/client'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { TransactionBlock } = require('@mysten/sui.js/transactions'); -const { BCS, getSuiMoveConfig } = require('@mysten/bcs'); -const { getConfig } = require('../utils'); - -async function setTrustedAddresses(client, keypair, envAlias, chainNames, trustedAddresses) { - const itsInfo = getConfig('its', envAlias); - const itsPackageId = itsInfo.packageId; - const itsObjectId = itsInfo['its::ITS'].objectId; - - const axelarInfo = getConfig('axelar', envAlias); - const axelarPackageId = axelarInfo.packageId; - - const governance = getConfig('governance', envAlias)['governance::Governance']; - - const bcs = new BCS(getSuiMoveConfig()); - bcs.registerStructType('TrustedAddressInfo', { - chainNames: 'vector', - trustedAddresses: 'vector', - }); - - const trustedAddressInfo = bcs - .ser('TrustedAddressInfo', { - chainNames: chainNames, - trustedAddresses: trustedAddresses, - }) - .toBytes(); - const payload = defaultAbiCoder.encode( - ['bytes32', 'bytes'], - ['0x2af37a0d5d48850a855b1aaaf57f726c107eb99b40eabf4cc1ba30410cfa2f68', trustedAddressInfo], - ); - - const payloadHash = keccak256(payload); - - const commandId = await approveContractCall( - client, - keypair, - axelarInfo, - governance.trusted_source_chain, - governance.trusted_source_address, - itsInfo['its::ITS'].channel, - payloadHash, - ); - - let tx = new TransactionBlock(); - - const ApprovedMessage = tx.moveCall({ - target: `${axelarPackageId}::gateway::take_approved_message`, - arguments: [ - tx.object(axelarInfo['gateway::Gateway'].objectId), - tx.pure.address(commandId), - tx.pure.string(governance.trusted_source_chain), - tx.pure.string(governance.trusted_source_address), - tx.pure.address(itsInfo['its::ITS'].channel), - tx.pure(bcs.ser('vector', arrayify(payload)).toBytes()), - ], - typeArguments: [], - }); - tx.moveCall({ - target: `${itsPackageId}::service::set_trusted_addresses`, - arguments: [tx.object(itsObjectId), tx.object(governance.objectId), ApprovedMessage], - typeArguments: [], - }); - - await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - }, - requestType: 'WaitForLocalExecution', - }); -} - -module.exports = { - setTrustedAddresses, -}; - -if (require.main === module) { - const env = process.argv[2] || 'localnet'; - const chainName = process.argv[3] || 'Ethereum'; - const trustedAddress = process.argv[4] || '0x1234'; - - (async () => { - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - const keypair = Ed25519Keypair.fromSecretKey(privKey); - const address = keypair.getPublicKey().toSuiAddress(); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: getFullnodeUrl(env) }); - - try { - await requestSuiFromFaucetV0({ - // use getFaucetHost to make sure you're using correct faucet address - // you can also just use the address (see Sui Typescript SDK Quick Start for values) - host: getFaucetHost(env), - recipient: address, - }); - } catch (e) { - console.log(e); - } - - await setTrustedAddresses(client, keypair, env, [chainName], [trustedAddress]); - })(); -} diff --git a/scripts/publish-all.js b/scripts/publish-all.js deleted file mode 100644 index 1dcc047f..00000000 --- a/scripts/publish-all.js +++ /dev/null @@ -1,73 +0,0 @@ -require('dotenv').config(); -const { initializeGovernance, takeUpgradeCaps } = require('./governance'); -const { SuiClient, getFullnodeUrl } = require('@mysten/sui.js/client'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { setSquidDiscovery } = require('./squid/discovery.js'); -const { publishPackageFull } = require('./publish-package'); -const { setItsDiscovery } = require('./its/discovery.js'); -const { requestSuiFromFaucet } = require('./utils'); - -async function publishAll(client, keypair, env) { - const upgradeCaps = {}; - const packageIds = {}; - - for (const packagePath of ['abi', 'axelar', 'governance', 'gas_service', 'its', 'squid']) { - console.log(packagePath); - - while (true) - try { - const { packageId, publishTxn } = await publishPackageFull(packagePath, client, keypair, env); - upgradeCaps[packagePath] = publishTxn.objectChanges.find((obj) => obj.objectType === '0x2::package::UpgradeCap'); - packageIds[packagePath] = packageId; - break; - } catch (e) { - console.log(e); - console.log(`Retrying for ${packagePath}`); - } - } - - await initializeGovernance(upgradeCaps.governance, client, keypair, env); - - await takeUpgradeCaps( - ['abi', 'axelar', 'gas_service', 'its'].map((packagePath) => upgradeCaps[packagePath]), - client, - keypair, - env, - ); - - await setItsDiscovery(client, keypair, env.alias); - await setSquidDiscovery(client, keypair, env.alias); -} - -module.exports = { - publishAll, -}; - -if (require.main === module) { - const env = ((arg) => { - switch (arg?.toLowerCase()) { - case 'localnet': - case 'devnet': - case 'testnet': - case 'mainnet': - return { alias: arg, url: getFullnodeUrl(arg) }; - default: - return JSON.parse(arg); - } - })(process.argv[2] || 'localnet'); - const faucet = process.argv[3]?.toLowerCase?.() === 'true'; - - (async () => { - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - const keypair = Ed25519Keypair.fromSecretKey(privKey); - const address = keypair.getPublicKey().toSuiAddress(); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: env.url }); - - if (faucet) { - requestSuiFromFaucet(env, address); - } - - await publishAll(client, keypair, env); - })(); -} diff --git a/scripts/publish-package.js b/scripts/publish-package.js deleted file mode 100644 index 9c5e7046..00000000 --- a/scripts/publish-package.js +++ /dev/null @@ -1,84 +0,0 @@ -require('dotenv').config(); -const { requestSuiFromFaucet, updateMoveToml } = require('./utils'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { TransactionBlock } = require('@mysten/sui.js/transactions'); -const { SuiClient } = require('@mysten/sui.js/client'); -const { execSync } = require('child_process'); -const { parseEnv } = require('./utils'); -const tmp = require('tmp'); -const path = require('path'); - -async function publishPackage(packageName, client, keypair) { - updateMoveToml(packageName, '0x0'); - - // remove all controlled temporary objects on process exit - const address = keypair.getPublicKey().toSuiAddress(); - tmp.setGracefulCleanup(); - - const tmpobj = tmp.dirSync({ unsafeCleanup: true }); - - const { modules, dependencies } = JSON.parse( - execSync( - `sui move build --dump-bytecode-as-base64 --path ${path.join(__dirname, '/../move/', packageName)} --install-dir ${ - tmpobj.name - }`, - { - encoding: 'utf-8', - stdio: 'pipe', // silent the output - }, - ), - ); - - const tx = new TransactionBlock(); - const cap = tx.publish({ - modules, - dependencies, - }); - - // Transfer the upgrade capability to the sender so they can upgrade the package later if they want. - tx.transferObjects([cap], tx.pure(address)); - - const publishTxn = await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - showContent: true, - }, - }); - if (publishTxn.effects?.status.status !== 'success') throw new Error('Publish Tx failed'); - - const packageId = (publishTxn.objectChanges?.find((a) => a.type === 'published') ?? []).packageId; - - console.info(`Published package ${packageId} from address ${address}}`); - - updateMoveToml(packageName, packageId); - - return { packageId, publishTxn }; -} - -module.exports = { - publishPackage, -}; - -if (require.main === module) { - const packageName = process.argv[2] || 'axelar'; - const env = parseEnv(process.argv[3] || 'localnet'); - const faucet = process.argv[4]?.toLowerCase?.() === 'true'; - - (async () => { - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - const keypair = Ed25519Keypair.fromSecretKey(privKey); - const address = keypair.getPublicKey().toSuiAddress(); - console.log(address); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: env.url }); - - if (faucet) { - await requestSuiFromFaucet(env, address); - } - - await publishPackage(packageName, client, keypair, env); - })(); -} diff --git a/scripts/squid/discovery.js b/scripts/squid/discovery.js deleted file mode 100644 index 9386d668..00000000 --- a/scripts/squid/discovery.js +++ /dev/null @@ -1,57 +0,0 @@ -require('dotenv').config(); -const { requestSuiFromFaucetV0, getFaucetHost } = require('@mysten/sui.js/faucet'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { TransactionBlock } = require('@mysten/sui.js/transactions'); -const { SuiClient } = require('@mysten/sui.js/client'); -const { getConfig } = require('../utils'); -async function setSquidDiscovery(client, keypair, env) { - const squid_info = getConfig('squid', env.alias); - const itsId = getConfig('its', env.alias)['its::ITS'].objectId; - const relayerDiscoveryId = getConfig('axelar', env.alias)['discovery::RelayerDiscovery'].objectId; - const tx = new TransactionBlock(); - console.log(squid_info['squid::Squid'].objectId, itsId, relayerDiscoveryId); - tx.moveCall({ - target: `${squid_info.packageId}::discovery::register_transaction`, - arguments: [tx.object(squid_info['squid::Squid'].objectId), tx.object(itsId), tx.object(relayerDiscoveryId)], - type_arguments: [], - }); - - await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - }, - requestType: 'WaitForLocalExecution', - }); -} - -module.exports = { - setSquidDiscovery, -}; - -if (require.main === module) { - const env = process.argv[2] || 'localnet'; - - (async () => { - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - const keypair = Ed25519Keypair.fromSecretKey(privKey); - const address = keypair.getPublicKey().toSuiAddress(); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: getFullnodeUrl(env) }); - - try { - await requestSuiFromFaucetV0({ - // use getFaucetHost to make sure you're using correct faucet address - // you can also just use the address (see Sui Typescript SDK Quick Start for values) - host: getFaucetHost(env), - recipient: address, - }); - } catch (e) { - console.log(e); - } - - await setSquidDiscovery(client, keypair, env); - })(); -} diff --git a/scripts/squid/squid.js b/scripts/squid/squid.js deleted file mode 100644 index b1ba1b4a..00000000 --- a/scripts/squid/squid.js +++ /dev/null @@ -1,586 +0,0 @@ -require('dotenv').config(); -const { requestSuiFromFaucetV0, getFaucetHost } = require('@mysten/sui.js/faucet'); -const { publishInterchainToken } = require('../its/publish-interchain-token'); -const { publishPackageFull, publishPackage } = require('../publish-package'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { setTrustedAddresses } = require('../its/set-trusted-address'); -const { registerInterchainToken } = require('../its/register-token'); -const { TransactionBlock } = require('@mysten/sui.js/transactions'); -const { hexlify, defaultAbiCoder } = require('ethers/lib/utils'); -const { parseEnv, setConfig, getConfig } = require('../utils'); -const { receiveCall } = require('../test-receive-call'); -const { setItsDiscovery } = require('../its/discovery'); -const { SuiClient } = require('@mysten/sui.js/client'); -const { - utils: { arrayify }, -} = require('ethers'); -const { bcs } = require('@mysten/bcs'); - -const deepbook = '0xdee9'; - -const tickSize = 1e6; -const lotSize = 1e3; -const amountBase = 1e6 * 1e9; -const amountQuote = amountBase; -const amount = lotSize * 1000000; -const sourceChain = 'sourceAddress'; -const sourceAddress = 'trustedITsAddress'; - -const SWEEP_DUST_SWAP_TYPE = 0; -const DEEPBOOK_SWAP_TYPE = 1; -const SUI_TRANSFER_SWAP_TYPE = 2; -const ITS_TRANSFER_SWAP_TYPE = 3; - -const MESSAGE_TYPE_INTERCHAIN_TRANSFER = 0; - -async function sleep(ms = 100) { - await new Promise((resolve) => { - setTimeout(resolve, ms); - }); -} - -async function placeLimitOrder(client, keypair, env, isBid, price, amount) { - const { pool, accountCap, base, quote } = getConfig('trading', env.alias); - - const tx = new TransactionBlock(); - if (isBid) { - const coin = tx.moveCall({ - target: `0x2::coin::split`, - arguments: [tx.object(quote.objectId), tx.pure(Math.floor((amount * price) / 1e9))], - typeArguments: [quote.type], - }); - - tx.moveCall({ - target: `0xdee9::clob_v2::deposit_quote`, - arguments: [tx.object(pool), coin, tx.object(accountCap)], - typeArguments: [base.type, quote.type], - }); - } else { - const coin = tx.moveCall({ - target: `0x2::coin::split`, - arguments: [tx.object(base.objectId), tx.pure(amount)], - typeArguments: [base.type], - }); - - tx.moveCall({ - target: `0xdee9::clob_v2::deposit_base`, - arguments: [tx.object(pool), coin, tx.object(accountCap)], - typeArguments: [base.type, quote.type], - }); - } - - tx.moveCall({ - target: `0xdee9::clob_v2::place_limit_order`, - arguments: [ - tx.object(pool), - tx.pure(0), - tx.pure(price), - tx.pure(amount), - tx.pure(0), - tx.pure(isBid), - tx.pure(10000000000000000000), - tx.pure(3), - tx.object('0x6'), - tx.object(accountCap), - ], - typeArguments: [base.type, quote.type], - }); - - await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - }, - requestType: 'WaitForLocalExecution', - }); -} - -async function prepare(client, keypair, env) { - const address = keypair.getPublicKey().toSuiAddress(); - try { - await requestSuiFromFaucetV0({ - // use getFaucetHost to make sure you're using correct faucet address - // you can also just use the address (see Sui Typescript SDK Quick Start for values) - host: getFaucetHost(env.alias), - recipient: address, - }); - } catch (e) { - console.log(e); - } - - //await publishPackageFull('trading', client, keypair, env); - - //const config = getConfig('trading', env.alias); - - const [baseId, baseType, baseCoin] = await registerInterchainToken( - client, - keypair, - getConfig('its', env.alias), - 'Base', - 'B', - 9, - amountBase, - ); - - const [quoteId, quoteType, quoteCoin] = await registerInterchainToken( - client, - keypair, - getConfig('its', env.alias), - 'Quote', - 'Q', - 9, - amountQuote, - ); - - let tx = new TransactionBlock(); - - const creationFee = tx.splitCoins(tx.gas, [tx.pure(100 * 1e9)]); - - tx.moveCall({ - target: `${deepbook}::clob_v2::create_pool`, - arguments: [tx.pure(tickSize), tx.pure(lotSize), creationFee], - typeArguments: [baseType, quoteType], - }); - - let account = tx.moveCall({ - target: `${deepbook}::clob_v2::create_account`, - arguments: [], - typeArguments: [], - }); - - tx.moveCall({ - target: `0x2::transfer::public_transfer`, - arguments: [account, tx.pure(keypair.toSuiAddress())], - typeArguments: ['0xdee9::custodian_v2::AccountCap'], - }); - - let result = await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - }, - requestType: 'WaitForLocalExecution', - }); - - const pool = result.objectChanges.find((object) => object.objectType.startsWith('0xdee9::clob_v2::Pool<')).objectId; - const poolCap = result.objectChanges.find((object) => object.objectType.startsWith('0xdee9::clob_v2::PoolOwnerCap')).objectId; - const accountCap = result.objectChanges.find((object) => object.objectType.startsWith('0xdee9::custodian_v2::AccountCap')).objectId; - const suiCoin = result.objectChanges.find((object) => object.objectType.startsWith('0x2::coin::Coin<')).objectId; - - setConfig('trading', env.alias, { - pool, - accountCap, - base: { - type: baseType, - tokenId: baseId, - objectId: baseCoin, - }, - quote: { - type: quoteType, - tokenId: quoteId, - objectId: quoteCoin, - }, - suiCoin, - }); - - await sleep(500); - await placeLimitOrder(client, keypair, env, false, 1100e6, amountQuote); - await sleep(500); - await placeLimitOrder(client, keypair, env, true, 990e6, amountBase); - - console.log(`Prepare Done`); -} - -async function postpare(client, keypair, env) { - await setItsDiscovery(client, keypair, env.alias); - await sleep(300); - await setTrustedAddresses(client, keypair, env.alias, [sourceChain], [sourceAddress]); - - console.log(`Postpare Done`); -} - -async function placeLimitOrders(client, keypair, env, isBid, n = 10) { - if (isBid) { - for (let i = 0; i < n; i++) { - console.log(i); - const price = 1e9 - tickSize * Math.floor(1 + Math.random() * 10); - const amount = 1e9 * Math.floor(100 + Math.random() * 100); - try { - await placeLimitOrder(client, keypair, env, true, price, amount); - } catch (e) { - console.log(e); - i--; - } - } - } else { - for (let i = 0; i < n; i++) { - console.log(i); - const price = 1e9 + tickSize * Math.floor(1 + Math.random() * 10); - const amount = 1e9 * Math.floor(100 + Math.random() * 100); - try { - await placeLimitOrder(client, keypair, env, false, price, amount); - } catch (e) { - console.log(e); - i--; - } - } - } -} - -async function testBaseForQuote(client, keypair, env) { - //await placeLimitOrders(client, keypair, env, true, 10); - - const { pool, base, quote } = getConfig('trading', env.alias); - - const { packageId } = await publishPackage('trading', client, keypair); - const tx = new TransactionBlock(); - - tx.moveCall({ - target: `${packageId}::trading::predict_base_for_quote`, - arguments: [tx.object(pool), tx.pure(amount), tx.pure(lotSize), tx.object('0x6')], - typeArguments: [base.type, quote.type], - }); - - const coin = tx.moveCall({ - target: `0x2::coin::split`, - arguments: [tx.object(base.objectId), tx.pure(amount)], - typeArguments: [base.type], - }); - - const accountCap = tx.moveCall({ - target: `${deepbook}::clob_v2::create_account`, - arguments: [], - typeArguments: [], - }); - - const [leftover_base, leftover_quote] = tx.moveCall({ - target: `${deepbook}::clob_v2::swap_exact_base_for_quote`, - arguments: [ - tx.object(pool), - tx.pure(0), - accountCap, - tx.pure(amount), - coin, - tx.moveCall({ target: '0x2::coin::zero', typeArguments: [quote.type] }), - tx.object('0x6'), - ], - typeArguments: [base.type, quote.type], - }); - - tx.moveCall({ - target: `${deepbook}::custodian_v2::delete_account_cap`, - arguments: [accountCap], - typeArguments: [], - }); - - tx.moveCall({ - target: `0x2::transfer::public_transfer`, - arguments: [leftover_base, tx.pure(keypair.toSuiAddress())], - typeArguments: [`0x2::coin::Coin<${base.type}>`], - }); - tx.moveCall({ - target: `0x2::transfer::public_transfer`, - arguments: [leftover_quote, tx.pure(keypair.toSuiAddress())], - typeArguments: [`0x2::coin::Coin<${quote.type}>`], - }); - - const result = await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - }, - requestType: 'WaitForLocalExecution', - }); - - const response = await client.queryEvents({ - query: { - MoveEventType: `${packageId}::trading::Event`, - }, - }); - console.log(response.data.map((event) => event.parsedJson)); - - const quoteCoinId = result.objectChanges.find((change) => change.objectType == `0x2::coin::Coin<${quote.type}>`).objectId; - const quoteCoin = await client.getObject({ - id: quoteCoinId, - options: { - showContent: true, - }, - }); - - const baseCoinId = result.objectChanges.find( - (change) => change.objectType == `0x2::coin::Coin<${base.type}>` && change.type === 'created', - ).objectId; - const baseCoin = await client.getObject({ - id: baseCoinId, - options: { - showContent: true, - }, - }); - - console.log({ - amount_left: baseCoin.data.content.fields.balance, - output: quoteCoin.data.content.fields.balance, - }); -} - -async function testQuoteForBase(client, keypair, env) { - await placeLimitOrders(client, keypair, env, false, 10); - - const { pool, base, quote } = getConfig('trading', env.alias); - - const { packageId } = await publishPackage('trading', client, keypair); - const tx = new TransactionBlock(); - - tx.moveCall({ - target: `${packageId}::trading::predict_quote_for_base`, - arguments: [tx.object(pool), tx.pure(amount), tx.pure(lotSize), tx.object('0x6')], - typeArguments: [base.type, quote.type], - }); - - const coin = tx.moveCall({ - target: `0x2::coin::split`, - arguments: [tx.object(quote.objectId), tx.pure(amount)], - typeArguments: [quote.type], - }); - - const accountCap = tx.moveCall({ - target: `${deepbook}::clob_v2::create_account`, - arguments: [], - typeArguments: [], - }); - - const [leftover_base, leftover_quote] = tx.moveCall({ - target: `${deepbook}::clob_v2::swap_exact_quote_for_base`, - arguments: [tx.object(pool), tx.pure(0), accountCap, tx.pure(amount), tx.object('0x6'), coin], - typeArguments: [base.type, quote.type], - }); - - tx.moveCall({ - target: `${deepbook}::custodian_v2::delete_account_cap`, - arguments: [accountCap], - typeArguments: [], - }); - - tx.moveCall({ - target: `0x2::transfer::public_transfer`, - arguments: [leftover_base, tx.pure(keypair.toSuiAddress())], - typeArguments: [`0x2::coin::Coin<${base.type}>`], - }); - tx.moveCall({ - target: `0x2::transfer::public_transfer`, - arguments: [leftover_quote, tx.pure(keypair.toSuiAddress())], - typeArguments: [`0x2::coin::Coin<${quote.type}>`], - }); - - const result = await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - }, - requestType: 'WaitForLocalExecution', - }); - - const response = await client.queryEvents({ - query: { - MoveEventType: `${packageId}::trading::Event`, - }, - }); - console.log(response.data.map((event) => event.parsedJson)); - - const quoteCoinId = result.objectChanges.find( - (change) => change.objectType == `0x2::coin::Coin<${quote.type}>` && change.type === 'created', - ).objectId; - const quoteCoin = await client.getObject({ - id: quoteCoinId, - options: { - showContent: true, - }, - }); - - const baseCoinId = result.objectChanges.find( - (change) => change.objectType == `0x2::coin::Coin<${base.type}>` && change.type === 'created', - ).objectId; - const baseCoin = await client.getObject({ - id: baseCoinId, - options: { - showContent: true, - }, - }); - - console.log({ - amount_left: quoteCoin.data.content.fields.balance, - output: baseCoin.data.content.fields.balance, - }); -} - -async function test(client, keypair, env) { - const address = bcs.fixedArray(32, bcs.u8()).transform({ - input: (id) => arrayify(id), - output: (id) => hexlify(id), - }); - - const squid_info = getConfig('squid', env.alias); - const { pool, base, quote } = getConfig('trading', env.alias); - const its_info = getConfig('its', env.alias); - - const swapInfoStruct = bcs.struct('SwapInfo', { - swap_data: bcs.vector(bcs.vector(bcs.u8())), - }); - - const deepbookSwapStruct = bcs.struct('DeepbookSwap', { - swap_type: bcs.u8(), - pool_id: address, - has_base: bcs.bool(), - min_out: bcs.u64(), - base_type: bcs.string(), - quote_type: bcs.string(), - lot_size: bcs.u64(), - should_sweep: bcs.bool(), - }); - - const sweepDust = bcs.struct('SweepDust', { - swap_type: bcs.u8(), - type: bcs.string(), - }); - - const suiTransfer = bcs.struct('SuiTransfer', { - swap_type: bcs.u8(), - type: bcs.string(), - destination: address, - }); - - const itsTransfer = bcs.struct('ItsTransfer', { - swap_type: bcs.u8(), - type: bcs.string(), - token_id: address, - destination_chain: bcs.string(), - destination_address: bcs.string(), - metadata: bcs.vector(bcs.u8()), - }); - - const swapInfoData = swapInfoStruct - .serialize({ - swap_data: [ - deepbookSwapStruct - .serialize({ - swap_type: DEEPBOOK_SWAP_TYPE, - pool_id: pool, - has_base: true, - min_out: 0, - base_type: base.type.substring(2), - quote_type: quote.type.substring(2), - lot_size: lotSize, - should_sweep: true, - }) - .toBytes(), - deepbookSwapStruct - .serialize({ - swap_type: DEEPBOOK_SWAP_TYPE, - pool_id: pool, - has_base: false, - min_out: 0, - base_type: base.type.substring(2), - quote_type: quote.type.substring(2), - lot_size: lotSize, - should_sweep: true, - }) - .toBytes(), - deepbookSwapStruct - .serialize({ - swap_type: DEEPBOOK_SWAP_TYPE, - pool_id: pool, - has_base: true, - min_out: 0, - base_type: base.type.substring(2), - quote_type: quote.type.substring(2), - lot_size: lotSize, - should_sweep: true, - }) - .toBytes(), - suiTransfer - .serialize({ - swap_type: SUI_TRANSFER_SWAP_TYPE, - type: quote.type.substring(2), - destination: keypair.toSuiAddress(), - }) - .toBytes(), - ], - }) - .toBytes(); - - const payload = defaultAbiCoder.encode( - ['uint256', 'bytes32', 'bytes', 'bytes', 'uint256', 'bytes'], - [MESSAGE_TYPE_INTERCHAIN_TRANSFER, base.tokenId, '0x', squid_info['squid::Squid'].channel, amount, swapInfoData], - ); - const receipt = await receiveCall( - client, - keypair, - getConfig('axelar', env.alias), - sourceChain, - sourceAddress, - its_info['its::ITS'].channel, - payload, - ); - - const quoteCoinId = receipt.objectChanges.find( - (change) => change.type === 'created' && change.objectType === `0x2::coin::Coin<${quote.type}>`, - ).objectId; - - const quoteCoin = await client.getObject({ - id: quoteCoinId, - options: { - showContent: true, - }, - }); - - console.log({ - output: quoteCoin.data.content.fields.balance, - }); -} - -async function registerTransaction(client, keypair, env) { - const squid_info = getConfig('squid', env.alias); - const itsId = getConfig('its', env.alias)['its::ITS'].objectId; - const relayerDiscoveryId = getConfig('axelar', env.alias)['discovery::RelayerDiscovery'].objectId; - const tx = new TransactionBlock(); - console.log(squid_info['squid::Squid'].objectId, itsId, relayerDiscoveryId); - tx.moveCall({ - target: `${squid_info.packageId}::discovery::register_transaction`, - arguments: [tx.object(squid_info['squid::Squid'].objectId), tx.object(itsId), tx.object(relayerDiscoveryId)], - type_arguments: [], - }); - - await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - }, - requestType: 'WaitForLocalExecution', - }); -} - -(async () => { - const env = parseEnv(process.argv[2] || 'localnet'); - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - - // get the public key in a compressed format - const keypair = Ed25519Keypair.fromSecretKey(privKey); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: env.url }); - - await prepare(client, keypair, env); - await postpare(client, keypair, env); - await publishPackageFull('squid', client, keypair, env); - await registerTransaction(client, keypair, env); - await test(client, keypair, env); -})(); diff --git a/scripts/test-receive-call.js b/scripts/test-receive-call.js deleted file mode 100644 index c805c583..00000000 --- a/scripts/test-receive-call.js +++ /dev/null @@ -1,190 +0,0 @@ -require('dotenv').config(); - -const { BCS, getSuiMoveConfig, bcs: bcsEncoder } = require('@mysten/bcs'); -const { - utils: { keccak256, arrayify, hexlify }, -} = require('ethers'); -const { SuiClient, getFullnodeUrl } = require('@mysten/sui.js/client'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { TransactionBlock } = require('@mysten/sui.js/transactions'); -const { approveContractCall } = require('./gateway'); - -async function receiveCall(client, keypair, axelarInfo, sourceChain, sourceAddress, destinationAddress, payload) { - const axelarPackageId = axelarInfo.packageId; - const gateway = axelarInfo['gateway::Gateway']; - const discovery = axelarInfo['discovery::RelayerDiscovery']; - const payloadHash = keccak256(payload); - - await approveContractCall(client, keypair, axelarInfo, sourceChain, sourceAddress, destinationAddress, payloadHash); - - const eventData = await client.queryEvents({ - query: { - MoveEventType: `${axelarPackageId}::gateway::ContractCallApproved`, - }, - }); - const event = eventData.data[0].parsedJson; - - const discoveryArg = [0]; - discoveryArg.push(...arrayify(discovery.objectId)); - const targetIdArg = [1]; - targetIdArg.push(...arrayify(event.target_id)); - let moveCalls = [ - { - function: { - package_id: axelarPackageId, - module_name: 'discovery', - name: 'get_transaction', - }, - arguments: [discoveryArg, targetIdArg], - type_arguments: [], - }, - ]; - let isFinal = false; - - while (!isFinal) { - const tx = new TransactionBlock(); - makeCalls(tx, moveCalls, payload); - const resp = await client.devInspectTransactionBlock({ - sender: keypair.getPublicKey().toSuiAddress(), - transactionBlock: tx, - }); - - const txData = resp.results[0].returnValues[0][0]; - const nextTx = getTransactionBcs().de('Transaction', new Uint8Array(txData)); - isFinal = nextTx.isFinal; - moveCalls = nextTx.move_calls; - } - - const tx = new TransactionBlock(); - const ApprovedMessage = tx.moveCall({ - target: `${axelarPackageId}::gateway::take_approved_message`, - arguments: [ - tx.object(gateway.objectId), - tx.pure(event.cmd_id), - tx.pure(event.source_chain), - tx.pure(event.source_address), - tx.pure(event.target_id), - tx.pure(String.fromCharCode(...arrayify(payload))), - ], - typeArguments: [], - }); - makeCalls(tx, moveCalls, payload, ApprovedMessage); - return await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - }, - requestType: 'WaitForLocalExecution', - }); -} - -function makeCalls(tx, moveCalls, payload, ApprovedMessage) { - const returns = []; - - for (const call of moveCalls) { - let result = tx.moveCall(buildMoveCall(tx, call, payload, ApprovedMessage, returns)); - if (!Array.isArray(result)) result = [result]; - returns.push(result); - } -} - -function getTransactionBcs() { - const bcs = new BCS(getSuiMoveConfig()); - - // input argument for the tx - bcs.registerStructType('Function', { - package_id: 'address', - module_name: 'string', - name: 'string', - }); - bcs.registerStructType('MoveCall', { - function: 'Function', - arguments: 'vector>', - type_arguments: 'vector', - }); - bcs.registerStructType('Transaction', { - isFinal: 'bool', - move_calls: 'vector', - }); - return bcs; -} - -function buildMoveCall(tx, moveCallInfo, payload, callContractObj, previousReturns) { - const decodeArgs = (args, tx) => - args.map((arg) => { - if (arg[0] === 0) { - return tx.object(hexlify(arg.slice(1))); - } else if (arg[0] === 1) { - return tx.pure(new Uint8Array(arg.slice(1))); - } else if (arg[0] === 2) { - return callContractObj; - } else if (arg[0] === 3) { - return tx.pure(bcsEncoder.vector(bcsEncoder.u8()).serialize(arrayify(payload))); - } else if (arg[0] === 4) { - return previousReturns[arg[1]][arg[2]]; - } - - throw new Error(`Invalid argument prefix: ${arg[0]}`); - }); - const decodeDescription = (description) => `${description.package_id}::${description.module_name}::${description.name}`; - return { - target: decodeDescription(moveCallInfo.function), - arguments: decodeArgs(moveCallInfo.arguments, tx), - typeArguments: moveCallInfo.type_arguments, - }; -} - -module.exports = { - receiveCall, - getTransactionBcs, -}; - -if (require.main === module) { - (async () => { - const env = process.argv[2] || 'localnet'; - const axelarInfo = require('../info/axelar.json')[env]; - const testInfo = require('../info/test.json')[env]; - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - - const discovery = axelarInfo['discovery::RelayerDiscovery']; - - // get the public key in a compressed format - const keypair = Ed25519Keypair.fromSecretKey(privKey); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: getFullnodeUrl(env) }); - - const testPackageId = testInfo.packageId; - const test = testInfo['test::Singleton']; - - const payload = '0x1234'; - - const tx = new TransactionBlock(); - tx.moveCall({ - target: `${testPackageId}::test::register_transaction`, - arguments: [tx.object(discovery.objectId), tx.object(test.objectId)], - }); - await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - }, - requestType: 'WaitForLocalExecution', - }); - - await receiveCall(client, keypair, axelarInfo, 'Ethereum', '0x0', test.channel, payload); - - const event = ( - await client.queryEvents({ - query: { - MoveEventType: `${testPackageId}::test::Executed`, - }, - }) - ).data[0].parsedJson; - - if (hexlify(event.data) !== payload) throw new Error(`Emmited payload missmatch: ${hexlify(event.data)} != ${payload}`); - })(); -} diff --git a/scripts/test-send-call.js b/scripts/test-send-call.js deleted file mode 100644 index 46849a35..00000000 --- a/scripts/test-send-call.js +++ /dev/null @@ -1,60 +0,0 @@ -require('dotenv').config(); - -const { SuiClient } = require('@mysten/sui.js/client'); - -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { TransactionBlock } = require('@mysten/sui.js/transactions'); -const { - utils: { hexlify }, -} = require('ethers'); - -const { toPure, parseEnv } = require('./utils'); - -(async () => { - const env = parseEnv(process.argv[2] || 'localnet'); - const destinationChain = process.argv[3] || 'ethereum'; - const destinationAddress = process.argv[4] || '0x68B93045fe7D8794a7cAF327e7f855CD6Cd03BB8'; - const payload = '0x' + Buffer.from(process.argv[5] || 'hello world', 'utf8').toString('hex'); - const axelarInfo = require('../info/axelar.json')[env.alias]; - const testInfo = require('../info/test.json')[env.alias]; - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - - // get the public key in a compressed format - const keypair = Ed25519Keypair.fromSecretKey(privKey); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: env.url }); - - const axlearPackageId = axelarInfo.packageId; - const testPackageId = testInfo.packageId; - const test = testInfo['test::Singleton']; - - const tx = new TransactionBlock(); - - tx.moveCall({ - target: `${testPackageId}::test::send_call`, - arguments: [tx.object(test.objectId), tx.pure(destinationChain), tx.pure(destinationAddress), tx.pure(toPure(payload))], - typeArguments: [], - }); - - await client.signAndExecuteTransactionBlock({ - transactionBlock: tx, - signer: keypair, - options: { - showEffects: true, - showObjectChanges: true, - }, - requestType: 'WaitForLocalExecution', - }); - - const event = ( - await client.queryEvents({ - query: { - MoveEventType: `${axlearPackageId}::gateway::ContractCall`, - }, - }) - ).data[0].parsedJson; - console.log(event); - - if (hexlify(event.source_id) !== test.channel) - throw new Error(`Emmited payload missmatch: ${hexlify(event.source)} != ${test.channel}`); -})(); diff --git a/scripts/test-transfer-operatorship.js b/scripts/test-transfer-operatorship.js deleted file mode 100644 index 31773801..00000000 --- a/scripts/test-transfer-operatorship.js +++ /dev/null @@ -1,25 +0,0 @@ -require('dotenv').config(); -const { transferOperatorship, getRandomOperators } = require('./gateway'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { SuiClient } = require('@mysten/sui.js/client'); -const { parseEnv } = require('./utils'); -const fs = require('fs'); - -(async () => { - const env = parseEnv(process.argv[2] || 'localnet'); - const allInfo = require(`../info/axelar.json`); - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - - // get the public key in a compressed format - const keypair = Ed25519Keypair.fromSecretKey(privKey); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: env.url }); - - const operators = getRandomOperators(5); - - await transferOperatorship(allInfo[env.alias], client, keypair, operators.pubKeys, operators.weights, operators.threshold); - - allInfo[env.alias].activeOperators = operators; - - fs.writeFileSync(`info/axelar.json`, JSON.stringify(allInfo, null, 4)); -})(); diff --git a/scripts/transfer-genesis-operatorship.js b/scripts/transfer-genesis-operatorship.js deleted file mode 100644 index 368b818c..00000000 --- a/scripts/transfer-genesis-operatorship.js +++ /dev/null @@ -1,27 +0,0 @@ -require('dotenv').config(); -const { transferOperatorship, getAmplifierWorkers } = require('./gateway'); -const { Ed25519Keypair } = require('@mysten/sui.js/keypairs/ed25519'); -const { SuiClient } = require('@mysten/sui.js/client'); -const { parseEnv } = require('./utils'); -const fs = require('fs'); - -(async () => { - const env = parseEnv(process.argv[2] || 'localnet'); - const rpc = process.argv[3] || 'http://localhost:26657'; - const proverAddr = process.argv[4]; - const allInfo = require(`../info/axelar.json`); - const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); - - // get the public key in a compressed format - const keypair = Ed25519Keypair.fromSecretKey(privKey); - // create a new SuiClient object pointing to the network you want to use - const client = new SuiClient({ url: env.url }); - - const operators = await getAmplifierWorkers(rpc, proverAddr); - - await transferOperatorship(allInfo[env.alias], client, keypair, operators.pubKeys, operators.weights, operators.threshold); - - allInfo[env.alias].activeOperators = operators; - - fs.writeFileSync(`info/axelar.json`, JSON.stringify(allInfo, null, 4)); -})(); diff --git a/src/bcs.ts b/src/bcs.ts new file mode 100644 index 00000000..a2ed68f0 --- /dev/null +++ b/src/bcs.ts @@ -0,0 +1,118 @@ +import { bcs } from '@mysten/sui.js/bcs'; + +function getAxelarStructs() { + const Bytes32 = bcs.Address; + + const Message = bcs.struct('Message', { + source_chain: bcs.String, + message_id: bcs.String, + source_address: bcs.String, + destination_id: bcs.Address, + payload_hash: Bytes32, + }); + + const WeightedSigner = bcs.struct('WeightedSigner', { + pubkey: bcs.vector(bcs.U8), + weight: bcs.U128, + }); + + const WeightedSigners = bcs.struct('WeightedSigners', { + signers: bcs.vector(WeightedSigner), + threshold: bcs.U128, + nonce: Bytes32, + }); + + const Signature = bcs.vector(bcs.U8); + + const Proof = bcs.struct('Proof', { + signers: WeightedSigners, + signatures: bcs.vector(Signature), + }); + + const MessageToSign = bcs.struct('MessageToSign', { + domain_separator: Bytes32, + signers_hash: Bytes32, + data_hash: Bytes32, + }); + + const Function = bcs.struct('Function', { + package_id: bcs.Address, + module_name: bcs.String, + name: bcs.String, + }); + + /// Arguments are prefixed with: + /// - 0 for objects followed by exactly 32 bytes that cointain the object id + /// - 1 for pures followed by the bcs encoded form of the pure + /// - 2 for the call contract object, followed by nothing (to be passed into the target function) + /// - 3 for the payload of the contract call (to be passed into the intermediate function) + /// - 4 for an argument returned from a previous move call, followed by a u8 specified which call to get the return of (0 for the first transaction AFTER the one that gets ApprovedMessage out), and then another u8 specifying which argument to input. + const MoveCall = bcs.struct('MoveCall', { + function: Function, + arguments: bcs.vector(bcs.vector(bcs.U8)), + type_arguments: bcs.vector(bcs.String), + }); + + const Transaction = bcs.struct('Transaction', { + is_final: bcs.Bool, + move_calls: bcs.vector(MoveCall), + }); + + const EncodedMessage = bcs.struct('EncodedMessage', { + message_type: bcs.U8, + data: bcs.vector(bcs.U8), + }); + + return { + Bytes32, + Message, + WeightedSigner, + WeightedSigners, + Signature, + Proof, + MessageToSign, + Function, + MoveCall, + Transaction, + EncodedMessage, + }; +} + +function getSquidStructs() { + const DeepbookV2SwapData = bcs.struct('DeepbookV2SwapData', { + swap_type: bcs.U8, + pool_id: bcs.Address, + has_base: bcs.Bool, + min_output: bcs.U64, + base_type: bcs.String, + quote_type: bcs.String, + lot_size: bcs.U64, + should_sweep: bcs.Bool, + }); + + const SuiTransferSwapData = bcs.struct('SuiTransferSwapData', { + swap_type: bcs.U8, + coin_type: bcs.String, + recipient: bcs.Address, + }); + + const ItsTransferSwapData = bcs.struct('ItsTransferSwapData', { + swap_type: bcs.U8, + coin_type: bcs.String, + token_id: bcs.Address, + destination_chain: bcs.String, + destination_address: bcs.vector(bcs.U8), + metadata: bcs.vector(bcs.U8), + }); + + return { + DeepbookV2SwapData, + SuiTransferSwapData, + ItsTransferSwapData, + }; +} + +export const bcsStructs = { + axelarStructs: getAxelarStructs(), + squidStructs: getSquidStructs(), +}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..32d7443f --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +export * from './bcs'; +export * from './tx-builder'; +export * from './utils'; diff --git a/src/tx-builder.ts b/src/tx-builder.ts new file mode 100644 index 00000000..5ebc2ba9 --- /dev/null +++ b/src/tx-builder.ts @@ -0,0 +1,314 @@ +import { execSync } from 'child_process'; +import path from 'path'; +import { bcs, BcsType } from '@mysten/bcs'; +import { SuiClient, SuiMoveNormalizedType, SuiObjectChange, SuiTransactionBlockResponseOptions } from '@mysten/sui.js/client'; +import { Keypair } from '@mysten/sui.js/dist/cjs/cryptography'; +import { TransactionBlock, TransactionObjectInput } from '@mysten/sui.js/transactions'; +import { utils as ethersUtils } from 'ethers'; +import tmp from 'tmp'; +import { updateMoveToml } from './utils'; + +const { arrayify, hexlify } = ethersUtils; + +const objectCache = {} as { [id in string]: SuiObjectChange }; + +function updateCache(objectChanges: SuiObjectChange[]) { + for (const change of objectChanges) { + if ( + !( + change as { + objectId: string; + } + ).objectId + ) + continue; + objectCache[ + ( + change as { + objectId: string; + } + ).objectId + ] = change; + } +} + +function getObject(tx: TransactionBlock, object: TransactionObjectInput) { + if (Array.isArray(object)) { + object = hexlify(object); + } + + if (typeof object === 'string') { + const cached = objectCache[object]; + + if (cached) { + // TODO: figure out how to load the object version/digest into the TransactionBlock because it seems impossible for non gas payment objects + const txObject = tx.object(object); + return txObject; + } + + return tx.object(object); + } + + return object; +} + +function getTypeName(type: SuiMoveNormalizedType): string { + type Type = { address: string; module: string; name: string; typeArguments: string[] }; + + function get(type: Type) { + let name = `${type.address}::${type.module}::${type.name}`; + + if (type.typeArguments.length > 0) { + name += `<${type.typeArguments[0]}`; + + for (let i = 1; i < type.typeArguments.length; i++) { + name += `,${type.typeArguments[i]}`; + } + + name += '>'; + } + + return name; + } + + if ((type as { Struct: Type }).Struct) { + return get((type as { Struct: Type }).Struct); + } else if ((type as { Reference: SuiMoveNormalizedType }).Reference) { + return getTypeName((type as { Reference: SuiMoveNormalizedType }).Reference); + } else if ((type as { MutableReference: SuiMoveNormalizedType }).MutableReference) { + return getTypeName((type as { MutableReference: SuiMoveNormalizedType }).MutableReference); + } else if ((type as { Vector: SuiMoveNormalizedType }).Vector) { + return `vector<${getTypeName((type as { Vector: SuiMoveNormalizedType }).Vector)}>`; + } + + return (type as string).toLowerCase(); +} + +function getNestedStruct(tx: TransactionBlock, type: SuiMoveNormalizedType, arg: TransactionObjectInput) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let inside = type as any; + + while ((inside as { Vector: SuiMoveNormalizedType }).Vector) { + inside = inside.Vector; + } + + if (!inside.Struct && !inside.Reference && !inside.MutableReference) { + return null; + } + + if (isString(inside)) { + return null; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if ((type as any).Struct || (type as any).Reference || (type as any).MutableReference) { + return getObject(tx, arg); + } + + if (!(type as { Vector: SuiMoveNormalizedType }).Vector) return null; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const nested = (arg as any).map((arg: any) => getNestedStruct(tx, (type as any).Vector, arg)); + const typeName = getTypeName((type as { Vector: SuiMoveNormalizedType }).Vector); + return tx.makeMoveVec({ + type: typeName, + objects: nested, + }); +} + +function serialize(tx: TransactionBlock, type: SuiMoveNormalizedType, arg: TransactionObjectInput) { + const struct = getNestedStruct(tx, type, arg); + + if (struct) { + return struct; + } + + const vectorU8 = () => + bcs.vector(bcs.u8()).transform({ + input(val: unknown) { + if (typeof val === 'string') val = arrayify(val); + return val as Iterable & { length: number }; + }, + output(value: number[]) { + return hexlify(value); + }, + }); + + const serializer = (type: SuiMoveNormalizedType): BcsType => { + if (isString(type)) { + return bcs.string() as BcsType; + } + + if (typeof type === 'string') { + if (type === 'Address') { + return bcs.fixedArray(32, bcs.u8()).transform({ + input: (id) => arrayify(id as number), + output: (id) => hexlify(id), + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (bcs as any)[(type as string).toLowerCase()](); + } else if ((type as { Vector: SuiMoveNormalizedType }).Vector) { + if ((type as { Vector: SuiMoveNormalizedType }).Vector === 'U8') { + return vectorU8() as BcsType; + } + + return bcs.vector(serializer((type as { Vector: SuiMoveNormalizedType }).Vector)) as BcsType; + } + + throw new Error(`Type ${JSON.stringify(type)} cannot be serialized`); + }; + + return tx.pure(serializer(type).serialize(arg).toBytes()); +} + +function isTxContext(parameter: SuiMoveNormalizedType) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let inside = parameter as any; + + if (inside.MutableReference) { + inside = inside.MutableReference.Struct; + if (!inside) return false; + } else if (inside.Reference) { + inside = inside.Reference.Struct; + if (!inside) return false; + } else { + return false; + } + + return inside.address === '0x2' && inside.module === 'tx_context' && inside.name === 'TxContext'; +} + +function isString(parameter: SuiMoveNormalizedType) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let asAny = parameter as any; + if (asAny.MutableReference) parameter = asAny.MutableReference; + if (asAny.Reference) asAny = asAny.Reference; + asAny = asAny.Struct; + if (!asAny) return false; + const isAsciiString = asAny.address === '0x1' && asAny.module === 'ascii' && asAny.name === 'String'; + const isStringString = asAny.address === '0x1' && asAny.module === 'string' && asAny.name === 'String'; + return isAsciiString || isStringString; +} + +class TxBuilder { + client: SuiClient; + tx: TransactionBlock; + constructor(client: SuiClient) { + this.client = client; + this.tx = new TransactionBlock(); + } + + async moveCall(moveCallInfo: { + arguments?: TransactionObjectInput[]; + typeArguments?: string[]; + target: `${string}::${string}::${string}` | { package: string; module: string; function: string }; + }) { + let target = moveCallInfo.target; + + // If target is string, convert to object that `getNormalizedMoveFunction` accepts. + if (typeof target === 'string') { + const first = target.indexOf(':'); + const last = target.indexOf(':', first + 2); + const packageId = target.slice(0, first); + const module = target.slice(first + 2, last); + const functionName = target.slice(last + 2); + target = { + package: packageId, + module, + function: functionName, + }; + } + + const moveFn = await this.client.getNormalizedMoveFunction(target); + + let length = moveFn.parameters.length; + if (isTxContext(moveFn.parameters[length - 1])) length = length - 1; + if (!moveCallInfo.arguments) moveCallInfo.arguments = []; + if (length !== moveCallInfo.arguments.length) + throw new Error( + `Function ${target.package}::${target.module}::${target.function} takes ${moveFn.parameters.length} arguments but given ${moveCallInfo.arguments.length}`, + ); + + const convertedArgs = moveCallInfo.arguments.map((arg, index) => serialize(this.tx, moveFn.parameters[index], arg)); + + return this.tx.moveCall({ + target: `${target.package}::${target.module}::${target.function}`, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + arguments: convertedArgs as any, + typeArguments: moveCallInfo.typeArguments, + }); + } + + async publishPackage(packageName: string, moveDir: string = `${__dirname}/../move`) { + updateMoveToml(packageName, '0x0', moveDir); + + tmp.setGracefulCleanup(); + + const tmpobj = tmp.dirSync({ unsafeCleanup: true }); + + const { modules, dependencies } = JSON.parse( + execSync(`sui move build --dump-bytecode-as-base64 --path ${path.join(moveDir, packageName)} --install-dir ${tmpobj.name}`, { + encoding: 'utf-8', + stdio: 'pipe', // silent the output + }), + ); + + return this.tx.publish({ + modules, + dependencies, + }); + } + + async publishPackageAndTransferCap(packageName: string, to: string, moveDir = `${__dirname}/../move`) { + const cap = await this.publishPackage(packageName, moveDir); + + this.tx.transferObjects([cap], to); + } + + async signAndExecute(keypair: Keypair, options: SuiTransactionBlockResponseOptions) { + let result = await this.client.signAndExecuteTransactionBlock({ + transactionBlock: this.tx, + signer: keypair, + options: { + showEffects: true, + showObjectChanges: true, + ...options, + }, + }); + + if (!result.confirmedLocalExecution) { + while (true) { + try { + result = await this.client.getTransactionBlock({ + digest: result.digest, + options: { + showEffects: true, + showObjectChanges: true, + ...options, + }, + }); + break; + } catch (e) { + console.log(e); + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + } + } + + updateCache(result.objectChanges as SuiObjectChange[]); + return result; + } + + async devInspect(sender: string) { + const result = await this.client.devInspectTransactionBlock({ + transactionBlock: this.tx, + sender, + }); + return result; + } +} + +module.exports = { + TxBuilder, +}; diff --git a/scripts/utils.js b/src/utils.ts similarity index 53% rename from scripts/utils.js rename to src/utils.ts index e6323c47..69bec017 100644 --- a/scripts/utils.js +++ b/src/utils.ts @@ -1,17 +1,16 @@ -const fs = require('fs'); -const { requestSuiFromFaucetV0, getFaucetHost } = require('@mysten/sui.js/faucet'); -const { getFullnodeUrl } = require('@mysten/sui.js/client'); +import fs from 'fs'; +import { getFullnodeUrl } from '@mysten/sui.js/client'; -function getModuleNameFromSymbol(symbol) { - function isNumber(char) { +export function getModuleNameFromSymbol(symbol: string) { + function isNumber(char: string) { return char >= '0' && char <= '9'; } - function isLowercase(char) { + function isLowercase(char: string) { return char >= 'a' && char <= 'z'; } - function isUppercase(char) { + function isUppercase(char: string) { return char >= 'A' && char <= 'Z'; } @@ -40,27 +39,14 @@ function getModuleNameFromSymbol(symbol) { return moduleName; } -async function requestSuiFromFaucet(env, address) { - try { - await requestSuiFromFaucetV0({ - // use getFaucetHost to make sure you're using correct faucet address - // you can also just use the address (see Sui Typescript SDK Quick Start for values) - host: getFaucetHost(env.alias), - recipient: address, - }); - } catch (e) { - console.log(e); - } -} - -function updateMoveToml(packageName, packageId, moveDir = `${__dirname}/../move_compile`) { +export function updateMoveToml(packageName: string, packageId: string, moveDir: string = `${__dirname}/../move`) { const path = `${moveDir}/${packageName}/Move.toml`; let toml = fs.readFileSync(path, 'utf8'); const lines = toml.split('\n'); - const versionLineIndex = lines.findIndex((line) => line.slice(0, 7) === 'version'); + const versionLineIndex = lines.findIndex((line: string) => line.slice(0, 7) === 'version'); if (!(lines[versionLineIndex + 1].slice(0, 12) === 'published-at')) { lines.splice(versionLineIndex + 1, 0, ''); @@ -68,7 +54,7 @@ function updateMoveToml(packageName, packageId, moveDir = `${__dirname}/../move_ lines[versionLineIndex + 1] = `published-at = "${packageId}"`; - const addressesIndex = lines.findIndex((line) => line.slice(0, 11) === '[addresses]'); + const addressesIndex = lines.findIndex((line: string) => line.slice(0, 11) === '[addresses]'); for (let i = addressesIndex + 1; i < lines.length; i++) { const line = lines[i]; @@ -90,28 +76,14 @@ function updateMoveToml(packageName, packageId, moveDir = `${__dirname}/../move_ fs.writeFileSync(path, toml); } -function copyMoveModule(packageName, moveDir = `${__dirname}../move`, moveCompileDir = `${__dirname}/move_compile`) { - const src = `${moveDir}/${packageName}`; - const dest = `${moveDir}/${packageName}`; - fs.cpSync(src, dest, { recursive: true }); -} - -function parseEnv(arg) { +export function parseEnv(arg: string) { switch (arg?.toLowerCase()) { case 'localnet': case 'devnet': case 'testnet': case 'mainnet': - return { alias: arg, url: getFullnodeUrl(arg) }; + return { alias: arg, url: getFullnodeUrl(arg as 'localnet' | 'devnet' | 'testnet' | 'mainnet') }; default: return JSON.parse(arg); } } - -module.exports = { - getModuleNameFromSymbol, - parseEnv, - requestSuiFromFaucet, - updateMoveToml, - copyMoveModule, -}; diff --git a/test/test.js b/test/test.js new file mode 100644 index 00000000..52b0e3b9 --- /dev/null +++ b/test/test.js @@ -0,0 +1,17 @@ +require('dotenv').config(); +const { TxBuilder } = require('../dist/tx-builder'); +const { SuiClient, getFullnodeUrl } = require('@mysten/sui.js/client'); + +describe('test', () => { + let client, keypair; + before(async() => { + client = new SuiClient(getFullnodeUrl('localnet')); + keypair = new SuiKeypair(); + const builder = new TxBuilder(client); + + const response = await builder.signAndExecute(keypair); + }) + it('test', async () => { + + }); +}); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..4545c07e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + /* Language and Environment */ + "target": "es6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "lib": ["es2020", "dom"] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, + + /* Modules */ + "module": "CommonJS" /* Specify what module code is generated. */, + + "resolveJsonModule": true, + /* Emit */ + "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + "declarationMap": true /* Create sourcemaps for d.ts files. */, + "sourceMap": true /* Create source map files for emitted JavaScript files. */, + "rootDirs": ["./src", "src/__test__"] /* Specify the root folder within your source files. */, + "outDir": "dist" /* Specify an output folder for all emitted files. */, + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + "strictNullChecks": true /* When type checking, take into account `null` and `undefined`. */, + + /* Completeness */ + "skipDefaultLibCheck": true /* Skip type checking .d.ts files that are included with TypeScript. */, + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": ["src"], + "exclude": ["node_modules", "dist", "src/__tests__/"] +}