From 818489d2d3f23e5cd8d7940cb1056e39bd0e5495 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Tue, 12 Sep 2023 20:09:44 +0530 Subject: [PATCH 01/45] wip: intitial changes for adding ledger utils --- evm/utils.js | 5 +- package-lock.json | 1492 +++++++++++++++++++++++++++++++++++++++++++-- package.json | 3 +- 3 files changed, 1436 insertions(+), 64 deletions(-) diff --git a/evm/utils.js b/evm/utils.js index ce32f01e3..384c07e50 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -3,8 +3,11 @@ const { ContractFactory, Contract, - utils: { computeAddress, getContractAddress, keccak256, isAddress, getCreate2Address, defaultAbiCoder }, + provider, + BigNumber, + utils: { computeAddress, getContractAddress, keccak256, isAddress, getCreate2Address, defaultAbiCoder, serializeTransaction }, } = require('ethers'); +const { LedgerSigner } = require('@ethersproject/hardware-wallets'); const https = require('https'); const http = require('http'); const { outputJsonSync } = require('fs-extra'); diff --git a/package-lock.json b/package-lock.json index 56f634f39..40a19a28b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@axelar-network/axelar-gmp-sdk-solidity": "5.3.1", "@axelar-network/interchain-token-service": "0.3.0", "@cosmjs/cosmwasm-stargate": "^0.31.1", + "@ethersproject/hardware-wallets": "^5.5.0", "ethers": "^5.7.2" }, "devDependencies": { @@ -623,6 +624,30 @@ "@ethersproject/transactions": "^5.7.0" } }, + "node_modules/@ethersproject/hardware-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hardware-wallets/-/hardware-wallets-5.7.0.tgz", + "integrity": "sha512-DjMMXIisRc8xFvEoLoYz1w7JDOYmaz/a0X9sp7Zu668RR8U1zCAyj5ow25HLRW+TCzEC5XiFetTXqS5kXonFCQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ledgerhq/hw-app-eth": "5.27.2", + "@ledgerhq/hw-transport": "5.26.0", + "@ledgerhq/hw-transport-u2f": "5.26.0", + "ethers": "^5.7.0" + }, + "optionalDependencies": { + "@ledgerhq/hw-transport-node-hid": "5.26.0" + } + }, "node_modules/@ethersproject/hash": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", @@ -1141,6 +1166,190 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@ledgerhq/cryptoassets": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/cryptoassets/-/cryptoassets-5.53.0.tgz", + "integrity": "sha512-M3ibc3LRuHid5UtL7FI3IC6nMEppvly98QHFoSa7lJU0HDzQxY6zHec/SPM4uuJUC8sXoGVAiRJDkgny54damw==", + "dependencies": { + "invariant": "2" + } + }, + "node_modules/@ledgerhq/devices": { + "version": "5.51.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-5.51.1.tgz", + "integrity": "sha512-4w+P0VkbjzEXC7kv8T1GJ/9AVaP9I6uasMZ/JcdwZBS3qwvKo5A5z9uGhP5c7TvItzcmPb44b5Mw2kT+WjUuAA==", + "dependencies": { + "@ledgerhq/errors": "^5.50.0", + "@ledgerhq/logs": "^5.50.0", + "rxjs": "6", + "semver": "^7.3.5" + } + }, + "node_modules/@ledgerhq/devices/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ledgerhq/devices/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ledgerhq/devices/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/@ledgerhq/errors": { + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-5.50.0.tgz", + "integrity": "sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow==" + }, + "node_modules/@ledgerhq/hw-app-eth": { + "version": "5.27.2", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-app-eth/-/hw-app-eth-5.27.2.tgz", + "integrity": "sha512-llNdrE894cCN8j6yxJEUniciyLVcLmu5N0UmIJLOObztG+5rOF4bX54h4SreTWK+E10Z0CzHSeyE5Lz/tVcqqQ==", + "dependencies": { + "@ledgerhq/cryptoassets": "^5.27.2", + "@ledgerhq/errors": "^5.26.0", + "@ledgerhq/hw-transport": "^5.26.0", + "bignumber.js": "^9.0.1", + "rlp": "^2.2.6" + } + }, + "node_modules/@ledgerhq/hw-transport": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-5.26.0.tgz", + "integrity": "sha512-NFeJOJmyEfAX8uuIBTpocWHcz630sqPcXbu864Q+OCBm4EK5UOKV1h/pX7e0xgNIKY8zhJ/O4p4cIZp9tnXLHQ==", + "dependencies": { + "@ledgerhq/devices": "^5.26.0", + "@ledgerhq/errors": "^5.26.0", + "events": "^3.2.0" + } + }, + "node_modules/@ledgerhq/hw-transport-node-hid": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-5.26.0.tgz", + "integrity": "sha512-qhaefZVZatJ6UuK8Wb6WSFNOLWc2mxcv/xgsfKi5HJCIr4bPF/ecIeN+7fRcEaycxj4XykY6Z4A7zDVulfFH4w==", + "optional": true, + "dependencies": { + "@ledgerhq/devices": "^5.26.0", + "@ledgerhq/errors": "^5.26.0", + "@ledgerhq/hw-transport": "^5.26.0", + "@ledgerhq/hw-transport-node-hid-noevents": "^5.26.0", + "@ledgerhq/logs": "^5.26.0", + "lodash": "^4.17.20", + "node-hid": "1.3.0", + "usb": "^1.6.3" + } + }, + "node_modules/@ledgerhq/hw-transport-node-hid-noevents": { + "version": "5.51.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.51.1.tgz", + "integrity": "sha512-9wFf1L8ZQplF7XOY2sQGEeOhpmBRzrn+4X43kghZ7FBDoltrcK+s/D7S+7ffg3j2OySyP6vIIIgloXylao5Scg==", + "optional": true, + "dependencies": { + "@ledgerhq/devices": "^5.51.1", + "@ledgerhq/errors": "^5.50.0", + "@ledgerhq/hw-transport": "^5.51.1", + "@ledgerhq/logs": "^5.50.0", + "node-hid": "2.1.1" + } + }, + "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/@ledgerhq/hw-transport": { + "version": "5.51.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz", + "integrity": "sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw==", + "optional": true, + "dependencies": { + "@ledgerhq/devices": "^5.51.1", + "@ledgerhq/errors": "^5.50.0", + "events": "^3.3.0" + } + }, + "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "optional": true + }, + "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/node-hid": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-2.1.1.tgz", + "integrity": "sha512-Skzhqow7hyLZU93eIPthM9yjot9lszg9xrKxESleEs05V2NcbUptZc5HFqzjOkSmL0sFlZFr3kmvaYebx06wrw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^3.0.2", + "prebuild-install": "^6.0.0" + }, + "bin": { + "hid-showdevices": "src/show-devices.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/prebuild-install": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", + "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.21.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@ledgerhq/hw-transport-u2f": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-u2f/-/hw-transport-u2f-5.26.0.tgz", + "integrity": "sha512-QTxP1Rsh+WZ184LUOelYVLeaQl3++V3I2jFik+l9JZtakwEHjD0XqOT750xpYNL/vfHsy31Wlz+oicdxGzFk+w==", + "deprecated": "@ledgerhq/hw-transport-u2f is deprecated. Please use @ledgerhq/hw-transport-webusb or @ledgerhq/hw-transport-webhid. https://github.com/LedgerHQ/ledgerjs/blob/master/docs/migrate_webusb.md", + "dependencies": { + "@ledgerhq/errors": "^5.26.0", + "@ledgerhq/hw-transport": "^5.26.0", + "@ledgerhq/logs": "^5.26.0", + "u2f-api": "0.2.7" + } + }, + "node_modules/@ledgerhq/logs": { + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-5.50.0.tgz", + "integrity": "sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA==" + }, "node_modules/@metamask/eth-sig-util": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", @@ -2473,6 +2682,52 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/are-we-there-yet/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -2791,6 +3046,14 @@ "node": ">=14.0.0" } }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2800,6 +3063,50 @@ "node": ">=8" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/blakejs": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", @@ -3160,6 +3467,12 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "optional": true + }, "node_modules/ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -3252,6 +3565,15 @@ "node": ">=8" } }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -3440,6 +3762,12 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "optional": true + }, "node_modules/cookie": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", @@ -3463,8 +3791,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "peer": true + "devOptional": true }, "node_modules/cosmjs-types": { "version": "0.8.0", @@ -3590,6 +3917,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -3606,8 +3945,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "peer": true, + "devOptional": true, "engines": { "node": ">=4.0.0" } @@ -3704,6 +4042,12 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "optional": true + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -3713,6 +4057,18 @@ "node": ">= 0.8" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-port": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", @@ -3834,6 +4190,15 @@ "node": ">=6" } }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -5319,6 +5684,14 @@ "node": ">=6" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -5328,6 +5701,15 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -5424,6 +5806,12 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -5552,6 +5940,12 @@ "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", "dev": true }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "optional": true + }, "node_modules/fs-extra": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", @@ -5630,6 +6024,69 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", + "optional": true, + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "optional": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "optional": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "optional": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -5749,6 +6206,12 @@ "node": ">=4" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "optional": true + }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -6279,6 +6742,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "optional": true + }, "node_modules/hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", @@ -6509,8 +6978,7 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "peer": true + "devOptional": true }, "node_modules/internal-slot": { "version": "1.0.5", @@ -6536,6 +7004,14 @@ "node": ">= 0.10" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/io-ts": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", @@ -6674,8 +7150,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "peer": true, + "devOptional": true, "engines": { "node": ">=4" } @@ -6868,8 +7343,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "peer": true + "devOptional": true }, "node_modules/isexe": { "version": "2.0.0", @@ -6907,6 +7381,11 @@ "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -7290,6 +7769,17 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/loupe": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", @@ -7520,6 +8010,18 @@ "node": ">= 0.6" } }, + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -7546,7 +8048,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, + "devOptional": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7564,6 +8066,12 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "optional": true + }, "node_modules/mnemonist": { "version": "0.38.5", "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", @@ -7699,6 +8207,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "optional": true + }, "node_modules/nanoid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", @@ -7711,6 +8225,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "optional": true + }, "node_modules/napi-macros": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz", @@ -7730,6 +8250,24 @@ "dev": true, "peer": true }, + "node_modules/node-abi": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", + "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", + "optional": true, + "dependencies": { + "semver": "^5.4.1" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", @@ -7776,6 +8314,25 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-hid": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-1.3.0.tgz", + "integrity": "sha512-BA6G4V84kiNd1uAChub/Z/5s/xS3EHBCxotQ0nyYrUG65mXewUDHE1tWOSqA2dp3N+mV0Ffq9wo2AW9t4p/G7g==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.14.0", + "node-abi": "^2.18.0", + "prebuild-install": "^5.3.4" + }, + "bin": { + "hid-showdevices": "src/show-devices.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nofilter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", @@ -7786,6 +8343,12 @@ "node": ">=12.19" } }, + "node_modules/noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha512-6kM8CLXvuW5crTxsAtva2YLrRrDaiTIkIePWs9moLHqbFWT94WpNFjwS/5dfLfECg5i/lkmw3aoqVidxt23TEQ==", + "optional": true + }, "node_modules/nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -7808,6 +8371,27 @@ "node": ">=0.10.0" } }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "optional": true, + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/number-to-bn": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", @@ -7844,8 +8428,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "peer": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -7932,7 +8515,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, + "devOptional": true, "dependencies": { "wrappy": "1" } @@ -8268,6 +8851,35 @@ "node": ">=0.10.0" } }, + "node_modules/prebuild-install": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.6.tgz", + "integrity": "sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -8296,8 +8908,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, - "peer": true + "devOptional": true }, "node_modules/promise": { "version": "8.3.0", @@ -8346,6 +8957,16 @@ "dev": true, "peer": true }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -8419,6 +9040,30 @@ "node": ">= 0.8" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -8795,6 +9440,17 @@ "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, "node_modules/safe-array-concat": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", @@ -9033,8 +9689,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "peer": true + "devOptional": true }, "node_modules/setimmediate": { "version": "1.0.5", @@ -9126,6 +9781,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "optional": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -10081,8 +10773,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "peer": true, + "devOptional": true, "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -10095,8 +10786,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "peer": true, + "devOptional": true, "engines": { "node": ">=4" } @@ -10105,8 +10795,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "peer": true, + "devOptional": true, "dependencies": { "ansi-regex": "^3.0.0" }, @@ -10373,6 +11062,34 @@ "node": ">=8" } }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -10552,8 +11269,7 @@ "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/tsort": { "version": "0.0.1", @@ -10565,8 +11281,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "peer": true, + "devOptional": true, "dependencies": { "safe-buffer": "^5.0.1" }, @@ -10758,6 +11473,11 @@ "node": ">=8" } }, + "node_modules/u2f-api": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/u2f-api/-/u2f-api-0.2.7.tgz", + "integrity": "sha512-fqLNg8vpvLOD5J/z4B6wpPg4Lvowz1nJ9xdHcCzdUPKcFE/qNCceV2gNZxSJd5vhAZemHr/K/hbzVA0zxB5mkg==" + }, "node_modules/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -10826,6 +11546,26 @@ "punycode": "^2.1.0" } }, + "node_modules/usb": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/usb/-/usb-1.9.2.tgz", + "integrity": "sha512-dryNz030LWBPAf6gj8vyq0Iev3vPbCLHCT8dBw3gQRXRzVNsIdeuU+VjPp3ksmSPkeMAl1k+kQ14Ij0QHyeiAg==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-addon-api": "^4.2.0", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/usb/node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "optional": true + }, "node_modules/utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", @@ -10944,6 +11684,15 @@ "dev": true, "peer": true }, + "node_modules/which-pm-runs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", + "optional": true, + "engines": { + "node": ">=4" + } + }, "node_modules/which-typed-array": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", @@ -10968,8 +11717,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "peer": true, + "devOptional": true, "dependencies": { "string-width": "^1.0.2 || 2" } @@ -11097,7 +11845,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "devOptional": true }, "node_modules/ws": { "version": "7.4.6", @@ -11708,6 +12456,18 @@ "@ethersproject/transactions": "^5.7.0" } }, + "@ethersproject/hardware-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hardware-wallets/-/hardware-wallets-5.7.0.tgz", + "integrity": "sha512-DjMMXIisRc8xFvEoLoYz1w7JDOYmaz/a0X9sp7Zu668RR8U1zCAyj5ow25HLRW+TCzEC5XiFetTXqS5kXonFCQ==", + "requires": { + "@ledgerhq/hw-app-eth": "5.27.2", + "@ledgerhq/hw-transport": "5.26.0", + "@ledgerhq/hw-transport-node-hid": "5.26.0", + "@ledgerhq/hw-transport-u2f": "5.26.0", + "ethers": "^5.7.0" + } + }, "@ethersproject/hash": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", @@ -12013,6 +12773,171 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@ledgerhq/cryptoassets": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/cryptoassets/-/cryptoassets-5.53.0.tgz", + "integrity": "sha512-M3ibc3LRuHid5UtL7FI3IC6nMEppvly98QHFoSa7lJU0HDzQxY6zHec/SPM4uuJUC8sXoGVAiRJDkgny54damw==", + "requires": { + "invariant": "2" + } + }, + "@ledgerhq/devices": { + "version": "5.51.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-5.51.1.tgz", + "integrity": "sha512-4w+P0VkbjzEXC7kv8T1GJ/9AVaP9I6uasMZ/JcdwZBS3qwvKo5A5z9uGhP5c7TvItzcmPb44b5Mw2kT+WjUuAA==", + "requires": { + "@ledgerhq/errors": "^5.50.0", + "@ledgerhq/logs": "^5.50.0", + "rxjs": "6", + "semver": "^7.3.5" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "@ledgerhq/errors": { + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-5.50.0.tgz", + "integrity": "sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow==" + }, + "@ledgerhq/hw-app-eth": { + "version": "5.27.2", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-app-eth/-/hw-app-eth-5.27.2.tgz", + "integrity": "sha512-llNdrE894cCN8j6yxJEUniciyLVcLmu5N0UmIJLOObztG+5rOF4bX54h4SreTWK+E10Z0CzHSeyE5Lz/tVcqqQ==", + "requires": { + "@ledgerhq/cryptoassets": "^5.27.2", + "@ledgerhq/errors": "^5.26.0", + "@ledgerhq/hw-transport": "^5.26.0", + "bignumber.js": "^9.0.1", + "rlp": "^2.2.6" + } + }, + "@ledgerhq/hw-transport": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-5.26.0.tgz", + "integrity": "sha512-NFeJOJmyEfAX8uuIBTpocWHcz630sqPcXbu864Q+OCBm4EK5UOKV1h/pX7e0xgNIKY8zhJ/O4p4cIZp9tnXLHQ==", + "requires": { + "@ledgerhq/devices": "^5.26.0", + "@ledgerhq/errors": "^5.26.0", + "events": "^3.2.0" + } + }, + "@ledgerhq/hw-transport-node-hid": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-5.26.0.tgz", + "integrity": "sha512-qhaefZVZatJ6UuK8Wb6WSFNOLWc2mxcv/xgsfKi5HJCIr4bPF/ecIeN+7fRcEaycxj4XykY6Z4A7zDVulfFH4w==", + "optional": true, + "requires": { + "@ledgerhq/devices": "^5.26.0", + "@ledgerhq/errors": "^5.26.0", + "@ledgerhq/hw-transport": "^5.26.0", + "@ledgerhq/hw-transport-node-hid-noevents": "^5.26.0", + "@ledgerhq/logs": "^5.26.0", + "lodash": "^4.17.20", + "node-hid": "1.3.0", + "usb": "^1.6.3" + } + }, + "@ledgerhq/hw-transport-node-hid-noevents": { + "version": "5.51.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.51.1.tgz", + "integrity": "sha512-9wFf1L8ZQplF7XOY2sQGEeOhpmBRzrn+4X43kghZ7FBDoltrcK+s/D7S+7ffg3j2OySyP6vIIIgloXylao5Scg==", + "optional": true, + "requires": { + "@ledgerhq/devices": "^5.51.1", + "@ledgerhq/errors": "^5.50.0", + "@ledgerhq/hw-transport": "^5.51.1", + "@ledgerhq/logs": "^5.50.0", + "node-hid": "2.1.1" + }, + "dependencies": { + "@ledgerhq/hw-transport": { + "version": "5.51.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz", + "integrity": "sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw==", + "optional": true, + "requires": { + "@ledgerhq/devices": "^5.51.1", + "@ledgerhq/errors": "^5.50.0", + "events": "^3.3.0" + } + }, + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "optional": true + }, + "node-hid": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-2.1.1.tgz", + "integrity": "sha512-Skzhqow7hyLZU93eIPthM9yjot9lszg9xrKxESleEs05V2NcbUptZc5HFqzjOkSmL0sFlZFr3kmvaYebx06wrw==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "node-addon-api": "^3.0.2", + "prebuild-install": "^6.0.0" + } + }, + "prebuild-install": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", + "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "optional": true, + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.21.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + } + } + } + }, + "@ledgerhq/hw-transport-u2f": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-u2f/-/hw-transport-u2f-5.26.0.tgz", + "integrity": "sha512-QTxP1Rsh+WZ184LUOelYVLeaQl3++V3I2jFik+l9JZtakwEHjD0XqOT750xpYNL/vfHsy31Wlz+oicdxGzFk+w==", + "requires": { + "@ledgerhq/errors": "^5.26.0", + "@ledgerhq/hw-transport": "^5.26.0", + "@ledgerhq/logs": "^5.26.0", + "u2f-api": "0.2.7" + } + }, + "@ledgerhq/logs": { + "version": "5.50.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-5.50.0.tgz", + "integrity": "sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA==" + }, "@metamask/eth-sig-util": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", @@ -13095,6 +14020,54 @@ "picomatch": "^2.0.4" } }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -13343,12 +14316,49 @@ "integrity": "sha512-U1RbE3aX9ayCUVcIPHuPDPKcK3SFOXf93J1UK/iHlJuQB7bhagPIX06/CLpLEsDThJ7KA4Dhrnzynl+d2weTiw==", "dev": true }, + "bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==" + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "optional": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "optional": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } + }, "blakejs": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", @@ -13625,6 +14635,12 @@ } } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "optional": true + }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -13701,6 +14717,12 @@ } } }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "optional": true + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -13863,6 +14885,12 @@ } } }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "optional": true + }, "cookie": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", @@ -13878,8 +14906,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "peer": true + "devOptional": true }, "cosmjs-types": { "version": "0.8.0", @@ -13976,6 +15003,15 @@ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "optional": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, "deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -13989,8 +15025,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "peer": true + "devOptional": true }, "deep-is": { "version": "0.1.4", @@ -14054,12 +15089,24 @@ "dev": true, "peer": true }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "optional": true + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "optional": true + }, "detect-port": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", @@ -14161,6 +15208,15 @@ "level-errors": "^2.0.0" } }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "optional": true, + "requires": { + "once": "^1.4.0" + } + }, "enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -15330,6 +16386,11 @@ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "dev": true }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, "evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -15339,6 +16400,12 @@ "safe-buffer": "^5.1.1" } }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "optional": true + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -15425,6 +16492,12 @@ "flat-cache": "^3.0.4" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -15515,6 +16588,12 @@ "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", "dev": true }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "optional": true + }, "fs-extra": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", @@ -15574,6 +16653,59 @@ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -15665,6 +16797,12 @@ } } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "optional": true + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -16060,6 +17198,12 @@ "has-symbols": "^1.0.2" } }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "optional": true + }, "hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", @@ -16241,8 +17385,7 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "peer": true + "devOptional": true }, "internal-slot": { "version": "1.0.5", @@ -16262,6 +17405,14 @@ "dev": true, "peer": true }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "io-ts": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", @@ -16350,8 +17501,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, - "peer": true + "devOptional": true }, "is-glob": { "version": "4.0.3", @@ -16477,8 +17627,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "peer": true + "devOptional": true }, "isexe": { "version": "2.0.0", @@ -16510,6 +17659,11 @@ "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -16807,6 +17961,14 @@ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, "loupe": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", @@ -16994,6 +18156,12 @@ "mime-db": "1.52.0" } }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "optional": true + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -17017,7 +18185,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true + "devOptional": true }, "mkdirp": { "version": "0.5.6", @@ -17029,6 +18197,12 @@ "minimist": "^1.2.6" } }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "optional": true + }, "mnemonist": { "version": "0.38.5", "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", @@ -17131,12 +18305,24 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "optional": true + }, "nanoid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", "dev": true }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "optional": true + }, "napi-macros": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz", @@ -17156,6 +18342,23 @@ "dev": true, "peer": true }, + "node-abi": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", + "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", + "optional": true, + "requires": { + "semver": "^5.4.1" + }, + "dependencies": { + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "optional": true + } + } + }, "node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", @@ -17196,6 +18399,18 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==" }, + "node-hid": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-1.3.0.tgz", + "integrity": "sha512-BA6G4V84kiNd1uAChub/Z/5s/xS3EHBCxotQ0nyYrUG65mXewUDHE1tWOSqA2dp3N+mV0Ffq9wo2AW9t4p/G7g==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.14.0", + "node-abi": "^2.18.0", + "prebuild-install": "^5.3.4" + } + }, "nofilter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", @@ -17203,6 +18418,12 @@ "dev": true, "peer": true }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha512-6kM8CLXvuW5crTxsAtva2YLrRrDaiTIkIePWs9moLHqbFWT94WpNFjwS/5dfLfECg5i/lkmw3aoqVidxt23TEQ==", + "optional": true + }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -17219,6 +18440,24 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "optional": true + }, "number-to-bn": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", @@ -17250,8 +18489,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "peer": true + "devOptional": true }, "object-inspect": { "version": "1.12.3", @@ -17311,7 +18549,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, + "devOptional": true, "requires": { "wrappy": "1" } @@ -17557,6 +18795,29 @@ "xtend": "^4.0.0" } }, + "prebuild-install": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.6.tgz", + "integrity": "sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==", + "optional": true, + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -17573,8 +18834,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, - "peer": true + "devOptional": true }, "promise": { "version": "8.3.0", @@ -17618,6 +18878,16 @@ "dev": true, "peer": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "optional": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -17665,6 +18935,26 @@ "unpipe": "1.0.0" } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "optional": true + } + } + }, "readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -17926,6 +19216,14 @@ "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "requires": { + "tslib": "^1.9.0" + } + }, "safe-array-concat": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", @@ -18112,8 +19410,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "peer": true + "devOptional": true }, "setimmediate": { "version": "1.0.5", @@ -18184,6 +19481,29 @@ "object-inspect": "^1.9.0" } }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "optional": true + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "optional": true + }, + "simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "optional": true, + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -18948,8 +20268,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "peer": true, + "devOptional": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -18959,15 +20278,13 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "dev": true, - "peer": true + "devOptional": true }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", - "dev": true, - "peer": true, + "devOptional": true, "requires": { "ansi-regex": "^3.0.0" } @@ -19171,6 +20488,31 @@ } } }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "optional": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "optional": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -19309,8 +20651,7 @@ "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "tsort": { "version": "0.0.1", @@ -19322,8 +20663,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "peer": true, + "devOptional": true, "requires": { "safe-buffer": "^5.0.1" } @@ -19465,6 +20805,11 @@ "dev": true, "peer": true }, + "u2f-api": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/u2f-api/-/u2f-api-0.2.7.tgz", + "integrity": "sha512-fqLNg8vpvLOD5J/z4B6wpPg4Lvowz1nJ9xdHcCzdUPKcFE/qNCceV2gNZxSJd5vhAZemHr/K/hbzVA0zxB5mkg==" + }, "uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -19515,6 +20860,24 @@ "punycode": "^2.1.0" } }, + "usb": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/usb/-/usb-1.9.2.tgz", + "integrity": "sha512-dryNz030LWBPAf6gj8vyq0Iev3vPbCLHCT8dBw3gQRXRzVNsIdeuU+VjPp3ksmSPkeMAl1k+kQ14Ij0QHyeiAg==", + "optional": true, + "requires": { + "node-addon-api": "^4.2.0", + "node-gyp-build": "^4.3.0" + }, + "dependencies": { + "node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "optional": true + } + } + }, "utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", @@ -19615,6 +20978,12 @@ "dev": true, "peer": true }, + "which-pm-runs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", + "optional": true + }, "which-typed-array": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", @@ -19633,8 +21002,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "peer": true, + "devOptional": true, "requires": { "string-width": "^1.0.2 || 2" } @@ -19736,7 +21104,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "devOptional": true }, "ws": { "version": "7.4.6", diff --git a/package.json b/package.json index 68e0cdcd8..afc851ddf 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "@axelar-network/axelar-gmp-sdk-solidity": "5.3.1", "@axelar-network/interchain-token-service": "0.3.0", "@cosmjs/cosmwasm-stargate": "^0.31.1", - "ethers": "^5.7.2" + "ethers": "^5.7.2", + "@ethersproject/hardware-wallets": "^5.5.0" }, "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^2.0.2", From eae0544bafda58abf03873fd823dd846517c8989 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Thu, 14 Sep 2023 18:46:03 +0530 Subject: [PATCH 02/45] removed ledger utils from utils.js and created new offline sign utils file --- evm/offline-sign-utils.js | 158 ++++++++++++++++++++++++++++++++++++++ evm/utils.js | 30 +++++--- 2 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 evm/offline-sign-utils.js diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js new file mode 100644 index 000000000..6ad0d9529 --- /dev/null +++ b/evm/offline-sign-utils.js @@ -0,0 +1,158 @@ +'use strict'; + +const { + BigNumber, + utils: {isAddress, serializeTransaction }, +} = require('ethers'); +const { LedgerSigner } = require('@ethersproject/hardware-wallets'); +const {printObj, isNumber, printError } = require('./utils'); +const fs = require('fs'); + +async function getNonce(provider, wallet) { + const nonce = await provider.getTransactionCount(await wallet.getAddress()); + return nonce; + } + +// function to create a ledgerSigner type wallet object +function getLedgerWallet(provider, path) { + // Check if the parameters are undefined and assign default values if necessary + if(provider === undefined || provider === null) { + throw new Error("Provider is not provided while creating a ledger wallet"); + } + provider = provider + const type = "hid"; + path = path || "m/44'/60'/0'/0/0"; + return new LedgerSigner(provider, type, path); + } + +async function ledgerSign(offline, gasLimit, gasPrice, nonce, network, chain, wallet, to, amount, contract, functionName, ...args) { + const tx = {}; + if(offline !== "true") { + if(network.toLowerCase() !== "mainnet" && isNumber(nonce)) { + tx.nonce = nonce; + } + tx.gasLimit = gasLimit || undefined; + tx.gasPrice = gasPrice || undefined; + } + + if(!chain) { + throw new Error("Chain is missing in the function arguments"); + } + tx.chainId = chain.chainId; + if(!wallet) { + throw new Error("Wallet is missing/not provided correctly in function arguments"); + } + if(!to || !isAddress(to)) { + throw new Error("Target address is missing/not provided as valid address for the tx in funciton arguments"); + } + + tx.to = to; + tx.value = amount || undefined; + + if(contract) { + if(to.toLowerCase() !== contract.address.toLowerCase()) { + throw new Error("Contract address do not matches the to address provided in function arguments"); + } + if(!functionName) { + throw new Error("Function name is missing in the funciton arguments"); + } + const data = contract.interface.encodeFunctionData(functionName, args); + + tx.data = data || undefined; + } + + const baseTx = { + chainId: (tx.chainId || undefined), + data: (tx.data || undefined), + gasLimit: (tx.gasLimit || undefined), + gasPrice: (tx.gasPrice || undefined), + nonce: (tx.nonce ? BigNumber.from(tx.nonce).toNumber() : await getNonce(wallet)), + to: (tx.to || undefined), + value: (tx.value || undefined), + }; + + console.log("Printing Base obj"); + printObj(baseTx); + if(offline !== "true") { + return baseTx; + } + + const unsignedTx = serializeTransaction(baseTx).substring(2); + console.log("Before trying to sign using ledger wallet"); + const sig = await wallet._retry((eth) => eth.signTransaction("m/44'/60'/0'/0/0", unsignedTx)); + + // EIP-155 sig.v computation + // v in {0,1} + 2 * chainId + 35 + // Ledger gives this value mod 256 + // So from that, compute whether v is 0 or 1 and then add to 2 * chainId + 35 without doing a mod + var v = BigNumber.from("0x" + sig.v).toNumber() + v = (2 * chain.chainId + 35) + (v + 256 * 100000000000 - (2 * chain.chainId + 35)) % 256 + + // console.log("sig v", BigNumber.from("0x" + sig.v).toNumber(), v, "chain", chainID) + + return serializeTransaction(baseTx, { + v: v, + r: ("0x" + sig.r), + s: ("0x" + sig.s), + }); +} + +async function fetchExisitingTransactions(dirPath, fileName) { + // Read the existing transactions from the file or create a new array if the file doesn't exist + let transactions = []; + dirPath = dirPath || './tx'; + fileName = fileName || 'signed_transactions.txt'; + const filePath = dirPath + '/' + fileName; + try { + const existingData = fs.readFileSync(filePath); + transactions = JSON.parse(existingData).transactions; + } catch (error) { + printError("File doesn't exist yet, that's fine"); + } + return transactions; +} + +function updateTransactions(transactions, msg, signedTransaction) { + transactions.push({ "msg": msg, "signedTransaction": signedTransaction }); + return transactions; +} + +async function storeTransactionsData(dirPath, fileName, msg, signedTransaction) { + let transactions = await fetchExisitingTransactions(dirPath, fileName); + transactions = updateTransactions(transactions, msg, signedTransaction); + dirPath = dirPath || './tx'; + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath); + } + fileName = fileName || 'signed_transactions.txt'; + const filePath = dirPath + '/' + fileName; + const data = JSON.stringify({ transactions }, null, 2); + fs.writeFileSync(filePath, data, (err) => { + if (err) { + printError(err); + return; + } + print(`Data has been successfully stored in the ${filePath} file.`); + }); +} + + async function sendTx(tx, provider) { + const receipt = await provider.sendTransaction(tx).then((tx) => tx.wait()); + console.log("command", receipt); + } + + async function getNonce(wallet) { + const provider = wallet.provider; + const nonce = await provider.getTransactionCount(await wallet.getAddress()); + return nonce; + } + + +module.exports = { + getUnsignedTx, + getNonce, + getLedgerWallet, + ledgerSign, + sendTx, + storeTransactionsData +} \ No newline at end of file diff --git a/evm/utils.js b/evm/utils.js index 384c07e50..5cec1f736 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -5,7 +5,7 @@ const { Contract, provider, BigNumber, - utils: { computeAddress, getContractAddress, keccak256, isAddress, getCreate2Address, defaultAbiCoder, serializeTransaction }, + utils: { computeAddress, getContractAddress, keccak256, isAddress, getCreate2Address, defaultAbiCoder, serializeTransaction, isHexString }, } = require('ethers'); const { LedgerSigner } = require('@ethersproject/hardware-wallets'); const https = require('https'); @@ -214,7 +214,13 @@ const isAddressArray = (arg) => { } return true; -}; +} + +const getCurrentTimeInSeconds = () => { + const now = new Date(); + const currentTimeInSecs = Math.floor(now.getTime() / 1000); + return currentTimeInSecs; +} /** * Determines if a given input is a valid keccak256 hash. @@ -450,10 +456,10 @@ function saveConfig(config, env) { } async function printWalletInfo(wallet) { - printInfo('Wallet address', wallet.address); - const balance = await wallet.provider.getBalance(wallet.address); + printInfo('Wallet address', await wallet.getAddress()); + const balance = await wallet.provider.getBalance(await wallet.getAddress()); printInfo('Wallet balance', `${balance / 1e18}`); - printInfo('Wallet nonce', (await wallet.provider.getTransactionCount(wallet.address)).toString()); + printInfo('Wallet nonce', (await wallet.provider.getTransactionCount(await wallet.getAddress())).toString()); if (balance.isZero()) { printError('Wallet balance is 0'); @@ -543,6 +549,15 @@ function isValidTimeFormat(timeString) { return regex.test(timeString); } +// Validate if the input privateKey is correct +function isValidPrivateKey(privateKey) { + // Check if it's a valid hexadecimal string + if (!isHexString(privateKey) || privateKey.length !== 66) { + return false; + } + return true; +} + const etaToUnixTimestamp = (utcTimeString) => { if (utcTimeString === '0') { return 0; @@ -557,10 +572,6 @@ const etaToUnixTimestamp = (utcTimeString) => { return Math.floor(date.getTime() / 1000); }; -const getCurrentTimeInSeconds = () => { - return Date.now() / 1000; -}; - /** * Check if a specific event was emitted in a transaction receipt. * @@ -620,4 +631,5 @@ module.exports = { getCurrentTimeInSeconds, wasEventEmitted, isContract, + isValidPrivateKey }; From 4361f688bc1d76e2156ef244d08bf51ef5967949 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Thu, 14 Sep 2023 18:46:35 +0530 Subject: [PATCH 03/45] add new ledger packages --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index afc851ddf..d0fabbbb1 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "@axelar-network/interchain-token-service": "0.3.0", "@cosmjs/cosmwasm-stargate": "^0.31.1", "ethers": "^5.7.2", - "@ethersproject/hardware-wallets": "^5.5.0" + "@ethersproject/hardware-wallets": "^5.5.0", + "@ledgerhq/hw-transport-node-hid": "^6.11.2" }, "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^2.0.2", From 87f8507f3d3fee27a5fbdb5017b5fa0c22121a98 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Thu, 14 Sep 2023 18:47:34 +0530 Subject: [PATCH 04/45] wip: update script for using ledger for signer sendTokens call --- evm/send-tokens.js | 62 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 4e2062b5c..64fc4723f 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -3,25 +3,39 @@ require('dotenv').config(); const { ethers } = require('hardhat'); -const { Wallet, getDefaultProvider } = ethers; +const { Wallet, getDefaultProvider, utils : {parseEther}, } = ethers; const { Command, Option } = require('commander'); const chalk = require('chalk'); -const { printInfo, printWalletInfo } = require('./utils'); +const { printInfo, printWalletInfo, isValidPrivateKey, isNumber, isAddressArray, getCurrentTimeInSeconds } = require('./utils'); +const { getLedgerWallet, sendTx, ledgerSign, storeTransactionsData } = require('./offline-sign-utils.js'); const readlineSync = require('readline-sync'); async function sendTokens(chain, options) { + const {privateKey, amount, recipients, offline, env} = options; + env = (env === "local") ? "testnet" : env; + let wallet; + const provider = getDefaultProvider(chain.rpc); - const wallet = new Wallet(options.privateKey, provider); + recipients = options.recipients.split(',').map((str) => str.trim()); + amount = parseEther(options.amount); + + if (privateKey === "ledger") { + wallet = getLedgerWallet(provider); // Need to think whether we will take path for ledger wallet from user or somewhere else like config/ or use default one + } else { + if(!isValidPrivateKey(privateKey)) { + throw new Error("Private key is missing/ not provided correctly in the user info"); + } + + wallet = new Wallet(privateKey, provider); + } + console.log("Wallet address 1", await wallet.getAddress()); const balance = await printWalletInfo(wallet); - const amount = ethers.utils.parseEther(options.amount); if (balance.lte(amount)) { throw new Error(`Wallet has insufficient funds.`); } - const recipients = options.recipients.split(',').map((str) => str.trim()); - if (!options.yes) { const anwser = readlineSync.question( `Proceed with the transfer of ${chalk.green(options.amount)} ${chalk.green(chain.tokenSymbol)} to ${recipients} on ${ @@ -33,15 +47,32 @@ async function sendTokens(chain, options) { for (const recipient of recipients) { printInfo('Recipient', recipient); - - const tx = await wallet.sendTransaction({ - to: recipient, - value: amount, - }); - - printInfo('Transaction hash', tx.hash); - - await tx.wait(); + const nonce = parseInt(getCurrentTimeInSeconds()); + + if(privateKey === "ledger") { + if(offline === "true") { + const tx = await ledgerSign(offline, 50000, 100, nonce, env, chain, wallet, recipient, amount); + const msg = `Transaction created at ${nonce}. This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; + await storeTransactionsData(undefined, undefined, msg, tx); + + } + else { + const signedTx = await ledgerSign(offline, 50000, 10000000000, nonce, env, chain, wallet, recipient, amount); + console.log("Sending signed tx through provider"); + const tx = await sendTx(signedTx, provider); + printInfo('Transaction hash', tx.hash); + } + } + else { + const tx = await wallet.sendTransaction({ + to: recipient, + value: amount, + }); + + printInfo('Transaction hash', tx.hash); + + await tx.wait(); + } } } @@ -79,6 +110,7 @@ if (require.main === module) { program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); program.addOption(new Option('-r, --recipients ', 'comma-separated recipients of tokens').makeOptionMandatory(true)); program.addOption(new Option('-a, --amount ', 'amount to transfer (in terms of ETH)').makeOptionMandatory(true)); + program.addOption(new Option('-o, --offline ', 'If this option is set as true, then ').choices(["true", "false"]).makeOptionMandatory(false)); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { From fc7409756e520da86c997194669cd7e2a15015c0 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Thu, 14 Sep 2023 18:48:07 +0530 Subject: [PATCH 05/45] run prettier --- evm/offline-sign-utils.js | 117 +++++++++++++++++++------------------- evm/send-tokens.js | 43 +++++++------- evm/utils.js | 13 ++++- 3 files changed, 92 insertions(+), 81 deletions(-) diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 6ad0d9529..210cc6316 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -2,98 +2,98 @@ const { BigNumber, - utils: {isAddress, serializeTransaction }, + utils: { isAddress, serializeTransaction }, } = require('ethers'); const { LedgerSigner } = require('@ethersproject/hardware-wallets'); -const {printObj, isNumber, printError } = require('./utils'); +const { printObj, isNumber, printError } = require('./utils'); const fs = require('fs'); async function getNonce(provider, wallet) { const nonce = await provider.getTransactionCount(await wallet.getAddress()); return nonce; - } +} // function to create a ledgerSigner type wallet object function getLedgerWallet(provider, path) { - // Check if the parameters are undefined and assign default values if necessary - if(provider === undefined || provider === null) { - throw new Error("Provider is not provided while creating a ledger wallet"); - } - provider = provider - const type = "hid"; - path = path || "m/44'/60'/0'/0/0"; - return new LedgerSigner(provider, type, path); + // Check if the parameters are undefined and assign default values if necessary + if (provider === undefined || provider === null) { + throw new Error('Provider is not provided while creating a ledger wallet'); } + provider = provider; + const type = 'hid'; + path = path || "m/44'/60'/0'/0/0"; + return new LedgerSigner(provider, type, path); +} async function ledgerSign(offline, gasLimit, gasPrice, nonce, network, chain, wallet, to, amount, contract, functionName, ...args) { const tx = {}; - if(offline !== "true") { - if(network.toLowerCase() !== "mainnet" && isNumber(nonce)) { + if (offline !== 'true') { + if (network.toLowerCase() !== 'mainnet' && isNumber(nonce)) { tx.nonce = nonce; } tx.gasLimit = gasLimit || undefined; tx.gasPrice = gasPrice || undefined; } - if(!chain) { - throw new Error("Chain is missing in the function arguments"); + if (!chain) { + throw new Error('Chain is missing in the function arguments'); } tx.chainId = chain.chainId; - if(!wallet) { - throw new Error("Wallet is missing/not provided correctly in function arguments"); + if (!wallet) { + throw new Error('Wallet is missing/not provided correctly in function arguments'); } - if(!to || !isAddress(to)) { - throw new Error("Target address is missing/not provided as valid address for the tx in funciton arguments"); + if (!to || !isAddress(to)) { + throw new Error('Target address is missing/not provided as valid address for the tx in funciton arguments'); } tx.to = to; tx.value = amount || undefined; - if(contract) { - if(to.toLowerCase() !== contract.address.toLowerCase()) { - throw new Error("Contract address do not matches the to address provided in function arguments"); + if (contract) { + if (to.toLowerCase() !== contract.address.toLowerCase()) { + throw new Error('Contract address do not matches the to address provided in function arguments'); } - if(!functionName) { - throw new Error("Function name is missing in the funciton arguments"); + if (!functionName) { + throw new Error('Function name is missing in the funciton arguments'); } const data = contract.interface.encodeFunctionData(functionName, args); tx.data = data || undefined; - } + } const baseTx = { - chainId: (tx.chainId || undefined), - data: (tx.data || undefined), - gasLimit: (tx.gasLimit || undefined), - gasPrice: (tx.gasPrice || undefined), - nonce: (tx.nonce ? BigNumber.from(tx.nonce).toNumber() : await getNonce(wallet)), - to: (tx.to || undefined), - value: (tx.value || undefined), + chainId: tx.chainId || undefined, + data: tx.data || undefined, + gasLimit: tx.gasLimit || undefined, + gasPrice: tx.gasPrice || undefined, + nonce: tx.nonce ? BigNumber.from(tx.nonce).toNumber() : await getNonce(wallet), + to: tx.to || undefined, + value: tx.value || undefined, }; - console.log("Printing Base obj"); + console.log('Printing Base obj'); printObj(baseTx); - if(offline !== "true") { + if (offline !== 'true') { return baseTx; } - + const unsignedTx = serializeTransaction(baseTx).substring(2); - console.log("Before trying to sign using ledger wallet"); + console.log('Before trying to sign using ledger wallet'); const sig = await wallet._retry((eth) => eth.signTransaction("m/44'/60'/0'/0/0", unsignedTx)); - + // EIP-155 sig.v computation // v in {0,1} + 2 * chainId + 35 // Ledger gives this value mod 256 // So from that, compute whether v is 0 or 1 and then add to 2 * chainId + 35 without doing a mod - var v = BigNumber.from("0x" + sig.v).toNumber() - v = (2 * chain.chainId + 35) + (v + 256 * 100000000000 - (2 * chain.chainId + 35)) % 256 - + var v = BigNumber.from('0x' + sig.v).toNumber(); + v = 2 * chain.chainId + 35 + ((v + 256 * 100000000000 - (2 * chain.chainId + 35)) % 256); + // console.log("sig v", BigNumber.from("0x" + sig.v).toNumber(), v, "chain", chainID) - + return serializeTransaction(baseTx, { - v: v, - r: ("0x" + sig.r), - s: ("0x" + sig.s), + v: v, + r: '0x' + sig.r, + s: '0x' + sig.s, }); } @@ -104,8 +104,8 @@ async function fetchExisitingTransactions(dirPath, fileName) { fileName = fileName || 'signed_transactions.txt'; const filePath = dirPath + '/' + fileName; try { - const existingData = fs.readFileSync(filePath); - transactions = JSON.parse(existingData).transactions; + const existingData = fs.readFileSync(filePath); + transactions = JSON.parse(existingData).transactions; } catch (error) { printError("File doesn't exist yet, that's fine"); } @@ -113,7 +113,7 @@ async function fetchExisitingTransactions(dirPath, fileName) { } function updateTransactions(transactions, msg, signedTransaction) { - transactions.push({ "msg": msg, "signedTransaction": signedTransaction }); + transactions.push({ msg: msg, signedTransaction: signedTransaction }); return transactions; } @@ -123,30 +123,29 @@ async function storeTransactionsData(dirPath, fileName, msg, signedTransaction) dirPath = dirPath || './tx'; if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath); - } + } fileName = fileName || 'signed_transactions.txt'; const filePath = dirPath + '/' + fileName; const data = JSON.stringify({ transactions }, null, 2); fs.writeFileSync(filePath, data, (err) => { if (err) { - printError(err); - return; + printError(err); + return; } print(`Data has been successfully stored in the ${filePath} file.`); - }); + }); } - async function sendTx(tx, provider) { +async function sendTx(tx, provider) { const receipt = await provider.sendTransaction(tx).then((tx) => tx.wait()); - console.log("command", receipt); - } + console.log('command', receipt); +} - async function getNonce(wallet) { +async function getNonce(wallet) { const provider = wallet.provider; const nonce = await provider.getTransactionCount(await wallet.getAddress()); return nonce; - } - +} module.exports = { getUnsignedTx, @@ -154,5 +153,5 @@ module.exports = { getLedgerWallet, ledgerSign, sendTx, - storeTransactionsData -} \ No newline at end of file + storeTransactionsData, +}; diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 64fc4723f..11310dca0 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -3,7 +3,11 @@ require('dotenv').config(); const { ethers } = require('hardhat'); -const { Wallet, getDefaultProvider, utils : {parseEther}, } = ethers; +const { + Wallet, + getDefaultProvider, + utils: { parseEther }, +} = ethers; const { Command, Option } = require('commander'); const chalk = require('chalk'); const { printInfo, printWalletInfo, isValidPrivateKey, isNumber, isAddressArray, getCurrentTimeInSeconds } = require('./utils'); @@ -11,24 +15,24 @@ const { getLedgerWallet, sendTx, ledgerSign, storeTransactionsData } = require(' const readlineSync = require('readline-sync'); async function sendTokens(chain, options) { - const {privateKey, amount, recipients, offline, env} = options; - env = (env === "local") ? "testnet" : env; + const { privateKey, amount, recipients, offline, env } = options; + env = env === 'local' ? 'testnet' : env; let wallet; const provider = getDefaultProvider(chain.rpc); recipients = options.recipients.split(',').map((str) => str.trim()); amount = parseEther(options.amount); - if (privateKey === "ledger") { + if (privateKey === 'ledger') { wallet = getLedgerWallet(provider); // Need to think whether we will take path for ledger wallet from user or somewhere else like config/ or use default one - } else { - if(!isValidPrivateKey(privateKey)) { - throw new Error("Private key is missing/ not provided correctly in the user info"); + } else { + if (!isValidPrivateKey(privateKey)) { + throw new Error('Private key is missing/ not provided correctly in the user info'); } wallet = new Wallet(privateKey, provider); - } - console.log("Wallet address 1", await wallet.getAddress()); + } + console.log('Wallet address 1', await wallet.getAddress()); const balance = await printWalletInfo(wallet); @@ -49,28 +53,25 @@ async function sendTokens(chain, options) { printInfo('Recipient', recipient); const nonce = parseInt(getCurrentTimeInSeconds()); - if(privateKey === "ledger") { - if(offline === "true") { + if (privateKey === 'ledger') { + if (offline === 'true') { const tx = await ledgerSign(offline, 50000, 100, nonce, env, chain, wallet, recipient, amount); const msg = `Transaction created at ${nonce}. This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; await storeTransactionsData(undefined, undefined, msg, tx); - - } - else { + } else { const signedTx = await ledgerSign(offline, 50000, 10000000000, nonce, env, chain, wallet, recipient, amount); - console.log("Sending signed tx through provider"); + console.log('Sending signed tx through provider'); const tx = await sendTx(signedTx, provider); printInfo('Transaction hash', tx.hash); } - } - else { + } else { const tx = await wallet.sendTransaction({ to: recipient, value: amount, }); - + printInfo('Transaction hash', tx.hash); - + await tx.wait(); } } @@ -110,7 +111,9 @@ if (require.main === module) { program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); program.addOption(new Option('-r, --recipients ', 'comma-separated recipients of tokens').makeOptionMandatory(true)); program.addOption(new Option('-a, --amount ', 'amount to transfer (in terms of ETH)').makeOptionMandatory(true)); - program.addOption(new Option('-o, --offline ', 'If this option is set as true, then ').choices(["true", "false"]).makeOptionMandatory(false)); + program.addOption( + new Option('-o, --offline ', 'If this option is set as true, then ').choices(['true', 'false']).makeOptionMandatory(false), + ); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { diff --git a/evm/utils.js b/evm/utils.js index 5cec1f736..9e1a05c49 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -5,7 +5,16 @@ const { Contract, provider, BigNumber, - utils: { computeAddress, getContractAddress, keccak256, isAddress, getCreate2Address, defaultAbiCoder, serializeTransaction, isHexString }, + utils: { + computeAddress, + getContractAddress, + keccak256, + isAddress, + getCreate2Address, + defaultAbiCoder, + serializeTransaction, + isHexString, + }, } = require('ethers'); const { LedgerSigner } = require('@ethersproject/hardware-wallets'); const https = require('https'); @@ -220,7 +229,7 @@ const getCurrentTimeInSeconds = () => { const now = new Date(); const currentTimeInSecs = Math.floor(now.getTime() / 1000); return currentTimeInSecs; -} +}; /** * Determines if a given input is a valid keccak256 hash. From c8c0f9500377552b19982503e97b0f88e6afa66d Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Thu, 14 Sep 2023 20:01:11 +0530 Subject: [PATCH 06/45] refactor logic for signing, and remove unused functions --- evm/send-tokens.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 11310dca0..50bd67ad9 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -10,12 +10,12 @@ const { } = ethers; const { Command, Option } = require('commander'); const chalk = require('chalk'); -const { printInfo, printWalletInfo, isValidPrivateKey, isNumber, isAddressArray, getCurrentTimeInSeconds } = require('./utils'); -const { getLedgerWallet, sendTx, ledgerSign, storeTransactionsData } = require('./offline-sign-utils.js'); +const { printInfo, printWalletInfo, isValidPrivateKey } = require('./utils'); +const { getLedgerWallet, sendTx, ledgerSign, storeTransactionsData, getNonce } = require('./offline-sign-utils.js'); const readlineSync = require('readline-sync'); async function sendTokens(chain, options) { - const { privateKey, amount, recipients, offline, env } = options; + let { privateKey, amount, recipients, offline, env } = options; env = env === 'local' ? 'testnet' : env; let wallet; @@ -51,18 +51,17 @@ async function sendTokens(chain, options) { for (const recipient of recipients) { printInfo('Recipient', recipient); - const nonce = parseInt(getCurrentTimeInSeconds()); + const nonce = await getNonce(wallet); + const tx = await ledgerSign(offline, 50000, 10000000000, nonce, env, chain, wallet, recipient, amount); if (privateKey === 'ledger') { if (offline === 'true') { - const tx = await ledgerSign(offline, 50000, 100, nonce, env, chain, wallet, recipient, amount); const msg = `Transaction created at ${nonce}. This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; await storeTransactionsData(undefined, undefined, msg, tx); } else { - const signedTx = await ledgerSign(offline, 50000, 10000000000, nonce, env, chain, wallet, recipient, amount); - console.log('Sending signed tx through provider'); - const tx = await sendTx(signedTx, provider); - printInfo('Transaction hash', tx.hash); + const signedTx = await wallet.signTransaction(tx); + const response = await sendTx(signedTx, provider); + printInfo('Transaction hash', response.transactionHash); } } else { const tx = await wallet.sendTransaction({ From 09434f74452cffee7094360ed3bcda327dba4d24 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Thu, 14 Sep 2023 20:01:58 +0530 Subject: [PATCH 07/45] update getNonce logic, remove extra consoles --- evm/offline-sign-utils.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 210cc6316..869cb991f 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -8,8 +8,9 @@ const { LedgerSigner } = require('@ethersproject/hardware-wallets'); const { printObj, isNumber, printError } = require('./utils'); const fs = require('fs'); -async function getNonce(provider, wallet) { - const nonce = await provider.getTransactionCount(await wallet.getAddress()); +async function getNonce(wallet) { + const provider = wallet.provider; + const nonce = (await wallet.provider.getTransactionCount(await wallet.getAddress())).toString(); return nonce; } @@ -71,14 +72,11 @@ async function ledgerSign(offline, gasLimit, gasPrice, nonce, network, chain, wa value: tx.value || undefined, }; - console.log('Printing Base obj'); - printObj(baseTx); if (offline !== 'true') { return baseTx; } const unsignedTx = serializeTransaction(baseTx).substring(2); - console.log('Before trying to sign using ledger wallet'); const sig = await wallet._retry((eth) => eth.signTransaction("m/44'/60'/0'/0/0", unsignedTx)); // EIP-155 sig.v computation @@ -88,8 +86,6 @@ async function ledgerSign(offline, gasLimit, gasPrice, nonce, network, chain, wa var v = BigNumber.from('0x' + sig.v).toNumber(); v = 2 * chain.chainId + 35 + ((v + 256 * 100000000000 - (2 * chain.chainId + 35)) % 256); - // console.log("sig v", BigNumber.from("0x" + sig.v).toNumber(), v, "chain", chainID) - return serializeTransaction(baseTx, { v: v, r: '0x' + sig.r, @@ -138,7 +134,7 @@ async function storeTransactionsData(dirPath, fileName, msg, signedTransaction) async function sendTx(tx, provider) { const receipt = await provider.sendTransaction(tx).then((tx) => tx.wait()); - console.log('command', receipt); + return receipt; } async function getNonce(wallet) { @@ -148,7 +144,6 @@ async function getNonce(wallet) { } module.exports = { - getUnsignedTx, getNonce, getLedgerWallet, ledgerSign, From 2887a536f0f03836c334fb3812caa68d7efa613a Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 05:11:40 +0530 Subject: [PATCH 08/45] resolved errors and updated logic --- evm/broadcast-transactions.js | 78 ++++++++++++ evm/offline-sign-utils.js | 223 +++++++++++++++++++++++++++------- evm/send-tokens.js | 68 ++++++++--- 3 files changed, 305 insertions(+), 64 deletions(-) create mode 100644 evm/broadcast-transactions.js diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js new file mode 100644 index 000000000..4a834506d --- /dev/null +++ b/evm/broadcast-transactions.js @@ -0,0 +1,78 @@ +'use strict'; + +const { + BigNumber, + utils: { isAddress, serializeTransaction }, + providers: { JsonRpcProvider}, + Wallet, +} = require('ethers'); +const { LedgerSigner } = require('@ethersproject/hardware-wallets'); +const { printObj, isNumber, printError } = require('./utils'); +const { sendTx, getNonce, updateTxNonceAndStatus, getLatestNonceFromData, getSignerData } = require('./offline-sign-utils'); + + +async function processTransactions(dirPath, fileName, provider, signerAddress) { + try { + const signerData = getSignerData(dirPath, fileName, signerAddress); + const nonceFromData = getLatestNonceFromData(signerData); + const nonce = parseInt(await getNonceFromProvider(provider, signerAddress)); + if(nonce > nonceFromData) { + signerData = updateTxNonceAndStatus(signerData, nonce); + } + + for (const transaction of signerData) { + if (transaction.status === 'PENDING') { + try { + // Send the signed transaction + const response = await sendTx(transaction.signedTransaction, provider); + + // Update the transaction status and store transaction hash + transaction.status = "SUCCESS"; + transaction.transactionHash = response.transactionHash; + } catch (error) { + // Update the transaction status and store error message + transaction.status = "FAILED"; + transaction.error = error.message; + } + } + } + + // Write back the updated JSON object to the file + fs.writeFileSync(filePath, JSON.stringify(jsonData, null, 2)); + + console.log('Transactions processed successfully.'); + } catch (error) { + console.error('Error processing transactions:', error); + } +} + +async function main(options) { + const {directoryPath, fileName, rpcUrl, signerAddress, yes} = options; + const provider = new JsonRpcProvider(rpcUrl); + const network = await provider.getNetwork(); + + if (!options.yes) { + const anwser = readlineSync.question( + `Proceed with the broadcasting of all pending signed transactions for address ${chalk.green(signerAddress)} on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, + ); + if (anwser !== 'y') return; + } + + await processTransactions(directoryPath, fileName, provider, signerAddress); +} + +const program = new Command(); + +program.name('broadcast-transactions').description('Broadcast all the pending signed transactions of the signer'); + +program.addOption(new Option('-d, --directoryPath ', 'The folder where all the signed tx files are stored').makeOptionMandatory(true)); +program.addOption(new Option('-f, --fileName ', 'The fileName where the signed tx are stored').makeOptionMandatory(true)); +program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to broadcast the transactions').makeOptionMandatory(true)); +program.addOption(new Option('-s, --signerAddress ', 'private key').makeOptionMandatory(true)); +program.addOption(new Option('-y, --yes', 'skip prompts')); + +program.action((options) => { + main(options); +}); + +program.parse(); \ No newline at end of file diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 869cb991f..9c2030057 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -8,11 +8,6 @@ const { LedgerSigner } = require('@ethersproject/hardware-wallets'); const { printObj, isNumber, printError } = require('./utils'); const fs = require('fs'); -async function getNonce(wallet) { - const provider = wallet.provider; - const nonce = (await wallet.provider.getTransactionCount(await wallet.getAddress())).toString(); - return nonce; -} // function to create a ledgerSigner type wallet object function getLedgerWallet(provider, path) { @@ -26,20 +21,25 @@ function getLedgerWallet(provider, path) { return new LedgerSigner(provider, type, path); } -async function ledgerSign(offline, gasLimit, gasPrice, nonce, network, chain, wallet, to, amount, contract, functionName, ...args) { +async function ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, to, amount, contract, functionName, ...args) { const tx = {}; - if (offline !== 'true') { - if (network.toLowerCase() !== 'mainnet' && isNumber(nonce)) { - tx.nonce = nonce; - } - tx.gasLimit = gasLimit || undefined; - tx.gasPrice = gasPrice || undefined; - } if (!chain) { throw new Error('Chain is missing in the function arguments'); } tx.chainId = chain.chainId; + if (!gasLimit) { + throw new Error('Gas limit is missing in the function arguments'); + } + tx.gasLimit = gasLimit; + if (!gasPrice) { + throw new Error('Gas price is missing in the function arguments'); + } + tx.gasPrice = gasPrice; + if (!nonce) { + throw new Error('Nonce is missing in the function arguments'); + } + tx.nonce = nonce; if (!wallet) { throw new Error('Wallet is missing/not provided correctly in function arguments'); } @@ -67,40 +67,22 @@ async function ledgerSign(offline, gasLimit, gasPrice, nonce, network, chain, wa data: tx.data || undefined, gasLimit: tx.gasLimit || undefined, gasPrice: tx.gasPrice || undefined, - nonce: tx.nonce ? BigNumber.from(tx.nonce).toNumber() : await getNonce(wallet), + nonce: tx.nonce ? BigNumber.from(tx.nonce).toNumber() : undefined, to: tx.to || undefined, value: tx.value || undefined, }; - if (offline !== 'true') { - return baseTx; - } - const unsignedTx = serializeTransaction(baseTx).substring(2); - const sig = await wallet._retry((eth) => eth.signTransaction("m/44'/60'/0'/0/0", unsignedTx)); - - // EIP-155 sig.v computation - // v in {0,1} + 2 * chainId + 35 - // Ledger gives this value mod 256 - // So from that, compute whether v is 0 or 1 and then add to 2 * chainId + 35 without doing a mod - var v = BigNumber.from('0x' + sig.v).toNumber(); - v = 2 * chain.chainId + 35 + ((v + 256 * 100000000000 - (2 * chain.chainId + 35)) % 256); - - return serializeTransaction(baseTx, { - v: v, - r: '0x' + sig.r, - s: '0x' + sig.s, - }); + const signedTx = await wallet.signTransaction(baseTx); + return [baseTx, signedTx]; } -async function fetchExisitingTransactions(dirPath, fileName) { +async function fetchExisitingTransactions(directoryPath, fileName) { // Read the existing transactions from the file or create a new array if the file doesn't exist let transactions = []; - dirPath = dirPath || './tx'; - fileName = fileName || 'signed_transactions.txt'; - const filePath = dirPath + '/' + fileName; + const filePath = getFilePath(directoryPath, fileName); try { - const existingData = fs.readFileSync(filePath); + const existingData = getFileData(filePath); transactions = JSON.parse(existingData).transactions; } catch (error) { printError("File doesn't exist yet, that's fine"); @@ -108,20 +90,34 @@ async function fetchExisitingTransactions(dirPath, fileName) { return transactions; } +function getFilePath(directoryPath, fileName) { + if(!directoryPath) { + throw new Error("Directory path is missing in the function arguments"); + } + if(!fileName) { + throw new Error("File name is missing in the function arguments"); + } + if (!fs.existsSync(directoryPath)) { + fs.mkdirSync(directoryPath); + } + const filePath = directoryPath + '/' + fileName + '.json'; + return filePath; +} + function updateTransactions(transactions, msg, signedTransaction) { transactions.push({ msg: msg, signedTransaction: signedTransaction }); return transactions; } -async function storeTransactionsData(dirPath, fileName, msg, signedTransaction) { - let transactions = await fetchExisitingTransactions(dirPath, fileName); +async function storeTransactionsData(directoryPath, fileName, msg, signedTransaction) { + let transactions = await fetchExisitingTransactions(directoryPath, fileName); transactions = updateTransactions(transactions, msg, signedTransaction); - dirPath = dirPath || './tx'; - if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath); + directoryPath = directoryPath || './tx'; + if (!fs.existsSync(directoryPath)) { + fs.mkdirSync(directoryPath); } fileName = fileName || 'signed_transactions.txt'; - const filePath = dirPath + '/' + fileName; + const filePath = directoryPath + '/' + fileName; const data = JSON.stringify({ transactions }, null, 2); fs.writeFileSync(filePath, data, (err) => { if (err) { @@ -137,16 +133,149 @@ async function sendTx(tx, provider) { return receipt; } -async function getNonce(wallet) { - const provider = wallet.provider; - const nonce = await provider.getTransactionCount(await wallet.getAddress()); +async function updateSignersData(directoryPath, fileName, signersData) { + const filePath = getFilePath(directoryPath, fileName); + fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2), (err) => { + if (err) { + printError(err); + return; + } + print(`Data has been successfully stored in the ${filePath} file.`); + }); +} + +async function getNonceFromProvider(provider, address) { + const nonce = await provider.getTransactionCount(address); return nonce; } +async function getLatestNonceAndUpdateData(directoryPath, fileName, wallet) { + try { + const provider = wallet.provider; + const signerAddress = await wallet.getAddress(); + let signersData = getAllSignersData(directoryPath, fileName); + let signerData = signersData[signerAddress]; + const nonceFromData = getLatestNonceFromData(signerData); + let nonce = await getNonceFromProvider(provider, signerAddress); + if(nonce > nonceFromData) { + signerData = updateTxNonceAndStatus(signerData, nonce); + signersData[signerAddress] = signerData; + await updateSignersData(directoryPath, fileName, signersData); + } + else { + nonce = nonceFromData + 1; + } + return nonce; + + } catch(error) { + printError(error.message); + } +} + +function updateTxNonceAndStatus(signerData, nonce) { + if(signerData) { + for(const transaction of signerData) { + if(nonce > transaction.nonce && (transaction.status === "PENDING" || transaction.status === "BROADCASTED")) { + transaction.status = "FAILED"; + transaction.error = `Transaction nonce value of ${transaction.nonce} is less than the required signer nonce value of ${nonce}`; + } + } + } + return signerData +} + +function getLatestNonceFromData(signerData) { + if(signerData) { + const length = signerData.length; + return parseInt(signerData[length - 1].nonce); + } + return 0; +} + +async function getAllSignersData(directoryPath, fileName) { + const signersData = {}; + try { + const filePath = getFilePath(directoryPath, fileName); + // Read the content of the file + const data = getFileData(filePath); + if(data) { + const jsonData = JSON.parse(data); + if(!isValidJSON(jsonData)) { + return signersData; + } + return jsonData; + } + return signersData; + } catch(error) { + printError(error.message); + } +} + +function getFileData(filePath) { + try { + if (!fs.existsSync(filePath)) { + // File does not exist, create it + fs.writeFileSync(filePath, {}); + return undefined; + } + // Read the content of the file + const data = fs.readFileSync(filePath); + return data; + } catch(error) { + printError(error.message); + } +} + +async function getSignerData(directoryPath, fileName, signerAddress) { + let signerData = []; + console.log(fileName); + try { + const filePath = getFilePath(directoryPath, fileName); + // Read the content of the file + const data = getFileData(filePath); + console.log(data); + if(data) { + const jsonData = JSON.parse(data); + console.log(jsonData); + if(!isValidJSON(jsonData)) { + return signerData; + } + // Access the transactions array from the JSON object + if(signerAddress in jsonData) { + console.log("line 245"); + signerData = jsonData[signerAddress]; + } + } + return signerData; + + } catch(error) { + printError(error.message); + } +} + +function isValidJSON(obj) { + if (obj === undefined || obj === null) { + return false; + } + + if (Object.keys(obj).length === 0 && obj.constructor === Object) { + return false; + } + + return true; + } + module.exports = { - getNonce, getLedgerWallet, ledgerSign, sendTx, storeTransactionsData, + getFilePath, + updateTxNonceAndStatus, + getNonceFromProvider, + getLatestNonceFromData, + getAllSignersData, + getSignerData, + updateSignersData, + getLatestNonceAndUpdateData }; diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 50bd67ad9..374282eb9 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -2,29 +2,31 @@ require('dotenv').config(); -const { ethers } = require('hardhat'); +// const { ethers } = require('hardhat'); const { Wallet, getDefaultProvider, - utils: { parseEther }, -} = ethers; + utils: { parseEther, parseUnits }, +} = require('ethers'); const { Command, Option } = require('commander'); const chalk = require('chalk'); -const { printInfo, printWalletInfo, isValidPrivateKey } = require('./utils'); -const { getLedgerWallet, sendTx, ledgerSign, storeTransactionsData, getNonce } = require('./offline-sign-utils.js'); +const { printInfo, printWalletInfo, isValidPrivateKey, printError } = require('./utils'); +const { getLedgerWallet, sendTx, ledgerSign, getAllSignersData, getNonceFromProvider, getLatestNonceFromData, updateSignersData, getLatestNonceAndUpdateData, getSignerData, updateTxNonceAndStatus } = require('./offline-sign-utils.js'); const readlineSync = require('readline-sync'); async function sendTokens(chain, options) { - let { privateKey, amount, recipients, offline, env } = options; - env = env === 'local' ? 'testnet' : env; let wallet; + const { privateKey, offline, env, ledgerPath} = options; + let {amount, recipients, directoryPath, fileName} = options; + const isOffline = (offline === "true") ? true : false; const provider = getDefaultProvider(chain.rpc); + printInfo(`provider: ${provider}`); recipients = options.recipients.split(',').map((str) => str.trim()); - amount = parseEther(options.amount); + amount = parseEther(amount); if (privateKey === 'ledger') { - wallet = getLedgerWallet(provider); // Need to think whether we will take path for ledger wallet from user or somewhere else like config/ or use default one + wallet = getLedgerWallet(provider, ledgerPath? ledgerPath : undefined); } else { if (!isValidPrivateKey(privateKey)) { throw new Error('Private key is missing/ not provided correctly in the user info'); @@ -32,8 +34,8 @@ async function sendTokens(chain, options) { wallet = new Wallet(privateKey, provider); } - console.log('Wallet address 1', await wallet.getAddress()); + const signerAddress = await wallet.getAddress(); const balance = await printWalletInfo(wallet); if (balance.lte(amount)) { @@ -49,18 +51,42 @@ async function sendTokens(chain, options) { if (anwser !== 'y') return; } + let gasLimit, gasPrice; + try { + gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'gwei'); + const block = await provider.getBlock('latest'); + gasLimit = block.gasLimit.toNumber() / 1000; + + printInfo('Gas Price:', gasPrice.toString()); + printInfo('Gas Limit:', gasLimit.toString()); + } catch (error) { + printError(error.message); + } + + let nonce = await getNonceFromProvider(provider, signerAddress); + let signersData = undefined; + let signerData = undefined; + if(isOffline) { + directoryPath = directoryPath || './tx'; + fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedTransactions'; + nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); + signersData = await getAllSignersData(directoryPath, fileName); + signerData = await getSignerData(directoryPath, fileName, signerAddress); + } for (const recipient of recipients) { printInfo('Recipient', recipient); - const nonce = await getNonce(wallet); - const tx = await ledgerSign(offline, 50000, 10000000000, nonce, env, chain, wallet, recipient, amount); if (privateKey === 'ledger') { - if (offline === 'true') { - const msg = `Transaction created at ${nonce}. This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; - await storeTransactionsData(undefined, undefined, msg, tx); + const [unsignedTx, tx] = await ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, recipient, amount); + if (isOffline) { + const tx = {}; + tx.nonce = nonce; + tx.msg = `This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; + tx.unsignedTx = unsignedTx; + tx.status = "PENDING"; + signerData.push(tx); } else { - const signedTx = await wallet.signTransaction(tx); - const response = await sendTx(signedTx, provider); + const response = await sendTx(tx, provider); printInfo('Transaction hash', response.transactionHash); } } else { @@ -73,6 +99,11 @@ async function sendTokens(chain, options) { await tx.wait(); } + ++nonce; + } + if(signerData) { + signersData[signerAddress] = signerData; + await updateSignersData(directoryPath, fileName, signersData); } } @@ -113,6 +144,9 @@ if (require.main === module) { program.addOption( new Option('-o, --offline ', 'If this option is set as true, then ').choices(['true', 'false']).makeOptionMandatory(false), ); + program.addOption(new Option('-l, --ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); + program.addOption(new Option('-d, --directoryPath ', 'The folder where all the signed tx files are stored').makeOptionMandatory(false)); + program.addOption(new Option('-f, --fileName ', 'The fileName where the signed tx will be stored').makeOptionMandatory(false)); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { From b5de2270c9acccdf29ec41dd3f533cdc6b8fb1ad Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 13:57:08 +0530 Subject: [PATCH 09/45] refactor transactions storing logic --- evm/send-tokens.js | 58 ++++++++++++++++++++++++++++++---------------- evm/utils.js | 19 ++++----------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 374282eb9..1c94c4925 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -11,22 +11,30 @@ const { const { Command, Option } = require('commander'); const chalk = require('chalk'); const { printInfo, printWalletInfo, isValidPrivateKey, printError } = require('./utils'); -const { getLedgerWallet, sendTx, ledgerSign, getAllSignersData, getNonceFromProvider, getLatestNonceFromData, updateSignersData, getLatestNonceAndUpdateData, getSignerData, updateTxNonceAndStatus } = require('./offline-sign-utils.js'); +const { + getLedgerWallet, + sendTx, + ledgerSign, + getAllSignersData, + getNonceFromProvider, + updateSignersData, + getLatestNonceAndUpdateData, + getSignerData, +} = require('./offline-sign-utils.js'); const readlineSync = require('readline-sync'); async function sendTokens(chain, options) { let wallet; - const { privateKey, offline, env, ledgerPath} = options; - let {amount, recipients, directoryPath, fileName} = options; - const isOffline = (offline === "true") ? true : false; + const { privateKey, offline, env, ledgerPath } = options; + let { amount, recipients, directoryPath, fileName } = options; + const isOffline = offline === 'true'; const provider = getDefaultProvider(chain.rpc); - printInfo(`provider: ${provider}`); recipients = options.recipients.split(',').map((str) => str.trim()); amount = parseEther(amount); if (privateKey === 'ledger') { - wallet = getLedgerWallet(provider, ledgerPath? ledgerPath : undefined); + wallet = getLedgerWallet(provider, ledgerPath); } else { if (!isValidPrivateKey(privateKey)) { throw new Error('Private key is missing/ not provided correctly in the user info'); @@ -52,41 +60,45 @@ async function sendTokens(chain, options) { } let gasLimit, gasPrice; + try { gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'gwei'); const block = await provider.getBlock('latest'); gasLimit = block.gasLimit.toNumber() / 1000; - + printInfo('Gas Price:', gasPrice.toString()); printInfo('Gas Limit:', gasLimit.toString()); - } catch (error) { + } catch (error) { printError(error.message); - } + } let nonce = await getNonceFromProvider(provider, signerAddress); - let signersData = undefined; - let signerData = undefined; - if(isOffline) { + let signersData, signerData; + + if (isOffline) { directoryPath = directoryPath || './tx'; - fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedTransactions'; + fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedTransactions'; nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); signersData = await getAllSignersData(directoryPath, fileName); signerData = await getSignerData(directoryPath, fileName, signerAddress); } + for (const recipient of recipients) { printInfo('Recipient', recipient); if (privateKey === 'ledger') { - const [unsignedTx, tx] = await ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, recipient, amount); + const [baseTx, signedTx] = await ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, recipient, amount); + if (isOffline) { const tx = {}; tx.nonce = nonce; tx.msg = `This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; - tx.unsignedTx = unsignedTx; - tx.status = "PENDING"; + tx.baseTx = baseTx; + tx.signedTx = signedTx; + tx.status = 'PENDING'; signerData.push(tx); } else { - const response = await sendTx(tx, provider); + const response = await sendTx(signedTx, provider); printInfo('Transaction hash', response.transactionHash); } } else { @@ -99,9 +111,11 @@ async function sendTokens(chain, options) { await tx.wait(); } + ++nonce; } - if(signerData) { + + if (signerData) { signersData[signerAddress] = signerData; await updateSignersData(directoryPath, fileName, signersData); } @@ -145,8 +159,12 @@ if (require.main === module) { new Option('-o, --offline ', 'If this option is set as true, then ').choices(['true', 'false']).makeOptionMandatory(false), ); program.addOption(new Option('-l, --ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); - program.addOption(new Option('-d, --directoryPath ', 'The folder where all the signed tx files are stored').makeOptionMandatory(false)); - program.addOption(new Option('-f, --fileName ', 'The fileName where the signed tx will be stored').makeOptionMandatory(false)); + program.addOption( + new Option('-d, --directoryPath ', 'The folder where all the signed tx files are stored').makeOptionMandatory(false), + ); + program.addOption( + new Option('-f, --fileName ', 'The fileName where the signed tx will be stored').makeOptionMandatory(false), + ); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { diff --git a/evm/utils.js b/evm/utils.js index 9e1a05c49..60cc5c41a 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -3,20 +3,8 @@ const { ContractFactory, Contract, - provider, - BigNumber, - utils: { - computeAddress, - getContractAddress, - keccak256, - isAddress, - getCreate2Address, - defaultAbiCoder, - serializeTransaction, - isHexString, - }, + utils: { computeAddress, getContractAddress, keccak256, isAddress, getCreate2Address, defaultAbiCoder, isHexString }, } = require('ethers'); -const { LedgerSigner } = require('@ethersproject/hardware-wallets'); const https = require('https'); const http = require('http'); const { outputJsonSync } = require('fs-extra'); @@ -223,7 +211,7 @@ const isAddressArray = (arg) => { } return true; -} +}; const getCurrentTimeInSeconds = () => { const now = new Date(); @@ -564,6 +552,7 @@ function isValidPrivateKey(privateKey) { if (!isHexString(privateKey) || privateKey.length !== 66) { return false; } + return true; } @@ -640,5 +629,5 @@ module.exports = { getCurrentTimeInSeconds, wasEventEmitted, isContract, - isValidPrivateKey + isValidPrivateKey, }; From 641222c22032e6d72ff4a210f1f88a596b75efa9 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 13:58:14 +0530 Subject: [PATCH 10/45] add util functions for broadcasting signed txscript --- evm/offline-sign-utils.js | 147 +++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 82 deletions(-) diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 9c2030057..ad8c4ac3b 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -2,20 +2,19 @@ const { BigNumber, - utils: { isAddress, serializeTransaction }, + utils: { isAddress }, } = require('ethers'); const { LedgerSigner } = require('@ethersproject/hardware-wallets'); -const { printObj, isNumber, printError } = require('./utils'); +const { printError } = require('./utils'); const fs = require('fs'); - // function to create a ledgerSigner type wallet object function getLedgerWallet(provider, path) { // Check if the parameters are undefined and assign default values if necessary if (provider === undefined || provider === null) { throw new Error('Provider is not provided while creating a ledger wallet'); } - provider = provider; + const type = 'hid'; path = path || "m/44'/60'/0'/0/0"; return new LedgerSigner(provider, type, path); @@ -27,22 +26,31 @@ async function ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, to, amount, if (!chain) { throw new Error('Chain is missing in the function arguments'); } + tx.chainId = chain.chainId; + if (!gasLimit) { throw new Error('Gas limit is missing in the function arguments'); } + tx.gasLimit = gasLimit; + if (!gasPrice) { throw new Error('Gas price is missing in the function arguments'); } + tx.gasPrice = gasPrice; + if (!nonce) { throw new Error('Nonce is missing in the function arguments'); } + tx.nonce = nonce; + if (!wallet) { throw new Error('Wallet is missing/not provided correctly in function arguments'); } + if (!to || !isAddress(to)) { throw new Error('Target address is missing/not provided as valid address for the tx in funciton arguments'); } @@ -54,11 +62,12 @@ async function ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, to, amount, if (to.toLowerCase() !== contract.address.toLowerCase()) { throw new Error('Contract address do not matches the to address provided in function arguments'); } + if (!functionName) { throw new Error('Function name is missing in the funciton arguments'); } - const data = contract.interface.encodeFunctionData(functionName, args); + const data = contract.interface.encodeFunctionData(functionName, args); tx.data = data || undefined; } @@ -72,62 +81,27 @@ async function ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, to, amount, value: tx.value || undefined, }; - const signedTx = await wallet.signTransaction(baseTx); return [baseTx, signedTx]; } -async function fetchExisitingTransactions(directoryPath, fileName) { - // Read the existing transactions from the file or create a new array if the file doesn't exist - let transactions = []; - const filePath = getFilePath(directoryPath, fileName); - try { - const existingData = getFileData(filePath); - transactions = JSON.parse(existingData).transactions; - } catch (error) { - printError("File doesn't exist yet, that's fine"); - } - return transactions; -} - function getFilePath(directoryPath, fileName) { - if(!directoryPath) { - throw new Error("Directory path is missing in the function arguments"); + if (!directoryPath) { + throw new Error('Directory path is missing in the function arguments'); } - if(!fileName) { - throw new Error("File name is missing in the function arguments"); + + if (!fileName) { + throw new Error('File name is missing in the function arguments'); } + if (!fs.existsSync(directoryPath)) { fs.mkdirSync(directoryPath); } + const filePath = directoryPath + '/' + fileName + '.json'; return filePath; } -function updateTransactions(transactions, msg, signedTransaction) { - transactions.push({ msg: msg, signedTransaction: signedTransaction }); - return transactions; -} - -async function storeTransactionsData(directoryPath, fileName, msg, signedTransaction) { - let transactions = await fetchExisitingTransactions(directoryPath, fileName); - transactions = updateTransactions(transactions, msg, signedTransaction); - directoryPath = directoryPath || './tx'; - if (!fs.existsSync(directoryPath)) { - fs.mkdirSync(directoryPath); - } - fileName = fileName || 'signed_transactions.txt'; - const filePath = directoryPath + '/' + fileName; - const data = JSON.stringify({ transactions }, null, 2); - fs.writeFileSync(filePath, data, (err) => { - if (err) { - printError(err); - return; - } - print(`Data has been successfully stored in the ${filePath} file.`); - }); -} - async function sendTx(tx, provider) { const receipt = await provider.sendTransaction(tx).then((tx) => tx.wait()); return receipt; @@ -140,7 +114,8 @@ async function updateSignersData(directoryPath, fileName, signersData) { printError(err); return; } - print(`Data has been successfully stored in the ${filePath} file.`); + + printError(`Data has been successfully stored in the ${filePath} file.`); }); } @@ -153,60 +128,67 @@ async function getLatestNonceAndUpdateData(directoryPath, fileName, wallet) { try { const provider = wallet.provider; const signerAddress = await wallet.getAddress(); - let signersData = getAllSignersData(directoryPath, fileName); + const signersData = await getAllSignersData(directoryPath, fileName); let signerData = signersData[signerAddress]; - const nonceFromData = getLatestNonceFromData(signerData); + const nonceFromData = getLatestNonceFromData(signerData); let nonce = await getNonceFromProvider(provider, signerAddress); - if(nonce > nonceFromData) { + + if (nonce > nonceFromData) { signerData = updateTxNonceAndStatus(signerData, nonce); signersData[signerAddress] = signerData; await updateSignersData(directoryPath, fileName, signersData); - } - else { + } else { nonce = nonceFromData + 1; } - return nonce; - } catch(error) { + return nonce; + } catch (error) { printError(error.message); } } function updateTxNonceAndStatus(signerData, nonce) { - if(signerData) { - for(const transaction of signerData) { - if(nonce > transaction.nonce && (transaction.status === "PENDING" || transaction.status === "BROADCASTED")) { - transaction.status = "FAILED"; + if (signerData) { + for (const transaction of signerData) { + if (nonce > transaction.nonce && (transaction.status === 'PENDING' || transaction.status === 'BROADCASTED')) { + transaction.status = 'FAILED'; transaction.error = `Transaction nonce value of ${transaction.nonce} is less than the required signer nonce value of ${nonce}`; } } } - return signerData + + return signerData; } function getLatestNonceFromData(signerData) { - if(signerData) { + if (signerData) { const length = signerData.length; return parseInt(signerData[length - 1].nonce); } + return 0; } async function getAllSignersData(directoryPath, fileName) { const signersData = {}; + try { const filePath = getFilePath(directoryPath, fileName); // Read the content of the file const data = getFileData(filePath); - if(data) { + + if (data) { const jsonData = JSON.parse(data); - if(!isValidJSON(jsonData)) { + + if (!isValidJSON(jsonData)) { return signersData; } + return jsonData; } + return signersData; - } catch(error) { + } catch (error) { printError(error.message); } } @@ -215,61 +197,61 @@ function getFileData(filePath) { try { if (!fs.existsSync(filePath)) { // File does not exist, create it - fs.writeFileSync(filePath, {}); + fs.writeFileSync(filePath, JSON.stringify({})); return undefined; } // Read the content of the file + const data = fs.readFileSync(filePath); return data; - } catch(error) { + } catch (error) { printError(error.message); } } async function getSignerData(directoryPath, fileName, signerAddress) { let signerData = []; - console.log(fileName); + try { const filePath = getFilePath(directoryPath, fileName); // Read the content of the file const data = getFileData(filePath); - console.log(data); - if(data) { + + if (data) { const jsonData = JSON.parse(data); - console.log(jsonData); - if(!isValidJSON(jsonData)) { + + if (!isValidJSON(jsonData)) { return signerData; } // Access the transactions array from the JSON object - if(signerAddress in jsonData) { - console.log("line 245"); + + if (signerAddress in jsonData) { signerData = jsonData[signerAddress]; } } - return signerData; - } catch(error) { + return signerData; + } catch (error) { printError(error.message); } } function isValidJSON(obj) { if (obj === undefined || obj === null) { - return false; + return false; } - + if (Object.keys(obj).length === 0 && obj.constructor === Object) { - return false; + return false; } - + return true; - } +} module.exports = { getLedgerWallet, ledgerSign, sendTx, - storeTransactionsData, getFilePath, updateTxNonceAndStatus, getNonceFromProvider, @@ -277,5 +259,6 @@ module.exports = { getAllSignersData, getSignerData, updateSignersData, - getLatestNonceAndUpdateData + getLatestNonceAndUpdateData, + isValidJSON, }; From aff63ffc0f653c8566ae429fc11f44f52eb1cd17 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 13:58:49 +0530 Subject: [PATCH 11/45] add script for broadcasting pending signed tx --- evm/broadcast-transactions.js | 70 ++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 4a834506d..4edb77f05 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -1,59 +1,75 @@ 'use strict'; const { - BigNumber, - utils: { isAddress, serializeTransaction }, - providers: { JsonRpcProvider}, - Wallet, + providers: { JsonRpcProvider }, } = require('ethers'); -const { LedgerSigner } = require('@ethersproject/hardware-wallets'); -const { printObj, isNumber, printError } = require('./utils'); -const { sendTx, getNonce, updateTxNonceAndStatus, getLatestNonceFromData, getSignerData } = require('./offline-sign-utils'); - +const { Command, Option } = require('commander'); +const { printError, printInfo } = require('./utils'); +const { + sendTx, + updateTxNonceAndStatus, + getLatestNonceFromData, + getNonceFromProvider, + getFilePath, + getAllSignersData, +} = require('./offline-sign-utils'); +const fs = require('fs'); +const chalk = require('chalk'); -async function processTransactions(dirPath, fileName, provider, signerAddress) { +async function processTransactions(directoryPath, fileName, provider, signerAddress) { try { - const signerData = getSignerData(dirPath, fileName, signerAddress); - const nonceFromData = getLatestNonceFromData(signerData); + const signersData = await getAllSignersData(directoryPath, fileName); + + if (!signersData[signerAddress]) { + throw new Error(`Signer data for address ${signerAddress} not found in the file ${fileName}`); + } + + let signerData = signersData[signerAddress]; + const nonceFromData = await getLatestNonceFromData(signerData); const nonce = parseInt(await getNonceFromProvider(provider, signerAddress)); - if(nonce > nonceFromData) { - signerData = updateTxNonceAndStatus(signerData, nonce); + + if (nonce > nonceFromData) { + signerData = updateTxNonceAndStatus(signerData, nonce); } for (const transaction of signerData) { if (transaction.status === 'PENDING') { try { // Send the signed transaction - const response = await sendTx(transaction.signedTransaction, provider); + const response = await sendTx(transaction.signedTx, provider); // Update the transaction status and store transaction hash - transaction.status = "SUCCESS"; + transaction.status = 'SUCCESS'; transaction.transactionHash = response.transactionHash; } catch (error) { // Update the transaction status and store error message - transaction.status = "FAILED"; + transaction.status = 'FAILED'; transaction.error = error.message; } } } // Write back the updated JSON object to the file - fs.writeFileSync(filePath, JSON.stringify(jsonData, null, 2)); + signersData[signerAddress] = signerData; + const filePath = getFilePath(directoryPath, fileName); + fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2)); - console.log('Transactions processed successfully.'); + printInfo('Transactions processed successfully.'); } catch (error) { - console.error('Error processing transactions:', error); + printError('Error processing transactions:', error.message); } } async function main(options) { - const {directoryPath, fileName, rpcUrl, signerAddress, yes} = options; + const { directoryPath, fileName, rpcUrl, signerAddress } = options; const provider = new JsonRpcProvider(rpcUrl); const network = await provider.getNetwork(); if (!options.yes) { - const anwser = readlineSync.question( - `Proceed with the broadcasting of all pending signed transactions for address ${chalk.green(signerAddress)} on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, + const anwser = fs.readlineSync.question( + `Proceed with the broadcasting of all pending signed transactions for address ${chalk.green( + signerAddress, + )} on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, ); if (anwser !== 'y') return; } @@ -65,9 +81,13 @@ const program = new Command(); program.name('broadcast-transactions').description('Broadcast all the pending signed transactions of the signer'); -program.addOption(new Option('-d, --directoryPath ', 'The folder where all the signed tx files are stored').makeOptionMandatory(true)); +program.addOption( + new Option('-d, --directoryPath ', 'The folder where all the signed tx files are stored').makeOptionMandatory(true), +); program.addOption(new Option('-f, --fileName ', 'The fileName where the signed tx are stored').makeOptionMandatory(true)); -program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to broadcast the transactions').makeOptionMandatory(true)); +program.addOption( + new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to broadcast the transactions').makeOptionMandatory(true), +); program.addOption(new Option('-s, --signerAddress ', 'private key').makeOptionMandatory(true)); program.addOption(new Option('-y, --yes', 'skip prompts')); @@ -75,4 +95,4 @@ program.action((options) => { main(options); }); -program.parse(); \ No newline at end of file +program.parse(); From ec1b62765843a6c5c41fdad192cd43832d45bd90 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 15:41:12 +0530 Subject: [PATCH 12/45] refactored code for broadcast script and utils, include more info about errors --- evm/broadcast-transactions.js | 19 ++++++++++++++----- evm/offline-sign-utils.js | 34 +++++++++++++++++++++++++--------- evm/send-tokens.js | 15 +++++++++++++-- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 4edb77f05..09b0f2ac3 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -4,14 +4,15 @@ const { providers: { JsonRpcProvider }, } = require('ethers'); const { Command, Option } = require('commander'); -const { printError, printInfo } = require('./utils'); +const { printError, printInfo, printObj } = require('./utils'); const { sendTx, updateTxNonceAndStatus, - getLatestNonceFromData, + getNonceFromData, getNonceFromProvider, getFilePath, getAllSignersData, + isValidJSON, } = require('./offline-sign-utils'); const fs = require('fs'); const chalk = require('chalk'); @@ -25,7 +26,7 @@ async function processTransactions(directoryPath, fileName, provider, signerAddr } let signerData = signersData[signerAddress]; - const nonceFromData = await getLatestNonceFromData(signerData); + const nonceFromData = await getNonceFromData(signerData); const nonce = parseInt(await getNonceFromProvider(provider, signerAddress)); if (nonce > nonceFromData) { @@ -34,17 +35,27 @@ async function processTransactions(directoryPath, fileName, provider, signerAddr for (const transaction of signerData) { if (transaction.status === 'PENDING') { + printInfo('Broadcasting transaction: '); + printObj(transaction.baseTx); + try { // Send the signed transaction const response = await sendTx(transaction.signedTx, provider); + if (!isValidJSON(response) || response.status.toString() !== '1') { + const error = `Execution failed:${response.status ? ` with txHash: ${response.transactionHash}` : ''}`; + throw new Error(error); + } + // Update the transaction status and store transaction hash transaction.status = 'SUCCESS'; transaction.transactionHash = response.transactionHash; + printInfo('Transactions executed successfully.'); } catch (error) { // Update the transaction status and store error message transaction.status = 'FAILED'; transaction.error = error.message; + printError(`Transaction failed with error: ${error.message}`); } } } @@ -53,8 +64,6 @@ async function processTransactions(directoryPath, fileName, provider, signerAddr signersData[signerAddress] = signerData; const filePath = getFilePath(directoryPath, fileName); fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2)); - - printInfo('Transactions processed successfully.'); } catch (error) { printError('Error processing transactions:', error.message); } diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index ad8c4ac3b..9f628c40c 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -103,8 +103,15 @@ function getFilePath(directoryPath, fileName) { } async function sendTx(tx, provider) { - const receipt = await provider.sendTransaction(tx).then((tx) => tx.wait()); - return receipt; + const response = {}; + + try { + const receipt = await provider.sendTransaction(tx).then((tx) => tx.wait()); + return receipt; + } catch (error) { + printError(error.message); + return response; + } } async function updateSignersData(directoryPath, fileName, signersData) { @@ -130,7 +137,7 @@ async function getLatestNonceAndUpdateData(directoryPath, fileName, wallet) { const signerAddress = await wallet.getAddress(); const signersData = await getAllSignersData(directoryPath, fileName); let signerData = signersData[signerAddress]; - const nonceFromData = getLatestNonceFromData(signerData); + const nonceFromData = getNonceFromData(signerData); let nonce = await getNonceFromProvider(provider, signerAddress); if (nonce > nonceFromData) { @@ -152,7 +159,9 @@ function updateTxNonceAndStatus(signerData, nonce) { for (const transaction of signerData) { if (nonce > transaction.nonce && (transaction.status === 'PENDING' || transaction.status === 'BROADCASTED')) { transaction.status = 'FAILED'; - transaction.error = `Transaction nonce value of ${transaction.nonce} is less than the required signer nonce value of ${nonce}`; + const error = `Transaction nonce value of ${transaction.nonce} is less than the required signer nonce value of ${nonce}`; + transaction.error = error; + printError(error + ` for signedTx: ${transaction.signedTx}`); } } } @@ -160,10 +169,17 @@ function updateTxNonceAndStatus(signerData, nonce) { return signerData; } -function getLatestNonceFromData(signerData) { - if (signerData) { - const length = signerData.length; - return parseInt(signerData[length - 1].nonce); +function getNonceFromData(signerData) { + try { + if (signerData) { + for (const transaction of signerData) { + if (transaction.status === 'PENDING') { + return transaction.nonce; + } + } + } + } catch (error) { + printError(error.message); } return 0; @@ -255,7 +271,7 @@ module.exports = { getFilePath, updateTxNonceAndStatus, getNonceFromProvider, - getLatestNonceFromData, + getNonceFromData, getAllSignersData, getSignerData, updateSignersData, diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 1c94c4925..5b4fc4fc7 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -20,6 +20,7 @@ const { updateSignersData, getLatestNonceAndUpdateData, getSignerData, + isValidJSON, } = require('./offline-sign-utils.js'); const readlineSync = require('readline-sync'); @@ -98,8 +99,18 @@ async function sendTokens(chain, options) { tx.status = 'PENDING'; signerData.push(tx); } else { - const response = await sendTx(signedTx, provider); - printInfo('Transaction hash', response.transactionHash); + try { + const response = await sendTx(signedTx, provider); + + if (!isValidJSON(response) || response.status.toString() !== '1') { + const error = `Execution failed${response.status ? ` with txHash: ${response.transactionHash}` : ''}`; + throw new Error(error); + } + + printInfo('Transaction hash', response.transactionHash); + } catch (error) { + printError(`Transaction failed with error: ${error.message}`); + } } } else { const tx = await wallet.sendTransaction({ From 41022ffa180ec2e399c8e000356ac1514c97b648 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 16:15:22 +0530 Subject: [PATCH 13/45] use hardhat ethers in scripts --- evm/broadcast-transactions.js | 3 ++- evm/offline-sign-utils.js | 3 ++- evm/send-tokens.js | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 09b0f2ac3..586e12aae 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -1,8 +1,9 @@ 'use strict'; +const { ethers } = require('hardhat'); const { providers: { JsonRpcProvider }, -} = require('ethers'); +} = ethers; const { Command, Option } = require('commander'); const { printError, printInfo, printObj } = require('./utils'); const { diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 9f628c40c..0a7a28394 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -1,9 +1,10 @@ 'use strict'; +const { ethers } = require('hardhat'); const { BigNumber, utils: { isAddress }, -} = require('ethers'); +} = ethers; const { LedgerSigner } = require('@ethersproject/hardware-wallets'); const { printError } = require('./utils'); const fs = require('fs'); diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 5b4fc4fc7..8eedd8065 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -2,12 +2,12 @@ require('dotenv').config(); -// const { ethers } = require('hardhat'); +const { ethers } = require('hardhat'); const { Wallet, getDefaultProvider, utils: { parseEther, parseUnits }, -} = require('ethers'); +} = ethers; const { Command, Option } = require('commander'); const chalk = require('chalk'); const { printInfo, printWalletInfo, isValidPrivateKey, printError } = require('./utils'); From 646c17db21f6fef8bbb2593fb750124d74aec9b5 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 18:11:50 +0530 Subject: [PATCH 14/45] offline signing logic for upgrade --- evm/deploy-gateway-v5.0.x.js | 98 ++++++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 15 deletions(-) diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index 7b9bff2a8..9e267d5d4 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -14,13 +14,23 @@ const { printError, printWalletInfo, printWarn, + isValidPrivateKey } = require('./utils'); +const { + getLedgerWallet, + ledgerSign, + getAllSignersData, + getNonceFromProvider, + updateSignersData, + getLatestNonceAndUpdateData, + getSignerData, +} = require('./offline-sign-utils.js'); const { ethers } = require('hardhat'); const { ContractFactory, Contract, Wallet, - utils: { defaultAbiCoder, getContractAddress, AddressZero }, + utils: { defaultAbiCoder, getContractAddress, AddressZero, parseUnits }, getDefaultProvider, } = ethers; const readlineSync = require('readline-sync'); @@ -300,21 +310,32 @@ async function deploy(config, options) { } async function upgrade(config, options) { - const { chainName, privateKey, yes } = options; - + const { chainName, privateKey, yes, ledgerPath, offline, env } = options; + let { directoryPath, fileName } = options; + const isOffline = offline === 'true'; const contractName = 'AxelarGateway'; const chain = config.chains[chainName] || { contracts: {}, name: chainName, id: chainName, rpc: options.rpc, tokenSymbol: 'ETH' }; const rpc = options.rpc || chain.rpc; const provider = getDefaultProvider(rpc); - const wallet = new Wallet(privateKey).connect(provider); + let wallet; + if (privateKey === 'ledger') { + wallet = getLedgerWallet(provider, ledgerPath); + } else { + if (!isValidPrivateKey(privateKey)) { + throw new Error('Private key is missing/ not provided correctly in the user info'); + } + + wallet = new Wallet(privateKey, provider); + } + const signerAddress = await wallet.getAddress(); await printWalletInfo(wallet); const contractConfig = chain.contracts[contractName]; const gateway = new Contract(contractConfig.address, AxelarGateway.abi, wallet); - const implementationCodehash = await getBytecodeHash(contractConfig.implementation, chainName, provider); + const implementationCodehash = await getBytecodeHash(gateway, chainName, provider); let governance = options.governance || contractConfig.governance; let mintLimiter = options.mintLimiter || contractConfig.mintLimiter; let setupParams = '0x'; @@ -341,20 +362,57 @@ async function upgrade(config, options) { if (anwser !== 'y') return; } - const tx = await gateway.upgrade(contractConfig.implementation, implementationCodehash, setupParams, gasOptions); - printInfo('Upgrade transaction', tx.hash); + // Offline signing + let gasLimit, gasPrice; + try { + gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'gwei'); + const block = await provider.getBlock('latest'); + gasLimit = block.gasLimit.toNumber() / 1000; + + printInfo('Gas Price:', gasPrice.toString()); + printInfo('Gas Limit:', gasLimit.toString()); + } catch (error) { + printError(error.message); + } + + let nonce = await getNonceFromProvider(provider, signerAddress); + let signersData, signerData; + + if (isOffline) { + directoryPath = directoryPath || './tx'; + fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedUpgradeTransactions'; + nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); + signersData = await getAllSignersData(directoryPath, fileName); + signerData = await getSignerData(directoryPath, fileName, signerAddress); + const [baseTx, signedTx] = await ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, gateway.address, undefined, gateway, "upgrade", contractConfig.implementation, implementationCodehash, setupParams); + const tx = {}; + tx.nonce = nonce; + tx.msg = `This transaction will perform upgrade of AxlearGateway contract having address ${gateway.address} with implementation ${contractConfig.implementation} on chain ${chain.name} with chainId ${chain.chainId}`; + tx.baseTx = baseTx; + tx.signedTx = signedTx; + tx.status = 'PENDING'; + signerData.push(tx); + if (signerData) { + signersData[signerAddress] = signerData; + await updateSignersData(directoryPath, fileName, signersData); + } + } + else { + const tx = await gateway.upgrade(contractConfig.implementation, implementationCodehash, setupParams, gasOptions); + printInfo('Upgrade transaction', tx.hash); - await tx.wait(chain.confirmations); + await tx.wait(chain.confirmations); - const newImplementation = await gateway.implementation(); - printInfo('New implementation', newImplementation); + const newImplementation = await gateway.implementation(); + printInfo('New implementation', newImplementation); - if (newImplementation !== contractConfig.implementation) { - printWarn('Implementation not upgraded yet!'); - return; - } + if (newImplementation !== contractConfig.implementation) { + printWarn('Implementation not upgraded yet!'); + return; + } - printInfo('Upgraded to', newImplementation); + printInfo('Upgraded to', newImplementation); + } } async function main(options) { @@ -398,6 +456,16 @@ async function programHandler() { program.addOption(new Option('-a, --amplifier', 'deploy amplifier gateway').env('AMPLIFIER')); program.addOption(new Option('--prevKeyIDs ', 'previous key IDs to be used for auth contract')); program.addOption(new Option('-u, --upgrade', 'upgrade gateway').env('UPGRADE')); + program.addOption( + new Option('-o, --offline ', 'If this option is set as true, then ').choices(['true', 'false']).makeOptionMandatory(false), + ); + program.addOption(new Option('-l, --ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); + program.addOption( + new Option('-d, --directoryPath ', 'The folder where all the signed tx files are stored').makeOptionMandatory(false), + ); + program.addOption( + new Option('-f, --fileName ', 'The fileName where the signed tx will be stored').makeOptionMandatory(false), + ); program.action((options) => { main(options); From 0dbe734964a608fdf0f4ae5509d04104cc67e7d5 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 18:56:24 +0530 Subject: [PATCH 15/45] print object for error, modify util names, remove console logs --- evm/broadcast-transactions.js | 23 +++++----- evm/deploy-gateway-v5.0.x.js | 85 +++++++++++++++++++++-------------- evm/offline-sign-utils.js | 72 +++++++++++++++-------------- evm/send-tokens.js | 32 +++++++------ 4 files changed, 123 insertions(+), 89 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 586e12aae..4de1ed0bd 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -1,22 +1,23 @@ 'use strict'; +const chalk = require('chalk'); +const { Command, Option } = require('commander'); +const fs = require('fs'); const { ethers } = require('hardhat'); const { providers: { JsonRpcProvider }, } = ethers; -const { Command, Option } = require('commander'); + const { printError, printInfo, printObj } = require('./utils'); const { sendTx, - updateTxNonceAndStatus, + getTxsWithUpdatedNonceAndStatus, getNonceFromData, getNonceFromProvider, getFilePath, getAllSignersData, isValidJSON, } = require('./offline-sign-utils'); -const fs = require('fs'); -const chalk = require('chalk'); async function processTransactions(directoryPath, fileName, provider, signerAddress) { try { @@ -26,15 +27,15 @@ async function processTransactions(directoryPath, fileName, provider, signerAddr throw new Error(`Signer data for address ${signerAddress} not found in the file ${fileName}`); } - let signerData = signersData[signerAddress]; - const nonceFromData = await getNonceFromData(signerData); + let transactions = signersData[signerAddress]; + const nonceFromData = await getNonceFromData(transactions); const nonce = parseInt(await getNonceFromProvider(provider, signerAddress)); if (nonce > nonceFromData) { - signerData = updateTxNonceAndStatus(signerData, nonce); + transactions = getTxsWithUpdatedNonceAndStatus(transactions, nonce); } - for (const transaction of signerData) { + for (const transaction of transactions) { if (transaction.status === 'PENDING') { printInfo('Broadcasting transaction: '); printObj(transaction.baseTx); @@ -44,7 +45,9 @@ async function processTransactions(directoryPath, fileName, provider, signerAddr const response = await sendTx(transaction.signedTx, provider); if (!isValidJSON(response) || response.status.toString() !== '1') { - const error = `Execution failed:${response.status ? ` with txHash: ${response.transactionHash}` : ''}`; + const error = `Execution failed${ + response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` + }`; throw new Error(error); } @@ -62,7 +65,7 @@ async function processTransactions(directoryPath, fileName, provider, signerAddr } // Write back the updated JSON object to the file - signersData[signerAddress] = signerData; + signersData[signerAddress] = transactions; const filePath = getFilePath(directoryPath, fileName); fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2)); } catch (error) { diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index 9e267d5d4..374836cb7 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -2,6 +2,18 @@ require('dotenv').config(); +const chalk = require('chalk'); +const { Command, Option } = require('commander'); +const { ethers } = require('hardhat'); +const { + ContractFactory, + Contract, + Wallet, + utils: { defaultAbiCoder, getContractAddress, AddressZero, parseUnits }, + getDefaultProvider, +} = ethers; +const readlineSync = require('readline-sync'); + const { saveConfig, loadConfig, @@ -14,7 +26,8 @@ const { printError, printWalletInfo, printWarn, - isValidPrivateKey + printObj, + isValidPrivateKey, } = require('./utils'); const { getLedgerWallet, @@ -23,19 +36,8 @@ const { getNonceFromProvider, updateSignersData, getLatestNonceAndUpdateData, - getSignerData, + getTransactions, } = require('./offline-sign-utils.js'); -const { ethers } = require('hardhat'); -const { - ContractFactory, - Contract, - Wallet, - utils: { defaultAbiCoder, getContractAddress, AddressZero, parseUnits }, - getDefaultProvider, -} = ethers; -const readlineSync = require('readline-sync'); -const { Command, Option } = require('commander'); -const chalk = require('chalk'); const AxelarGatewayProxy = require('@axelar-network/axelar-cgp-solidity/artifacts/contracts/AxelarGatewayProxy.sol/AxelarGatewayProxy.json'); const AxelarGateway = require('@axelar-network/axelar-cgp-solidity/artifacts/contracts/AxelarGateway.sol/AxelarGateway.json'); @@ -320,6 +322,7 @@ async function upgrade(config, options) { const provider = getDefaultProvider(rpc); let wallet; + if (privateKey === 'ledger') { wallet = getLedgerWallet(provider, ledgerPath); } else { @@ -329,6 +332,7 @@ async function upgrade(config, options) { wallet = new Wallet(privateKey, provider); } + const signerAddress = await wallet.getAddress(); await printWalletInfo(wallet); @@ -364,6 +368,7 @@ async function upgrade(config, options) { // Offline signing let gasLimit, gasPrice; + try { gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'gwei'); const block = await provider.getBlock('latest'); @@ -372,47 +377,61 @@ async function upgrade(config, options) { printInfo('Gas Price:', gasPrice.toString()); printInfo('Gas Limit:', gasLimit.toString()); } catch (error) { - printError(error.message); + printError('Gas price and limit could not be fetched from provider'); + printObj(error); } let nonce = await getNonceFromProvider(provider, signerAddress); - let signersData, signerData; + let signersData, transactions; if (isOffline) { directoryPath = directoryPath || './tx'; fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedUpgradeTransactions'; nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); signersData = await getAllSignersData(directoryPath, fileName); - signerData = await getSignerData(directoryPath, fileName, signerAddress); - const [baseTx, signedTx] = await ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, gateway.address, undefined, gateway, "upgrade", contractConfig.implementation, implementationCodehash, setupParams); + transactions = await getTransactions(directoryPath, fileName, signerAddress); + const { baseTx, signedTx } = await ledgerSign( + gasLimit, + gasPrice, + nonce, + chain, + wallet, + gateway.address, + undefined, + gateway, + 'upgrade', + contractConfig.implementation, + implementationCodehash, + setupParams, + ); const tx = {}; tx.nonce = nonce; tx.msg = `This transaction will perform upgrade of AxlearGateway contract having address ${gateway.address} with implementation ${contractConfig.implementation} on chain ${chain.name} with chainId ${chain.chainId}`; tx.baseTx = baseTx; tx.signedTx = signedTx; tx.status = 'PENDING'; - signerData.push(tx); - if (signerData) { - signersData[signerAddress] = signerData; + transactions.push(tx); + + if (transactions) { + signersData[signerAddress] = transactions; await updateSignersData(directoryPath, fileName, signersData); } - } - else { - const tx = await gateway.upgrade(contractConfig.implementation, implementationCodehash, setupParams, gasOptions); - printInfo('Upgrade transaction', tx.hash); + } else { + const tx = await gateway.upgrade(contractConfig.implementation, implementationCodehash, setupParams, gasOptions); + printInfo('Upgrade transaction', tx.hash); - await tx.wait(chain.confirmations); + await tx.wait(chain.confirmations); - const newImplementation = await gateway.implementation(); - printInfo('New implementation', newImplementation); + const newImplementation = await gateway.implementation(); + printInfo('New implementation', newImplementation); - if (newImplementation !== contractConfig.implementation) { - printWarn('Implementation not upgraded yet!'); - return; - } + if (newImplementation !== contractConfig.implementation) { + printWarn('Implementation not upgraded yet!'); + return; + } - printInfo('Upgraded to', newImplementation); - } + printInfo('Upgraded to', newImplementation); + } } async function main(options) { diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 0a7a28394..c26ffe58d 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -1,19 +1,20 @@ 'use strict'; +const fs = require('fs'); const { ethers } = require('hardhat'); const { BigNumber, utils: { isAddress }, } = ethers; const { LedgerSigner } = require('@ethersproject/hardware-wallets'); -const { printError } = require('./utils'); -const fs = require('fs'); + +const { printError, printInfo, printObj } = require('./utils'); // function to create a ledgerSigner type wallet object function getLedgerWallet(provider, path) { // Check if the parameters are undefined and assign default values if necessary if (provider === undefined || provider === null) { - throw new Error('Provider is not provided while creating a ledger wallet'); + throw new Error('Empty provider'); } const type = 'hid'; @@ -83,7 +84,7 @@ async function ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, to, amount, }; const signedTx = await wallet.signTransaction(baseTx); - return [baseTx, signedTx]; + return { baseTx, signedTx }; } function getFilePath(directoryPath, fileName) { @@ -104,14 +105,13 @@ function getFilePath(directoryPath, fileName) { } async function sendTx(tx, provider) { - const response = {}; - try { const receipt = await provider.sendTransaction(tx).then((tx) => tx.wait()); return receipt; } catch (error) { - printError(error.message); - return response; + printError('Error while broadcasting signed tx'); + printObj(error); + return error || { message: 'Error while broadcasting signed tx' }; } } @@ -119,11 +119,12 @@ async function updateSignersData(directoryPath, fileName, signersData) { const filePath = getFilePath(directoryPath, fileName); fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2), (err) => { if (err) { - printError(err); + printError(`Couldnot update signersData in file ${filePath}`); + printObj(err); return; } - printError(`Data has been successfully stored in the ${filePath} file.`); + printInfo(`Data has been successfully stored in the ${filePath} file.`); }); } @@ -137,13 +138,13 @@ async function getLatestNonceAndUpdateData(directoryPath, fileName, wallet) { const provider = wallet.provider; const signerAddress = await wallet.getAddress(); const signersData = await getAllSignersData(directoryPath, fileName); - let signerData = signersData[signerAddress]; - const nonceFromData = getNonceFromData(signerData); + let transactions = signersData[signerAddress]; + const nonceFromData = getNonceFromData(transactions); let nonce = await getNonceFromProvider(provider, signerAddress); if (nonce > nonceFromData) { - signerData = updateTxNonceAndStatus(signerData, nonce); - signersData[signerAddress] = signerData; + transactions = getTxsWithUpdatedNonceAndStatus(transactions, nonce); + signersData[signerAddress] = transactions; await updateSignersData(directoryPath, fileName, signersData); } else { nonce = nonceFromData + 1; @@ -151,13 +152,14 @@ async function getLatestNonceAndUpdateData(directoryPath, fileName, wallet) { return nonce; } catch (error) { - printError(error.message); + printError('Failed to calculate correct nonce for tx'); + printObj(error); } } -function updateTxNonceAndStatus(signerData, nonce) { - if (signerData) { - for (const transaction of signerData) { +function getTxsWithUpdatedNonceAndStatus(transactions, nonce) { + if (transactions) { + for (const transaction of transactions) { if (nonce > transaction.nonce && (transaction.status === 'PENDING' || transaction.status === 'BROADCASTED')) { transaction.status = 'FAILED'; const error = `Transaction nonce value of ${transaction.nonce} is less than the required signer nonce value of ${nonce}`; @@ -167,20 +169,21 @@ function updateTxNonceAndStatus(signerData, nonce) { } } - return signerData; + return transactions; } -function getNonceFromData(signerData) { +function getNonceFromData(transactions) { try { - if (signerData) { - for (const transaction of signerData) { + if (transactions) { + for (const transaction of transactions) { if (transaction.status === 'PENDING') { return transaction.nonce; } } } } catch (error) { - printError(error.message); + printError('Failed to get first pending nonce from file data'); + printObj(error); } return 0; @@ -206,7 +209,8 @@ async function getAllSignersData(directoryPath, fileName) { return signersData; } catch (error) { - printError(error.message); + printError(`Failed to get all signers data from the file ${fileName}`); + printObj(error); } } @@ -222,12 +226,13 @@ function getFileData(filePath) { const data = fs.readFileSync(filePath); return data; } catch (error) { - printError(error.message); + printError(`Failed to get file data from the file ${filePath}`); + printObj(error); } } -async function getSignerData(directoryPath, fileName, signerAddress) { - let signerData = []; +async function getTransactions(directoryPath, fileName, signerAddress) { + let transactions = []; try { const filePath = getFilePath(directoryPath, fileName); @@ -238,18 +243,19 @@ async function getSignerData(directoryPath, fileName, signerAddress) { const jsonData = JSON.parse(data); if (!isValidJSON(jsonData)) { - return signerData; + return transactions; } // Access the transactions array from the JSON object if (signerAddress in jsonData) { - signerData = jsonData[signerAddress]; + transactions = jsonData[signerAddress]; } } - return signerData; + return transactions; } catch (error) { - printError(error.message); + printError(`Failed to get transactions for ${signerAddress}`); + printObj(error); } } @@ -270,11 +276,11 @@ module.exports = { ledgerSign, sendTx, getFilePath, - updateTxNonceAndStatus, + getTxsWithUpdatedNonceAndStatus, getNonceFromProvider, getNonceFromData, getAllSignersData, - getSignerData, + getTransactions, updateSignersData, getLatestNonceAndUpdateData, isValidJSON, diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 8eedd8065..1f1785d92 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -2,14 +2,16 @@ require('dotenv').config(); +const chalk = require('chalk'); +const { Command, Option } = require('commander'); const { ethers } = require('hardhat'); const { Wallet, getDefaultProvider, utils: { parseEther, parseUnits }, } = ethers; -const { Command, Option } = require('commander'); -const chalk = require('chalk'); +const readlineSync = require('readline-sync'); + const { printInfo, printWalletInfo, isValidPrivateKey, printError } = require('./utils'); const { getLedgerWallet, @@ -19,10 +21,10 @@ const { getNonceFromProvider, updateSignersData, getLatestNonceAndUpdateData, - getSignerData, + getTransactions, isValidJSON, } = require('./offline-sign-utils.js'); -const readlineSync = require('readline-sync'); +const { printObj } = require('@axelar-network/axelar-gmp-sdk-solidity'); async function sendTokens(chain, options) { let wallet; @@ -70,25 +72,26 @@ async function sendTokens(chain, options) { printInfo('Gas Price:', gasPrice.toString()); printInfo('Gas Limit:', gasLimit.toString()); } catch (error) { - printError(error.message); + printError('Gas price and limit could not be fetched from provider'); + printObj(error); } let nonce = await getNonceFromProvider(provider, signerAddress); - let signersData, signerData; + let signersData, transactions; if (isOffline) { directoryPath = directoryPath || './tx'; fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedTransactions'; nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); signersData = await getAllSignersData(directoryPath, fileName); - signerData = await getSignerData(directoryPath, fileName, signerAddress); + transactions = await getTransactions(directoryPath, fileName, signerAddress); } for (const recipient of recipients) { printInfo('Recipient', recipient); if (privateKey === 'ledger') { - const [baseTx, signedTx] = await ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, recipient, amount); + const { baseTx, signedTx } = await ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, recipient, amount); if (isOffline) { const tx = {}; @@ -97,19 +100,22 @@ async function sendTokens(chain, options) { tx.baseTx = baseTx; tx.signedTx = signedTx; tx.status = 'PENDING'; - signerData.push(tx); + transactions.push(tx); } else { try { const response = await sendTx(signedTx, provider); if (!isValidJSON(response) || response.status.toString() !== '1') { - const error = `Execution failed${response.status ? ` with txHash: ${response.transactionHash}` : ''}`; + const error = `Execution failed${ + response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` + }`; throw new Error(error); } printInfo('Transaction hash', response.transactionHash); } catch (error) { - printError(`Transaction failed with error: ${error.message}`); + printError('Broadcasting Transaction failed'); + printObj(error); } } } else { @@ -126,8 +132,8 @@ async function sendTokens(chain, options) { ++nonce; } - if (signerData) { - signersData[signerAddress] = signerData; + if (transactions) { + signersData[signerAddress] = transactions; await updateSignersData(directoryPath, fileName, signersData); } } From ce926e6dc58e17daed433565c58fb0e11b755feb Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 19:04:24 +0530 Subject: [PATCH 16/45] update optional fileName --- evm/send-tokens.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 1f1785d92..cf017aa40 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -81,7 +81,7 @@ async function sendTokens(chain, options) { if (isOffline) { directoryPath = directoryPath || './tx'; - fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedTransactions'; + fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedSendTokensTransactions'; nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); signersData = await getAllSignersData(directoryPath, fileName); transactions = await getTransactions(directoryPath, fileName, signerAddress); From 418ec0ad689802548b86c51a064a835146be3f68 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 19:14:15 +0530 Subject: [PATCH 17/45] refactored util function name, and optional directory path --- evm/broadcast-transactions.js | 5 +++-- evm/deploy-gateway-v5.0.x.js | 2 +- evm/offline-sign-utils.js | 6 +++--- evm/send-tokens.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 4de1ed0bd..8df0340e4 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -28,10 +28,10 @@ async function processTransactions(directoryPath, fileName, provider, signerAddr } let transactions = signersData[signerAddress]; - const nonceFromData = await getNonceFromData(transactions); + const firstPendingnonceFromData = await getNonceFromData(transactions); const nonce = parseInt(await getNonceFromProvider(provider, signerAddress)); - if (nonce > nonceFromData) { + if (nonce > firstPendingnonceFromData) { transactions = getTxsWithUpdatedNonceAndStatus(transactions, nonce); } @@ -74,6 +74,7 @@ async function processTransactions(directoryPath, fileName, provider, signerAddr } async function main(options) { + // TODO: Enable multiple scripts to use offlineSigning const { directoryPath, fileName, rpcUrl, signerAddress } = options; const provider = new JsonRpcProvider(rpcUrl); const network = await provider.getNetwork(); diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index 374836cb7..e912d21a5 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -385,7 +385,7 @@ async function upgrade(config, options) { let signersData, transactions; if (isOffline) { - directoryPath = directoryPath || './tx'; + directoryPath = directoryPath || './txs'; fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedUpgradeTransactions'; nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); signersData = await getAllSignersData(directoryPath, fileName); diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index c26ffe58d..a38197603 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -139,15 +139,15 @@ async function getLatestNonceAndUpdateData(directoryPath, fileName, wallet) { const signerAddress = await wallet.getAddress(); const signersData = await getAllSignersData(directoryPath, fileName); let transactions = signersData[signerAddress]; - const nonceFromData = getNonceFromData(transactions); + const firstPendingnonceFromData = getNonceFromData(transactions); let nonce = await getNonceFromProvider(provider, signerAddress); - if (nonce > nonceFromData) { + if (nonce > firstPendingnonceFromData) { transactions = getTxsWithUpdatedNonceAndStatus(transactions, nonce); signersData[signerAddress] = transactions; await updateSignersData(directoryPath, fileName, signersData); } else { - nonce = nonceFromData + 1; + nonce = firstPendingnonceFromData + 1; } return nonce; diff --git a/evm/send-tokens.js b/evm/send-tokens.js index cf017aa40..5c2577fa6 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -80,7 +80,7 @@ async function sendTokens(chain, options) { let signersData, transactions; if (isOffline) { - directoryPath = directoryPath || './tx'; + directoryPath = directoryPath || './txs'; fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedSendTokensTransactions'; nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); signersData = await getAllSignersData(directoryPath, fileName); From 2c17bbc3cd8d63d2195687de0f7cff5b828ba224 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 19:17:06 +0530 Subject: [PATCH 18/45] add: package lock --- package-lock.json | 1161 ++++++++++++++++++++++++++++++++------------- 1 file changed, 842 insertions(+), 319 deletions(-) diff --git a/package-lock.json b/package-lock.json index 40a19a28b..9d9e681b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@axelar-network/interchain-token-service": "0.3.0", "@cosmjs/cosmwasm-stargate": "^0.31.1", "@ethersproject/hardware-wallets": "^5.5.0", + "@ledgerhq/hw-transport-node-hid": "^6.11.2", "ethers": "^5.7.2" }, "devDependencies": { @@ -648,6 +649,230 @@ "@ledgerhq/hw-transport-node-hid": "5.26.0" } }, + "node_modules/@ethersproject/hardware-wallets/node_modules/@ledgerhq/hw-transport-node-hid": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-5.26.0.tgz", + "integrity": "sha512-qhaefZVZatJ6UuK8Wb6WSFNOLWc2mxcv/xgsfKi5HJCIr4bPF/ecIeN+7fRcEaycxj4XykY6Z4A7zDVulfFH4w==", + "optional": true, + "dependencies": { + "@ledgerhq/devices": "^5.26.0", + "@ledgerhq/errors": "^5.26.0", + "@ledgerhq/hw-transport": "^5.26.0", + "@ledgerhq/hw-transport-node-hid-noevents": "^5.26.0", + "@ledgerhq/logs": "^5.26.0", + "lodash": "^4.17.20", + "node-hid": "1.3.0", + "usb": "^1.6.3" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/@ledgerhq/hw-transport-node-hid-noevents": { + "version": "5.51.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.51.1.tgz", + "integrity": "sha512-9wFf1L8ZQplF7XOY2sQGEeOhpmBRzrn+4X43kghZ7FBDoltrcK+s/D7S+7ffg3j2OySyP6vIIIgloXylao5Scg==", + "optional": true, + "dependencies": { + "@ledgerhq/devices": "^5.51.1", + "@ledgerhq/errors": "^5.50.0", + "@ledgerhq/hw-transport": "^5.51.1", + "@ledgerhq/logs": "^5.50.0", + "node-hid": "2.1.1" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/@ledgerhq/hw-transport": { + "version": "5.51.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz", + "integrity": "sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw==", + "optional": true, + "dependencies": { + "@ledgerhq/devices": "^5.51.1", + "@ledgerhq/errors": "^5.50.0", + "events": "^3.3.0" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "optional": true + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/node-hid": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-2.1.1.tgz", + "integrity": "sha512-Skzhqow7hyLZU93eIPthM9yjot9lszg9xrKxESleEs05V2NcbUptZc5HFqzjOkSmL0sFlZFr3kmvaYebx06wrw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^3.0.2", + "prebuild-install": "^6.0.0" + }, + "bin": { + "hid-showdevices": "src/show-devices.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/prebuild-install": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", + "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.21.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/node-abi": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", + "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", + "optional": true, + "dependencies": { + "semver": "^5.4.1" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "optional": true + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/node-hid": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-1.3.0.tgz", + "integrity": "sha512-BA6G4V84kiNd1uAChub/Z/5s/xS3EHBCxotQ0nyYrUG65mXewUDHE1tWOSqA2dp3N+mV0Ffq9wo2AW9t4p/G7g==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.14.0", + "node-abi": "^2.18.0", + "prebuild-install": "^5.3.4" + }, + "bin": { + "hid-showdevices": "src/show-devices.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/prebuild-install": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.6.tgz", + "integrity": "sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/@ethersproject/hardware-wallets/node_modules/usb": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/usb/-/usb-1.9.2.tgz", + "integrity": "sha512-dryNz030LWBPAf6gj8vyq0Iev3vPbCLHCT8dBw3gQRXRzVNsIdeuU+VjPp3ksmSPkeMAl1k+kQ14Ij0QHyeiAg==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-addon-api": "^4.2.0", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/@ethersproject/hash": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", @@ -1243,96 +1468,154 @@ } }, "node_modules/@ledgerhq/hw-transport-node-hid": { - "version": "5.26.0", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-5.26.0.tgz", - "integrity": "sha512-qhaefZVZatJ6UuK8Wb6WSFNOLWc2mxcv/xgsfKi5HJCIr4bPF/ecIeN+7fRcEaycxj4XykY6Z4A7zDVulfFH4w==", - "optional": true, - "dependencies": { - "@ledgerhq/devices": "^5.26.0", - "@ledgerhq/errors": "^5.26.0", - "@ledgerhq/hw-transport": "^5.26.0", - "@ledgerhq/hw-transport-node-hid-noevents": "^5.26.0", - "@ledgerhq/logs": "^5.26.0", - "lodash": "^4.17.20", - "node-hid": "1.3.0", - "usb": "^1.6.3" + "version": "6.27.21", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-6.27.21.tgz", + "integrity": "sha512-8G3Owpa2ex+TkGQSMkKoAbMEGZ7a23g0wZUvVzalQphMqbayebMhuXxue8iPp7F9pulm7uyLxgMYptYyw5i4yQ==", + "dependencies": { + "@ledgerhq/devices": "^8.0.7", + "@ledgerhq/errors": "^6.14.0", + "@ledgerhq/hw-transport": "^6.28.8", + "@ledgerhq/hw-transport-node-hid-noevents": "^6.27.19", + "@ledgerhq/logs": "^6.10.1", + "lodash": "^4.17.21", + "node-hid": "^2.1.2", + "usb": "2.9.0" } }, "node_modules/@ledgerhq/hw-transport-node-hid-noevents": { - "version": "5.51.1", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.51.1.tgz", - "integrity": "sha512-9wFf1L8ZQplF7XOY2sQGEeOhpmBRzrn+4X43kghZ7FBDoltrcK+s/D7S+7ffg3j2OySyP6vIIIgloXylao5Scg==", - "optional": true, + "version": "6.27.19", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-6.27.19.tgz", + "integrity": "sha512-zOIB1fBiQH9ZYFzoEpNY4n1lE7bGPgRT+k85fKuLM7cxxm5Sy+TgrdxImvBz0IQUS8EvrtZCm+dVWkb2sH/6OA==", "dependencies": { - "@ledgerhq/devices": "^5.51.1", - "@ledgerhq/errors": "^5.50.0", - "@ledgerhq/hw-transport": "^5.51.1", - "@ledgerhq/logs": "^5.50.0", - "node-hid": "2.1.1" + "@ledgerhq/devices": "^8.0.7", + "@ledgerhq/errors": "^6.14.0", + "@ledgerhq/hw-transport": "^6.28.8", + "@ledgerhq/logs": "^6.10.1", + "node-hid": "^2.1.2" + } + }, + "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/@ledgerhq/devices": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.0.7.tgz", + "integrity": "sha512-BbPyET52lXnVs7CxJWrGYqmtGdbGzj+XnfCqLsDnA7QYr1CZREysxmie+Rr6BKpNDBRVesAovXjtaVaZOn+upw==", + "dependencies": { + "@ledgerhq/errors": "^6.14.0", + "@ledgerhq/logs": "^6.10.1", + "rxjs": "6", + "semver": "^7.3.5" + } + }, + "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/@ledgerhq/errors": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.14.0.tgz", + "integrity": "sha512-ZWJw2Ti6Dq1Ott/+qYqJdDWeZm16qI3VNG5rFlb0TQ3UcAyLIQZbnnzzdcVVwVeZiEp66WIpINd/pBdqsHVyOA==" + }, + "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/@ledgerhq/hw-transport": { + "version": "6.28.8", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.28.8.tgz", + "integrity": "sha512-XxQVl4htd018u/M66r0iu5nlHi+J6QfdPsORzDF6N39jaz+tMqItb7tUlXM/isggcuS5lc7GJo7NOuJ8rvHZaQ==", + "dependencies": { + "@ledgerhq/devices": "^8.0.7", + "@ledgerhq/errors": "^6.14.0", + "events": "^3.3.0" + } + }, + "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/@ledgerhq/logs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-6.10.1.tgz", + "integrity": "sha512-z+ILK8Q3y+nfUl43ctCPuR4Y2bIxk/ooCQFwZxhtci1EhAtMDzMAx2W25qx8G1PPL9UUOdnUax19+F0OjXoj4w==" + }, + "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/@ledgerhq/hw-transport-node-hid/node_modules/@ledgerhq/devices": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.0.7.tgz", + "integrity": "sha512-BbPyET52lXnVs7CxJWrGYqmtGdbGzj+XnfCqLsDnA7QYr1CZREysxmie+Rr6BKpNDBRVesAovXjtaVaZOn+upw==", + "dependencies": { + "@ledgerhq/errors": "^6.14.0", + "@ledgerhq/logs": "^6.10.1", + "rxjs": "6", + "semver": "^7.3.5" } }, - "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/@ledgerhq/hw-transport": { - "version": "5.51.1", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz", - "integrity": "sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw==", - "optional": true, + "node_modules/@ledgerhq/hw-transport-node-hid/node_modules/@ledgerhq/errors": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.14.0.tgz", + "integrity": "sha512-ZWJw2Ti6Dq1Ott/+qYqJdDWeZm16qI3VNG5rFlb0TQ3UcAyLIQZbnnzzdcVVwVeZiEp66WIpINd/pBdqsHVyOA==" + }, + "node_modules/@ledgerhq/hw-transport-node-hid/node_modules/@ledgerhq/hw-transport": { + "version": "6.28.8", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.28.8.tgz", + "integrity": "sha512-XxQVl4htd018u/M66r0iu5nlHi+J6QfdPsORzDF6N39jaz+tMqItb7tUlXM/isggcuS5lc7GJo7NOuJ8rvHZaQ==", "dependencies": { - "@ledgerhq/devices": "^5.51.1", - "@ledgerhq/errors": "^5.50.0", + "@ledgerhq/devices": "^8.0.7", + "@ledgerhq/errors": "^6.14.0", "events": "^3.3.0" } }, - "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "optional": true + "node_modules/@ledgerhq/hw-transport-node-hid/node_modules/@ledgerhq/logs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-6.10.1.tgz", + "integrity": "sha512-z+ILK8Q3y+nfUl43ctCPuR4Y2bIxk/ooCQFwZxhtci1EhAtMDzMAx2W25qx8G1PPL9UUOdnUax19+F0OjXoj4w==" }, - "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/node-hid": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-2.1.1.tgz", - "integrity": "sha512-Skzhqow7hyLZU93eIPthM9yjot9lszg9xrKxESleEs05V2NcbUptZc5HFqzjOkSmL0sFlZFr3kmvaYebx06wrw==", - "hasInstallScript": true, - "optional": true, + "node_modules/@ledgerhq/hw-transport-node-hid/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dependencies": { - "bindings": "^1.5.0", - "node-addon-api": "^3.0.2", - "prebuild-install": "^6.0.0" - }, - "bin": { - "hid-showdevices": "src/show-devices.js" + "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, - "node_modules/@ledgerhq/hw-transport-node-hid-noevents/node_modules/prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", - "optional": true, + "node_modules/@ledgerhq/hw-transport-node-hid/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" + "lru-cache": "^6.0.0" }, "bin": { - "prebuild-install": "bin.js" + "semver": "bin/semver.js" }, "engines": { - "node": ">=6" + "node": ">=10" } }, + "node_modules/@ledgerhq/hw-transport-node-hid/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/@ledgerhq/hw-transport-u2f": { "version": "5.26.0", "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-u2f/-/hw-transport-u2f-5.26.0.tgz", @@ -2412,6 +2695,11 @@ "@types/node": "*" } }, + "node_modules/@types/w3c-web-usb": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.6.tgz", + "integrity": "sha512-cSjhgrr8g4KbPnnijAr/KJDNKa/bBa+ixYkywFRvrhvi9n1WEl7yYbtRyzE6jqNQiSxxJxoAW3STaOQwJHndaw==" + }, "node_modules/abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", @@ -3067,7 +3355,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, "dependencies": { "file-uri-to-path": "1.0.0" } @@ -3076,7 +3363,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "optional": true, "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -3101,7 +3387,6 @@ "url": "https://feross.org/support" } ], - "optional": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -3470,8 +3755,7 @@ "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "optional": true + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "node_modules/ci-info": { "version": "2.0.0", @@ -3918,15 +4202,17 @@ } }, "node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "optional": true, + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dependencies": { - "mimic-response": "^2.0.0" + "mimic-response": "^3.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/deep-eql": { @@ -3945,7 +4231,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "devOptional": true, "engines": { "node": ">=4.0.0" } @@ -4058,15 +4343,11 @@ } }, "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "optional": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", "engines": { - "node": ">=0.10" + "node": ">=8" } }, "node_modules/detect-port": { @@ -4194,7 +4475,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "optional": true, "dependencies": { "once": "^1.4.0" } @@ -5705,7 +5985,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "optional": true, "engines": { "node": ">=6" } @@ -5809,8 +6088,7 @@ "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, "node_modules/fill-range": { "version": "7.0.1", @@ -5943,8 +6221,7 @@ "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "optional": true + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "node_modules/fs-extra": { "version": "11.1.1", @@ -6209,8 +6486,7 @@ "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "optional": true + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, "node_modules/glob": { "version": "7.2.0", @@ -6977,8 +7253,7 @@ "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "devOptional": true + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/internal-slot": { "version": "1.0.5", @@ -8011,12 +8286,11 @@ } }, "node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "optional": true, + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -8048,7 +8322,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "devOptional": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8069,8 +8342,7 @@ "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "optional": true + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "node_modules/mnemonist": { "version": "0.38.5", @@ -8208,9 +8480,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", "optional": true }, "node_modules/nanoid": { @@ -8228,8 +8500,7 @@ "node_modules/napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "optional": true + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" }, "node_modules/napi-macros": { "version": "2.2.2", @@ -8251,23 +8522,46 @@ "peer": true }, "node_modules/node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "optional": true, + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.47.0.tgz", + "integrity": "sha512-2s6B2CWZM//kPgwnuI0KrYwNjfdByE25zvAaEpq9IH4zcNsarH8Ihu/UuX6XMPEogDAxkuUFeZn60pXNHAqn3A==", "dependencies": { - "semver": "^5.4.1" + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, "node_modules/node-abi/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "optional": true, + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { - "semver": "bin/semver" + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, + "node_modules/node-abi/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", @@ -8315,24 +8609,27 @@ } }, "node_modules/node-hid": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-1.3.0.tgz", - "integrity": "sha512-BA6G4V84kiNd1uAChub/Z/5s/xS3EHBCxotQ0nyYrUG65mXewUDHE1tWOSqA2dp3N+mV0Ffq9wo2AW9t4p/G7g==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-2.1.2.tgz", + "integrity": "sha512-qhCyQqrPpP93F/6Wc/xUR7L8mAJW0Z6R7HMQV8jCHHksAxNDe/4z4Un/H9CpLOT+5K39OPyt9tIQlavxWES3lg==", "hasInstallScript": true, - "optional": true, "dependencies": { "bindings": "^1.5.0", - "nan": "^2.14.0", - "node-abi": "^2.18.0", - "prebuild-install": "^5.3.4" + "node-addon-api": "^3.0.2", + "prebuild-install": "^7.1.1" }, "bin": { "hid-showdevices": "src/show-devices.js" }, "engines": { - "node": ">=6.0.0" + "node": ">=10" } }, + "node_modules/node-hid/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" + }, "node_modules/nofilter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", @@ -8515,7 +8812,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "devOptional": true, "dependencies": { "wrappy": "1" } @@ -8852,32 +9148,28 @@ } }, "node_modules/prebuild-install": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.6.tgz", - "integrity": "sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==", - "optional": true, + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", "dependencies": { - "detect-libc": "^1.0.3", + "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^1.0.1", - "node-abi": "^2.7.0", - "noop-logger": "^0.1.1", - "npmlog": "^4.0.1", + "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", - "simple-get": "^3.0.3", + "simple-get": "^4.0.0", "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0", - "which-pm-runs": "^1.0.0" + "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" }, "engines": { - "node": ">=6" + "node": ">=10" } }, "node_modules/prelude-ls": { @@ -8961,7 +9253,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "optional": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -9044,7 +9335,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -9059,7 +9349,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -9804,16 +10093,28 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true + ] }, "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "optional": true, + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "decompress-response": "^4.2.0", + "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } @@ -11066,7 +11367,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "optional": true, "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -11078,7 +11378,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "optional": true, "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -11281,7 +11580,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "devOptional": true, "dependencies": { "safe-buffer": "^5.0.1" }, @@ -11547,24 +11845,23 @@ } }, "node_modules/usb": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/usb/-/usb-1.9.2.tgz", - "integrity": "sha512-dryNz030LWBPAf6gj8vyq0Iev3vPbCLHCT8dBw3gQRXRzVNsIdeuU+VjPp3ksmSPkeMAl1k+kQ14Ij0QHyeiAg==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/usb/-/usb-2.9.0.tgz", + "integrity": "sha512-G0I/fPgfHUzWH8xo2KkDxTTFruUWfppgSFJ+bQxz/kVY2x15EQ/XDB7dqD1G432G4gBG4jYQuF3U7j/orSs5nw==", "hasInstallScript": true, - "optional": true, "dependencies": { - "node-addon-api": "^4.2.0", - "node-gyp-build": "^4.3.0" + "@types/w3c-web-usb": "^1.0.6", + "node-addon-api": "^6.0.0", + "node-gyp-build": "^4.5.0" }, "engines": { - "node": ">=10.16.0" + "node": ">=10.20.0 <11.x || >=12.17.0 <13.0 || >=14.0.0" } }, "node_modules/usb/node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", - "optional": true + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" }, "node_modules/utf8": { "version": "3.0.0", @@ -11844,8 +12141,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "devOptional": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { "version": "7.4.6", @@ -12466,6 +12762,186 @@ "@ledgerhq/hw-transport-node-hid": "5.26.0", "@ledgerhq/hw-transport-u2f": "5.26.0", "ethers": "^5.7.0" + }, + "dependencies": { + "@ledgerhq/hw-transport-node-hid": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-5.26.0.tgz", + "integrity": "sha512-qhaefZVZatJ6UuK8Wb6WSFNOLWc2mxcv/xgsfKi5HJCIr4bPF/ecIeN+7fRcEaycxj4XykY6Z4A7zDVulfFH4w==", + "optional": true, + "requires": { + "@ledgerhq/devices": "^5.26.0", + "@ledgerhq/errors": "^5.26.0", + "@ledgerhq/hw-transport": "^5.26.0", + "@ledgerhq/hw-transport-node-hid-noevents": "^5.26.0", + "@ledgerhq/logs": "^5.26.0", + "lodash": "^4.17.20", + "node-hid": "1.3.0", + "usb": "^1.6.3" + } + }, + "@ledgerhq/hw-transport-node-hid-noevents": { + "version": "5.51.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.51.1.tgz", + "integrity": "sha512-9wFf1L8ZQplF7XOY2sQGEeOhpmBRzrn+4X43kghZ7FBDoltrcK+s/D7S+7ffg3j2OySyP6vIIIgloXylao5Scg==", + "optional": true, + "requires": { + "@ledgerhq/devices": "^5.51.1", + "@ledgerhq/errors": "^5.50.0", + "@ledgerhq/hw-transport": "^5.51.1", + "@ledgerhq/logs": "^5.50.0", + "node-hid": "2.1.1" + }, + "dependencies": { + "@ledgerhq/hw-transport": { + "version": "5.51.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz", + "integrity": "sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw==", + "optional": true, + "requires": { + "@ledgerhq/devices": "^5.51.1", + "@ledgerhq/errors": "^5.50.0", + "events": "^3.3.0" + } + }, + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "optional": true + }, + "node-hid": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-2.1.1.tgz", + "integrity": "sha512-Skzhqow7hyLZU93eIPthM9yjot9lszg9xrKxESleEs05V2NcbUptZc5HFqzjOkSmL0sFlZFr3kmvaYebx06wrw==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "node-addon-api": "^3.0.2", + "prebuild-install": "^6.0.0" + } + }, + "prebuild-install": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", + "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", + "optional": true, + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.21.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + } + } + } + }, + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "optional": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "optional": true + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "optional": true + }, + "node-abi": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", + "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", + "optional": true, + "requires": { + "semver": "^5.4.1" + } + }, + "node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "optional": true + }, + "node-hid": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-1.3.0.tgz", + "integrity": "sha512-BA6G4V84kiNd1uAChub/Z/5s/xS3EHBCxotQ0nyYrUG65mXewUDHE1tWOSqA2dp3N+mV0Ffq9wo2AW9t4p/G7g==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.14.0", + "node-abi": "^2.18.0", + "prebuild-install": "^5.3.4" + } + }, + "prebuild-install": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.6.tgz", + "integrity": "sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==", + "optional": true, + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^2.7.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^3.0.3", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + } + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "optional": true + }, + "simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "optional": true, + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "usb": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/usb/-/usb-1.9.2.tgz", + "integrity": "sha512-dryNz030LWBPAf6gj8vyq0Iev3vPbCLHCT8dBw3gQRXRzVNsIdeuU+VjPp3ksmSPkeMAl1k+kQ14Ij0QHyeiAg==", + "optional": true, + "requires": { + "node-addon-api": "^4.2.0", + "node-gyp-build": "^4.3.0" + } + } } }, "@ethersproject/hash": { @@ -12843,82 +13319,137 @@ } }, "@ledgerhq/hw-transport-node-hid": { - "version": "5.26.0", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-5.26.0.tgz", - "integrity": "sha512-qhaefZVZatJ6UuK8Wb6WSFNOLWc2mxcv/xgsfKi5HJCIr4bPF/ecIeN+7fRcEaycxj4XykY6Z4A7zDVulfFH4w==", - "optional": true, - "requires": { - "@ledgerhq/devices": "^5.26.0", - "@ledgerhq/errors": "^5.26.0", - "@ledgerhq/hw-transport": "^5.26.0", - "@ledgerhq/hw-transport-node-hid-noevents": "^5.26.0", - "@ledgerhq/logs": "^5.26.0", - "lodash": "^4.17.20", - "node-hid": "1.3.0", - "usb": "^1.6.3" + "version": "6.27.21", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-6.27.21.tgz", + "integrity": "sha512-8G3Owpa2ex+TkGQSMkKoAbMEGZ7a23g0wZUvVzalQphMqbayebMhuXxue8iPp7F9pulm7uyLxgMYptYyw5i4yQ==", + "requires": { + "@ledgerhq/devices": "^8.0.7", + "@ledgerhq/errors": "^6.14.0", + "@ledgerhq/hw-transport": "^6.28.8", + "@ledgerhq/hw-transport-node-hid-noevents": "^6.27.19", + "@ledgerhq/logs": "^6.10.1", + "lodash": "^4.17.21", + "node-hid": "^2.1.2", + "usb": "2.9.0" + }, + "dependencies": { + "@ledgerhq/devices": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.0.7.tgz", + "integrity": "sha512-BbPyET52lXnVs7CxJWrGYqmtGdbGzj+XnfCqLsDnA7QYr1CZREysxmie+Rr6BKpNDBRVesAovXjtaVaZOn+upw==", + "requires": { + "@ledgerhq/errors": "^6.14.0", + "@ledgerhq/logs": "^6.10.1", + "rxjs": "6", + "semver": "^7.3.5" + } + }, + "@ledgerhq/errors": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.14.0.tgz", + "integrity": "sha512-ZWJw2Ti6Dq1Ott/+qYqJdDWeZm16qI3VNG5rFlb0TQ3UcAyLIQZbnnzzdcVVwVeZiEp66WIpINd/pBdqsHVyOA==" + }, + "@ledgerhq/hw-transport": { + "version": "6.28.8", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.28.8.tgz", + "integrity": "sha512-XxQVl4htd018u/M66r0iu5nlHi+J6QfdPsORzDF6N39jaz+tMqItb7tUlXM/isggcuS5lc7GJo7NOuJ8rvHZaQ==", + "requires": { + "@ledgerhq/devices": "^8.0.7", + "@ledgerhq/errors": "^6.14.0", + "events": "^3.3.0" + } + }, + "@ledgerhq/logs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-6.10.1.tgz", + "integrity": "sha512-z+ILK8Q3y+nfUl43ctCPuR4Y2bIxk/ooCQFwZxhtci1EhAtMDzMAx2W25qx8G1PPL9UUOdnUax19+F0OjXoj4w==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } } }, "@ledgerhq/hw-transport-node-hid-noevents": { - "version": "5.51.1", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.51.1.tgz", - "integrity": "sha512-9wFf1L8ZQplF7XOY2sQGEeOhpmBRzrn+4X43kghZ7FBDoltrcK+s/D7S+7ffg3j2OySyP6vIIIgloXylao5Scg==", - "optional": true, + "version": "6.27.19", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-6.27.19.tgz", + "integrity": "sha512-zOIB1fBiQH9ZYFzoEpNY4n1lE7bGPgRT+k85fKuLM7cxxm5Sy+TgrdxImvBz0IQUS8EvrtZCm+dVWkb2sH/6OA==", "requires": { - "@ledgerhq/devices": "^5.51.1", - "@ledgerhq/errors": "^5.50.0", - "@ledgerhq/hw-transport": "^5.51.1", - "@ledgerhq/logs": "^5.50.0", - "node-hid": "2.1.1" + "@ledgerhq/devices": "^8.0.7", + "@ledgerhq/errors": "^6.14.0", + "@ledgerhq/hw-transport": "^6.28.8", + "@ledgerhq/logs": "^6.10.1", + "node-hid": "^2.1.2" }, "dependencies": { + "@ledgerhq/devices": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.0.7.tgz", + "integrity": "sha512-BbPyET52lXnVs7CxJWrGYqmtGdbGzj+XnfCqLsDnA7QYr1CZREysxmie+Rr6BKpNDBRVesAovXjtaVaZOn+upw==", + "requires": { + "@ledgerhq/errors": "^6.14.0", + "@ledgerhq/logs": "^6.10.1", + "rxjs": "6", + "semver": "^7.3.5" + } + }, + "@ledgerhq/errors": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.14.0.tgz", + "integrity": "sha512-ZWJw2Ti6Dq1Ott/+qYqJdDWeZm16qI3VNG5rFlb0TQ3UcAyLIQZbnnzzdcVVwVeZiEp66WIpINd/pBdqsHVyOA==" + }, "@ledgerhq/hw-transport": { - "version": "5.51.1", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz", - "integrity": "sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw==", - "optional": true, + "version": "6.28.8", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.28.8.tgz", + "integrity": "sha512-XxQVl4htd018u/M66r0iu5nlHi+J6QfdPsORzDF6N39jaz+tMqItb7tUlXM/isggcuS5lc7GJo7NOuJ8rvHZaQ==", "requires": { - "@ledgerhq/devices": "^5.51.1", - "@ledgerhq/errors": "^5.50.0", + "@ledgerhq/devices": "^8.0.7", + "@ledgerhq/errors": "^6.14.0", "events": "^3.3.0" } }, - "node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "optional": true + "@ledgerhq/logs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-6.10.1.tgz", + "integrity": "sha512-z+ILK8Q3y+nfUl43ctCPuR4Y2bIxk/ooCQFwZxhtci1EhAtMDzMAx2W25qx8G1PPL9UUOdnUax19+F0OjXoj4w==" }, - "node-hid": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-2.1.1.tgz", - "integrity": "sha512-Skzhqow7hyLZU93eIPthM9yjot9lszg9xrKxESleEs05V2NcbUptZc5HFqzjOkSmL0sFlZFr3kmvaYebx06wrw==", - "optional": true, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "requires": { - "bindings": "^1.5.0", - "node-addon-api": "^3.0.2", - "prebuild-install": "^6.0.0" + "yallist": "^4.0.0" } }, - "prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", - "optional": true, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" + "lru-cache": "^6.0.0" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -13826,6 +14357,11 @@ "@types/node": "*" } }, + "@types/w3c-web-usb": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.6.tgz", + "integrity": "sha512-cSjhgrr8g4KbPnnijAr/KJDNKa/bBa+ixYkywFRvrhvi9n1WEl7yYbtRyzE6jqNQiSxxJxoAW3STaOQwJHndaw==" + }, "abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", @@ -14331,7 +14867,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, "requires": { "file-uri-to-path": "1.0.0" } @@ -14340,7 +14875,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "optional": true, "requires": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -14351,7 +14885,6 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "optional": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -14638,8 +15171,7 @@ "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "optional": true + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "ci-info": { "version": "2.0.0", @@ -15004,12 +15536,11 @@ "dev": true }, "decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "optional": true, + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "requires": { - "mimic-response": "^2.0.0" + "mimic-response": "^3.1.0" } }, "deep-eql": { @@ -15024,8 +15555,7 @@ "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "devOptional": true + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, "deep-is": { "version": "0.1.4", @@ -15102,10 +15632,9 @@ "dev": true }, "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "optional": true + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==" }, "detect-port": { "version": "1.5.1", @@ -15212,7 +15741,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "optional": true, "requires": { "once": "^1.4.0" } @@ -16403,8 +16931,7 @@ "expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "optional": true + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" }, "extend": { "version": "3.0.2", @@ -16495,8 +17022,7 @@ "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, "fill-range": { "version": "7.0.1", @@ -16591,8 +17117,7 @@ "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "optional": true + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "fs-extra": { "version": "11.1.1", @@ -16800,8 +17325,7 @@ "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "optional": true + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, "glob": { "version": "7.2.0", @@ -17384,8 +17908,7 @@ "ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "devOptional": true + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "internal-slot": { "version": "1.0.5", @@ -18157,10 +18680,9 @@ } }, "mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "optional": true + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" }, "minimalistic-assert": { "version": "1.0.1", @@ -18184,8 +18706,7 @@ "minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "devOptional": true + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, "mkdirp": { "version": "0.5.6", @@ -18200,8 +18721,7 @@ "mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "optional": true + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "mnemonist": { "version": "0.38.5", @@ -18306,9 +18826,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "nan": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", - "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", "optional": true }, "nanoid": { @@ -18320,8 +18840,7 @@ "napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "optional": true + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" }, "napi-macros": { "version": "2.2.2", @@ -18343,19 +18862,33 @@ "peer": true }, "node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "optional": true, + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.47.0.tgz", + "integrity": "sha512-2s6B2CWZM//kPgwnuI0KrYwNjfdByE25zvAaEpq9IH4zcNsarH8Ihu/UuX6XMPEogDAxkuUFeZn60pXNHAqn3A==", "requires": { - "semver": "^5.4.1" + "semver": "^7.3.5" }, "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "optional": true + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -18400,15 +18933,20 @@ "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==" }, "node-hid": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-1.3.0.tgz", - "integrity": "sha512-BA6G4V84kiNd1uAChub/Z/5s/xS3EHBCxotQ0nyYrUG65mXewUDHE1tWOSqA2dp3N+mV0Ffq9wo2AW9t4p/G7g==", - "optional": true, + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-hid/-/node-hid-2.1.2.tgz", + "integrity": "sha512-qhCyQqrPpP93F/6Wc/xUR7L8mAJW0Z6R7HMQV8jCHHksAxNDe/4z4Un/H9CpLOT+5K39OPyt9tIQlavxWES3lg==", "requires": { "bindings": "^1.5.0", - "nan": "^2.14.0", - "node-abi": "^2.18.0", - "prebuild-install": "^5.3.4" + "node-addon-api": "^3.0.2", + "prebuild-install": "^7.1.1" + }, + "dependencies": { + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" + } } }, "nofilter": { @@ -18549,7 +19087,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "devOptional": true, "requires": { "wrappy": "1" } @@ -18796,26 +19333,22 @@ } }, "prebuild-install": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.6.tgz", - "integrity": "sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==", - "optional": true, + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", "requires": { - "detect-libc": "^1.0.3", + "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^1.0.1", - "node-abi": "^2.7.0", - "noop-logger": "^0.1.1", - "npmlog": "^4.0.1", + "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", - "simple-get": "^3.0.3", + "simple-get": "^4.0.0", "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0", - "which-pm-runs": "^1.0.0" + "tunnel-agent": "^0.6.0" } }, "prelude-ls": { @@ -18882,7 +19415,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "optional": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -18939,7 +19471,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -18950,8 +19481,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "optional": true + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" } } }, @@ -19490,16 +20020,14 @@ "simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "optional": true + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" }, "simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "optional": true, + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", "requires": { - "decompress-response": "^4.2.0", + "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } @@ -20492,7 +21020,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "optional": true, "requires": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -20504,7 +21031,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "optional": true, "requires": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -20663,7 +21189,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "devOptional": true, "requires": { "safe-buffer": "^5.0.1" } @@ -20861,20 +21386,19 @@ } }, "usb": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/usb/-/usb-1.9.2.tgz", - "integrity": "sha512-dryNz030LWBPAf6gj8vyq0Iev3vPbCLHCT8dBw3gQRXRzVNsIdeuU+VjPp3ksmSPkeMAl1k+kQ14Ij0QHyeiAg==", - "optional": true, + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/usb/-/usb-2.9.0.tgz", + "integrity": "sha512-G0I/fPgfHUzWH8xo2KkDxTTFruUWfppgSFJ+bQxz/kVY2x15EQ/XDB7dqD1G432G4gBG4jYQuF3U7j/orSs5nw==", "requires": { - "node-addon-api": "^4.2.0", - "node-gyp-build": "^4.3.0" + "@types/w3c-web-usb": "^1.0.6", + "node-addon-api": "^6.0.0", + "node-gyp-build": "^4.5.0" }, "dependencies": { "node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", - "optional": true + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" } } }, @@ -21103,8 +21627,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "devOptional": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "ws": { "version": "7.4.6", From 07450ff15d2d9d552d3d8432be2d0676e88428da Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 19:37:36 +0530 Subject: [PATCH 19/45] adding more logs for info --- evm/deploy-gateway-v5.0.x.js | 1 + evm/offline-sign-utils.js | 30 +++++++++++++++++++++--------- evm/send-tokens.js | 1 + 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index e912d21a5..de9a83cc7 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -387,6 +387,7 @@ async function upgrade(config, options) { if (isOffline) { directoryPath = directoryPath || './txs'; fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedUpgradeTransactions'; + printInfo(`Storing signed Txs offline in file ${fileName}`); nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); signersData = await getAllSignersData(directoryPath, fileName); transactions = await getTransactions(directoryPath, fileName, signerAddress); diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index a38197603..53617e48b 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -12,14 +12,19 @@ const { printError, printInfo, printObj } = require('./utils'); // function to create a ledgerSigner type wallet object function getLedgerWallet(provider, path) { - // Check if the parameters are undefined and assign default values if necessary - if (provider === undefined || provider === null) { - throw new Error('Empty provider'); - } + try { + // Check if the parameters are undefined and assign default values if necessary + if (provider === undefined || provider === null) { + throw new Error('Empty provider'); + } - const type = 'hid'; - path = path || "m/44'/60'/0'/0/0"; - return new LedgerSigner(provider, type, path); + const type = 'hid'; + path = path || "m/44'/60'/0'/0/0"; + return new LedgerSigner(provider, type, path); + } catch (error) { + printError('Error trying to coonect to ledger wallet'); + printObj(error); + } } async function ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, to, amount, contract, functionName, ...args) { @@ -82,8 +87,15 @@ async function ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, to, amount, to: tx.to || undefined, value: tx.value || undefined, }; - - const signedTx = await wallet.signTransaction(baseTx); + + let signedTx; + try { + signedTx = await wallet.signTransaction(baseTx); + printInfo(`Signed Tx from ledger with signedTxHash as: ${signedTx}`); + } catch(error) { + printError("Failed to sign tx from ledger"); + printObj(error); + } return { baseTx, signedTx }; } diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 5c2577fa6..6da043927 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -80,6 +80,7 @@ async function sendTokens(chain, options) { let signersData, transactions; if (isOffline) { + printInfo(`Storing signed Txs offline in file ${fileName}`); directoryPath = directoryPath || './txs'; fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedSendTokensTransactions'; nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); From 00b3b41355e7c3668bdaa11ebf5fbebe877d9cd6 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 20:19:13 +0530 Subject: [PATCH 20/45] chore: improved error handling --- evm/broadcast-transactions.js | 2 +- evm/offline-sign-utils.js | 8 ++++---- evm/send-tokens.js | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 8df0340e4..faa230f24 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -44,7 +44,7 @@ async function processTransactions(directoryPath, fileName, provider, signerAddr // Send the signed transaction const response = await sendTx(transaction.signedTx, provider); - if (!isValidJSON(response) || response.status.toString() !== '1') { + if (response.error || !isValidJSON(response) || response.status.toString() !== '1') { const error = `Execution failed${ response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` }`; diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 53617e48b..f7e80d505 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -59,7 +59,7 @@ async function ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, to, amount, } if (!to || !isAddress(to)) { - throw new Error('Target address is missing/not provided as valid address for the tx in funciton arguments'); + throw new Error('Target address is missing/not provided as valid address for the tx in function arguments'); } tx.to = to; @@ -71,7 +71,7 @@ async function ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, to, amount, } if (!functionName) { - throw new Error('Function name is missing in the funciton arguments'); + throw new Error('Function name is missing in the function arguments'); } const data = contract.interface.encodeFunctionData(functionName, args); @@ -123,7 +123,7 @@ async function sendTx(tx, provider) { } catch (error) { printError('Error while broadcasting signed tx'); printObj(error); - return error || { message: 'Error while broadcasting signed tx' }; + return error || { error: true, message: 'Error while broadcasting signed tx' }; } } @@ -131,7 +131,7 @@ async function updateSignersData(directoryPath, fileName, signersData) { const filePath = getFilePath(directoryPath, fileName); fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2), (err) => { if (err) { - printError(`Couldnot update signersData in file ${filePath}`); + printError(`Could not update signersData in file ${filePath}`); printObj(err); return; } diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 6da043927..ce4c9b95b 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -95,7 +95,7 @@ async function sendTokens(chain, options) { const { baseTx, signedTx } = await ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, recipient, amount); if (isOffline) { - const tx = {}; + let tx = {}; tx.nonce = nonce; tx.msg = `This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; tx.baseTx = baseTx; @@ -106,7 +106,7 @@ async function sendTokens(chain, options) { try { const response = await sendTx(signedTx, provider); - if (!isValidJSON(response) || response.status.toString() !== '1') { + if (response.error || !isValidJSON(response) || response.status.toString() !== '1') { const error = `Execution failed${ response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` }`; From 1388c2342e7fc7d415a706e10eb47b1885c99608 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 20:20:09 +0530 Subject: [PATCH 21/45] update: print logs info --- evm/broadcast-transactions.js | 2 +- evm/offline-sign-utils.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index faa230f24..400177221 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -54,7 +54,7 @@ async function processTransactions(directoryPath, fileName, provider, signerAddr // Update the transaction status and store transaction hash transaction.status = 'SUCCESS'; transaction.transactionHash = response.transactionHash; - printInfo('Transactions executed successfully.'); + printInfo(`Transactions executed successfully ${response.transactionHash}`); } catch (error) { // Update the transaction status and store error message transaction.status = 'FAILED'; diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index f7e80d505..4e327656b 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -87,15 +87,17 @@ async function ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, to, amount, to: tx.to || undefined, value: tx.value || undefined, }; - + let signedTx; + try { signedTx = await wallet.signTransaction(baseTx); printInfo(`Signed Tx from ledger with signedTxHash as: ${signedTx}`); - } catch(error) { - printError("Failed to sign tx from ledger"); + } catch (error) { + printError('Failed to sign tx from ledger'); printObj(error); } + return { baseTx, signedTx }; } From 6d6d6d26f99b325f9f98529cf12e68d12107cf56 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 15 Sep 2023 21:06:44 +0530 Subject: [PATCH 22/45] fix: error catching for offline txs --- evm/broadcast-transactions.js | 2 +- evm/deploy-gateway-v5.0.x.js | 18 +++++++++--------- evm/offline-sign-utils.js | 6 +++--- evm/send-tokens.js | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 400177221..644f253ca 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -44,7 +44,7 @@ async function processTransactions(directoryPath, fileName, provider, signerAddr // Send the signed transaction const response = await sendTx(transaction.signedTx, provider); - if (response.error || !isValidJSON(response) || response.status.toString() !== '1') { + if (response.error || !isValidJSON(response) || response.status !== 1) { const error = `Execution failed${ response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` }`; diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index de9a83cc7..e87b68266 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -407,7 +407,7 @@ async function upgrade(config, options) { ); const tx = {}; tx.nonce = nonce; - tx.msg = `This transaction will perform upgrade of AxlearGateway contract having address ${gateway.address} with implementation ${contractConfig.implementation} on chain ${chain.name} with chainId ${chain.chainId}`; + tx.msg = `This transaction will perform upgrade of AxelarGateway contract having address ${gateway.address} with implementation ${contractConfig.implementation} on chain ${chain.name} with chainId ${chain.chainId}`; tx.baseTx = baseTx; tx.signedTx = signedTx; tx.status = 'PENDING'; @@ -422,17 +422,17 @@ async function upgrade(config, options) { printInfo('Upgrade transaction', tx.hash); await tx.wait(chain.confirmations); + } - const newImplementation = await gateway.implementation(); - printInfo('New implementation', newImplementation); - - if (newImplementation !== contractConfig.implementation) { - printWarn('Implementation not upgraded yet!'); - return; - } + const newImplementation = await gateway.implementation(); + printInfo('New implementation', newImplementation); - printInfo('Upgraded to', newImplementation); + if (newImplementation !== contractConfig.implementation) { + printWarn('Implementation not upgraded yet!'); + return; } + + printInfo('Upgraded to', newImplementation); } async function main(options) { diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 4e327656b..a2965d954 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -122,10 +122,10 @@ async function sendTx(tx, provider) { try { const receipt = await provider.sendTransaction(tx).then((tx) => tx.wait()); return receipt; - } catch (error) { + } catch (errorObj) { printError('Error while broadcasting signed tx'); - printObj(error); - return error || { error: true, message: 'Error while broadcasting signed tx' }; + printObj(errorObj); + return errorObj || { error: true, message: 'Error while broadcasting signed tx' }; } } diff --git a/evm/send-tokens.js b/evm/send-tokens.js index ce4c9b95b..432351482 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -95,7 +95,7 @@ async function sendTokens(chain, options) { const { baseTx, signedTx } = await ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, recipient, amount); if (isOffline) { - let tx = {}; + const tx = {}; tx.nonce = nonce; tx.msg = `This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; tx.baseTx = baseTx; @@ -106,7 +106,7 @@ async function sendTokens(chain, options) { try { const response = await sendTx(signedTx, provider); - if (response.error || !isValidJSON(response) || response.status.toString() !== '1') { + if (response.error || !isValidJSON(response) || response.status !== 1) { const error = `Execution failed${ response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` }`; From c688c03c21682c4a274f229c539413902519eec4 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Sat, 16 Sep 2023 18:44:32 -0400 Subject: [PATCH 23/45] use ethers from hardhat --- evm/deploy-const-address-deployer.js | 2 +- evm/deploy-contract.js | 3 ++- evm/deploy-its.js | 3 ++- evm/deploy-upgradable.js | 3 ++- evm/execute-contract.js | 3 ++- evm/governance.js | 3 ++- evm/operators.js | 3 ++- evm/upgradable.js | 3 ++- evm/utils.js | 3 ++- 9 files changed, 17 insertions(+), 9 deletions(-) diff --git a/evm/deploy-const-address-deployer.js b/evm/deploy-const-address-deployer.js index 0b31e6afb..442e7d579 100644 --- a/evm/deploy-const-address-deployer.js +++ b/evm/deploy-const-address-deployer.js @@ -3,7 +3,7 @@ require('dotenv').config(); const { ethers } = require('hardhat'); -const { Wallet, getDefaultProvider, ContractFactory } = require('ethers'); +const { Wallet, getDefaultProvider, ContractFactory } = ethers; const readlineSync = require('readline-sync'); const { Command, Option } = require('commander'); const chalk = require('chalk'); diff --git a/evm/deploy-contract.js b/evm/deploy-contract.js index fbaa39e69..b9f59cbbe 100644 --- a/evm/deploy-contract.js +++ b/evm/deploy-contract.js @@ -2,11 +2,12 @@ require('dotenv').config(); +const { ethers } = require('hardhat'); const { Wallet, getDefaultProvider, utils: { isAddress }, -} = require('ethers'); +} = ethers; const readlineSync = require('readline-sync'); const { Command, Option } = require('commander'); const chalk = require('chalk'); diff --git a/evm/deploy-its.js b/evm/deploy-its.js index ff3396258..9b647c439 100644 --- a/evm/deploy-its.js +++ b/evm/deploy-its.js @@ -2,12 +2,13 @@ require('dotenv').config(); const { getCreate3Address } = require('@axelar-network/axelar-gmp-sdk-solidity'); const { deployCreate3, deployCreate2 } = require('./utils'); +const { ethers } = require('hardhat'); const { Wallet, Contract, getDefaultProvider, utils: { defaultAbiCoder, isAddress, keccak256 }, -} = require('ethers'); +} = ethers; const readlineSync = require('readline-sync'); const chalk = require('chalk'); const { printInfo, loadConfig, saveConfig } = require('./utils'); diff --git a/evm/deploy-upgradable.js b/evm/deploy-upgradable.js index 66ad42d51..26bc0c3ed 100644 --- a/evm/deploy-upgradable.js +++ b/evm/deploy-upgradable.js @@ -2,12 +2,13 @@ require('dotenv').config(); +const { ethers } = require('hardhat'); const { Contract, Wallet, getDefaultProvider, utils: { isAddress }, -} = require('ethers'); +} = ethers; const readlineSync = require('readline-sync'); const IUpgradable = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IUpgradable.json'); const { Command, Option } = require('commander'); diff --git a/evm/execute-contract.js b/evm/execute-contract.js index 4c9e4e33d..3daddeb48 100644 --- a/evm/execute-contract.js +++ b/evm/execute-contract.js @@ -2,12 +2,13 @@ require('dotenv').config(); +const ethers = require('hardhat'); const { Wallet, Contract, getDefaultProvider, utils: { isAddress }, -} = require('ethers'); +} = ethers; const readlineSync = require('readline-sync'); const { Command, Option } = require('commander'); diff --git a/evm/governance.js b/evm/governance.js index fc64b898d..bd6f27740 100644 --- a/evm/governance.js +++ b/evm/governance.js @@ -2,13 +2,14 @@ require('dotenv').config(); +const { ethers } = require('hardhat'); const { Wallet, getDefaultProvider, utils: { isAddress, defaultAbiCoder, keccak256, Interface }, Contract, BigNumber, -} = require('ethers'); +} = ethers; const readlineSync = require('readline-sync'); const { Command, Option } = require('commander'); diff --git a/evm/operators.js b/evm/operators.js index d86e39431..57df6cbae 100644 --- a/evm/operators.js +++ b/evm/operators.js @@ -2,12 +2,13 @@ require('dotenv').config(); +const { ethers } = require('hardhat'); const { Wallet, getDefaultProvider, utils: { isAddress, Interface }, Contract, -} = require('ethers'); +} = ethers; const { Command, Option } = require('commander'); const readlineSync = require('readline-sync'); const chalk = require('chalk'); diff --git a/evm/upgradable.js b/evm/upgradable.js index 472d9211f..c7f1f29ef 100644 --- a/evm/upgradable.js +++ b/evm/upgradable.js @@ -1,6 +1,7 @@ 'use strict'; -const { Contract, ContractFactory } = require('ethers'); +const { ethers } = require('hardhat'); +const { Contract, ContractFactory } = ethers; const { deployAndInitContractConstant, create3DeployAndInitContract } = require('@axelar-network/axelar-gmp-sdk-solidity'); const IUpgradable = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IUpgradable.json'); diff --git a/evm/utils.js b/evm/utils.js index 60cc5c41a..bef6af9a5 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -1,10 +1,11 @@ 'use strict'; +const { ethers } = require('hardhat'); const { ContractFactory, Contract, utils: { computeAddress, getContractAddress, keccak256, isAddress, getCreate2Address, defaultAbiCoder, isHexString }, -} = require('ethers'); +} = ethers; const https = require('https'); const http = require('http'); const { outputJsonSync } = require('fs-extra'); From 2ff7f5536e2414707a6112d8023b110a06ebb357 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Mon, 18 Sep 2023 02:54:53 -0400 Subject: [PATCH 24/45] fixes --- evm/.example.hardhat.config.js | 2 +- evm/deploy-gateway-v4.3.x.js | 19 +++++++++++++++--- evm/deploy-gateway-v5.0.x.js | 36 +++++++++++++++++++++++++--------- evm/utils.js | 11 ++++++----- hardhat.config.js | 2 +- 5 files changed, 51 insertions(+), 19 deletions(-) diff --git a/evm/.example.hardhat.config.js b/evm/.example.hardhat.config.js index 897c654b7..6207e20f2 100644 --- a/evm/.example.hardhat.config.js +++ b/evm/.example.hardhat.config.js @@ -2,7 +2,7 @@ require('@nomicfoundation/hardhat-toolbox'); const env = process.env.ENV || 'testnet'; -const { importNetworks, readJSON } = require('@axelar-network/axelar-contract-deployments'); +const { importNetworks, readJSON } = require('@axelar-network/axelar-chains-config'); const chains = require(`@axelar-network/axelar-chains-config/info/${env}.json`); const keys = readJSON(`${__dirname}/keys.json`); const { networks, etherscan } = importNetworks(chains, keys); diff --git a/evm/deploy-gateway-v4.3.x.js b/evm/deploy-gateway-v4.3.x.js index 90d34f357..82a2d679d 100644 --- a/evm/deploy-gateway-v4.3.x.js +++ b/evm/deploy-gateway-v4.3.x.js @@ -13,6 +13,8 @@ const { saveConfig, loadConfig, printWalletInfo, + isAddressArray, + isNumber, } = require('./utils'); const { ethers } = require('hardhat'); const { @@ -26,8 +28,8 @@ const { Command, Option } = require('commander'); const chalk = require('chalk'); async function getAuthParams(config, chain) { - const { addresses, weights, threshold } = await getEVMAddresses(config, chain); - printObj(JSON.stringify({ addresses, weights, threshold })); + const { addresses, weights, threshold, keyID } = await getEVMAddresses(config, chain); + printObj(JSON.stringify({ status: 'latest', keyID, addresses, weights, threshold })); const paramsAuth = [defaultAbiCoder.encode(['address[]', 'uint256[]', 'uint256'], [addresses, weights, threshold])]; return paramsAuth; } @@ -63,7 +65,7 @@ async function deploy(config, options) { }); printInfo('Predicted proxy address', proxyAddress); - const gasOptions = contractConfig.gasOptions || chain.gasOptions || {}; + const gasOptions = contractConfig.gasOptions || chain.gasOptions || { gasLimit: 6e6 }; printInfo('Gas override', JSON.stringify(gasOptions, null, 2)); printInfo('Is verification enabled?', verify ? 'y' : 'n'); printInfo('Skip existing contracts?', skipExisting ? 'y' : 'n'); @@ -73,6 +75,16 @@ async function deploy(config, options) { const tokenDeployerFactory = await getContractFactory('TokenDeployer', wallet); const gatewayProxyFactory = await getContractFactory('AxelarGatewayProxy', wallet); + if (!adminAddresses || !isAddressArray(JSON.parse(adminAddresses))) { + printError('Invalid admin addresses', `${adminAddresses}`); + return; + } + + if (!adminThreshold || !isNumber(parseInt(adminThreshold))) { + printError('Invalid admin threshold', `${adminThreshold}`); + return; + } + let gateway; let auth; let tokenDeployer; @@ -219,6 +231,7 @@ async function deploy(config, options) { contractConfig.address = gateway.address; contractConfig.implementation = implementation.address; + contractConfig.implementationCodehash = implementationCodehash; contractConfig.authModule = auth.address; contractConfig.tokenDeployer = tokenDeployer.address; diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index e87b68266..b208da598 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -60,13 +60,13 @@ async function getAuthParams(config, chain, options) { if (options.prevKeyIDs) { for (const keyID of options.prevKeyIDs.split(',')) { const { addresses, weights, threshold } = await getEVMAddresses(config, chain, { ...options, keyID }); - printInfo(JSON.stringify({ keyID, addresses, weights, threshold })); + printInfo(JSON.stringify({ status: 'old', keyID, addresses, weights, threshold })); params.push(defaultAbiCoder.encode(['address[]', 'uint256[]', 'uint256'], [addresses, weights, threshold])); } } - const { addresses, weights, threshold } = await getEVMAddresses(config, chain, options); - printInfo(JSON.stringify({ keyID: 'latest', addresses, weights, threshold })); + const { addresses, weights, threshold, keyID } = await getEVMAddresses(config, chain, options); + printInfo(JSON.stringify({ status: 'latest', keyID, addresses, weights, threshold })); params.push(defaultAbiCoder.encode(['address[]', 'uint256[]', 'uint256'], [addresses, weights, threshold])); return params; @@ -291,6 +291,7 @@ async function deploy(config, options) { contractConfig.address = gateway.address; contractConfig.implementation = implementation.address; + contractConfig.implementationCodehash = implementationCodehash; contractConfig.authModule = auth.address; contractConfig.tokenDeployer = tokenDeployer.address; contractConfig.governance = governance; @@ -327,7 +328,7 @@ async function upgrade(config, options) { wallet = getLedgerWallet(provider, ledgerPath); } else { if (!isValidPrivateKey(privateKey)) { - throw new Error('Private key is missing/ not provided correctly in the user info'); + throw new Error('Private key is missing/not provided correctly in the user info'); } wallet = new Wallet(privateKey, provider); @@ -339,11 +340,28 @@ async function upgrade(config, options) { const contractConfig = chain.contracts[contractName]; const gateway = new Contract(contractConfig.address, AxelarGateway.abi, wallet); - const implementationCodehash = await getBytecodeHash(gateway, chainName, provider); + let implementationCodehash = contractConfig.implementationCodehash; let governance = options.governance || contractConfig.governance; let mintLimiter = options.mintLimiter || contractConfig.mintLimiter; let setupParams = '0x'; + if (!isOffline) { + const codehash = await getBytecodeHash(contractConfig.implementation, chainName, provider); + + if (!implementationCodehash) { + // retrieve codehash dynamically if not specified in the config file + implementationCodehash = codehash; + } else if (codehash !== implementationCodehash) { + throw new Error( + `Implementation codehash mismatch. Expected ${implementationCodehash} but got ${codehash}. Please check if the implementation contract is deployed correctly.`, + ); + } + } else { + if (!implementationCodehash) { + throw new Error('Implementation codehash is missing in the config file'); + } + } + if (governance || mintLimiter) { governance = governance || AddressZero; mintLimiter = mintLimiter || AddressZero; @@ -354,7 +372,7 @@ async function upgrade(config, options) { printInfo('Gateway Proxy', gateway.address); printInfo('Current implementation', await gateway.implementation()); printInfo('Upgrading to implementation', contractConfig.implementation); - printInfo('Implementation codehash', implementationCodehash); + printInfo('New Implementation codehash', implementationCodehash); printInfo('Setup params', setupParams); const gasOptions = contractConfig.gasOptions || chain.gasOptions || {}; @@ -374,8 +392,8 @@ async function upgrade(config, options) { const block = await provider.getBlock('latest'); gasLimit = block.gasLimit.toNumber() / 1000; - printInfo('Gas Price:', gasPrice.toString()); - printInfo('Gas Limit:', gasLimit.toString()); + printInfo('Gas Price', gasPrice.toString()); + printInfo('Gas Limit', gasLimit.toString()); } catch (error) { printError('Gas price and limit could not be fetched from provider'); printObj(error); @@ -387,7 +405,7 @@ async function upgrade(config, options) { if (isOffline) { directoryPath = directoryPath || './txs'; fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedUpgradeTransactions'; - printInfo(`Storing signed Txs offline in file ${fileName}`); + printInfo('Storing signed Txs offline in file', fileName); nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); signersData = await getAllSignersData(directoryPath, fileName); transactions = await getTransactions(directoryPath, fileName, signerAddress); diff --git a/evm/utils.js b/evm/utils.js index bef6af9a5..f8afbb88e 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -9,7 +9,6 @@ const { const https = require('https'); const http = require('http'); const { outputJsonSync } = require('fs-extra'); -const { readJSON, importNetworks, verifyContract } = require('../axelar-chains-config'); const zkevm = require('@0xpolygonhermez/zkevm-commonjs'); const chalk = require('chalk'); const { @@ -21,6 +20,7 @@ const { const { CosmWasmClient } = require('@cosmjs/cosmwasm-stargate'); const CreateDeploy = require('@axelar-network/axelar-gmp-sdk-solidity/artifacts/contracts/deploy/CreateDeploy.sol/CreateDeploy.json'); const IDeployer = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IDeployer.json'); +const { verifyContract } = require(`${__dirname}/../axelar-chains-config`); const getSaltFromKey = (key) => { return keccak256(defaultAbiCoder.encode(['string'], [key.toString()])); @@ -423,7 +423,7 @@ const getEVMAddresses = async (config, chain, options = {}) => { const weights = sortedAddresses.map((weightedAddress) => Number(weightedAddress.weight)); const threshold = Number(evmAddresses.threshold); - return { addresses, weights, threshold }; + return { addresses, weights, threshold, keyID: evmAddresses.key_id }; }; const getAmplifierKeyAddresses = async (config, chain, keyID = '') => { @@ -550,6 +550,10 @@ function isValidTimeFormat(timeString) { // Validate if the input privateKey is correct function isValidPrivateKey(privateKey) { // Check if it's a valid hexadecimal string + if (!privateKey.startsWith('0x')) { + privateKey = '0x' + privateKey; + } + if (!isHexString(privateKey) || privateKey.length !== 66) { return false; } @@ -600,11 +604,8 @@ module.exports = { deployCreate2, deployCreate3, deployContract, - readJSON, writeJSON, httpGet, - importNetworks, - verifyContract, printObj, printLog, printInfo, diff --git a/hardhat.config.js b/hardhat.config.js index 26011a71b..e9414377c 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -1,7 +1,7 @@ require('@nomicfoundation/hardhat-toolbox'); const env = process.env.NETWORK || 'testnet'; -const { importNetworks, readJSON } = require('./evm/utils.js'); +const { importNetworks, readJSON } = require(`${__dirname}/axelar-chains-config`); const chains = require(`${__dirname}/axelar-chains-config/info/${env}.json`); const keys = readJSON(`${__dirname}/keys.json`); const { networks, etherscan } = importNetworks(chains, keys); From c3d2a5d536add1afefc63877b8882ba430d2962c Mon Sep 17 00:00:00 2001 From: Blockchain Guy Date: Mon, 18 Sep 2023 13:47:05 +0530 Subject: [PATCH 25/45] Update --offline option to remove arg Co-authored-by: Milap Sheth --- evm/send-tokens.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 432351482..3f3ad9b2d 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -174,7 +174,7 @@ if (require.main === module) { program.addOption(new Option('-r, --recipients ', 'comma-separated recipients of tokens').makeOptionMandatory(true)); program.addOption(new Option('-a, --amount ', 'amount to transfer (in terms of ETH)').makeOptionMandatory(true)); program.addOption( - new Option('-o, --offline ', 'If this option is set as true, then ').choices(['true', 'false']).makeOptionMandatory(false), + new Option('--offline', 'Run in offline mode'), ); program.addOption(new Option('-l, --ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); program.addOption( From b330e2de45d73bb446617275ca954341c6e1a790 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Mon, 18 Sep 2023 19:32:10 +0530 Subject: [PATCH 26/45] addressed pr comments --- evm/broadcast-transactions.js | 88 +++++++++------------ evm/deploy-gateway-v5.0.x.js | 95 ++++++++--------------- evm/offline-sign-utils.js | 141 ++++++++++++---------------------- evm/offline-sign.js | 75 ++++++++++++++++++ evm/send-tokens.js | 126 ++++++++++++------------------ evm/updateNonces.js | 74 ++++++++++++++++++ evm/utils.js | 6 +- package-lock.json | 62 ++++++++++++++- package.json | 5 +- 9 files changed, 378 insertions(+), 294 deletions(-) create mode 100644 evm/offline-sign.js create mode 100644 evm/updateNonces.js diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 644f253ca..d3d5ebd2b 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -19,55 +19,45 @@ const { isValidJSON, } = require('./offline-sign-utils'); -async function processTransactions(directoryPath, fileName, provider, signerAddress) { +async function processTransactions(filePath, provider) { try { - const signersData = await getAllSignersData(directoryPath, fileName); - - if (!signersData[signerAddress]) { - throw new Error(`Signer data for address ${signerAddress} not found in the file ${fileName}`); - } - - let transactions = signersData[signerAddress]; - const firstPendingnonceFromData = await getNonceFromData(transactions); - const nonce = parseInt(await getNonceFromProvider(provider, signerAddress)); - - if (nonce > firstPendingnonceFromData) { - transactions = getTxsWithUpdatedNonceAndStatus(transactions, nonce); - } - - for (const transaction of transactions) { - if (transaction.status === 'PENDING') { - printInfo('Broadcasting transaction: '); - printObj(transaction.baseTx); - - try { - // Send the signed transaction - const response = await sendTx(transaction.signedTx, provider); - - if (response.error || !isValidJSON(response) || response.status !== 1) { - const error = `Execution failed${ - response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` - }`; - throw new Error(error); - } + const signersData = await getAllSignersData(filePath); + + for (const [signerAddress, transactions] of Object.entries(signersData)) { + console.log(`${key} ${value}`); // "a 5", "b 7", "c 9" + const firstPendingnonceFromData = await getNonceFromData(transactions); + const nonce = parseInt(await getNonceFromProvider(provider, signerAddress)); + + if (nonce > firstPendingnonceFromData) { + transactions = getTxsWithUpdatedNonceAndStatus(transactions, nonce); + } - // Update the transaction status and store transaction hash - transaction.status = 'SUCCESS'; - transaction.transactionHash = response.transactionHash; - printInfo(`Transactions executed successfully ${response.transactionHash}`); - } catch (error) { - // Update the transaction status and store error message - transaction.status = 'FAILED'; - transaction.error = error.message; - printError(`Transaction failed with error: ${error.message}`); + for (const transaction of transactions) { + if (transaction.status === 'PENDING') { + printInfo('Broadcasting transaction: '); + printObj(transaction.baseTx); + + try { + // Send the signed transaction + const response = await sendTx(transaction.signedTx, provider); + + // Update the transaction status and store transaction hash + transaction.status = 'SUCCESS'; + transaction.transactionHash = response.transactionHash; + printInfo(`Transactions executed successfully ${response.transactionHash}`); + } catch (error) { + // Update the transaction status and store error message + transaction.status = 'FAILED'; + transaction.error = error.message; + printError(`Transaction failed with error: ${error.message}`); + } } } - } + // Write back the updated JSON object to the file + signersData[signerAddress] = transactions; + } + fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2)); - // Write back the updated JSON object to the file - signersData[signerAddress] = transactions; - const filePath = getFilePath(directoryPath, fileName); - fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2)); } catch (error) { printError('Error processing transactions:', error.message); } @@ -75,7 +65,7 @@ async function processTransactions(directoryPath, fileName, provider, signerAddr async function main(options) { // TODO: Enable multiple scripts to use offlineSigning - const { directoryPath, fileName, rpcUrl, signerAddress } = options; + const { filePath, rpcUrl } = options; const provider = new JsonRpcProvider(rpcUrl); const network = await provider.getNetwork(); @@ -88,21 +78,17 @@ async function main(options) { if (anwser !== 'y') return; } - await processTransactions(directoryPath, fileName, provider, signerAddress); + await processTransactions(filePath, provider); } const program = new Command(); program.name('broadcast-transactions').description('Broadcast all the pending signed transactions of the signer'); -program.addOption( - new Option('-d, --directoryPath ', 'The folder where all the signed tx files are stored').makeOptionMandatory(true), -); -program.addOption(new Option('-f, --fileName ', 'The fileName where the signed tx are stored').makeOptionMandatory(true)); +program.addOption(new Option('--filePath ', 'The file where the signed tx are stored').makeOptionMandatory(true)); program.addOption( new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to broadcast the transactions').makeOptionMandatory(true), ); -program.addOption(new Option('-s, --signerAddress ', 'private key').makeOptionMandatory(true)); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index b208da598..87069a158 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -26,17 +26,15 @@ const { printError, printWalletInfo, printWarn, - printObj, - isValidPrivateKey, } = require('./utils'); const { - getLedgerWallet, - ledgerSign, getAllSignersData, getNonceFromProvider, updateSignersData, getLatestNonceAndUpdateData, getTransactions, + getWallet, + getUnsignedTx, } = require('./offline-sign-utils.js'); const AxelarGatewayProxy = require('@axelar-network/axelar-cgp-solidity/artifacts/contracts/AxelarGatewayProxy.sol/AxelarGatewayProxy.json'); @@ -314,25 +312,14 @@ async function deploy(config, options) { async function upgrade(config, options) { const { chainName, privateKey, yes, ledgerPath, offline, env } = options; - let { directoryPath, fileName } = options; - const isOffline = offline === 'true'; + let filePath = options.filePath; const contractName = 'AxelarGateway'; const chain = config.chains[chainName] || { contracts: {}, name: chainName, id: chainName, rpc: options.rpc, tokenSymbol: 'ETH' }; const rpc = options.rpc || chain.rpc; const provider = getDefaultProvider(rpc); - let wallet; - - if (privateKey === 'ledger') { - wallet = getLedgerWallet(provider, ledgerPath); - } else { - if (!isValidPrivateKey(privateKey)) { - throw new Error('Private key is missing/not provided correctly in the user info'); - } - - wallet = new Wallet(privateKey, provider); - } + const wallet = getWallet(privateKey, provider, ledgerPath); const signerAddress = await wallet.getAddress(); await printWalletInfo(wallet); @@ -345,7 +332,7 @@ async function upgrade(config, options) { let mintLimiter = options.mintLimiter || contractConfig.mintLimiter; let setupParams = '0x'; - if (!isOffline) { + if (!offline) { const codehash = await getBytecodeHash(contractConfig.implementation, chainName, provider); if (!implementationCodehash) { @@ -384,57 +371,39 @@ async function upgrade(config, options) { if (anwser !== 'y') return; } - // Offline signing - let gasLimit, gasPrice; - - try { - gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'gwei'); - const block = await provider.getBlock('latest'); - gasLimit = block.gasLimit.toNumber() / 1000; - - printInfo('Gas Price', gasPrice.toString()); - printInfo('Gas Limit', gasLimit.toString()); - } catch (error) { - printError('Gas price and limit could not be fetched from provider'); - printObj(error); - } - let nonce = await getNonceFromProvider(provider, signerAddress); + const nonceData = getAllSignersData(nonceFilePath); + nonce = (nonce > nonceData[signerAddress]) ? nonce : nonceData[signerAddress]; let signersData, transactions; if (isOffline) { - directoryPath = directoryPath || './txs'; - fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedUpgradeTransactions'; - printInfo('Storing signed Txs offline in file', fileName); - nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); - signersData = await getAllSignersData(directoryPath, fileName); - transactions = await getTransactions(directoryPath, fileName, signerAddress); - const { baseTx, signedTx } = await ledgerSign( - gasLimit, - gasPrice, - nonce, - chain, - wallet, - gateway.address, - undefined, - gateway, - 'upgrade', - contractConfig.implementation, - implementationCodehash, - setupParams, - ); - const tx = {}; + filePath = filePath || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'unsignedTransactions.json'; + printInfo(`Storing signed Txs offline in file ${filePath}`); + nonce = await getLatestNonceAndUpdateData(filePath, wallet, nonce); + signersData = await getAllSignersData(filePath); + transactions = await getTransactions(filePath, signerAddress); + const network = await provider.getNetwork(); + chainId = network.chainId; + + const data = {}, tx = {}; + tx = await gateway.populateTransaction['upgrade'](contractConfig.implementation, implementationCodehash, setupParams); tx.nonce = nonce; - tx.msg = `This transaction will perform upgrade of AxelarGateway contract having address ${gateway.address} with implementation ${contractConfig.implementation} on chain ${chain.name} with chainId ${chain.chainId}`; - tx.baseTx = baseTx; - tx.signedTx = signedTx; - tx.status = 'PENDING'; - transactions.push(tx); + tx.chainId = chainId; + const unsignedTx = getUnsignedTx(chain, tx); + // Storing the fields in the data that will be stored in file + data.msg = `This transaction will perform upgrade of AxelarGateway contract having address ${gateway.address} with implementation ${contractConfig.implementation} on chain ${chain.name} with chainId ${chain.chainId}`; + data.unsignedTx = unsignedTx; + data.status = 'NOT_SIGNED'; + + transactions.push(data); if (transactions) { signersData[signerAddress] = transactions; - await updateSignersData(directoryPath, fileName, signersData); + await updateSignersData(filePath, signersData); } + // Updating Nonce data for this Address + nonceData[signerAddress] = nonce; + await updateSignersData(nonceFilePath, nonceData); } else { const tx = await gateway.upgrade(contractConfig.implementation, implementationCodehash, setupParams, gasOptions); printInfo('Upgrade transaction', tx.hash); @@ -498,11 +467,9 @@ async function programHandler() { new Option('-o, --offline ', 'If this option is set as true, then ').choices(['true', 'false']).makeOptionMandatory(false), ); program.addOption(new Option('-l, --ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); + program.addOption(new Option('--nonceFilePath ', 'The File where nonce value to use for each address is stored').makeOptionMandatory(false)); program.addOption( - new Option('-d, --directoryPath ', 'The folder where all the signed tx files are stored').makeOptionMandatory(false), - ); - program.addOption( - new Option('-f, --fileName ', 'The fileName where the signed tx will be stored').makeOptionMandatory(false), + new Option('--filePath ', 'The file where the signed tx will be stored').makeOptionMandatory(false), ); program.action((options) => { diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index a2965d954..f20f7fe6c 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -3,9 +3,11 @@ const fs = require('fs'); const { ethers } = require('hardhat'); const { + Wallet, BigNumber, utils: { isAddress }, } = ethers; +const path = require('path'); const { LedgerSigner } = require('@ethersproject/hardware-wallets'); const { printError, printInfo, printObj } = require('./utils'); @@ -27,110 +29,42 @@ function getLedgerWallet(provider, path) { } } -async function ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, to, amount, contract, functionName, ...args) { - const tx = {}; - - if (!chain) { - throw new Error('Chain is missing in the function arguments'); - } - - tx.chainId = chain.chainId; - - if (!gasLimit) { - throw new Error('Gas limit is missing in the function arguments'); - } - - tx.gasLimit = gasLimit; - - if (!gasPrice) { - throw new Error('Gas price is missing in the function arguments'); - } - - tx.gasPrice = gasPrice; - - if (!nonce) { - throw new Error('Nonce is missing in the function arguments'); - } - - tx.nonce = nonce; - - if (!wallet) { - throw new Error('Wallet is missing/not provided correctly in function arguments'); - } - - if (!to || !isAddress(to)) { +function getUnsignedTx(chain, tx) { + if (!tx.to || !isAddress(tx.to)) { throw new Error('Target address is missing/not provided as valid address for the tx in function arguments'); } - tx.to = to; - tx.value = amount || undefined; - - if (contract) { - if (to.toLowerCase() !== contract.address.toLowerCase()) { - throw new Error('Contract address do not matches the to address provided in function arguments'); - } - - if (!functionName) { - throw new Error('Function name is missing in the function arguments'); - } - - const data = contract.interface.encodeFunctionData(functionName, args); - tx.data = data || undefined; - } - const baseTx = { - chainId: tx.chainId || undefined, + chainId: tx.chainId || chain.chainId || undefined, data: tx.data || undefined, - gasLimit: tx.gasLimit || undefined, + gasLimit: tx.gasLimit || chain.gasOptions?.gasLimit || undefined, gasPrice: tx.gasPrice || undefined, nonce: tx.nonce ? BigNumber.from(tx.nonce).toNumber() : undefined, to: tx.to || undefined, value: tx.value || undefined, }; - let signedTx; - - try { - signedTx = await wallet.signTransaction(baseTx); - printInfo(`Signed Tx from ledger with signedTxHash as: ${signedTx}`); - } catch (error) { - printError('Failed to sign tx from ledger'); - printObj(error); - } - - return { baseTx, signedTx }; -} - -function getFilePath(directoryPath, fileName) { - if (!directoryPath) { - throw new Error('Directory path is missing in the function arguments'); - } - - if (!fileName) { - throw new Error('File name is missing in the function arguments'); - } - - if (!fs.existsSync(directoryPath)) { - fs.mkdirSync(directoryPath); - } - - const filePath = directoryPath + '/' + fileName + '.json'; - return filePath; + return baseTx; } async function sendTx(tx, provider) { try { const receipt = await provider.sendTransaction(tx).then((tx) => tx.wait()); + if(!isValidJSON(receipt) || response.status !== 1) { + const error = `Execution failed${ + response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` + }`; + throw new Error(error); + } return receipt; } catch (errorObj) { printError('Error while broadcasting signed tx'); printObj(errorObj); - return errorObj || { error: true, message: 'Error while broadcasting signed tx' }; } } -async function updateSignersData(directoryPath, fileName, signersData) { - const filePath = getFilePath(directoryPath, fileName); +async function updateSignersData(filePath, signersData) { + fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2), (err) => { if (err) { printError(`Could not update signersData in file ${filePath}`); @@ -147,19 +81,19 @@ async function getNonceFromProvider(provider, address) { return nonce; } -async function getLatestNonceAndUpdateData(directoryPath, fileName, wallet) { +async function getLatestNonceAndUpdateData(filePath, wallet, nonce) { try { const provider = wallet.provider; const signerAddress = await wallet.getAddress(); - const signersData = await getAllSignersData(directoryPath, fileName); + const signersData = await getAllSignersData(filePath); let transactions = signersData[signerAddress]; const firstPendingnonceFromData = getNonceFromData(transactions); - let nonce = await getNonceFromProvider(provider, signerAddress); + console.log("firstPendingnonceFromData",firstPendingnonceFromData); - if (nonce > firstPendingnonceFromData) { + if (nonce >= firstPendingnonceFromData) { transactions = getTxsWithUpdatedNonceAndStatus(transactions, nonce); signersData[signerAddress] = transactions; - await updateSignersData(directoryPath, fileName, signersData); + await updateSignersData(filePath, signersData); } else { nonce = firstPendingnonceFromData + 1; } @@ -203,11 +137,11 @@ function getNonceFromData(transactions) { return 0; } -async function getAllSignersData(directoryPath, fileName) { +async function getAllSignersData(filePath) { const signersData = {}; try { - const filePath = getFilePath(directoryPath, fileName); + // Read the content of the file const data = getFileData(filePath); @@ -223,14 +157,21 @@ async function getAllSignersData(directoryPath, fileName) { return signersData; } catch (error) { - printError(`Failed to get all signers data from the file ${fileName}`); + printError(`Failed to get all signers data from the file ${filePath}`); printObj(error); } } function getFileData(filePath) { try { + // Extract the directory path + const directoryPath = path.dirname(filePath); + // Check if the directory and file exists, create it if it doesn't + if (!fs.existsSync(directoryPath)){ + fs.mkdirSync(directoryPath); + } if (!fs.existsSync(filePath)) { + console.log("Creating file"); // File does not exist, create it fs.writeFileSync(filePath, JSON.stringify({})); return undefined; @@ -245,11 +186,11 @@ function getFileData(filePath) { } } -async function getTransactions(directoryPath, fileName, signerAddress) { +async function getTransactions(filePath, signerAddress) { let transactions = []; try { - const filePath = getFilePath(directoryPath, fileName); + // Read the content of the file const data = getFileData(filePath); @@ -285,11 +226,23 @@ function isValidJSON(obj) { return true; } +const getWallet = (privateKey, provider, ledgerPath) => { + let wallet; + if (privateKey === 'ledger') { + wallet = getLedgerWallet(provider, ledgerPath || undefined); + } else { + if (!isValidPrivateKey(privateKey)) { + throw new Error('Private key is missing/ not provided correctly'); + } + + wallet = new Wallet(privateKey, provider); + } + return wallet; +} + module.exports = { getLedgerWallet, - ledgerSign, sendTx, - getFilePath, getTxsWithUpdatedNonceAndStatus, getNonceFromProvider, getNonceFromData, @@ -298,4 +251,6 @@ module.exports = { updateSignersData, getLatestNonceAndUpdateData, isValidJSON, + getWallet, + getUnsignedTx, }; diff --git a/evm/offline-sign.js b/evm/offline-sign.js new file mode 100644 index 000000000..6d32303c7 --- /dev/null +++ b/evm/offline-sign.js @@ -0,0 +1,75 @@ +'use strict'; + +const chalk = require('chalk'); +const { Command, Option } = require('commander'); +const fs = require('fs'); +const { ethers } = require('hardhat'); +const { getDefaultProvider } = ethers; + +const { printError, printInfo, printObj } = require('./utils'); +const { + getWallet, + getTransactions, + getAllSignersData, + updateSignersData, +} = require('./offline-sign-utils'); + +async function processTransactions(wallet, signerAddress, filePath) { + try { + let signersData = await getAllSignersData(filePath); + let transactions = await getTransactions(filePath, signerAddress); + + for(const transaction of transactions) { + if(transaction.status === "NOT_SIGNED") { + const signedTx = await wallet.signTransaction(transaction.unsignedTx); + transaction.status = "PENDING"; + transaction.signedTx = signedTx; + } + } + + signersData[signerAddress] = transactions; + await updateSignersData(filePath, signersData); + printInfo(`Transactions signed succesfully and stored in file ${filePath}`); + + } catch(error) { + printError(`Transactions signing failed with error: ${error.message}`); + printObj(error); + } +} + + +async function main(options) { + const { filePath, ledgerPath, rpcUrl } = options; + const provider = getDefaultProvider(rpcUrl); + const network = await provider.getNetwork(); + const wallet = getWallet("ledger", provider, ledgerPath); + const signerAddress = await wallet.getAddress(); + + if (!options.yes) { + const anwser = fs.readlineSync.question( + `Proceed with the signing of all unSigned transactions for address ${chalk.green( + signerAddress, + )} on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, + ); + if (anwser !== 'y') return; + } + + await processTransactions(wallet, signerAddress, filePath); +} + +const program = new Command(); + +program.name('Offline-Signing').description('Offline sign all the unsigned transactions in the file'); + +program.addOption(new Option('-f, --filePath ', 'The filePath where the signed tx will be stored').makeOptionMandatory(true)); +program.addOption( + new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to sign the transactions').makeOptionMandatory(true), +); +program.addOption(new Option('--ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); +program.addOption(new Option('-y, --yes', 'skip prompts')); + +program.action((options) => { + main(options); +}); + +program.parse(); \ No newline at end of file diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 3f3ad9b2d..c69d69684 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -12,39 +12,27 @@ const { } = ethers; const readlineSync = require('readline-sync'); -const { printInfo, printWalletInfo, isValidPrivateKey, printError } = require('./utils'); +const { printInfo, printWalletInfo, isValidPrivateKey, printError, printObj } = require('./utils'); const { - getLedgerWallet, - sendTx, - ledgerSign, getAllSignersData, getNonceFromProvider, updateSignersData, getLatestNonceAndUpdateData, getTransactions, - isValidJSON, + getWallet, + getUnsignedTx, } = require('./offline-sign-utils.js'); -const { printObj } = require('@axelar-network/axelar-gmp-sdk-solidity'); +const { blob } = require('stream/consumers'); async function sendTokens(chain, options) { - let wallet; - const { privateKey, offline, env, ledgerPath } = options; - let { amount, recipients, directoryPath, fileName } = options; - const isOffline = offline === 'true'; + const { privateKey, offline, env, ledgerPath, nonceFilePath } = options; + let { amount, recipients, filePath} = options; const provider = getDefaultProvider(chain.rpc); recipients = options.recipients.split(',').map((str) => str.trim()); amount = parseEther(amount); - if (privateKey === 'ledger') { - wallet = getLedgerWallet(provider, ledgerPath); - } else { - if (!isValidPrivateKey(privateKey)) { - throw new Error('Private key is missing/ not provided correctly in the user info'); - } - - wallet = new Wallet(privateKey, provider); - } + const wallet = getWallet(privateKey, provider, ledgerPath); const signerAddress = await wallet.getAddress(); const balance = await printWalletInfo(wallet); @@ -62,68 +50,47 @@ async function sendTokens(chain, options) { if (anwser !== 'y') return; } - let gasLimit, gasPrice; - - try { - gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'gwei'); - const block = await provider.getBlock('latest'); - gasLimit = block.gasLimit.toNumber() / 1000; - - printInfo('Gas Price:', gasPrice.toString()); - printInfo('Gas Limit:', gasLimit.toString()); - } catch (error) { - printError('Gas price and limit could not be fetched from provider'); - printObj(error); - } - let nonce = await getNonceFromProvider(provider, signerAddress); - let signersData, transactions; - - if (isOffline) { - printInfo(`Storing signed Txs offline in file ${fileName}`); - directoryPath = directoryPath || './txs'; - fileName = fileName || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'signedSendTokensTransactions'; - nonce = await getLatestNonceAndUpdateData(directoryPath, fileName, wallet); - signersData = await getAllSignersData(directoryPath, fileName); - transactions = await getTransactions(directoryPath, fileName, signerAddress); + console.log("Provider nonce", nonce); + const nonceData = getAllSignersData(nonceFilePath); + let nonceFromFile = nonceData[signerAddress] || 0; + console.log("Nonce file", nonceFromFile); + nonce = (nonce >= parseInt(nonceFromFile)) ? nonce : nonceFromFile; + console.log("Nonce after from nonce file if greatest", nonce); + let signersData, transactions, chainId; + + if (offline) { + filePath = filePath || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'unsignedTransactions.json'; + printInfo(`Storing signed Txs offline in file ${filePath}`); + nonce = await getLatestNonceAndUpdateData(filePath, wallet, nonce); + console.log("Final nonce from getLatestNonce function", nonce); + signersData = await getAllSignersData(filePath); + transactions = await getTransactions(filePath, signerAddress); + const network = await provider.getNetwork(); + chainId = network.chainId; } for (const recipient of recipients) { printInfo('Recipient', recipient); + const tx = { + to: recipient, + value: amount, + } - if (privateKey === 'ledger') { - const { baseTx, signedTx } = await ledgerSign(gasLimit, gasPrice, nonce, chain, wallet, recipient, amount); - - if (isOffline) { - const tx = {}; + if (offline) { + const data = {}; tx.nonce = nonce; - tx.msg = `This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; - tx.baseTx = baseTx; - tx.signedTx = signedTx; - tx.status = 'PENDING'; - transactions.push(tx); - } else { - try { - const response = await sendTx(signedTx, provider); - - if (response.error || !isValidJSON(response) || response.status !== 1) { - const error = `Execution failed${ - response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` - }`; - throw new Error(error); - } - - printInfo('Transaction hash', response.transactionHash); - } catch (error) { - printError('Broadcasting Transaction failed'); - printObj(error); - } + tx.chainId = chainId; + const unsignedTx = getUnsignedTx(chain, tx); + // Storing the fields in the data that will be stored in file + data.msg = `This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chainId}`; + data.unsignedTx = unsignedTx; + data.status = 'NOT_SIGNED'; + + transactions.push(data); } - } else { - const tx = await wallet.sendTransaction({ - to: recipient, - value: amount, - }); + else { + const tx = await wallet.sendTransaction(tx); printInfo('Transaction hash', tx.hash); @@ -135,8 +102,11 @@ async function sendTokens(chain, options) { if (transactions) { signersData[signerAddress] = transactions; - await updateSignersData(directoryPath, fileName, signersData); + await updateSignersData(filePath, signersData); } + // Updating Nonce data for this Address + nonceData[signerAddress] = nonce; + await updateSignersData(nonceFilePath, nonceData); } async function main(options) { @@ -176,13 +146,11 @@ if (require.main === module) { program.addOption( new Option('--offline', 'Run in offline mode'), ); - program.addOption(new Option('-l, --ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); - program.addOption( - new Option('-d, --directoryPath ', 'The folder where all the signed tx files are stored').makeOptionMandatory(false), - ); + program.addOption(new Option('--ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); program.addOption( - new Option('-f, --fileName ', 'The fileName where the signed tx will be stored').makeOptionMandatory(false), + new Option('--filePath ', 'The filePath where the signed tx will be stored').makeOptionMandatory(false), ); + program.addOption(new Option('--nonceFilePath ', 'The File where nonce value to use for each address is stored').makeOptionMandatory(false)); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { diff --git a/evm/updateNonces.js b/evm/updateNonces.js new file mode 100644 index 000000000..82d5a8d93 --- /dev/null +++ b/evm/updateNonces.js @@ -0,0 +1,74 @@ +'use strict'; + +const chalk = require('chalk'); +const { Command, Option } = require('commander'); +const fs = require('fs'); +const { ethers } = require('hardhat'); +const { + getDefaultProvider +} = ethers; + +const { printError, printInfo, printObj } = require('./utils'); +const { + getNonceFromProvider, getAllSignersData, +} = require('./offline-sign-utils'); + +function updateNonce(provider, addresses, filePath) { + try { + const nonceData = getAllSignersData(filePath); + addresses = JSON.parse(addresses); + + for(const address of addresses) { + const nonce = getNonceFromProvider(provider, address); + nonceData[address]= nonce; + } + + fs.writeFileSync(filePath, JSON.stringify(nonceData, null, 2), (err) => { + if (err) { + printError(`Could not update Nonce in file ${filePath}`); + printObj(err); + return; + } + + }); + printInfo(`Nonce updated succesfully and stored in file ${filePath}`); + } catch(error) { + printError(`Nonce updation failed with error: ${error.message}`); + printObj(error); + } +} + + +async function main(options) { + const { filePath, rpcUrl, addresses } = options; + const provider = getDefaultProvider(rpcUrl); + const network = await provider.getNetwork(); + + if (!options.yes) { + const anwser = fs.readlineSync.question( + `Proceed with the nonces update of all addresses ${chalk.green( + addresses, + )} on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, + ); + if (anwser !== 'y') return; + } + + await updateNonce(provider, addresses, filePath); +} + +const program = new Command(); + +program.name('Update-Nonces').description('Offline sign all the unsigned transactions in the file'); + +program.addOption(new Option('-f, --filePath ', 'The filePath where the nonce for addresses will be stored').makeOptionMandatory(true)); +program.addOption( + new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to sign the transactions').makeOptionMandatory(true), +); +program.addOption(new Option('-a --addresses ', 'The Array of addresses for which the nonces to update').env("ADDRESSES").makeOptionMandatory(true)); +program.addOption(new Option('-y, --yes', 'skip prompts')); + +program.action((options) => { + main(options); +}); + +program.parse(); \ No newline at end of file diff --git a/evm/utils.js b/evm/utils.js index f8afbb88e..809081255 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -16,10 +16,12 @@ const { deployContractConstant, predictContractConstant, getCreate3Address, + printObj, } = require('@axelar-network/axelar-gmp-sdk-solidity'); const { CosmWasmClient } = require('@cosmjs/cosmwasm-stargate'); const CreateDeploy = require('@axelar-network/axelar-gmp-sdk-solidity/artifacts/contracts/deploy/CreateDeploy.sol/CreateDeploy.json'); const IDeployer = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IDeployer.json'); +const { privateKeyExport } = require('secp256k1'); const { verifyContract } = require(`${__dirname}/../axelar-chains-config`); const getSaltFromKey = (key) => { @@ -105,10 +107,6 @@ const deployCreate3 = async ( return contract; }; -const printObj = (obj) => { - console.log(JSON.stringify(obj, null, 2)); -}; - const printInfo = (msg, info = '') => { if (info) { console.log(`${msg}: ${chalk.green(info)}\n`); diff --git a/package-lock.json b/package-lock.json index 9d9e681b0..ce138dd45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,8 @@ "@cosmjs/cosmwasm-stargate": "^0.31.1", "@ethersproject/hardware-wallets": "^5.5.0", "@ledgerhq/hw-transport-node-hid": "^6.11.2", - "ethers": "^5.7.2" + "ethers": "^5.7.2", + "path": "^0.12.7" }, "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^2.0.2", @@ -8933,6 +8934,15 @@ "dev": true, "peer": true }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -9196,6 +9206,14 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -11870,11 +11888,24 @@ "dev": true, "peer": true }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -19178,6 +19209,15 @@ "dev": true, "peer": true }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "requires": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -19363,6 +19403,11 @@ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -21409,6 +21454,21 @@ "dev": true, "peer": true }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + } + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index d0fabbbb1..b9e4fec7c 100644 --- a/package.json +++ b/package.json @@ -27,9 +27,10 @@ "@axelar-network/axelar-gmp-sdk-solidity": "5.3.1", "@axelar-network/interchain-token-service": "0.3.0", "@cosmjs/cosmwasm-stargate": "^0.31.1", - "ethers": "^5.7.2", "@ethersproject/hardware-wallets": "^5.5.0", - "@ledgerhq/hw-transport-node-hid": "^6.11.2" + "@ledgerhq/hw-transport-node-hid": "^6.11.2", + "ethers": "^5.7.2", + "path": "^0.12.7" }, "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^2.0.2", From a4b684895fcee0c07475462f3b70f83dc5937c78 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Mon, 18 Sep 2023 20:36:29 +0530 Subject: [PATCH 27/45] use offline in if condition, remove extra logs --- evm/broadcast-transactions.js | 1 - evm/deploy-gateway-v5.0.x.js | 4 ++-- evm/offline-sign-utils.js | 3 --- evm/send-tokens.js | 4 ---- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index d3d5ebd2b..ff3dd10c6 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -24,7 +24,6 @@ async function processTransactions(filePath, provider) { const signersData = await getAllSignersData(filePath); for (const [signerAddress, transactions] of Object.entries(signersData)) { - console.log(`${key} ${value}`); // "a 5", "b 7", "c 9" const firstPendingnonceFromData = await getNonceFromData(transactions); const nonce = parseInt(await getNonceFromProvider(provider, signerAddress)); diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index 87069a158..d1d00d839 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -376,14 +376,14 @@ async function upgrade(config, options) { nonce = (nonce > nonceData[signerAddress]) ? nonce : nonceData[signerAddress]; let signersData, transactions; - if (isOffline) { + if (offline) { filePath = filePath || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'unsignedTransactions.json'; printInfo(`Storing signed Txs offline in file ${filePath}`); nonce = await getLatestNonceAndUpdateData(filePath, wallet, nonce); signersData = await getAllSignersData(filePath); transactions = await getTransactions(filePath, signerAddress); const network = await provider.getNetwork(); - chainId = network.chainId; + const chainId = network.chainId; const data = {}, tx = {}; tx = await gateway.populateTransaction['upgrade'](contractConfig.implementation, implementationCodehash, setupParams); diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index f20f7fe6c..f34a8f78e 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -83,12 +83,10 @@ async function getNonceFromProvider(provider, address) { async function getLatestNonceAndUpdateData(filePath, wallet, nonce) { try { - const provider = wallet.provider; const signerAddress = await wallet.getAddress(); const signersData = await getAllSignersData(filePath); let transactions = signersData[signerAddress]; const firstPendingnonceFromData = getNonceFromData(transactions); - console.log("firstPendingnonceFromData",firstPendingnonceFromData); if (nonce >= firstPendingnonceFromData) { transactions = getTxsWithUpdatedNonceAndStatus(transactions, nonce); @@ -171,7 +169,6 @@ function getFileData(filePath) { fs.mkdirSync(directoryPath); } if (!fs.existsSync(filePath)) { - console.log("Creating file"); // File does not exist, create it fs.writeFileSync(filePath, JSON.stringify({})); return undefined; diff --git a/evm/send-tokens.js b/evm/send-tokens.js index c69d69684..bcc2acdfd 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -51,19 +51,15 @@ async function sendTokens(chain, options) { } let nonce = await getNonceFromProvider(provider, signerAddress); - console.log("Provider nonce", nonce); const nonceData = getAllSignersData(nonceFilePath); let nonceFromFile = nonceData[signerAddress] || 0; - console.log("Nonce file", nonceFromFile); nonce = (nonce >= parseInt(nonceFromFile)) ? nonce : nonceFromFile; - console.log("Nonce after from nonce file if greatest", nonce); let signersData, transactions, chainId; if (offline) { filePath = filePath || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'unsignedTransactions.json'; printInfo(`Storing signed Txs offline in file ${filePath}`); nonce = await getLatestNonceAndUpdateData(filePath, wallet, nonce); - console.log("Final nonce from getLatestNonce function", nonce); signersData = await getAllSignersData(filePath); transactions = await getTransactions(filePath, signerAddress); const network = await provider.getNetwork(); From add0c0245da5649497ab591c387ba6d8231e150e Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Tue, 19 Sep 2023 03:25:59 +0530 Subject: [PATCH 28/45] combine tx creation and ledger signing, code refactoring --- evm/broadcast-transactions.js | 51 +++++++----------- evm/deploy-gateway-v5.0.x.js | 64 +++++++++++++---------- evm/offline-sign-utils.js | 73 ++++++++++++++++++++------ evm/send-tokens.js | 66 +++++++++++++---------- evm/update-static-gas-options.js | 89 ++++++++++++++++++++++++++++++++ evm/utils.js | 5 ++ 6 files changed, 243 insertions(+), 105 deletions(-) create mode 100644 evm/update-static-gas-options.js diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index ff3dd10c6..2c1870642 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -2,21 +2,17 @@ const chalk = require('chalk'); const { Command, Option } = require('commander'); -const fs = require('fs'); const { ethers } = require('hardhat'); const { providers: { JsonRpcProvider }, } = ethers; +const readlineSync = require('readline-sync'); const { printError, printInfo, printObj } = require('./utils'); const { sendTx, - getTxsWithUpdatedNonceAndStatus, - getNonceFromData, - getNonceFromProvider, - getFilePath, getAllSignersData, - isValidJSON, + updateSignersData, } = require('./offline-sign-utils'); async function processTransactions(filePath, provider) { @@ -24,38 +20,31 @@ async function processTransactions(filePath, provider) { const signersData = await getAllSignersData(filePath); for (const [signerAddress, transactions] of Object.entries(signersData)) { - const firstPendingnonceFromData = await getNonceFromData(transactions); - const nonce = parseInt(await getNonceFromProvider(provider, signerAddress)); - - if (nonce > firstPendingnonceFromData) { - transactions = getTxsWithUpdatedNonceAndStatus(transactions, nonce); - } - for (const transaction of transactions) { if (transaction.status === 'PENDING') { printInfo('Broadcasting transaction: '); - printObj(transaction.baseTx); + printObj(transaction.unsignedTx); - try { // Send the signed transaction - const response = await sendTx(transaction.signedTx, provider); - - // Update the transaction status and store transaction hash - transaction.status = 'SUCCESS'; - transaction.transactionHash = response.transactionHash; - printInfo(`Transactions executed successfully ${response.transactionHash}`); - } catch (error) { - // Update the transaction status and store error message - transaction.status = 'FAILED'; - transaction.error = error.message; - printError(`Transaction failed with error: ${error.message}`); - } + const {success, response} = await sendTx(transaction.signedTx, provider); + + if(success) { + // Update the transaction status and store transaction hash + transaction.status = 'SUCCESS'; + transaction.transactionHash = response.transactionHash; + printInfo(`Transaction executed successfully ${response.transactionHash}`); + } + else { + // Update the transaction status and store error message + transaction.status = 'FAILED'; + printError("Error broadcasting tx: ", transaction.signedTx); + } } } // Write back the updated JSON object to the file signersData[signerAddress] = transactions; } - fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2)); + updateSignersData(filePath, signersData); } catch (error) { printError('Error processing transactions:', error.message); @@ -69,10 +58,8 @@ async function main(options) { const network = await provider.getNetwork(); if (!options.yes) { - const anwser = fs.readlineSync.question( - `Proceed with the broadcasting of all pending signed transactions for address ${chalk.green( - signerAddress, - )} on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, + const anwser = readlineSync.question( + `Proceed with the broadcasting of all pending signed transactions for file ${chalk.green(options.filePath)} on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, ); if (anwser !== 'y') return; } diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index d1d00d839..24ee42872 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -9,7 +9,7 @@ const { ContractFactory, Contract, Wallet, - utils: { defaultAbiCoder, getContractAddress, AddressZero, parseUnits }, + utils: { defaultAbiCoder, getContractAddress, AddressZero }, getDefaultProvider, } = ethers; const readlineSync = require('readline-sync'); @@ -26,15 +26,16 @@ const { printError, printWalletInfo, printWarn, + printObj, } = require('./utils'); const { getAllSignersData, - getNonceFromProvider, updateSignersData, - getLatestNonceAndUpdateData, + ledgerSign, getTransactions, getWallet, - getUnsignedTx, + getLocalNonce, + updateLocalNonce, } = require('./offline-sign-utils.js'); const AxelarGatewayProxy = require('@axelar-network/axelar-cgp-solidity/artifacts/contracts/AxelarGatewayProxy.sol/AxelarGatewayProxy.json'); @@ -311,17 +312,22 @@ async function deploy(config, options) { } async function upgrade(config, options) { - const { chainName, privateKey, yes, ledgerPath, offline, env } = options; - let filePath = options.filePath; + const { chainName, privateKey, yes, ledgerPath, offline, env, nonceFilePath, filePath, nonceOffset } = options; const contractName = 'AxelarGateway'; const chain = config.chains[chainName] || { contracts: {}, name: chainName, id: chainName, rpc: options.rpc, tokenSymbol: 'ETH' }; const rpc = options.rpc || chain.rpc; const provider = getDefaultProvider(rpc); - const wallet = getWallet(privateKey, provider, ledgerPath); - + const {wallet, providerNonce} = await getWallet(privateKey, provider, ledgerPath); const signerAddress = await wallet.getAddress(); + + let nonce = getLocalNonce(nonceFilePath, signerAddress); + + if(providerNonce > nonce) { + updateLocalNonce(nonceFilePath, signerAddress, providerNonce); + nonce = providerNonce; + } await printWalletInfo(wallet); const contractConfig = chain.contracts[contractName]; @@ -371,30 +377,32 @@ async function upgrade(config, options) { if (anwser !== 'y') return; } - let nonce = await getNonceFromProvider(provider, signerAddress); - const nonceData = getAllSignersData(nonceFilePath); - nonce = (nonce > nonceData[signerAddress]) ? nonce : nonceData[signerAddress]; let signersData, transactions; if (offline) { - filePath = filePath || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'unsignedTransactions.json'; + if(!filePath) { + throw new Error("FilePath is not provided in user info"); + } + if(nonceOffset ) { + if(!isValidNumber(nonceOffset)) { + throw new Error("Provided nonce offset is not a valid number"); + } + nonce += parseInt(nonceOffset); + } printInfo(`Storing signed Txs offline in file ${filePath}`); - nonce = await getLatestNonceAndUpdateData(filePath, wallet, nonce); signersData = await getAllSignersData(filePath); transactions = await getTransactions(filePath, signerAddress); - const network = await provider.getNetwork(); - const chainId = network.chainId; - - const data = {}, tx = {}; - tx = await gateway.populateTransaction['upgrade'](contractConfig.implementation, implementationCodehash, setupParams); + const staticGasOptions = chain.staticGasOptions || {}; + const data = {}; + let tx = await gateway.populateTransaction['upgrade'](contractConfig.implementation, implementationCodehash, setupParams); tx.nonce = nonce; - tx.chainId = chainId; - const unsignedTx = getUnsignedTx(chain, tx); + tx.chainId = chain.chainId; + const {baseTx, signedTx} = await ledgerSign(wallet, chain, tx, staticGasOptions); // Storing the fields in the data that will be stored in file data.msg = `This transaction will perform upgrade of AxelarGateway contract having address ${gateway.address} with implementation ${contractConfig.implementation} on chain ${chain.name} with chainId ${chain.chainId}`; - data.unsignedTx = unsignedTx; - data.status = 'NOT_SIGNED'; - + data.unsignedTx = baseTx; + data.signedTx = signedTx; + data.status = "PENDING"; transactions.push(data); if (transactions) { @@ -402,8 +410,7 @@ async function upgrade(config, options) { await updateSignersData(filePath, signersData); } // Updating Nonce data for this Address - nonceData[signerAddress] = nonce; - await updateSignersData(nonceFilePath, nonceData); + updateLocalNonce(nonceFilePath, signerAddress, nonce); } else { const tx = await gateway.upgrade(contractConfig.implementation, implementationCodehash, setupParams, gasOptions); printInfo('Upgrade transaction', tx.hash); @@ -464,13 +471,14 @@ async function programHandler() { program.addOption(new Option('--prevKeyIDs ', 'previous key IDs to be used for auth contract')); program.addOption(new Option('-u, --upgrade', 'upgrade gateway').env('UPGRADE')); program.addOption( - new Option('-o, --offline ', 'If this option is set as true, then ').choices(['true', 'false']).makeOptionMandatory(false), + new Option('--offline', 'Run in offline mode'), ); program.addOption(new Option('-l, --ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); - program.addOption(new Option('--nonceFilePath ', 'The File where nonce value to use for each address is stored').makeOptionMandatory(false)); program.addOption( - new Option('--filePath ', 'The file where the signed tx will be stored').makeOptionMandatory(false), + new Option('--filePath ', 'The filePath where the signed tx will be stored').makeOptionMandatory(false), ); + program.addOption(new Option('--nonceFilePath ', 'The File where nonce value to use for each address is stored').makeOptionMandatory(false)); + program.addOption(new Option('--nonceOffset ', 'The value to add in local nonce if it deviates from actual wallet nonce').makeOptionMandatory(false)); program.action((options) => { main(options); diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index f34a8f78e..0d5910948 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -29,41 +29,60 @@ function getLedgerWallet(provider, path) { } } -function getUnsignedTx(chain, tx) { +async function ledgerSign(wallet, chain, tx, gasOptions) { if (!tx.to || !isAddress(tx.to)) { throw new Error('Target address is missing/not provided as valid address for the tx in function arguments'); } + if(gasOptions) { + tx.gasLimit = gasOptions.gasLimit; + tx.gasPrice = gasOptions.gasPrice; + } + const baseTx = { chainId: tx.chainId || chain.chainId || undefined, data: tx.data || undefined, gasLimit: tx.gasLimit || chain.gasOptions?.gasLimit || undefined, gasPrice: tx.gasPrice || undefined, - nonce: tx.nonce ? BigNumber.from(tx.nonce).toNumber() : undefined, + nonce: (tx.nonce !== undefined && tx.nonce !== null) ? BigNumber.from(tx.nonce).toNumber() : undefined, to: tx.to || undefined, value: tx.value || undefined, }; - return baseTx; + let signedTx; + + try { + signedTx = await wallet.signTransaction(baseTx); + printInfo(`Signed Tx from ledger with signedTxHash as: ${signedTx}`); + } catch (error) { + printError('Failed to sign tx from ledger'); + printObj(error); + } + + return { baseTx, signedTx }; } async function sendTx(tx, provider) { + let success; try { - const receipt = await provider.sendTransaction(tx).then((tx) => tx.wait()); - if(!isValidJSON(receipt) || response.status !== 1) { + const response = await provider.sendTransaction(tx).then((tx) => tx.wait()); + if(response.error || !isValidJSON(response) || response.status !== 1) { const error = `Execution failed${ response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` }`; throw new Error(error); } - return receipt; + success = true; + return { success, response}; } catch (errorObj) { printError('Error while broadcasting signed tx'); printObj(errorObj); + success = false; + return {success, undefined}; } } -async function updateSignersData(filePath, signersData) { +function updateSignersData(filePath, signersData) { fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2), (err) => { if (err) { @@ -77,23 +96,28 @@ async function updateSignersData(filePath, signersData) { } async function getNonceFromProvider(provider, address) { - const nonce = await provider.getTransactionCount(address); + let nonce = 0; + try { + nonce = await provider.getTransactionCount(address); + } catch(error) { + printError("Could not fetch nonnce from provider", error.message); + } return nonce; } -async function getLatestNonceAndUpdateData(filePath, wallet, nonce) { +async function getLatestNonceAndUpdateData(filePath, wallet) { try { const signerAddress = await wallet.getAddress(); + const provider = wallet.provider; + const providerNonce = getNonceFromProvider(provider, signerAddress); const signersData = await getAllSignersData(filePath); let transactions = signersData[signerAddress]; - const firstPendingnonceFromData = getNonceFromData(transactions); + let latestTransactionNonce = transactions[transactions.length - 1].unsignedTx.nonce; - if (nonce >= firstPendingnonceFromData) { + if (providerNonce >= transactions.unsignedTx.nonce && (transactions.status === "NOT_SIGNED" || transactions.status === "PENDING")) { transactions = getTxsWithUpdatedNonceAndStatus(transactions, nonce); signersData[signerAddress] = transactions; await updateSignersData(filePath, signersData); - } else { - nonce = firstPendingnonceFromData + 1; } return nonce; @@ -135,7 +159,7 @@ function getNonceFromData(transactions) { return 0; } -async function getAllSignersData(filePath) { +function getAllSignersData(filePath) { const signersData = {}; try { @@ -223,7 +247,7 @@ function isValidJSON(obj) { return true; } -const getWallet = (privateKey, provider, ledgerPath) => { +const getWallet = async(privateKey, provider, ledgerPath) => { let wallet; if (privateKey === 'ledger') { wallet = getLedgerWallet(provider, ledgerPath || undefined); @@ -234,7 +258,20 @@ const getWallet = (privateKey, provider, ledgerPath) => { wallet = new Wallet(privateKey, provider); } - return wallet; + const signerAddress = await wallet.getAddress(); + const providerNonce = await getNonceFromProvider(provider, signerAddress); + return {wallet, providerNonce}; +} + +const getLocalNonce = (nonceFilePath, signerAddress) => { + const nonceData = getAllSignersData(nonceFilePath); + return nonceData[signerAddress] || 0; +} + +const updateLocalNonce = (nonceFilePath, signerAddress, nonce) => { + let nonceData = getAllSignersData(nonceFilePath); + nonceData[signerAddress] = nonce; + updateSignersData(nonceFilePath, nonceData); } module.exports = { @@ -249,5 +286,7 @@ module.exports = { getLatestNonceAndUpdateData, isValidJSON, getWallet, - getUnsignedTx, + getLocalNonce, + updateLocalNonce, + ledgerSign, }; diff --git a/evm/send-tokens.js b/evm/send-tokens.js index bcc2acdfd..f24237888 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -6,35 +6,43 @@ const chalk = require('chalk'); const { Command, Option } = require('commander'); const { ethers } = require('hardhat'); const { - Wallet, getDefaultProvider, - utils: { parseEther, parseUnits }, + utils: { parseEther }, } = ethers; const readlineSync = require('readline-sync'); -const { printInfo, printWalletInfo, isValidPrivateKey, printError, printObj } = require('./utils'); +const { printInfo, printWalletInfo, isValidNumber } = require('./utils'); const { getAllSignersData, - getNonceFromProvider, updateSignersData, - getLatestNonceAndUpdateData, getTransactions, getWallet, - getUnsignedTx, + ledgerSign, + getLocalNonce, + updateLocalNonce, } = require('./offline-sign-utils.js'); -const { blob } = require('stream/consumers'); async function sendTokens(chain, options) { - const { privateKey, offline, env, ledgerPath, nonceFilePath } = options; - let { amount, recipients, filePath} = options; + const { privateKey, offline, ledgerPath, filePath, nonceFilePath, nonceOffset } = options; + let { amount, recipients} = options; + + if(!nonceFilePath) { + throw new Error("Nonce FilePath is not provided in user info"); + } const provider = getDefaultProvider(chain.rpc); recipients = options.recipients.split(',').map((str) => str.trim()); amount = parseEther(amount); - const wallet = getWallet(privateKey, provider, ledgerPath); - + const { wallet, providerNonce } = await getWallet(privateKey, provider, ledgerPath); const signerAddress = await wallet.getAddress(); + let nonce = getLocalNonce(nonceFilePath, signerAddress); + + if(providerNonce > nonce) { + updateLocalNonce(nonceFilePath, signerAddress, providerNonce); + nonce = providerNonce; + } + const balance = await printWalletInfo(wallet); if (balance.lte(amount)) { @@ -50,20 +58,22 @@ async function sendTokens(chain, options) { if (anwser !== 'y') return; } - let nonce = await getNonceFromProvider(provider, signerAddress); - const nonceData = getAllSignersData(nonceFilePath); - let nonceFromFile = nonceData[signerAddress] || 0; - nonce = (nonce >= parseInt(nonceFromFile)) ? nonce : nonceFromFile; - let signersData, transactions, chainId; + let signersData, transactions; + const gasOptions = chain.staticGasOptions || {}; if (offline) { - filePath = filePath || env.toLowerCase() + '-' + chain.name.toLowerCase() + '-' + 'unsignedTransactions.json'; + if(!filePath) { + throw new Error("FilePath is not provided in user info"); + } + if(nonceOffset) { + if(!isValidNumber(nonceOffset)) { + throw new Error("Provided nonce offset is not a valid number"); + } + nonce += parseInt(nonceOffset); + } printInfo(`Storing signed Txs offline in file ${filePath}`); - nonce = await getLatestNonceAndUpdateData(filePath, wallet, nonce); signersData = await getAllSignersData(filePath); transactions = await getTransactions(filePath, signerAddress); - const network = await provider.getNetwork(); - chainId = network.chainId; } for (const recipient of recipients) { @@ -76,13 +86,13 @@ async function sendTokens(chain, options) { if (offline) { const data = {}; tx.nonce = nonce; - tx.chainId = chainId; - const unsignedTx = getUnsignedTx(chain, tx); + tx.chainId = chain.chainId; + const {baseTx, signedTx} = await ledgerSign(wallet, chain, tx, gasOptions); // Storing the fields in the data that will be stored in file - data.msg = `This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chainId}`; - data.unsignedTx = unsignedTx; - data.status = 'NOT_SIGNED'; - + data.msg = `This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; + data.unsignedTx = baseTx; + data.signedTx = signedTx; + data.status = "PENDING"; transactions.push(data); } else { @@ -101,8 +111,7 @@ async function sendTokens(chain, options) { await updateSignersData(filePath, signersData); } // Updating Nonce data for this Address - nonceData[signerAddress] = nonce; - await updateSignersData(nonceFilePath, nonceData); + updateLocalNonce(nonceFilePath, signerAddress, nonce); } async function main(options) { @@ -147,6 +156,7 @@ if (require.main === module) { new Option('--filePath ', 'The filePath where the signed tx will be stored').makeOptionMandatory(false), ); program.addOption(new Option('--nonceFilePath ', 'The File where nonce value to use for each address is stored').makeOptionMandatory(false)); + program.addOption(new Option('--nonceOffset ', 'The value to add in local nonce if it deviates from actual wallet nonce').makeOptionMandatory(false)); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { diff --git a/evm/update-static-gas-options.js b/evm/update-static-gas-options.js new file mode 100644 index 000000000..d0d325294 --- /dev/null +++ b/evm/update-static-gas-options.js @@ -0,0 +1,89 @@ +'use strict'; + +const chalk = require('chalk'); +const { Command, Option } = require('commander'); +const { ethers } = require('hardhat'); +const { + getDefaultProvider +} = ethers; +const readlineSync = require('readline-sync'); + +const { printError, printInfo, printObj, saveConfig } = require('./utils'); + +async function updateStaticGasOptions(chain, options, filePath) { + const{rpcUrl, yes} = options; + const provider = getDefaultProvider(rpcUrl); + const network = await provider.getNetwork(); + + if (!yes) { + const anwser = readlineSync.question( + `Proceed with the static gasOption update on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, + ); + if (anwser !== 'y') return; + } + try { + const gasPrice = await provider.getGasPrice() * 5; + const block = await provider.getBlock('latest'); + const gasLimit = block.gasLimit.toNumber() / 500; + const staticGasOptions = {}; + staticGasOptions.gasLimit = gasLimit; + staticGasOptions.gasPrice = gasPrice; + chain.staticGasOptions = staticGasOptions; + console.log("chain"); + printObj(chain); + + printInfo(`GasOptions updated succesfully and stored in config file ${filePath}`); + } catch(error) { + printError(`GasOptions updation failed with error: ${error.message}`); + printObj(error); + } + return chain; +} + + +async function main(options) { + const { env, chainNames } = options; + + const filePath = `${__dirname}/../axelar-chains-config/info/${env === 'local' ? 'testnet' : env}.json` + const config = require(filePath); + + const chains = chainNames.split(',').map((str) => str.trim()); + + for (const chainName of chains) { + if (config.chains[chainName.toLowerCase()] === undefined) { + throw new Error(`Chain ${chainName} is not defined in the info file`); + } + } + + for (const chainName of chains) { + const chain = config.chains[chainName.toLowerCase()]; + + config.chains[chainName.toLowerCase()] = await updateStaticGasOptions(chain, options, filePath); + console.log("Config"); + printObj(config); + } + saveConfig(config, env); +} + +const program = new Command(); + +program.name('Update-GasOptions').description('Update gasOptions to be used in offline signing'); + +program.addOption( + new Option('-e, --env ', 'environment') + .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) + .default('testnet') + .makeOptionMandatory(true) + .env('ENV'), +); +program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); +program.addOption( + new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions').makeOptionMandatory(true), +); +program.addOption(new Option('-y, --yes', 'skip prompts')); + +program.action((options) => { + main(options); +}); + +program.parse(); \ No newline at end of file diff --git a/evm/utils.js b/evm/utils.js index 809081255..c0f76f15d 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -186,6 +186,10 @@ const isNumber = (arg) => { return Number.isInteger(arg); }; +const isValidNumber = (arg) => { + return !isNaN(parseInt(arg)) && isFinite(arg); + }; + const isNumberArray = (arr) => { if (!Array.isArray(arr)) { return false; @@ -614,6 +618,7 @@ module.exports = { getDeployedAddress, isString, isNumber, + isValidNumber, isNumberArray, isAddressArray, isKeccak256Hash, From 67aca952351815e465272888165b3747ef58aa87 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Tue, 19 Sep 2023 11:39:02 +0530 Subject: [PATCH 29/45] refactor code for unused imports, correct nonce usage --- evm/broadcast-transactions.js | 42 ++++++------ evm/deploy-gateway-v5.0.x.js | 46 +++++++------ evm/offline-sign-utils.js | 108 ++++++++----------------------- evm/offline-sign.js | 75 --------------------- evm/send-tokens.js | 76 ++++++++++++---------- evm/update-static-gas-options.js | 31 ++++----- evm/updateNonces.js | 38 +++++------ evm/utils.js | 3 +- 8 files changed, 150 insertions(+), 269 deletions(-) delete mode 100644 evm/offline-sign.js diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 2c1870642..94c53d39f 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -9,11 +9,7 @@ const { const readlineSync = require('readline-sync'); const { printError, printInfo, printObj } = require('./utils'); -const { - sendTx, - getAllSignersData, - updateSignersData, -} = require('./offline-sign-utils'); +const { sendTx, getAllSignersData, updateSignersData } = require('./offline-sign-utils'); async function processTransactions(filePath, provider) { try { @@ -25,27 +21,27 @@ async function processTransactions(filePath, provider) { printInfo('Broadcasting transaction: '); printObj(transaction.unsignedTx); - // Send the signed transaction - const {success, response} = await sendTx(transaction.signedTx, provider); - - if(success) { - // Update the transaction status and store transaction hash - transaction.status = 'SUCCESS'; - transaction.transactionHash = response.transactionHash; - printInfo(`Transaction executed successfully ${response.transactionHash}`); - } - else { - // Update the transaction status and store error message - transaction.status = 'FAILED'; - printError("Error broadcasting tx: ", transaction.signedTx); - } + // Send the signed transaction + const { success, response } = await sendTx(transaction.signedTx, provider); + + if (success) { + // Update the transaction status and store transaction hash + transaction.status = 'SUCCESS'; + transaction.transactionHash = response.transactionHash; + printInfo(`Transaction executed successfully ${response.transactionHash}`); + } else { + // Update the transaction status and store error message + transaction.status = 'FAILED'; + printError('Error broadcasting tx: ', transaction.signedTx); + } } } // Write back the updated JSON object to the file + signersData[signerAddress] = transactions; - } - updateSignersData(filePath, signersData); + } + updateSignersData(filePath, signersData); } catch (error) { printError('Error processing transactions:', error.message); } @@ -59,7 +55,9 @@ async function main(options) { if (!options.yes) { const anwser = readlineSync.question( - `Proceed with the broadcasting of all pending signed transactions for file ${chalk.green(options.filePath)} on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, + `Proceed with the broadcasting of all pending signed transactions for file ${chalk.green( + options.filePath, + )} on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, ); if (anwser !== 'y') return; } diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index 24ee42872..0f99a256b 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -26,7 +26,7 @@ const { printError, printWalletInfo, printWarn, - printObj, + isValidNumber, } = require('./utils'); const { getAllSignersData, @@ -312,22 +312,23 @@ async function deploy(config, options) { } async function upgrade(config, options) { - const { chainName, privateKey, yes, ledgerPath, offline, env, nonceFilePath, filePath, nonceOffset } = options; + const { chainName, privateKey, yes, ledgerPath, offline, nonceFilePath, filePath, nonceOffset } = options; const contractName = 'AxelarGateway'; const chain = config.chains[chainName] || { contracts: {}, name: chainName, id: chainName, rpc: options.rpc, tokenSymbol: 'ETH' }; const rpc = options.rpc || chain.rpc; const provider = getDefaultProvider(rpc); - const {wallet, providerNonce} = await getWallet(privateKey, provider, ledgerPath); + const { wallet, providerNonce } = await getWallet(privateKey, provider, ledgerPath); const signerAddress = await wallet.getAddress(); let nonce = getLocalNonce(nonceFilePath, signerAddress); - if(providerNonce > nonce) { + if (providerNonce !== undefined && providerNonce !== null) { updateLocalNonce(nonceFilePath, signerAddress, providerNonce); nonce = providerNonce; } + await printWalletInfo(wallet); const contractConfig = chain.contracts[contractName]; @@ -380,29 +381,32 @@ async function upgrade(config, options) { let signersData, transactions; if (offline) { - if(!filePath) { - throw new Error("FilePath is not provided in user info"); + if (!filePath) { + throw new Error('FilePath is not provided in user info'); } - if(nonceOffset ) { - if(!isValidNumber(nonceOffset)) { - throw new Error("Provided nonce offset is not a valid number"); + + if (nonceOffset) { + if (!isValidNumber(nonceOffset)) { + throw new Error('Provided nonce offset is not a valid number'); } + nonce += parseInt(nonceOffset); } + printInfo(`Storing signed Txs offline in file ${filePath}`); signersData = await getAllSignersData(filePath); transactions = await getTransactions(filePath, signerAddress); const staticGasOptions = chain.staticGasOptions || {}; const data = {}; - let tx = await gateway.populateTransaction['upgrade'](contractConfig.implementation, implementationCodehash, setupParams); + const tx = await gateway.populateTransaction.upgrade(contractConfig.implementation, implementationCodehash, setupParams); tx.nonce = nonce; tx.chainId = chain.chainId; - const {baseTx, signedTx} = await ledgerSign(wallet, chain, tx, staticGasOptions); + const { baseTx, signedTx } = await ledgerSign(wallet, chain, tx, staticGasOptions); // Storing the fields in the data that will be stored in file data.msg = `This transaction will perform upgrade of AxelarGateway contract having address ${gateway.address} with implementation ${contractConfig.implementation} on chain ${chain.name} with chainId ${chain.chainId}`; - data.unsignedTx = baseTx; + data.unsignedTx = baseTx; data.signedTx = signedTx; - data.status = "PENDING"; + data.status = 'PENDING'; transactions.push(data); if (transactions) { @@ -410,6 +414,7 @@ async function upgrade(config, options) { await updateSignersData(filePath, signersData); } // Updating Nonce data for this Address + updateLocalNonce(nonceFilePath, signerAddress, nonce); } else { const tx = await gateway.upgrade(contractConfig.implementation, implementationCodehash, setupParams, gasOptions); @@ -470,15 +475,20 @@ async function programHandler() { program.addOption(new Option('-a, --amplifier', 'deploy amplifier gateway').env('AMPLIFIER')); program.addOption(new Option('--prevKeyIDs ', 'previous key IDs to be used for auth contract')); program.addOption(new Option('-u, --upgrade', 'upgrade gateway').env('UPGRADE')); + program.addOption(new Option('--offline', 'Run in offline mode')); + program.addOption(new Option('-l, --ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); + program.addOption(new Option('--filePath ', 'The filePath where the signed tx will be stored').makeOptionMandatory(false)); program.addOption( - new Option('--offline', 'Run in offline mode'), + new Option('--nonceFilePath ', 'The File where nonce value to use for each address is stored').makeOptionMandatory( + false, + ), ); - program.addOption(new Option('-l, --ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); program.addOption( - new Option('--filePath ', 'The filePath where the signed tx will be stored').makeOptionMandatory(false), + new Option( + '--nonceOffset ', + 'The value to add in local nonce if it deviates from actual wallet nonce', + ).makeOptionMandatory(false), ); - program.addOption(new Option('--nonceFilePath ', 'The File where nonce value to use for each address is stored').makeOptionMandatory(false)); - program.addOption(new Option('--nonceOffset ', 'The value to add in local nonce if it deviates from actual wallet nonce').makeOptionMandatory(false)); program.action((options) => { main(options); diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 0d5910948..8bc414715 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -10,7 +10,7 @@ const { const path = require('path'); const { LedgerSigner } = require('@ethersproject/hardware-wallets'); -const { printError, printInfo, printObj } = require('./utils'); +const { printError, printInfo, printObj, isValidPrivateKey } = require('./utils'); // function to create a ledgerSigner type wallet object function getLedgerWallet(provider, path) { @@ -34,7 +34,7 @@ async function ledgerSign(wallet, chain, tx, gasOptions) { throw new Error('Target address is missing/not provided as valid address for the tx in function arguments'); } - if(gasOptions) { + if (gasOptions) { tx.gasLimit = gasOptions.gasLimit; tx.gasPrice = gasOptions.gasPrice; } @@ -44,7 +44,7 @@ async function ledgerSign(wallet, chain, tx, gasOptions) { data: tx.data || undefined, gasLimit: tx.gasLimit || chain.gasOptions?.gasLimit || undefined, gasPrice: tx.gasPrice || undefined, - nonce: (tx.nonce !== undefined && tx.nonce !== null) ? BigNumber.from(tx.nonce).toNumber() : undefined, + nonce: tx.nonce !== undefined && tx.nonce !== null ? BigNumber.from(tx.nonce).toNumber() : undefined, to: tx.to || undefined, value: tx.value || undefined, }; @@ -53,7 +53,7 @@ async function ledgerSign(wallet, chain, tx, gasOptions) { try { signedTx = await wallet.signTransaction(baseTx); - printInfo(`Signed Tx from ledger with signedTxHash as: ${signedTx}`); + printInfo('Signed Tx from ledger with signedTxHash as', signedTx); } catch (error) { printError('Failed to sign tx from ledger'); printObj(error); @@ -64,26 +64,28 @@ async function ledgerSign(wallet, chain, tx, gasOptions) { async function sendTx(tx, provider) { let success; + try { const response = await provider.sendTransaction(tx).then((tx) => tx.wait()); - if(response.error || !isValidJSON(response) || response.status !== 1) { + + if (response.error || !isValidJSON(response) || response.status !== 1) { const error = `Execution failed${ response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` }`; throw new Error(error); } + success = true; - return { success, response}; + return { success, response }; } catch (errorObj) { printError('Error while broadcasting signed tx'); printObj(errorObj); success = false; - return {success, undefined}; + return { success, undefined }; } } function updateSignersData(filePath, signersData) { - fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2), (err) => { if (err) { printError(`Could not update signersData in file ${filePath}`); @@ -97,73 +99,20 @@ function updateSignersData(filePath, signersData) { async function getNonceFromProvider(provider, address) { let nonce = 0; - try { - nonce = await provider.getTransactionCount(address); - } catch(error) { - printError("Could not fetch nonnce from provider", error.message); - } - return nonce; -} - -async function getLatestNonceAndUpdateData(filePath, wallet) { - try { - const signerAddress = await wallet.getAddress(); - const provider = wallet.provider; - const providerNonce = getNonceFromProvider(provider, signerAddress); - const signersData = await getAllSignersData(filePath); - let transactions = signersData[signerAddress]; - let latestTransactionNonce = transactions[transactions.length - 1].unsignedTx.nonce; - - if (providerNonce >= transactions.unsignedTx.nonce && (transactions.status === "NOT_SIGNED" || transactions.status === "PENDING")) { - transactions = getTxsWithUpdatedNonceAndStatus(transactions, nonce); - signersData[signerAddress] = transactions; - await updateSignersData(filePath, signersData); - } - - return nonce; - } catch (error) { - printError('Failed to calculate correct nonce for tx'); - printObj(error); - } -} - -function getTxsWithUpdatedNonceAndStatus(transactions, nonce) { - if (transactions) { - for (const transaction of transactions) { - if (nonce > transaction.nonce && (transaction.status === 'PENDING' || transaction.status === 'BROADCASTED')) { - transaction.status = 'FAILED'; - const error = `Transaction nonce value of ${transaction.nonce} is less than the required signer nonce value of ${nonce}`; - transaction.error = error; - printError(error + ` for signedTx: ${transaction.signedTx}`); - } - } - } - - return transactions; -} -function getNonceFromData(transactions) { try { - if (transactions) { - for (const transaction of transactions) { - if (transaction.status === 'PENDING') { - return transaction.nonce; - } - } - } + nonce = await provider.getTransactionCount(address); } catch (error) { - printError('Failed to get first pending nonce from file data'); - printObj(error); + printError('Could not fetch nonnce from provider', error.message); } - return 0; + return nonce; } function getAllSignersData(filePath) { const signersData = {}; try { - // Read the content of the file const data = getFileData(filePath); @@ -189,9 +138,11 @@ function getFileData(filePath) { // Extract the directory path const directoryPath = path.dirname(filePath); // Check if the directory and file exists, create it if it doesn't - if (!fs.existsSync(directoryPath)){ + + if (!fs.existsSync(directoryPath)) { fs.mkdirSync(directoryPath); } + if (!fs.existsSync(filePath)) { // File does not exist, create it fs.writeFileSync(filePath, JSON.stringify({})); @@ -202,7 +153,7 @@ function getFileData(filePath) { const data = fs.readFileSync(filePath); return data; } catch (error) { - printError(`Failed to get file data from the file ${filePath}`); + printError(`Failed to get data from the file ${filePath}`); printObj(error); } } @@ -211,7 +162,6 @@ async function getTransactions(filePath, signerAddress) { let transactions = []; try { - // Read the content of the file const data = getFileData(filePath); @@ -247,8 +197,9 @@ function isValidJSON(obj) { return true; } -const getWallet = async(privateKey, provider, ledgerPath) => { +const getWallet = async (privateKey, provider, ledgerPath) => { let wallet; + if (privateKey === 'ledger') { wallet = getLedgerWallet(provider, ledgerPath || undefined); } else { @@ -258,33 +209,28 @@ const getWallet = async(privateKey, provider, ledgerPath) => { wallet = new Wallet(privateKey, provider); } + const signerAddress = await wallet.getAddress(); const providerNonce = await getNonceFromProvider(provider, signerAddress); - return {wallet, providerNonce}; -} + return { wallet, providerNonce }; +}; const getLocalNonce = (nonceFilePath, signerAddress) => { const nonceData = getAllSignersData(nonceFilePath); return nonceData[signerAddress] || 0; -} +}; const updateLocalNonce = (nonceFilePath, signerAddress, nonce) => { - let nonceData = getAllSignersData(nonceFilePath); - nonceData[signerAddress] = nonce; - updateSignersData(nonceFilePath, nonceData); -} + const nonceData = getAllSignersData(nonceFilePath); + nonceData[signerAddress] = nonce; + updateSignersData(nonceFilePath, nonceData); +}; module.exports = { - getLedgerWallet, sendTx, - getTxsWithUpdatedNonceAndStatus, - getNonceFromProvider, - getNonceFromData, getAllSignersData, getTransactions, updateSignersData, - getLatestNonceAndUpdateData, - isValidJSON, getWallet, getLocalNonce, updateLocalNonce, diff --git a/evm/offline-sign.js b/evm/offline-sign.js deleted file mode 100644 index 6d32303c7..000000000 --- a/evm/offline-sign.js +++ /dev/null @@ -1,75 +0,0 @@ -'use strict'; - -const chalk = require('chalk'); -const { Command, Option } = require('commander'); -const fs = require('fs'); -const { ethers } = require('hardhat'); -const { getDefaultProvider } = ethers; - -const { printError, printInfo, printObj } = require('./utils'); -const { - getWallet, - getTransactions, - getAllSignersData, - updateSignersData, -} = require('./offline-sign-utils'); - -async function processTransactions(wallet, signerAddress, filePath) { - try { - let signersData = await getAllSignersData(filePath); - let transactions = await getTransactions(filePath, signerAddress); - - for(const transaction of transactions) { - if(transaction.status === "NOT_SIGNED") { - const signedTx = await wallet.signTransaction(transaction.unsignedTx); - transaction.status = "PENDING"; - transaction.signedTx = signedTx; - } - } - - signersData[signerAddress] = transactions; - await updateSignersData(filePath, signersData); - printInfo(`Transactions signed succesfully and stored in file ${filePath}`); - - } catch(error) { - printError(`Transactions signing failed with error: ${error.message}`); - printObj(error); - } -} - - -async function main(options) { - const { filePath, ledgerPath, rpcUrl } = options; - const provider = getDefaultProvider(rpcUrl); - const network = await provider.getNetwork(); - const wallet = getWallet("ledger", provider, ledgerPath); - const signerAddress = await wallet.getAddress(); - - if (!options.yes) { - const anwser = fs.readlineSync.question( - `Proceed with the signing of all unSigned transactions for address ${chalk.green( - signerAddress, - )} on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, - ); - if (anwser !== 'y') return; - } - - await processTransactions(wallet, signerAddress, filePath); -} - -const program = new Command(); - -program.name('Offline-Signing').description('Offline sign all the unsigned transactions in the file'); - -program.addOption(new Option('-f, --filePath ', 'The filePath where the signed tx will be stored').makeOptionMandatory(true)); -program.addOption( - new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to sign the transactions').makeOptionMandatory(true), -); -program.addOption(new Option('--ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); -program.addOption(new Option('-y, --yes', 'skip prompts')); - -program.action((options) => { - main(options); -}); - -program.parse(); \ No newline at end of file diff --git a/evm/send-tokens.js b/evm/send-tokens.js index f24237888..884e1ac39 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -24,10 +24,10 @@ const { async function sendTokens(chain, options) { const { privateKey, offline, ledgerPath, filePath, nonceFilePath, nonceOffset } = options; - let { amount, recipients} = options; + let { amount, recipients } = options; - if(!nonceFilePath) { - throw new Error("Nonce FilePath is not provided in user info"); + if (!nonceFilePath) { + throw new Error('Nonce FilePath is not provided in user info'); } const provider = getDefaultProvider(chain.rpc); @@ -38,7 +38,7 @@ async function sendTokens(chain, options) { const signerAddress = await wallet.getAddress(); let nonce = getLocalNonce(nonceFilePath, signerAddress); - if(providerNonce > nonce) { + if (providerNonce !== undefined && providerNonce !== null) { updateLocalNonce(nonceFilePath, signerAddress, providerNonce); nonce = providerNonce; } @@ -62,15 +62,18 @@ async function sendTokens(chain, options) { const gasOptions = chain.staticGasOptions || {}; if (offline) { - if(!filePath) { - throw new Error("FilePath is not provided in user info"); + if (!filePath) { + throw new Error('FilePath is not provided in user info'); } - if(nonceOffset) { - if(!isValidNumber(nonceOffset)) { - throw new Error("Provided nonce offset is not a valid number"); + + if (nonceOffset) { + if (!isValidNumber(nonceOffset)) { + throw new Error('Provided nonce offset is not a valid number'); } + nonce += parseInt(nonceOffset); } + printInfo(`Storing signed Txs offline in file ${filePath}`); signersData = await getAllSignersData(filePath); transactions = await getTransactions(filePath, signerAddress); @@ -81,26 +84,23 @@ async function sendTokens(chain, options) { const tx = { to: recipient, value: amount, - } - - if (offline) { - const data = {}; - tx.nonce = nonce; - tx.chainId = chain.chainId; - const {baseTx, signedTx} = await ledgerSign(wallet, chain, tx, gasOptions); - // Storing the fields in the data that will be stored in file - data.msg = `This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; - data.unsignedTx = baseTx; - data.signedTx = signedTx; - data.status = "PENDING"; - transactions.push(data); - } - else { - const tx = await wallet.sendTransaction(tx); - - printInfo('Transaction hash', tx.hash); - - await tx.wait(); + }; + + if (offline) { + const data = {}; + tx.nonce = nonce; + tx.chainId = chain.chainId; + const { baseTx, signedTx } = await ledgerSign(wallet, chain, tx, gasOptions); + // Storing the fields in the data that will be stored in file + data.msg = `This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; + data.unsignedTx = baseTx; + data.signedTx = signedTx; + data.status = 'PENDING'; + transactions.push(data); + } else { + const response = await wallet.sendTransaction(tx); + await response.wait(); + printInfo('Transaction hash', response.transactionHash); } ++nonce; @@ -108,9 +108,10 @@ async function sendTokens(chain, options) { if (transactions) { signersData[signerAddress] = transactions; - await updateSignersData(filePath, signersData); + updateSignersData(filePath, signersData); } // Updating Nonce data for this Address + updateLocalNonce(nonceFilePath, signerAddress, nonce); } @@ -148,15 +149,20 @@ if (require.main === module) { program.addOption(new Option('-p, --privateKey ', 'private key').makeOptionMandatory(true).env('PRIVATE_KEY')); program.addOption(new Option('-r, --recipients ', 'comma-separated recipients of tokens').makeOptionMandatory(true)); program.addOption(new Option('-a, --amount ', 'amount to transfer (in terms of ETH)').makeOptionMandatory(true)); + program.addOption(new Option('--offline', 'Run in offline mode')); + program.addOption(new Option('--ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); + program.addOption(new Option('--filePath ', 'The filePath where the signed tx will be stored').makeOptionMandatory(false)); program.addOption( - new Option('--offline', 'Run in offline mode'), + new Option('--nonceFilePath ', 'The File where nonce value to use for each address is stored').makeOptionMandatory( + false, + ), ); - program.addOption(new Option('--ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); program.addOption( - new Option('--filePath ', 'The filePath where the signed tx will be stored').makeOptionMandatory(false), + new Option( + '--nonceOffset ', + 'The value to add in local nonce if it deviates from actual wallet nonce', + ).makeOptionMandatory(false), ); - program.addOption(new Option('--nonceFilePath ', 'The File where nonce value to use for each address is stored').makeOptionMandatory(false)); - program.addOption(new Option('--nonceOffset ', 'The value to add in local nonce if it deviates from actual wallet nonce').makeOptionMandatory(false)); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { diff --git a/evm/update-static-gas-options.js b/evm/update-static-gas-options.js index d0d325294..eece9cd19 100644 --- a/evm/update-static-gas-options.js +++ b/evm/update-static-gas-options.js @@ -4,47 +4,48 @@ const chalk = require('chalk'); const { Command, Option } = require('commander'); const { ethers } = require('hardhat'); const { - getDefaultProvider + getDefaultProvider, + utils: { parseUnits }, } = ethers; const readlineSync = require('readline-sync'); const { printError, printInfo, printObj, saveConfig } = require('./utils'); async function updateStaticGasOptions(chain, options, filePath) { - const{rpcUrl, yes} = options; + const { rpcUrl, yes } = options; const provider = getDefaultProvider(rpcUrl); const network = await provider.getNetwork(); if (!yes) { const anwser = readlineSync.question( - `Proceed with the static gasOption update on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, + `Proceed with the static gasOption update on network ${chalk.green(network.name)} with chainId ${chalk.green( + network.chainId, + )} ${chalk.green('(y/n)')} `, ); if (anwser !== 'y') return; } + try { - const gasPrice = await provider.getGasPrice() * 5; + const gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'gwei') * 5; const block = await provider.getBlock('latest'); const gasLimit = block.gasLimit.toNumber() / 500; const staticGasOptions = {}; staticGasOptions.gasLimit = gasLimit; staticGasOptions.gasPrice = gasPrice; chain.staticGasOptions = staticGasOptions; - console.log("chain"); - printObj(chain); - printInfo(`GasOptions updated succesfully and stored in config file ${filePath}`); - } catch(error) { + } catch (error) { printError(`GasOptions updation failed with error: ${error.message}`); printObj(error); } + return chain; } - async function main(options) { const { env, chainNames } = options; - const filePath = `${__dirname}/../axelar-chains-config/info/${env === 'local' ? 'testnet' : env}.json` + const filePath = `${__dirname}/../axelar-chains-config/info/${env === 'local' ? 'testnet' : env}.json`; const config = require(filePath); const chains = chainNames.split(',').map((str) => str.trim()); @@ -57,11 +58,9 @@ async function main(options) { for (const chainName of chains) { const chain = config.chains[chainName.toLowerCase()]; - config.chains[chainName.toLowerCase()] = await updateStaticGasOptions(chain, options, filePath); - console.log("Config"); - printObj(config); } + saveConfig(config, env); } @@ -77,13 +76,11 @@ program.addOption( .env('ENV'), ); program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption( - new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions').makeOptionMandatory(true), -); +program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions').makeOptionMandatory(true)); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { main(options); }); -program.parse(); \ No newline at end of file +program.parse(); diff --git a/evm/updateNonces.js b/evm/updateNonces.js index 82d5a8d93..d558cfbd0 100644 --- a/evm/updateNonces.js +++ b/evm/updateNonces.js @@ -4,51 +4,45 @@ const chalk = require('chalk'); const { Command, Option } = require('commander'); const fs = require('fs'); const { ethers } = require('hardhat'); -const { - getDefaultProvider -} = ethers; +const { getDefaultProvider } = ethers; +const readlineSync = require('readline-sync'); const { printError, printInfo, printObj } = require('./utils'); -const { - getNonceFromProvider, getAllSignersData, -} = require('./offline-sign-utils'); +const { getNonceFromProvider, getAllSignersData } = require('./offline-sign-utils'); function updateNonce(provider, addresses, filePath) { try { const nonceData = getAllSignersData(filePath); addresses = JSON.parse(addresses); - for(const address of addresses) { + for (const address of addresses) { const nonce = getNonceFromProvider(provider, address); - nonceData[address]= nonce; + nonceData[address] = nonce; } fs.writeFileSync(filePath, JSON.stringify(nonceData, null, 2), (err) => { if (err) { printError(`Could not update Nonce in file ${filePath}`); printObj(err); - return; } - }); printInfo(`Nonce updated succesfully and stored in file ${filePath}`); - } catch(error) { + } catch (error) { printError(`Nonce updation failed with error: ${error.message}`); printObj(error); } } - async function main(options) { const { filePath, rpcUrl, addresses } = options; const provider = getDefaultProvider(rpcUrl); const network = await provider.getNetwork(); if (!options.yes) { - const anwser = fs.readlineSync.question( - `Proceed with the nonces update of all addresses ${chalk.green( - addresses, - )} on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, + const anwser = readlineSync.question( + `Proceed with the nonces update of all addresses ${chalk.green(addresses)} on network ${chalk.green( + network.name, + )} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, ); if (anwser !== 'y') return; } @@ -60,15 +54,21 @@ const program = new Command(); program.name('Update-Nonces').description('Offline sign all the unsigned transactions in the file'); -program.addOption(new Option('-f, --filePath ', 'The filePath where the nonce for addresses will be stored').makeOptionMandatory(true)); +program.addOption( + new Option('-f, --filePath ', 'The filePath where the nonce for addresses will be stored').makeOptionMandatory(true), +); program.addOption( new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to sign the transactions').makeOptionMandatory(true), ); -program.addOption(new Option('-a --addresses ', 'The Array of addresses for which the nonces to update').env("ADDRESSES").makeOptionMandatory(true)); +program.addOption( + new Option('-a --addresses ', 'The Array of addresses for which the nonces to update') + .env('ADDRESSES') + .makeOptionMandatory(true), +); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { main(options); }); -program.parse(); \ No newline at end of file +program.parse(); diff --git a/evm/utils.js b/evm/utils.js index c0f76f15d..4d7a12ed0 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -21,7 +21,6 @@ const { const { CosmWasmClient } = require('@cosmjs/cosmwasm-stargate'); const CreateDeploy = require('@axelar-network/axelar-gmp-sdk-solidity/artifacts/contracts/deploy/CreateDeploy.sol/CreateDeploy.json'); const IDeployer = require('@axelar-network/axelar-gmp-sdk-solidity/interfaces/IDeployer.json'); -const { privateKeyExport } = require('secp256k1'); const { verifyContract } = require(`${__dirname}/../axelar-chains-config`); const getSaltFromKey = (key) => { @@ -188,7 +187,7 @@ const isNumber = (arg) => { const isValidNumber = (arg) => { return !isNaN(parseInt(arg)) && isFinite(arg); - }; +}; const isNumberArray = (arr) => { if (!Array.isArray(arr)) { From 767d75a359e53466b69bce34a8448e94cbe97a95 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Tue, 19 Sep 2023 14:12:57 +0530 Subject: [PATCH 30/45] moced nonce logic inside if --- evm/deploy-gateway-v5.0.x.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index 0f99a256b..b3ac11eb5 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -322,13 +322,6 @@ async function upgrade(config, options) { const { wallet, providerNonce } = await getWallet(privateKey, provider, ledgerPath); const signerAddress = await wallet.getAddress(); - let nonce = getLocalNonce(nonceFilePath, signerAddress); - - if (providerNonce !== undefined && providerNonce !== null) { - updateLocalNonce(nonceFilePath, signerAddress, providerNonce); - nonce = providerNonce; - } - await printWalletInfo(wallet); const contractConfig = chain.contracts[contractName]; @@ -381,6 +374,13 @@ async function upgrade(config, options) { let signersData, transactions; if (offline) { + let nonce = getLocalNonce(nonceFilePath, signerAddress); + + if (providerNonce !== undefined && providerNonce !== null) { + updateLocalNonce(nonceFilePath, signerAddress, providerNonce); + nonce = providerNonce; + } + if (!filePath) { throw new Error('FilePath is not provided in user info'); } From 043b0d315e68d679238788f7a600999d14192cf7 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Tue, 19 Sep 2023 18:59:13 +0530 Subject: [PATCH 31/45] store nonce in chain config, update static gasOptions to be store and make online getter calls when script not run offline --- evm/deploy-gateway-v5.0.x.js | 49 +++++++++++++++++++------------- evm/offline-sign-utils.js | 14 +++++---- evm/send-tokens.js | 40 +++++++++++++------------- evm/update-static-gas-options.js | 2 +- evm/utils.js | 1 + 5 files changed, 59 insertions(+), 47 deletions(-) diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index b3ac11eb5..04fa98602 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -312,7 +312,7 @@ async function deploy(config, options) { } async function upgrade(config, options) { - const { chainName, privateKey, yes, ledgerPath, offline, nonceFilePath, filePath, nonceOffset } = options; + const { chainName, privateKey, yes, ledgerPath, offline, env, filePath, nonceOffset } = options; const contractName = 'AxelarGateway'; const chain = config.chains[chainName] || { contracts: {}, name: chainName, id: chainName, rpc: options.rpc, tokenSymbol: 'ETH' }; @@ -322,7 +322,9 @@ async function upgrade(config, options) { const { wallet, providerNonce } = await getWallet(privateKey, provider, ledgerPath); const signerAddress = await wallet.getAddress(); - await printWalletInfo(wallet); + if (!offline) { + await printWalletInfo(wallet); + } const contractConfig = chain.contracts[contractName]; @@ -357,7 +359,11 @@ async function upgrade(config, options) { printInfo('Chain', chain.name); printInfo('Gateway Proxy', gateway.address); - printInfo('Current implementation', await gateway.implementation()); + + if (!offline) { + printInfo('Current implementation', await gateway.implementation()); + } + printInfo('Upgrading to implementation', contractConfig.implementation); printInfo('New Implementation codehash', implementationCodehash); printInfo('Setup params', setupParams); @@ -374,10 +380,11 @@ async function upgrade(config, options) { let signersData, transactions; if (offline) { - let nonce = getLocalNonce(nonceFilePath, signerAddress); + let nonce = getLocalNonce(chain, signerAddress); - if (providerNonce !== undefined && providerNonce !== null) { - updateLocalNonce(nonceFilePath, signerAddress, providerNonce); + if (providerNonce !== undefined && providerNonce !== null && providerNonce > nonce) { + config.chains[chain.name.toLowerCase()] = updateLocalNonce(chain, providerNonce, signerAddress); + saveConfig(config, env); nonce = providerNonce; } @@ -401,6 +408,7 @@ async function upgrade(config, options) { const tx = await gateway.populateTransaction.upgrade(contractConfig.implementation, implementationCodehash, setupParams); tx.nonce = nonce; tx.chainId = chain.chainId; + printInfo('Waiting for user to approve transaction through ledger wallet'); const { baseTx, signedTx } = await ledgerSign(wallet, chain, tx, staticGasOptions); // Storing the fields in the data that will be stored in file data.msg = `This transaction will perform upgrade of AxelarGateway contract having address ${gateway.address} with implementation ${contractConfig.implementation} on chain ${chain.name} with chainId ${chain.chainId}`; @@ -411,27 +419,28 @@ async function upgrade(config, options) { if (transactions) { signersData[signerAddress] = transactions; - await updateSignersData(filePath, signersData); + updateSignersData(filePath, signersData); } // Updating Nonce data for this Address - updateLocalNonce(nonceFilePath, signerAddress, nonce); + config.chains[chain.name.toLowerCase()] = updateLocalNonce(chain, ++nonce, signerAddress); + saveConfig(config, env); } else { const tx = await gateway.upgrade(contractConfig.implementation, implementationCodehash, setupParams, gasOptions); printInfo('Upgrade transaction', tx.hash); await tx.wait(chain.confirmations); - } - const newImplementation = await gateway.implementation(); - printInfo('New implementation', newImplementation); + const newImplementation = await gateway.implementation(); + printInfo('New implementation', newImplementation); - if (newImplementation !== contractConfig.implementation) { - printWarn('Implementation not upgraded yet!'); - return; - } + if (newImplementation !== contractConfig.implementation) { + printWarn('Implementation not upgraded yet!'); + return; + } - printInfo('Upgraded to', newImplementation); + printInfo('Upgraded to', newImplementation); + } } async function main(options) { @@ -477,11 +486,11 @@ async function programHandler() { program.addOption(new Option('-u, --upgrade', 'upgrade gateway').env('UPGRADE')); program.addOption(new Option('--offline', 'Run in offline mode')); program.addOption(new Option('-l, --ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); - program.addOption(new Option('--filePath ', 'The filePath where the signed tx will be stored').makeOptionMandatory(false)); program.addOption( - new Option('--nonceFilePath ', 'The File where nonce value to use for each address is stored').makeOptionMandatory( - false, - ), + new Option( + '--filePath ', + 'The filePath where the signed tx will be stored. It will create the file if not already exists. File name Should end with .json. Example => ./txs/unsignedTransactions.json', + ).makeOptionMandatory(false), ); program.addOption( new Option( diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 8bc414715..08e9a8cb5 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -215,15 +215,17 @@ const getWallet = async (privateKey, provider, ledgerPath) => { return { wallet, providerNonce }; }; -const getLocalNonce = (nonceFilePath, signerAddress) => { - const nonceData = getAllSignersData(nonceFilePath); - return nonceData[signerAddress] || 0; +const getLocalNonce = (chain, signerAddress) => { + const nonceData = chain ? chain.nonceData : undefined; + const nonce = nonceData ? nonceData[signerAddress] || 0 : 0; + return nonce; }; -const updateLocalNonce = (nonceFilePath, signerAddress, nonce) => { - const nonceData = getAllSignersData(nonceFilePath); +const updateLocalNonce = (chain, nonce, signerAddress) => { + const nonceData = chain.nonceData || {}; nonceData[signerAddress] = nonce; - updateSignersData(nonceFilePath, nonceData); + chain.nonceData = nonceData; + return chain; }; module.exports = { diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 884e1ac39..bcf1c1ad8 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -11,7 +11,7 @@ const { } = ethers; const readlineSync = require('readline-sync'); -const { printInfo, printWalletInfo, isValidNumber } = require('./utils'); +const { printInfo, printWalletInfo, isValidNumber, saveConfig } = require('./utils'); const { getAllSignersData, updateSignersData, @@ -22,31 +22,29 @@ const { updateLocalNonce, } = require('./offline-sign-utils.js'); -async function sendTokens(chain, options) { - const { privateKey, offline, ledgerPath, filePath, nonceFilePath, nonceOffset } = options; +async function sendTokens(config, chain, options) { + const { privateKey, offline, ledgerPath, filePath, nonceOffset, env } = options; let { amount, recipients } = options; - - if (!nonceFilePath) { - throw new Error('Nonce FilePath is not provided in user info'); - } - const provider = getDefaultProvider(chain.rpc); recipients = options.recipients.split(',').map((str) => str.trim()); amount = parseEther(amount); const { wallet, providerNonce } = await getWallet(privateKey, provider, ledgerPath); const signerAddress = await wallet.getAddress(); - let nonce = getLocalNonce(nonceFilePath, signerAddress); + let nonce = getLocalNonce(chain, signerAddress); - if (providerNonce !== undefined && providerNonce !== null) { - updateLocalNonce(nonceFilePath, signerAddress, providerNonce); + if (providerNonce !== undefined && providerNonce !== null && providerNonce > nonce) { + config.chains[chain.name.toLowerCase()] = updateLocalNonce(chain, providerNonce, signerAddress); + saveConfig(config, env); nonce = providerNonce; } - const balance = await printWalletInfo(wallet); + if (!offline) { + const balance = await printWalletInfo(wallet); - if (balance.lte(amount)) { - throw new Error(`Wallet has insufficient funds.`); + if (balance.lte(amount)) { + throw new Error(`Wallet has insufficient funds.`); + } } if (!options.yes) { @@ -90,6 +88,7 @@ async function sendTokens(chain, options) { const data = {}; tx.nonce = nonce; tx.chainId = chain.chainId; + printInfo('Waiting for user to approve transaction through ledger wallet'); const { baseTx, signedTx } = await ledgerSign(wallet, chain, tx, gasOptions); // Storing the fields in the data that will be stored in file data.msg = `This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; @@ -112,7 +111,8 @@ async function sendTokens(chain, options) { } // Updating Nonce data for this Address - updateLocalNonce(nonceFilePath, signerAddress, nonce); + config.chains[chain.name.toLowerCase()] = updateLocalNonce(chain, nonce, signerAddress); + saveConfig(config, env); } async function main(options) { @@ -129,7 +129,7 @@ async function main(options) { for (const chainName of chains) { const chain = config.chains[chainName.toLowerCase()]; - await sendTokens(chain, options); + await sendTokens(config, chain, options); } } @@ -151,11 +151,11 @@ if (require.main === module) { program.addOption(new Option('-a, --amount ', 'amount to transfer (in terms of ETH)').makeOptionMandatory(true)); program.addOption(new Option('--offline', 'Run in offline mode')); program.addOption(new Option('--ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); - program.addOption(new Option('--filePath ', 'The filePath where the signed tx will be stored').makeOptionMandatory(false)); program.addOption( - new Option('--nonceFilePath ', 'The File where nonce value to use for each address is stored').makeOptionMandatory( - false, - ), + new Option( + '--filePath ', + 'The filePath where the signed tx will be stored. It will create the file if not already exists. File name Should end with .json. Example => ./txs/unsignedTransactions.json', + ).makeOptionMandatory(false), ); program.addOption( new Option( diff --git a/evm/update-static-gas-options.js b/evm/update-static-gas-options.js index eece9cd19..58b6242c9 100644 --- a/evm/update-static-gas-options.js +++ b/evm/update-static-gas-options.js @@ -28,7 +28,7 @@ async function updateStaticGasOptions(chain, options, filePath) { try { const gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'gwei') * 5; const block = await provider.getBlock('latest'); - const gasLimit = block.gasLimit.toNumber() / 500; + const gasLimit = block.gasLimit.toNumber() / 100; const staticGasOptions = {}; staticGasOptions.gasLimit = gasLimit; staticGasOptions.gasPrice = gasPrice; diff --git a/evm/utils.js b/evm/utils.js index 4d7a12ed0..336fe6eec 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -634,4 +634,5 @@ module.exports = { wasEventEmitted, isContract, isValidPrivateKey, + verifyContract, }; From 3a42056ae2e65e080ad51c91f1b4a04f5a682ca2 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Tue, 19 Sep 2023 19:31:23 +0530 Subject: [PATCH 32/45] made gasLimit static to 130000 in confifg for offline signing --- evm/update-static-gas-options.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/evm/update-static-gas-options.js b/evm/update-static-gas-options.js index 58b6242c9..e8f021fcd 100644 --- a/evm/update-static-gas-options.js +++ b/evm/update-static-gas-options.js @@ -27,10 +27,8 @@ async function updateStaticGasOptions(chain, options, filePath) { try { const gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'gwei') * 5; - const block = await provider.getBlock('latest'); - const gasLimit = block.gasLimit.toNumber() / 100; const staticGasOptions = {}; - staticGasOptions.gasLimit = gasLimit; + staticGasOptions.gasLimit = 130000; staticGasOptions.gasPrice = gasPrice; chain.staticGasOptions = staticGasOptions; printInfo(`GasOptions updated succesfully and stored in config file ${filePath}`); From 54e6cc0150891513ae362d24950144fd88af82a4 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Wed, 20 Sep 2023 19:03:13 +0530 Subject: [PATCH 33/45] add script to check address has minimum required balance --- evm/check-wallet-balance.js | 86 +++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 evm/check-wallet-balance.js diff --git a/evm/check-wallet-balance.js b/evm/check-wallet-balance.js new file mode 100644 index 000000000..910944d06 --- /dev/null +++ b/evm/check-wallet-balance.js @@ -0,0 +1,86 @@ +'use strict'; + +const { Command, Option } = require('commander'); +const { ethers } = require('hardhat'); +const { getDefaultProvider, BigNumber } = ethers; + +const { printError, printObj, loadConfig } = require('./utils'); +const { getNonceFileData } = require('./offline-sign-utils'); + +async function checkBalance(provider, env, chain, staticGasOptions, addresses) { + try { + const minRequiredBalance = BigNumber.from(staticGasOptions.gasLimit * staticGasOptions.gasPrice); + const chainName = chain.name.toLowerCase(); + let nonceData = getNonceFileData(); + let chainNonceData = nonceData[env][chainName]; + if(addresses) { + addresses = JSON.parse(addresses); + for (const address of addresses) { + const balance = await provider.getBalance(addressToCheck); + if(balance < minRequiredBalance) { + printError("Minimum required Balance is", minRequiredBalance); + printError(`Wallet Balance for address ${address} is less than minimum required amount. Wallet Balance: `, balance); + } + } + } + else { + for (const [address, ] of Object.entries(chainNonceData)) { + const balance = await provider.getBalance(addressToCheck); + if(balance < minRequiredBalance) { + printError("Minimum required Balance is", minRequiredBalance); + printError(`Wallet Balance for address ${address} is less than minimum required amount. Wallet Balance: `, balance); + } + } + } + } catch (error) { + printError(`Checking wallet balance failed with error: ${error.message}`); + printObj(error); + } +} + +async function main(options) { + const { env, chainNames, rpcUrl, addresses } = options; + const config = loadConfig(env); + const chains = chainNames.split(',').map((str) => str.trim()); + + for (const chainName of chains) { + if (config.chains[chainName.toLowerCase()] === undefined) { + throw new Error(`Chain ${chainName} is not defined in the info file`); + } + } + + for (const chainName of chains) { + const chain = config.chains[chainName.toLowerCase()]; + const staticGasOptions = chain.staticGasOptions; + if(!staticGasOptions) { + printError("Could not find staticGasOptions for chain ", chain.name.toLowerCase()); + continue; + } + const provider = rpcUrl ? getDefaultProvider(rpcUrl) : getDefaultProvider(chain.rpc); + await checkBalance(provider, env, chain, staticGasOptions, addresses); + } +} + +const program = new Command(); + +program.name('check-wallet-balance').description('Before offline signing checks if each signer has minimum required wallet balance'); + +program.addOption( + new Option('-e, --env ', 'environment') + .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) + .default('testnet') + .makeOptionMandatory(true) + .env('ENV'), +); +program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); +program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions')); +program.addOption( + new Option('-a --addresses ', 'The Array of addresses for which the balance to check') + .env('ADDRESSES') +); + +program.action((options) => { + main(options); +}); + +program.parse(); From 1746ce795a6dc85585343589e41e55fea8ae5a0b Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Wed, 20 Sep 2023 19:04:53 +0530 Subject: [PATCH 34/45] refactor deployv5 and send-tokens script logic to store single signedTx in a file --- evm/deploy-gateway-v5.0.x.js | 43 ++++------------------------ evm/send-tokens.js | 54 +++++++++--------------------------- 2 files changed, 19 insertions(+), 78 deletions(-) diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index 04fa98602..22dd7f4f5 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -29,13 +29,10 @@ const { isValidNumber, } = require('./utils'); const { - getAllSignersData, - updateSignersData, + storeSignedTx, ledgerSign, - getTransactions, getWallet, getLocalNonce, - updateLocalNonce, } = require('./offline-sign-utils.js'); const AxelarGatewayProxy = require('@axelar-network/axelar-cgp-solidity/artifacts/contracts/AxelarGatewayProxy.sol/AxelarGatewayProxy.json'); @@ -312,14 +309,14 @@ async function deploy(config, options) { } async function upgrade(config, options) { - const { chainName, privateKey, yes, ledgerPath, offline, env, filePath, nonceOffset } = options; + const { chainName, privateKey, yes, offline, env, nonceOffset } = options; const contractName = 'AxelarGateway'; const chain = config.chains[chainName] || { contracts: {}, name: chainName, id: chainName, rpc: options.rpc, tokenSymbol: 'ETH' }; const rpc = options.rpc || chain.rpc; const provider = getDefaultProvider(rpc); - const { wallet, providerNonce } = await getWallet(privateKey, provider, ledgerPath); + const wallet = await getWallet(privateKey, provider, options); const signerAddress = await wallet.getAddress(); if (!offline) { @@ -377,21 +374,9 @@ async function upgrade(config, options) { if (anwser !== 'y') return; } - let signersData, transactions; - if (offline) { let nonce = getLocalNonce(chain, signerAddress); - if (providerNonce !== undefined && providerNonce !== null && providerNonce > nonce) { - config.chains[chain.name.toLowerCase()] = updateLocalNonce(chain, providerNonce, signerAddress); - saveConfig(config, env); - nonce = providerNonce; - } - - if (!filePath) { - throw new Error('FilePath is not provided in user info'); - } - if (nonceOffset) { if (!isValidNumber(nonceOffset)) { throw new Error('Provided nonce offset is not a valid number'); @@ -400,9 +385,8 @@ async function upgrade(config, options) { nonce += parseInt(nonceOffset); } - printInfo(`Storing signed Txs offline in file ${filePath}`); - signersData = await getAllSignersData(filePath); - transactions = await getTransactions(filePath, signerAddress); + const filePath = `./tx/signed-tx-${env}-${chain.name.toLowerCase()}-send-tokens-address-${signerAddress}-nonce-${nonce}.txt`; + printInfo(`Storing signed Tx offline in file ${filePath}`); const staticGasOptions = chain.staticGasOptions || {}; const data = {}; const tx = await gateway.populateTransaction.upgrade(contractConfig.implementation, implementationCodehash, setupParams); @@ -415,16 +399,7 @@ async function upgrade(config, options) { data.unsignedTx = baseTx; data.signedTx = signedTx; data.status = 'PENDING'; - transactions.push(data); - - if (transactions) { - signersData[signerAddress] = transactions; - updateSignersData(filePath, signersData); - } - // Updating Nonce data for this Address - - config.chains[chain.name.toLowerCase()] = updateLocalNonce(chain, ++nonce, signerAddress); - saveConfig(config, env); + storeSignedTx(filePath, data); } else { const tx = await gateway.upgrade(contractConfig.implementation, implementationCodehash, setupParams, gasOptions); printInfo('Upgrade transaction', tx.hash); @@ -486,12 +461,6 @@ async function programHandler() { program.addOption(new Option('-u, --upgrade', 'upgrade gateway').env('UPGRADE')); program.addOption(new Option('--offline', 'Run in offline mode')); program.addOption(new Option('-l, --ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); - program.addOption( - new Option( - '--filePath ', - 'The filePath where the signed tx will be stored. It will create the file if not already exists. File name Should end with .json. Example => ./txs/unsignedTransactions.json', - ).makeOptionMandatory(false), - ); program.addOption( new Option( '--nonceOffset ', diff --git a/evm/send-tokens.js b/evm/send-tokens.js index bcf1c1ad8..e7380b311 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -11,33 +11,24 @@ const { } = ethers; const readlineSync = require('readline-sync'); -const { printInfo, printWalletInfo, isValidNumber, saveConfig } = require('./utils'); +const { printInfo, printWalletInfo, isValidNumber, loadConfig } = require('./utils'); const { - getAllSignersData, - updateSignersData, - getTransactions, + storeSignedTx, getWallet, ledgerSign, getLocalNonce, - updateLocalNonce, } = require('./offline-sign-utils.js'); -async function sendTokens(config, chain, options) { - const { privateKey, offline, ledgerPath, filePath, nonceOffset, env } = options; +async function sendTokens(chain, options) { + const { privateKey, offline, nonceOffset, env } = options; let { amount, recipients } = options; const provider = getDefaultProvider(chain.rpc); recipients = options.recipients.split(',').map((str) => str.trim()); amount = parseEther(amount); - const { wallet, providerNonce } = await getWallet(privateKey, provider, ledgerPath); + const wallet = await getWallet(privateKey, provider, options); const signerAddress = await wallet.getAddress(); - let nonce = getLocalNonce(chain, signerAddress); - - if (providerNonce !== undefined && providerNonce !== null && providerNonce > nonce) { - config.chains[chain.name.toLowerCase()] = updateLocalNonce(chain, providerNonce, signerAddress); - saveConfig(config, env); - nonce = providerNonce; - } + let nonce; if (!offline) { const balance = await printWalletInfo(wallet); @@ -56,14 +47,10 @@ async function sendTokens(config, chain, options) { if (anwser !== 'y') return; } - let signersData, transactions; const gasOptions = chain.staticGasOptions || {}; if (offline) { - if (!filePath) { - throw new Error('FilePath is not provided in user info'); - } - + nonce = getLocalNonce(chain, signerAddress); if (nonceOffset) { if (!isValidNumber(nonceOffset)) { throw new Error('Provided nonce offset is not a valid number'); @@ -72,9 +59,6 @@ async function sendTokens(config, chain, options) { nonce += parseInt(nonceOffset); } - printInfo(`Storing signed Txs offline in file ${filePath}`); - signersData = await getAllSignersData(filePath); - transactions = await getTransactions(filePath, signerAddress); } for (const recipient of recipients) { @@ -85,6 +69,8 @@ async function sendTokens(config, chain, options) { }; if (offline) { + const filePath = `./tx/signed-tx-${env}-${chain.name.toLowerCase()}-send-tokens-address-${signerAddress}-nonce-${nonce}.txt`; + printInfo(`Storing signed Tx offline in file ${filePath}`); const data = {}; tx.nonce = nonce; tx.chainId = chain.chainId; @@ -95,7 +81,7 @@ async function sendTokens(config, chain, options) { data.unsignedTx = baseTx; data.signedTx = signedTx; data.status = 'PENDING'; - transactions.push(data); + storeSignedTx(filePath, data); } else { const response = await wallet.sendTransaction(tx); await response.wait(); @@ -104,19 +90,11 @@ async function sendTokens(config, chain, options) { ++nonce; } - - if (transactions) { - signersData[signerAddress] = transactions; - updateSignersData(filePath, signersData); - } - // Updating Nonce data for this Address - - config.chains[chain.name.toLowerCase()] = updateLocalNonce(chain, nonce, signerAddress); - saveConfig(config, env); } async function main(options) { - const config = require(`${__dirname}/../axelar-chains-config/info/${options.env === 'local' ? 'testnet' : options.env}.json`); + + const config = loadConfig(options.env); const chains = options.chainNames.split(',').map((str) => str.trim()); @@ -129,7 +107,7 @@ async function main(options) { for (const chainName of chains) { const chain = config.chains[chainName.toLowerCase()]; - await sendTokens(config, chain, options); + await sendTokens(chain, options); } } @@ -151,12 +129,6 @@ if (require.main === module) { program.addOption(new Option('-a, --amount ', 'amount to transfer (in terms of ETH)').makeOptionMandatory(true)); program.addOption(new Option('--offline', 'Run in offline mode')); program.addOption(new Option('--ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); - program.addOption( - new Option( - '--filePath ', - 'The filePath where the signed tx will be stored. It will create the file if not already exists. File name Should end with .json. Example => ./txs/unsignedTransactions.json', - ).makeOptionMandatory(false), - ); program.addOption( new Option( '--nonceOffset ', From c17d727b9f1f52192694e114f5df4fa707fdaec8 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Wed, 20 Sep 2023 19:05:28 +0530 Subject: [PATCH 35/45] refactor broadcast logic to only sign single tx from the file --- evm/broadcast-transactions.js | 76 ++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 94c53d39f..ef49fb19c 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -4,53 +4,57 @@ const chalk = require('chalk'); const { Command, Option } = require('commander'); const { ethers } = require('hardhat'); const { - providers: { JsonRpcProvider }, + providers: { getDefaultProvider }, } = ethers; const readlineSync = require('readline-sync'); -const { printError, printInfo, printObj } = require('./utils'); -const { sendTx, getAllSignersData, updateSignersData } = require('./offline-sign-utils'); +const { printError, printInfo, printObj, loadConfig } = require('./utils'); +const { sendTx, getSignedTx, storeSignedTx } = require('./offline-sign-utils'); async function processTransactions(filePath, provider) { try { - const signersData = await getAllSignersData(filePath); - - for (const [signerAddress, transactions] of Object.entries(signersData)) { - for (const transaction of transactions) { - if (transaction.status === 'PENDING') { - printInfo('Broadcasting transaction: '); - printObj(transaction.unsignedTx); - - // Send the signed transaction - const { success, response } = await sendTx(transaction.signedTx, provider); - - if (success) { - // Update the transaction status and store transaction hash - transaction.status = 'SUCCESS'; - transaction.transactionHash = response.transactionHash; - printInfo(`Transaction executed successfully ${response.transactionHash}`); - } else { - // Update the transaction status and store error message - transaction.status = 'FAILED'; - printError('Error broadcasting tx: ', transaction.signedTx); - } - } + if(!filePath) { + throw new Error("FilePath is not provided in user info"); + } + const transaction = await getSignedTx(filePath); + + if (transaction.status === 'PENDING') { + printInfo('Broadcasting transaction: '); + printObj(transaction.unsignedTx); + + // Send the signed transaction + const { success, response } = await sendTx(transaction.signedTx, provider); + + if (success) { + // Update the transaction status and store transaction hash + transaction.status = 'SUCCESS'; + transaction.transactionHash = response.transactionHash; + printInfo(`Transaction executed successfully ${response.transactionHash}`); + } else { + // Update the transaction status and store error message + transaction.status = 'FAILED'; + printError('Error broadcasting tx: ', transaction.signedTx); } - // Write back the updated JSON object to the file - - signersData[signerAddress] = transactions; } + storeSignedTx(filePath, transaction); - updateSignersData(filePath, signersData); } catch (error) { printError('Error processing transactions:', error.message); } } async function main(options) { - // TODO: Enable multiple scripts to use offlineSigning - const { filePath, rpcUrl } = options; - const provider = new JsonRpcProvider(rpcUrl); + const { filePath, rpcUrl, env, chainName } = options; + + const config = loadConfig(env); + + if (config.chains[chainName.toLowerCase()] === undefined) { + throw new Error(`Chain ${chainName} is not defined in the info file`); + } + + const chain = config.chains[chainName.toLowerCase()]; + const provider = rpcUrl ? getDefaultProvider(rpcUrl) : getDefaultProvider(chain.rpc); + const network = await provider.getNetwork(); if (!options.yes) { @@ -71,8 +75,14 @@ program.name('broadcast-transactions').description('Broadcast all the pending si program.addOption(new Option('--filePath ', 'The file where the signed tx are stored').makeOptionMandatory(true)); program.addOption( - new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to broadcast the transactions').makeOptionMandatory(true), + new Option('-e, --env ', 'environment') + .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) + .default('testnet') + .makeOptionMandatory(true) + .env('ENV'), ); +program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); +program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions')); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { From 350fcd4b1c5b880b5dd99eea12968f6189965f46 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Wed, 20 Sep 2023 19:06:11 +0530 Subject: [PATCH 36/45] update logic to only update gasLimit if not already set --- evm/update-static-gas-options.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/evm/update-static-gas-options.js b/evm/update-static-gas-options.js index e8f021fcd..0426e97dd 100644 --- a/evm/update-static-gas-options.js +++ b/evm/update-static-gas-options.js @@ -9,11 +9,11 @@ const { } = ethers; const readlineSync = require('readline-sync'); -const { printError, printInfo, printObj, saveConfig } = require('./utils'); +const { printError, printInfo, printObj, saveConfig, loadConfig } = require('./utils'); async function updateStaticGasOptions(chain, options, filePath) { const { rpcUrl, yes } = options; - const provider = getDefaultProvider(rpcUrl); + const provider = rpcUrl ? getDefaultProvider(rpcUrl) : getDefaultProvider(chain.rpc); const network = await provider.getNetwork(); if (!yes) { @@ -27,10 +27,8 @@ async function updateStaticGasOptions(chain, options, filePath) { try { const gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'gwei') * 5; - const staticGasOptions = {}; - staticGasOptions.gasLimit = 130000; + chain.staticGasOptions.gasLimit = chain.staticGasOptions.gasLimit || 3e6; staticGasOptions.gasPrice = gasPrice; - chain.staticGasOptions = staticGasOptions; printInfo(`GasOptions updated succesfully and stored in config file ${filePath}`); } catch (error) { printError(`GasOptions updation failed with error: ${error.message}`); @@ -42,10 +40,7 @@ async function updateStaticGasOptions(chain, options, filePath) { async function main(options) { const { env, chainNames } = options; - - const filePath = `${__dirname}/../axelar-chains-config/info/${env === 'local' ? 'testnet' : env}.json`; - const config = require(filePath); - + const config = loadConfig(env); const chains = chainNames.split(',').map((str) => str.trim()); for (const chainName of chains) { @@ -64,7 +59,7 @@ async function main(options) { const program = new Command(); -program.name('Update-GasOptions').description('Update gasOptions to be used in offline signing'); +program.name('update-static-gas-options').description('Update staticGasOptions to be used when offline signing'); program.addOption( new Option('-e, --env ', 'environment') @@ -74,7 +69,7 @@ program.addOption( .env('ENV'), ); program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions').makeOptionMandatory(true)); +program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions')); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { From 176a53ec8779af954a3ca627abce03c4b78b4513 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Wed, 20 Sep 2023 20:18:35 +0530 Subject: [PATCH 37/45] refactored code to remove unused functions/vars, use const where possible --- evm/broadcast-transactions.js | 9 +-- evm/check-wallet-balance.js | 38 ++++++------ evm/deploy-gateway-v5.0.x.js | 11 +--- evm/offline-sign-utils.js | 99 ++++++++++++++++++++++---------- evm/send-tokens.js | 14 ++--- evm/update-static-gas-options.js | 9 ++- evm/updateNonces.js | 97 +++++++++++++++++++------------ 7 files changed, 167 insertions(+), 110 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index ef49fb19c..218a1b75a 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -13,9 +13,10 @@ const { sendTx, getSignedTx, storeSignedTx } = require('./offline-sign-utils'); async function processTransactions(filePath, provider) { try { - if(!filePath) { - throw new Error("FilePath is not provided in user info"); + if (!filePath) { + throw new Error('FilePath is not provided in user info'); } + const transaction = await getSignedTx(filePath); if (transaction.status === 'PENDING') { @@ -36,8 +37,8 @@ async function processTransactions(filePath, provider) { printError('Error broadcasting tx: ', transaction.signedTx); } } - storeSignedTx(filePath, transaction); + storeSignedTx(filePath, transaction); } catch (error) { printError('Error processing transactions:', error.message); } @@ -81,7 +82,7 @@ program.addOption( .makeOptionMandatory(true) .env('ENV'), ); -program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); +program.addOption(new Option('-n, --chainName ', 'chain names').makeOptionMandatory(true)); program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions')); program.addOption(new Option('-y, --yes', 'skip prompts')); diff --git a/evm/check-wallet-balance.js b/evm/check-wallet-balance.js index 910944d06..e45d947ec 100644 --- a/evm/check-wallet-balance.js +++ b/evm/check-wallet-balance.js @@ -11,23 +11,26 @@ async function checkBalance(provider, env, chain, staticGasOptions, addresses) { try { const minRequiredBalance = BigNumber.from(staticGasOptions.gasLimit * staticGasOptions.gasPrice); const chainName = chain.name.toLowerCase(); - let nonceData = getNonceFileData(); - let chainNonceData = nonceData[env][chainName]; - if(addresses) { + const nonceData = getNonceFileData(); + const chainNonceData = nonceData[env][chainName]; + + if (addresses) { addresses = JSON.parse(addresses); + for (const address of addresses) { - const balance = await provider.getBalance(addressToCheck); - if(balance < minRequiredBalance) { - printError("Minimum required Balance is", minRequiredBalance); + const balance = await provider.getBalance(address); + + if (balance < minRequiredBalance) { + printError('Minimum required Balance is', minRequiredBalance); printError(`Wallet Balance for address ${address} is less than minimum required amount. Wallet Balance: `, balance); } } - } - else { - for (const [address, ] of Object.entries(chainNonceData)) { - const balance = await provider.getBalance(addressToCheck); - if(balance < minRequiredBalance) { - printError("Minimum required Balance is", minRequiredBalance); + } else { + for (const [address] of Object.entries(chainNonceData)) { + const balance = await provider.getBalance(address); + + if (balance < minRequiredBalance) { + printError('Minimum required Balance is', minRequiredBalance); printError(`Wallet Balance for address ${address} is less than minimum required amount. Wallet Balance: `, balance); } } @@ -52,10 +55,12 @@ async function main(options) { for (const chainName of chains) { const chain = config.chains[chainName.toLowerCase()]; const staticGasOptions = chain.staticGasOptions; - if(!staticGasOptions) { - printError("Could not find staticGasOptions for chain ", chain.name.toLowerCase()); + + if (!staticGasOptions) { + printError('Could not find staticGasOptions for chain ', chain.name.toLowerCase()); continue; } + const provider = rpcUrl ? getDefaultProvider(rpcUrl) : getDefaultProvider(chain.rpc); await checkBalance(provider, env, chain, staticGasOptions, addresses); } @@ -74,10 +79,7 @@ program.addOption( ); program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions')); -program.addOption( - new Option('-a --addresses ', 'The Array of addresses for which the balance to check') - .env('ADDRESSES') -); +program.addOption(new Option('-a --addresses ', 'The Array of addresses for which the balance to check').env('ADDRESSES')); program.action((options) => { main(options); diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index 22dd7f4f5..2f4819ce8 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -28,12 +28,7 @@ const { printWarn, isValidNumber, } = require('./utils'); -const { - storeSignedTx, - ledgerSign, - getWallet, - getLocalNonce, -} = require('./offline-sign-utils.js'); +const { storeSignedTx, ledgerSign, getWallet, getLocalNonce } = require('./offline-sign-utils.js'); const AxelarGatewayProxy = require('@axelar-network/axelar-cgp-solidity/artifacts/contracts/AxelarGatewayProxy.sol/AxelarGatewayProxy.json'); const AxelarGateway = require('@axelar-network/axelar-cgp-solidity/artifacts/contracts/AxelarGateway.sol/AxelarGateway.json'); @@ -375,7 +370,7 @@ async function upgrade(config, options) { } if (offline) { - let nonce = getLocalNonce(chain, signerAddress); + let nonce = getLocalNonce(env, chain.name.toLowerCase(), signerAddress); if (nonceOffset) { if (!isValidNumber(nonceOffset)) { @@ -385,7 +380,7 @@ async function upgrade(config, options) { nonce += parseInt(nonceOffset); } - const filePath = `./tx/signed-tx-${env}-${chain.name.toLowerCase()}-send-tokens-address-${signerAddress}-nonce-${nonce}.txt`; + const filePath = `./tx/signed-tx-${env}-${chain.name.toLowerCase()}-send-tokens-address-${signerAddress}-nonce-${nonce}.json`; printInfo(`Storing signed Tx offline in file ${filePath}`); const staticGasOptions = chain.staticGasOptions || {}; const data = {}; diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 08e9a8cb5..304423d18 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -85,10 +85,11 @@ async function sendTx(tx, provider) { } } -function updateSignersData(filePath, signersData) { - fs.writeFileSync(filePath, JSON.stringify(signersData, null, 2), (err) => { +function storeSignedTx(filePath, signedTx) { + createFileIfNotExists(filePath); + fs.writeFileSync(filePath, JSON.stringify(signedTx, null, 2), (err) => { if (err) { - printError(`Could not update signersData in file ${filePath}`); + printError(`Could not store signedTx in file ${filePath}`); printObj(err); return; } @@ -109,8 +110,8 @@ async function getNonceFromProvider(provider, address) { return nonce; } -function getAllSignersData(filePath) { - const signersData = {}; +function getSignedTx(filePath) { + const signedTx = {}; try { // Read the content of the file @@ -120,13 +121,13 @@ function getAllSignersData(filePath) { const jsonData = JSON.parse(data); if (!isValidJSON(jsonData)) { - return signersData; + return signedTx; } return jsonData; } - return signersData; + return signedTx; } catch (error) { printError(`Failed to get all signers data from the file ${filePath}`); printObj(error); @@ -135,22 +136,10 @@ function getAllSignersData(filePath) { function getFileData(filePath) { try { - // Extract the directory path - const directoryPath = path.dirname(filePath); - // Check if the directory and file exists, create it if it doesn't - - if (!fs.existsSync(directoryPath)) { - fs.mkdirSync(directoryPath); - } - - if (!fs.existsSync(filePath)) { - // File does not exist, create it - fs.writeFileSync(filePath, JSON.stringify({})); - return undefined; - } + createFileIfNotExists(filePath); // Read the content of the file - const data = fs.readFileSync(filePath); + const data = fs.readFileSync(filePath, 'utf-8'); return data; } catch (error) { printError(`Failed to get data from the file ${filePath}`); @@ -197,11 +186,11 @@ function isValidJSON(obj) { return true; } -const getWallet = async (privateKey, provider, ledgerPath) => { +const getWallet = async (privateKey, provider, options) => { let wallet; if (privateKey === 'ledger') { - wallet = getLedgerWallet(provider, ledgerPath || undefined); + wallet = getLedgerWallet(provider, options?.ledgerPath); } else { if (!isValidPrivateKey(privateKey)) { throw new Error('Private key is missing/ not provided correctly'); @@ -210,14 +199,59 @@ const getWallet = async (privateKey, provider, ledgerPath) => { wallet = new Wallet(privateKey, provider); } - const signerAddress = await wallet.getAddress(); - const providerNonce = await getNonceFromProvider(provider, signerAddress); - return { wallet, providerNonce }; + return wallet; +}; + +const getNonceFileData = () => { + const filePath = `${__dirname}/../axelar-chains-config/info/nonces.json`; + const emptyData = {}; + const data = getFileData(filePath); + + if (data) { + const jsonData = JSON.parse(data); + + if (!isValidJSON(jsonData)) { + return emptyData; + } + + return jsonData; + } + + return emptyData; +}; + +function createFileIfNotExists(filePath) { + const directoryPath = path.dirname(filePath); + + // Check if the directory and file exists, create it if it doesn't + if (!fs.existsSync(directoryPath)) { + fs.mkdirSync(directoryPath, { recursive: true }); // Added { recursive: true } to create parent directories if needed + } + + if (!fs.existsSync(filePath)) { + // File does not exist, create it + fs.writeFileSync(filePath, JSON.stringify({}, null, 2)); + } +} + +const updateNonceFileData = (nonceData) => { + const filePath = `${__dirname}/../axelar-chains-config/info/nonces.json`; + createFileIfNotExists(filePath); + // Write nonceData to the file + + try { + fs.writeFileSync(filePath, JSON.stringify(nonceData, null, 2)); + printInfo(`Nonce updated successfully and stored in file ${filePath}`); + } catch (err) { + printError(`Could not update Nonce in file ${filePath}`); + printObj(err); + } }; -const getLocalNonce = (chain, signerAddress) => { - const nonceData = chain ? chain.nonceData : undefined; - const nonce = nonceData ? nonceData[signerAddress] || 0 : 0; +const getLocalNonce = (env, chainName, signerAddress) => { + const nonceData = getNonceFileData(); + const chainNonceData = chainName ? nonceData[env][chainName] : undefined; + const nonce = chainNonceData ? chainNonceData[signerAddress] || 0 : 0; return nonce; }; @@ -230,11 +264,14 @@ const updateLocalNonce = (chain, nonce, signerAddress) => { module.exports = { sendTx, - getAllSignersData, getTransactions, - updateSignersData, + storeSignedTx, + getSignedTx, getWallet, + getNonceFileData, + updateNonceFileData, getLocalNonce, updateLocalNonce, ledgerSign, + getNonceFromProvider, }; diff --git a/evm/send-tokens.js b/evm/send-tokens.js index e7380b311..1fe5ef4fb 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -12,12 +12,7 @@ const { const readlineSync = require('readline-sync'); const { printInfo, printWalletInfo, isValidNumber, loadConfig } = require('./utils'); -const { - storeSignedTx, - getWallet, - ledgerSign, - getLocalNonce, -} = require('./offline-sign-utils.js'); +const { storeSignedTx, getWallet, ledgerSign, getLocalNonce } = require('./offline-sign-utils.js'); async function sendTokens(chain, options) { const { privateKey, offline, nonceOffset, env } = options; @@ -50,7 +45,8 @@ async function sendTokens(chain, options) { const gasOptions = chain.staticGasOptions || {}; if (offline) { - nonce = getLocalNonce(chain, signerAddress); + nonce = getLocalNonce(env, chain.name.toLowerCase(), signerAddress); + if (nonceOffset) { if (!isValidNumber(nonceOffset)) { throw new Error('Provided nonce offset is not a valid number'); @@ -58,7 +54,6 @@ async function sendTokens(chain, options) { nonce += parseInt(nonceOffset); } - } for (const recipient of recipients) { @@ -69,7 +64,7 @@ async function sendTokens(chain, options) { }; if (offline) { - const filePath = `./tx/signed-tx-${env}-${chain.name.toLowerCase()}-send-tokens-address-${signerAddress}-nonce-${nonce}.txt`; + const filePath = `./tx/signed-tx-${env}-${chain.name.toLowerCase()}-send-tokens-address-${signerAddress}-nonce-${nonce}.json`; printInfo(`Storing signed Tx offline in file ${filePath}`); const data = {}; tx.nonce = nonce; @@ -93,7 +88,6 @@ async function sendTokens(chain, options) { } async function main(options) { - const config = loadConfig(options.env); const chains = options.chainNames.split(',').map((str) => str.trim()); diff --git a/evm/update-static-gas-options.js b/evm/update-static-gas-options.js index 0426e97dd..a700c8cb9 100644 --- a/evm/update-static-gas-options.js +++ b/evm/update-static-gas-options.js @@ -27,8 +27,12 @@ async function updateStaticGasOptions(chain, options, filePath) { try { const gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'gwei') * 5; - chain.staticGasOptions.gasLimit = chain.staticGasOptions.gasLimit || 3e6; - staticGasOptions.gasPrice = gasPrice; + + if (!(chain.staticGasOptions && chain.staticGasOptions.gasLimit !== undefined)) { + chain.staticGasOptions = { gasLimit: 3e6 }; + } + + chain.staticGasOptions.gasPrice = gasPrice; printInfo(`GasOptions updated succesfully and stored in config file ${filePath}`); } catch (error) { printError(`GasOptions updation failed with error: ${error.message}`); @@ -40,6 +44,7 @@ async function updateStaticGasOptions(chain, options, filePath) { async function main(options) { const { env, chainNames } = options; + const filePath = `${__dirname}/../axelar-chains-config/info/${env}.json`; const config = loadConfig(env); const chains = chainNames.split(',').map((str) => str.trim()); diff --git a/evm/updateNonces.js b/evm/updateNonces.js index d558cfbd0..afd7456f0 100644 --- a/evm/updateNonces.js +++ b/evm/updateNonces.js @@ -2,31 +2,44 @@ const chalk = require('chalk'); const { Command, Option } = require('commander'); -const fs = require('fs'); const { ethers } = require('hardhat'); const { getDefaultProvider } = ethers; const readlineSync = require('readline-sync'); -const { printError, printInfo, printObj } = require('./utils'); -const { getNonceFromProvider, getAllSignersData } = require('./offline-sign-utils'); +const { printError, printObj, loadConfig } = require('./utils'); +const { getNonceFromProvider, getNonceFileData, updateNonceFileData } = require('./offline-sign-utils'); -function updateNonce(provider, addresses, filePath) { +async function updateNonce(provider, env, chain, addresses) { try { - const nonceData = getAllSignersData(filePath); - addresses = JSON.parse(addresses); + const chainName = chain.name.toLowerCase(); + const nonceData = getNonceFileData(); - for (const address of addresses) { - const nonce = getNonceFromProvider(provider, address); - nonceData[address] = nonce; + if (!nonceData[env]) { + nonceData[env] = {}; } - fs.writeFileSync(filePath, JSON.stringify(nonceData, null, 2), (err) => { - if (err) { - printError(`Could not update Nonce in file ${filePath}`); - printObj(err); + if (!nonceData[env][chainName]) { + nonceData[env][chainName] = {}; + } + + const chainNonceData = nonceData[env][chainName]; + + if (addresses) { + addresses = JSON.parse(addresses); + + for (const address of addresses) { + const nonce = await getNonceFromProvider(provider, address); + chainNonceData[address] = nonce; + } + } else { + for (const [signerAddress] of Object.entries(chainNonceData)) { + const nonce = await getNonceFromProvider(provider, signerAddress); + chainNonceData[signerAddress] = nonce; } - }); - printInfo(`Nonce updated succesfully and stored in file ${filePath}`); + } + + nonceData[env][chainName] = chainNonceData; + updateNonceFileData(nonceData); } catch (error) { printError(`Nonce updation failed with error: ${error.message}`); printObj(error); @@ -34,20 +47,32 @@ function updateNonce(provider, addresses, filePath) { } async function main(options) { - const { filePath, rpcUrl, addresses } = options; - const provider = getDefaultProvider(rpcUrl); - const network = await provider.getNetwork(); - - if (!options.yes) { - const anwser = readlineSync.question( - `Proceed with the nonces update of all addresses ${chalk.green(addresses)} on network ${chalk.green( - network.name, - )} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, - ); - if (anwser !== 'y') return; + const { env, chainNames, rpcUrl, addresses, yes } = options; + const config = loadConfig(env); + const chains = chainNames.split(',').map((str) => str.trim()); + + for (const chainName of chains) { + if (config.chains[chainName.toLowerCase()] === undefined) { + throw new Error(`Chain ${chainName} is not defined in the info file`); + } } - await updateNonce(provider, addresses, filePath); + for (const chainName of chains) { + const chain = config.chains[chainName.toLowerCase()]; + const provider = rpcUrl ? getDefaultProvider(rpcUrl) : getDefaultProvider(chain.rpc); + const network = await provider.getNetwork(); + + if (!yes) { + const anwser = readlineSync.question( + `Proceed with the nonces update on network ${chalk.green(network.name)} with chainId ${chalk.green( + network.chainId, + )} ${chalk.green('(y/n)')} `, + ); + if (anwser !== 'y') return; + } + + await updateNonce(provider, env, chain, addresses); + } } const program = new Command(); @@ -55,20 +80,18 @@ const program = new Command(); program.name('Update-Nonces').description('Offline sign all the unsigned transactions in the file'); program.addOption( - new Option('-f, --filePath ', 'The filePath where the nonce for addresses will be stored').makeOptionMandatory(true), -); -program.addOption( - new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to sign the transactions').makeOptionMandatory(true), -); -program.addOption( - new Option('-a --addresses ', 'The Array of addresses for which the nonces to update') - .env('ADDRESSES') - .makeOptionMandatory(true), + new Option('-e, --env ', 'environment') + .choices(['local', 'devnet', 'stagenet', 'testnet', 'mainnet']) + .default('testnet') + .makeOptionMandatory(true) + .env('ENV'), ); +program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); +program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions')); +program.addOption(new Option('-a --addresses ', 'The Array of addresses for which the nonces to update').env('ADDRESSES')); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { main(options); }); - program.parse(); From bd34f1a5832fe08489e4086a1466e6e51c49d229 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Wed, 20 Sep 2023 20:43:18 +0530 Subject: [PATCH 38/45] resolve overflow error using BigNumber --- evm/check-wallet-balance.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/evm/check-wallet-balance.js b/evm/check-wallet-balance.js index e45d947ec..6a94b18a6 100644 --- a/evm/check-wallet-balance.js +++ b/evm/check-wallet-balance.js @@ -9,7 +9,9 @@ const { getNonceFileData } = require('./offline-sign-utils'); async function checkBalance(provider, env, chain, staticGasOptions, addresses) { try { - const minRequiredBalance = BigNumber.from(staticGasOptions.gasLimit * staticGasOptions.gasPrice); + const gasLimit = BigNumber.from(staticGasOptions.gasLimit); + const gasPrice = BigNumber.from(staticGasOptions.gasPrice); + const minRequiredBalance = gasLimit * gasPrice; const chainName = chain.name.toLowerCase(); const nonceData = getNonceFileData(); const chainNonceData = nonceData[env][chainName]; @@ -22,7 +24,7 @@ async function checkBalance(provider, env, chain, staticGasOptions, addresses) { if (balance < minRequiredBalance) { printError('Minimum required Balance is', minRequiredBalance); - printError(`Wallet Balance for address ${address} is less than minimum required amount. Wallet Balance: `, balance); + printError(`Wallet Balance for address ${address} is`, balance); } } } else { From 4b6f32068bdad262e4ec136fb336a10a92b4be24 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Thu, 21 Sep 2023 02:31:01 -0400 Subject: [PATCH 39/45] rename update nonces script --- evm/{updateNonces.js => update-nonces.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename evm/{updateNonces.js => update-nonces.js} (100%) diff --git a/evm/updateNonces.js b/evm/update-nonces.js similarity index 100% rename from evm/updateNonces.js rename to evm/update-nonces.js From ca1aac8eae5baeb0122aef9ac0cf79c5e87f8e2e Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Thu, 21 Sep 2023 03:59:58 -0400 Subject: [PATCH 40/45] minor refactor of broadcast script --- evm/broadcast-transactions.js | 42 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 218a1b75a..ccf99e4c8 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -11,7 +11,20 @@ const readlineSync = require('readline-sync'); const { printError, printInfo, printObj, loadConfig } = require('./utils'); const { sendTx, getSignedTx, storeSignedTx } = require('./offline-sign-utils'); -async function processTransactions(filePath, provider) { +async function processCommand(config, chain, options) { + const { filePath, rpc } = options; + + const provider = getDefaultProvider(rpc || chain.rpc); + + if (!options.yes) { + const anwser = readlineSync.question( + `Proceed with the broadcasting of all pending signed transactions for file ${chalk.green( + options.filePath, + )} on chain ${chalk.green(chain.name)} ${chalk.green('(y/n)')} `, + ); + if (anwser !== 'y') return; + } + try { if (!filePath) { throw new Error('FilePath is not provided in user info'); @@ -45,29 +58,14 @@ async function processTransactions(filePath, provider) { } async function main(options) { - const { filePath, rpcUrl, env, chainName } = options; - - const config = loadConfig(env); + const config = loadConfig(options.env); + const chain = config.chains[options.chainName.toLowerCase()]; - if (config.chains[chainName.toLowerCase()] === undefined) { + if (chain === undefined) { throw new Error(`Chain ${chainName} is not defined in the info file`); } - const chain = config.chains[chainName.toLowerCase()]; - const provider = rpcUrl ? getDefaultProvider(rpcUrl) : getDefaultProvider(chain.rpc); - - const network = await provider.getNetwork(); - - if (!options.yes) { - const anwser = readlineSync.question( - `Proceed with the broadcasting of all pending signed transactions for file ${chalk.green( - options.filePath, - )} on network ${chalk.green(network.name)} with chainId ${chalk.green(network.chainId)} ${chalk.green('(y/n)')} `, - ); - if (anwser !== 'y') return; - } - - await processTransactions(filePath, provider); + await processCommand(config, chain, options); } const program = new Command(); @@ -82,8 +80,8 @@ program.addOption( .makeOptionMandatory(true) .env('ENV'), ); -program.addOption(new Option('-n, --chainName ', 'chain names').makeOptionMandatory(true)); -program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions')); +program.addOption(new Option('-n, --chainName ', 'chain names').makeOptionMandatory(true)); +program.addOption(new Option('-r, --rpc ', 'The chain rpc')); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { From 16438eaeac0dae229ed335d7183148e652bb2818 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 26 Sep 2023 06:47:50 -0400 Subject: [PATCH 41/45] address comments --- evm/broadcast-transactions.js | 60 ++++---- evm/check-wallet-balance.js | 82 ++++------- evm/deploy-gateway-v5.0.x.js | 60 +++----- evm/offline-sign-utils.js | 238 +++++++++++++++++++------------ evm/remove-info.js | 21 +-- evm/send-tokens.js | 96 +++++-------- evm/update-nonces.js | 103 ++++++------- evm/update-static-gas-options.js | 53 ++----- evm/utils.js | 72 ++++++++-- 9 files changed, 374 insertions(+), 411 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index ccf99e4c8..50b45001e 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -8,8 +8,8 @@ const { } = ethers; const readlineSync = require('readline-sync'); -const { printError, printInfo, printObj, loadConfig } = require('./utils'); -const { sendTx, getSignedTx, storeSignedTx } = require('./offline-sign-utils'); +const { printError, printInfo, mainProcessor } = require('./utils'); +const { sendTransaction, getSignedTx, storeSignedTx } = require('./offline-sign-utils'); async function processCommand(config, chain, options) { const { filePath, rpc } = options; @@ -25,47 +25,37 @@ async function processCommand(config, chain, options) { if (anwser !== 'y') return; } - try { - if (!filePath) { - throw new Error('FilePath is not provided in user info'); - } + if (!filePath) { + throw new Error('FilePath is not provided in user info'); + } + + const transaction = await getSignedTx(filePath); + + if (transaction.status === 'PENDING') { + printInfo('Broadcasting transaction', JSON.stringify(transaction.unsignedTx, null, 2)); - const transaction = await getSignedTx(filePath); - - if (transaction.status === 'PENDING') { - printInfo('Broadcasting transaction: '); - printObj(transaction.unsignedTx); - - // Send the signed transaction - const { success, response } = await sendTx(transaction.signedTx, provider); - - if (success) { - // Update the transaction status and store transaction hash - transaction.status = 'SUCCESS'; - transaction.transactionHash = response.transactionHash; - printInfo(`Transaction executed successfully ${response.transactionHash}`); - } else { - // Update the transaction status and store error message - transaction.status = 'FAILED'; - printError('Error broadcasting tx: ', transaction.signedTx); - } + // Send the signed transaction + const { success, response } = await sendTransaction(transaction.signedTx, provider); + + if (success) { + // Update the transaction status and store transaction hash + transaction.status = 'SUCCESS'; + transaction.transactionHash = response.hash; + printInfo(`Transaction executed successfully ${response.hash}`); + } else { + // Update the transaction status and store error message + transaction.status = 'FAILED'; + printError('Error broadcasting tx: ', transaction.signedTx); } storeSignedTx(filePath, transaction); - } catch (error) { - printError('Error processing transactions:', error.message); + } else { + printInfo('Skipping broadcast, transaction status is', transaction.status); } } async function main(options) { - const config = loadConfig(options.env); - const chain = config.chains[options.chainName.toLowerCase()]; - - if (chain === undefined) { - throw new Error(`Chain ${chainName} is not defined in the info file`); - } - - await processCommand(config, chain, options); + await mainProcessor(options, processCommand); } const program = new Command(); diff --git a/evm/check-wallet-balance.js b/evm/check-wallet-balance.js index 6a94b18a6..f246a45ea 100644 --- a/evm/check-wallet-balance.js +++ b/evm/check-wallet-balance.js @@ -4,70 +4,48 @@ const { Command, Option } = require('commander'); const { ethers } = require('hardhat'); const { getDefaultProvider, BigNumber } = ethers; -const { printError, printObj, loadConfig } = require('./utils'); +const { printError, mainProcessor } = require('./utils'); const { getNonceFileData } = require('./offline-sign-utils'); -async function checkBalance(provider, env, chain, staticGasOptions, addresses) { - try { - const gasLimit = BigNumber.from(staticGasOptions.gasLimit); - const gasPrice = BigNumber.from(staticGasOptions.gasPrice); - const minRequiredBalance = gasLimit * gasPrice; - const chainName = chain.name.toLowerCase(); - const nonceData = getNonceFileData(); - const chainNonceData = nonceData[env][chainName]; +async function processCommand(_, chain, options) { + const { rpc } = options; + let { addresses } = options; - if (addresses) { - addresses = JSON.parse(addresses); + const chainName = chain.name.toLowerCase(); + const provider = getDefaultProvider(rpc || chain.rpc); + const staticGasOptions = chain.staticGasOptions; - for (const address of addresses) { - const balance = await provider.getBalance(address); - - if (balance < minRequiredBalance) { - printError('Minimum required Balance is', minRequiredBalance); - printError(`Wallet Balance for address ${address} is`, balance); - } - } - } else { - for (const [address] of Object.entries(chainNonceData)) { - const balance = await provider.getBalance(address); - - if (balance < minRequiredBalance) { - printError('Minimum required Balance is', minRequiredBalance); - printError(`Wallet Balance for address ${address} is less than minimum required amount. Wallet Balance: `, balance); - } - } - } - } catch (error) { - printError(`Checking wallet balance failed with error: ${error.message}`); - printObj(error); + if (!staticGasOptions) { + printError('Could not find staticGasOptions for chain', chain.name); + return; } -} -async function main(options) { - const { env, chainNames, rpcUrl, addresses } = options; - const config = loadConfig(env); - const chains = chainNames.split(',').map((str) => str.trim()); + const gasLimit = BigNumber.from(staticGasOptions.gasLimit); + const gasPrice = BigNumber.from(staticGasOptions.gasPrice); + const minRequiredBalance = gasLimit * gasPrice; + const nonceData = getNonceFileData(); + const nonces = nonceData[options.env][chainName]; - for (const chainName of chains) { - if (config.chains[chainName.toLowerCase()] === undefined) { - throw new Error(`Chain ${chainName} is not defined in the info file`); - } + if (addresses) { + addresses = JSON.parse(addresses); + } else { + addresses = Object.entries(nonces); } - for (const chainName of chains) { - const chain = config.chains[chainName.toLowerCase()]; - const staticGasOptions = chain.staticGasOptions; + for (const address of addresses) { + const balance = await provider.getBalance(address); - if (!staticGasOptions) { - printError('Could not find staticGasOptions for chain ', chain.name.toLowerCase()); - continue; + if (balance < minRequiredBalance) { + printError('Minimum required Balance is', `${minRequiredBalance / 1e18}`); + printError(`Wallet Balance for address ${address} is`, `${balance / 1e18}`); } - - const provider = rpcUrl ? getDefaultProvider(rpcUrl) : getDefaultProvider(chain.rpc); - await checkBalance(provider, env, chain, staticGasOptions, addresses); } } +async function main(options) { + await mainProcessor(options, processCommand); +} + const program = new Command(); program.name('check-wallet-balance').description('Before offline signing checks if each signer has minimum required wallet balance'); @@ -80,8 +58,8 @@ program.addOption( .env('ENV'), ); program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions')); -program.addOption(new Option('-a --addresses ', 'The Array of addresses for which the balance to check').env('ADDRESSES')); +program.addOption(new Option('-r, --rpc ', 'The rpc url for creating a provider to fetch gasOptions')); +program.addOption(new Option('--addresses ', 'The Array of addresses for which the balance to check').env('ADDRESSES')); program.action((options) => { main(options); diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index 2f4819ce8..77b37009d 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -16,7 +16,6 @@ const readlineSync = require('readline-sync'); const { saveConfig, - loadConfig, getBytecodeHash, verifyContract, printInfo, @@ -26,9 +25,9 @@ const { printError, printWalletInfo, printWarn, - isValidNumber, + mainProcessor, } = require('./utils'); -const { storeSignedTx, ledgerSign, getWallet, getLocalNonce } = require('./offline-sign-utils.js'); +const { storeSignedTx, signTransaction, getWallet } = require('./offline-sign-utils.js'); const AxelarGatewayProxy = require('@axelar-network/axelar-cgp-solidity/artifacts/contracts/AxelarGatewayProxy.sol/AxelarGatewayProxy.json'); const AxelarGateway = require('@axelar-network/axelar-cgp-solidity/artifacts/contracts/AxelarGateway.sol/AxelarGateway.json'); @@ -304,7 +303,7 @@ async function deploy(config, options) { } async function upgrade(config, options) { - const { chainName, privateKey, yes, offline, env, nonceOffset } = options; + const { chainName, privateKey, yes, offline, env } = options; const contractName = 'AxelarGateway'; const chain = config.chains[chainName] || { contracts: {}, name: chainName, id: chainName, rpc: options.rpc, tokenSymbol: 'ETH' }; @@ -312,11 +311,8 @@ async function upgrade(config, options) { const provider = getDefaultProvider(rpc); const wallet = await getWallet(privateKey, provider, options); - const signerAddress = await wallet.getAddress(); - if (!offline) { - await printWalletInfo(wallet); - } + const { address } = await printWalletInfo(wallet, options); const contractConfig = chain.contracts[contractName]; @@ -369,38 +365,26 @@ async function upgrade(config, options) { if (anwser !== 'y') return; } - if (offline) { - let nonce = getLocalNonce(env, chain.name.toLowerCase(), signerAddress); - - if (nonceOffset) { - if (!isValidNumber(nonceOffset)) { - throw new Error('Provided nonce offset is not a valid number'); - } + const tx = await gateway.populateTransaction.upgrade(contractConfig.implementation, implementationCodehash, setupParams); - nonce += parseInt(nonceOffset); - } + const { baseTx, signedTx } = await signTransaction(wallet, chain, tx, options); - const filePath = `./tx/signed-tx-${env}-${chain.name.toLowerCase()}-send-tokens-address-${signerAddress}-nonce-${nonce}.json`; + if (offline) { + const filePath = `./tx/signed-tx-${env}-${chainName}-gateway-upgrade-address-${address}-nonce-${baseTx.nonce}.json`; printInfo(`Storing signed Tx offline in file ${filePath}`); - const staticGasOptions = chain.staticGasOptions || {}; - const data = {}; - const tx = await gateway.populateTransaction.upgrade(contractConfig.implementation, implementationCodehash, setupParams); - tx.nonce = nonce; - tx.chainId = chain.chainId; - printInfo('Waiting for user to approve transaction through ledger wallet'); - const { baseTx, signedTx } = await ledgerSign(wallet, chain, tx, staticGasOptions); + // Storing the fields in the data that will be stored in file - data.msg = `This transaction will perform upgrade of AxelarGateway contract having address ${gateway.address} with implementation ${contractConfig.implementation} on chain ${chain.name} with chainId ${chain.chainId}`; - data.unsignedTx = baseTx; - data.signedTx = signedTx; - data.status = 'PENDING'; - storeSignedTx(filePath, data); - } else { - const tx = await gateway.upgrade(contractConfig.implementation, implementationCodehash, setupParams, gasOptions); - printInfo('Upgrade transaction', tx.hash); + const data = { + msg: `This transaction will upgrade gateway ${gateway.address} to implementation ${contractConfig.implementation} on chain ${chain.name}`, + unsignedTx: baseTx, + signedTx, + status: 'PENDING', + }; - await tx.wait(chain.confirmations); + storeSignedTx(filePath, data); + options.nonceOffset = (options.nonceOffset || 0) + 1; + } else { const newImplementation = await gateway.implementation(); printInfo('New implementation', newImplementation); @@ -413,16 +397,16 @@ async function upgrade(config, options) { } } -async function main(options) { - const config = loadConfig(options.env); - +async function processCommand(config, chain, options) { if (!options.upgrade) { await deploy(config, options); } else { await upgrade(config, options); } +} - saveConfig(config, options.env); +async function main(options) { + await mainProcessor(options, processCommand); } async function programHandler() { diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 304423d18..58259a0c7 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -5,85 +5,168 @@ const { ethers } = require('hardhat'); const { Wallet, BigNumber, - utils: { isAddress }, + utils: { isAddress, serializeTransaction }, } = ethers; const path = require('path'); const { LedgerSigner } = require('@ethersproject/hardware-wallets'); -const { printError, printInfo, printObj, isValidPrivateKey } = require('./utils'); +const { printError, printInfo, printObj, isValidPrivateKey, isNumber, isValidNumber } = require('./utils'); -// function to create a ledgerSigner type wallet object -function getLedgerWallet(provider, path) { - try { - // Check if the parameters are undefined and assign default values if necessary - if (provider === undefined || provider === null) { - throw new Error('Empty provider'); +/** + * Get a wallet object from a private key or a ledger device + * @param {*} privateKey - private key or 'ledger' + * @param {*} provider - provider object + * @param {*} options - options object. ledgerPath can be provided for custom HD derivation + * @returns + */ +const getWallet = async (privateKey, provider, options = {}) => { + let wallet; + + if (options.offline) { + provider = undefined; + } + + if (privateKey === 'ledger') { + wallet = getLedgerWallet(provider, options?.ledgerPath); + } else { + if (!isValidPrivateKey(privateKey)) { + throw new Error('Private key is missing/ not provided correctly'); } - const type = 'hid'; - path = path || "m/44'/60'/0'/0/0"; - return new LedgerSigner(provider, type, path); - } catch (error) { - printError('Error trying to coonect to ledger wallet'); - printObj(error); + wallet = new Wallet(privateKey, provider); } -} -async function ledgerSign(wallet, chain, tx, gasOptions) { + return wallet; +}; + +// function to create a ledgerSigner type wallet object +const getLedgerWallet = (provider, path) => { + const type = 'hid'; + path = path || "m/44'/60'/0'/0/0"; + return new LedgerSigner(provider, type, path); +}; + +/** + * Sign a transaction with a wallet. Supports offline mode, and a private key or ledger backend + * @param {*} wallet - Either private key or ledger wallet + * @param {*} chain - chain config + * @param {*} tx - unsigned base transaction + * @param {*} options + * @returns - unsigned and signed transaction + */ +const signTransaction = async (wallet, chain, tx, options = {}) => { if (!tx.to || !isAddress(tx.to)) { throw new Error('Target address is missing/not provided as valid address for the tx in function arguments'); } - if (gasOptions) { - tx.gasLimit = gasOptions.gasLimit; - tx.gasPrice = gasOptions.gasPrice; + if (options.gasOptions) { + tx = { + ...options.gasOptions, + ...tx, // prefer gas options from tx if they were set + }; } - const baseTx = { - chainId: tx.chainId || chain.chainId || undefined, - data: tx.data || undefined, - gasLimit: tx.gasLimit || chain.gasOptions?.gasLimit || undefined, - gasPrice: tx.gasPrice || undefined, - nonce: tx.nonce !== undefined && tx.nonce !== null ? BigNumber.from(tx.nonce).toNumber() : undefined, - to: tx.to || undefined, - value: tx.value || undefined, - }; + if (options.offline) { + const address = options.signerAddress || (await wallet.getAddress()); + + tx = { + ...chain.staticGasOptions, + nonce: options.nonce, + chainId: chain.chainId, + from: address, + ...tx, // prefer tx options if they were set + }; + + if (!tx.nonce) { + tx.nonce = getLocalNonce(options.env, chain.name.toLowerCase(), address); + } + + if (options.nonceOffset) { + if (!isValidNumber(options.nonceOffset)) { + throw new Error('Provided nonce offset is not a valid number'); + } + + tx.nonce += parseInt(options.nonceOffset); + } + + if (!tx.gasLimit) { + throw new Error('Gas limit is missing/not provided for the tx in function arguments'); + } + + if (!tx.gasPrice && !(isNumber(tx.maxFeePerGas) && isNumber(tx.maxPriorityFeePerGas))) { + throw new Error('Gas price (legacy or eip-1559) is missing/not provided for the tx in function arguments'); + } + } + + printInfo(JSON.stringify(tx, null, 2)); let signedTx; - try { - signedTx = await wallet.signTransaction(baseTx); - printInfo('Signed Tx from ledger with signedTxHash as', signedTx); - } catch (error) { - printError('Failed to sign tx from ledger'); - printObj(error); + if (wallet instanceof LedgerSigner) { + signedTx = await ledgerSign(wallet, chain, tx); + + if (!options.offline) { + await sendTransaction(signedTx, wallet.provider); + } + } else { + if (options.offline) { + signedTx = await wallet.signTransaction(tx); + } else { + await sendTransaction(signedTx, wallet.provider); + } } - return { baseTx, signedTx }; -} + return { baseTx: tx, signedTx }; +}; + +const ledgerSign = async (wallet, chain, baseTx) => { + printInfo('Waiting for user to approve transaction through ledger wallet'); + + const unsignedTx = serializeTransaction(baseTx).substring(2); + const sig = await wallet._retry((eth) => eth.signTransaction("m/44'/60'/0'/0/0", unsignedTx)); -async function sendTx(tx, provider) { - let success; + // EIP-155 sig.v computation + // v in {0,1} + 2 * chainId + 35 + // Ledger gives this value mod 256 + // So from that, compute whether v is 0 or 1 and then add to 2 * chainId + 35 without doing a mod + var v = BigNumber.from('0x' + sig.v).toNumber(); + v = 2 * chain.chainID + 35 + ((v + 256 * 100000000000 - (2 * chain.chainID + 35)) % 256); + + // console.log("sig v", BigNumber.from("0x" + sig.v).toNumber(), v, "chain", chainID) + + const signedTx = serializeTransaction(baseTx, { + v, + r: '0x' + sig.r, + s: '0x' + sig.s, + }); + printInfo('Signed Tx from ledger with signedTxHash as', signedTx); + + return signedTx; +}; + +const sendTransaction = async (tx, provider) => { try { - const response = await provider.sendTransaction(tx).then((tx) => tx.wait()); + const response = await provider.sendTransaction(tx); + const receipt = await response.wait(); - if (response.error || !isValidJSON(response) || response.status !== 1) { - const error = `Execution failed${ - response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` - }`; - throw new Error(error); - } + // if (response.error || !isValidJSON(response) || response.status !== 1) { + // const error = `Execution failed${ + // response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` + // }`; + // throw new Error(error); + // } + + printInfo('Broadcasted tx', response.hash); + printInfo('Tx receipt', JSON.stringify(receipt, null, 2)); - success = true; - return { success, response }; - } catch (errorObj) { - printError('Error while broadcasting signed tx'); - printObj(errorObj); - success = false; - return { success, undefined }; + return { success: true, response, receipt }; + } catch (error) { + printError('Error while broadcasting signed tx', `${error}`); + + return { success: false, response: undefined, receipt: undefined }; } -} +}; function storeSignedTx(filePath, signedTx) { createFileIfNotExists(filePath); @@ -98,17 +181,9 @@ function storeSignedTx(filePath, signedTx) { }); } -async function getNonceFromProvider(provider, address) { - let nonce = 0; - - try { - nonce = await provider.getTransactionCount(address); - } catch (error) { - printError('Could not fetch nonnce from provider', error.message); - } - - return nonce; -} +const getNonceFromProvider = async (provider, address) => { + return await provider.getTransactionCount(address); +}; function getSignedTx(filePath) { const signedTx = {}; @@ -186,22 +261,6 @@ function isValidJSON(obj) { return true; } -const getWallet = async (privateKey, provider, options) => { - let wallet; - - if (privateKey === 'ledger') { - wallet = getLedgerWallet(provider, options?.ledgerPath); - } else { - if (!isValidPrivateKey(privateKey)) { - throw new Error('Private key is missing/ not provided correctly'); - } - - wallet = new Wallet(privateKey, provider); - } - - return wallet; -}; - const getNonceFileData = () => { const filePath = `${__dirname}/../axelar-chains-config/info/nonces.json`; const emptyData = {}; @@ -237,22 +296,15 @@ function createFileIfNotExists(filePath) { const updateNonceFileData = (nonceData) => { const filePath = `${__dirname}/../axelar-chains-config/info/nonces.json`; createFileIfNotExists(filePath); - // Write nonceData to the file - try { - fs.writeFileSync(filePath, JSON.stringify(nonceData, null, 2)); - printInfo(`Nonce updated successfully and stored in file ${filePath}`); - } catch (err) { - printError(`Could not update Nonce in file ${filePath}`); - printObj(err); - } + // Write nonceData to the file + fs.writeFileSync(filePath, JSON.stringify(nonceData, null, 2)); + printInfo(`Nonce updated successfully and stored in file ${filePath}`); }; const getLocalNonce = (env, chainName, signerAddress) => { const nonceData = getNonceFileData(); - const chainNonceData = chainName ? nonceData[env][chainName] : undefined; - const nonce = chainNonceData ? chainNonceData[signerAddress] || 0 : 0; - return nonce; + return nonceData[env][chainName][signerAddress]; }; const updateLocalNonce = (chain, nonce, signerAddress) => { @@ -263,7 +315,7 @@ const updateLocalNonce = (chain, nonce, signerAddress) => { }; module.exports = { - sendTx, + sendTransaction, getTransactions, storeSignedTx, getSignedTx, @@ -272,6 +324,6 @@ module.exports = { updateNonceFileData, getLocalNonce, updateLocalNonce, - ledgerSign, + signTransaction, getNonceFromProvider, }; diff --git a/evm/remove-info.js b/evm/remove-info.js index 093a08bdf..6b1f4a5ef 100644 --- a/evm/remove-info.js +++ b/evm/remove-info.js @@ -3,7 +3,7 @@ require('dotenv').config(); const { Command, Option } = require('commander'); -const { loadConfig, saveConfig } = require('./utils'); +const { mainProcessor } = require('./utils'); async function processCommand(options, chain, _) { const { contractName } = options; @@ -16,24 +16,7 @@ async function processCommand(options, chain, _) { } async function main(options) { - const config = loadConfig(options.env); - - let chains = options.chainNames.split(',').map((str) => str.trim()); - - if (options.chainNames === 'all') { - chains = Object.keys(config.chains); - } - - for (const chain of chains) { - if (config.chains[chain.toLowerCase()] === undefined) { - throw new Error(`Chain ${chain} is not defined in the info file`); - } - } - - for (const chain of chains) { - await processCommand(options, config.chains[chain.toLowerCase()], config); - saveConfig(config, options.env); - } + await mainProcessor(options, processCommand, true); } const program = new Command(); diff --git a/evm/send-tokens.js b/evm/send-tokens.js index 1fe5ef4fb..efe2d127b 100644 --- a/evm/send-tokens.js +++ b/evm/send-tokens.js @@ -11,23 +11,33 @@ const { } = ethers; const readlineSync = require('readline-sync'); -const { printInfo, printWalletInfo, isValidNumber, loadConfig } = require('./utils'); -const { storeSignedTx, getWallet, ledgerSign, getLocalNonce } = require('./offline-sign-utils.js'); +const { printInfo, printWalletInfo, isAddressArray, mainProcessor, isValidDecimal } = require('./utils'); +const { storeSignedTx, getWallet, signTransaction } = require('./offline-sign-utils.js'); -async function sendTokens(chain, options) { - const { privateKey, offline, nonceOffset, env } = options; +async function processCommand(_, chain, options) { + const { privateKey, offline, env } = options; let { amount, recipients } = options; + + const chainName = chain.name.toLowerCase(); const provider = getDefaultProvider(chain.rpc); + recipients = options.recipients.split(',').map((str) => str.trim()); + + if (!isAddressArray(recipients)) { + throw new Error('Invalid recipient addresses'); + } + + if (!isValidDecimal(amount)) { + throw new Error('Invalid amount'); + } + amount = parseEther(amount); const wallet = await getWallet(privateKey, provider, options); - const signerAddress = await wallet.getAddress(); - let nonce; - if (!offline) { - const balance = await printWalletInfo(wallet); + const { address, balance } = await printWalletInfo(wallet, options); + if (!offline) { if (balance.lte(amount)) { throw new Error(`Wallet has insufficient funds.`); } @@ -42,20 +52,6 @@ async function sendTokens(chain, options) { if (anwser !== 'y') return; } - const gasOptions = chain.staticGasOptions || {}; - - if (offline) { - nonce = getLocalNonce(env, chain.name.toLowerCase(), signerAddress); - - if (nonceOffset) { - if (!isValidNumber(nonceOffset)) { - throw new Error('Provided nonce offset is not a valid number'); - } - - nonce += parseInt(nonceOffset); - } - } - for (const recipient of recipients) { printInfo('Recipient', recipient); const tx = { @@ -63,46 +59,29 @@ async function sendTokens(chain, options) { value: amount, }; + const { baseTx, signedTx } = await signTransaction(wallet, chain, tx, options); + if (offline) { - const filePath = `./tx/signed-tx-${env}-${chain.name.toLowerCase()}-send-tokens-address-${signerAddress}-nonce-${nonce}.json`; + const filePath = `./tx/signed-tx-${env}-${chainName}-send-tokens-address-${address}-nonce-${baseTx.nonce}.json`; printInfo(`Storing signed Tx offline in file ${filePath}`); - const data = {}; - tx.nonce = nonce; - tx.chainId = chain.chainId; - printInfo('Waiting for user to approve transaction through ledger wallet'); - const { baseTx, signedTx } = await ledgerSign(wallet, chain, tx, gasOptions); + // Storing the fields in the data that will be stored in file - data.msg = `This transaction will send ${amount} of native tokens to ${recipient} on chain ${chain.name} with chainId ${chain.chainId}`; - data.unsignedTx = baseTx; - data.signedTx = signedTx; - data.status = 'PENDING'; + const data = { + msg: `This transaction will send ${amount} of native tokens from ${address} to ${recipient} on chain ${chain.name}`, + unsignedTx: baseTx, + signedTx, + status: 'PENDING', + }; + storeSignedTx(filePath, data); - } else { - const response = await wallet.sendTransaction(tx); - await response.wait(); - printInfo('Transaction hash', response.transactionHash); - } - ++nonce; + options.nonceOffset = (options.nonceOffset || 0) + 1; + } } } async function main(options) { - const config = loadConfig(options.env); - - const chains = options.chainNames.split(',').map((str) => str.trim()); - - for (const chainName of chains) { - if (config.chains[chainName.toLowerCase()] === undefined) { - throw new Error(`Chain ${chainName} is not defined in the info file`); - } - } - - for (const chainName of chains) { - const chain = config.chains[chainName.toLowerCase()]; - - await sendTokens(chain, options); - } + await mainProcessor(options, processCommand); } if (require.main === module) { @@ -122,13 +101,8 @@ if (require.main === module) { program.addOption(new Option('-r, --recipients ', 'comma-separated recipients of tokens').makeOptionMandatory(true)); program.addOption(new Option('-a, --amount ', 'amount to transfer (in terms of ETH)').makeOptionMandatory(true)); program.addOption(new Option('--offline', 'Run in offline mode')); - program.addOption(new Option('--ledgerPath ', 'The path to identify the account in ledger').makeOptionMandatory(false)); - program.addOption( - new Option( - '--nonceOffset ', - 'The value to add in local nonce if it deviates from actual wallet nonce', - ).makeOptionMandatory(false), - ); + program.addOption(new Option('--ledgerPath ', 'The path to identify the account in ledger')); + program.addOption(new Option('--nonceOffset ', 'The value to add in local nonce if it deviates from actual wallet nonce')); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { @@ -137,5 +111,5 @@ if (require.main === module) { program.parse(); } else { - module.exports = { sendTokens }; + module.exports = { sendTokens: processCommand }; } diff --git a/evm/update-nonces.js b/evm/update-nonces.js index afd7456f0..e7d3e7d65 100644 --- a/evm/update-nonces.js +++ b/evm/update-nonces.js @@ -6,78 +6,55 @@ const { ethers } = require('hardhat'); const { getDefaultProvider } = ethers; const readlineSync = require('readline-sync'); -const { printError, printObj, loadConfig } = require('./utils'); +const { mainProcessor } = require('./utils'); const { getNonceFromProvider, getNonceFileData, updateNonceFileData } = require('./offline-sign-utils'); -async function updateNonce(provider, env, chain, addresses) { - try { - const chainName = chain.name.toLowerCase(); - const nonceData = getNonceFileData(); - - if (!nonceData[env]) { - nonceData[env] = {}; - } - - if (!nonceData[env][chainName]) { - nonceData[env][chainName] = {}; - } - - const chainNonceData = nonceData[env][chainName]; - - if (addresses) { - addresses = JSON.parse(addresses); - - for (const address of addresses) { - const nonce = await getNonceFromProvider(provider, address); - chainNonceData[address] = nonce; - } - } else { - for (const [signerAddress] of Object.entries(chainNonceData)) { - const nonce = await getNonceFromProvider(provider, signerAddress); - chainNonceData[signerAddress] = nonce; - } - } - - nonceData[env][chainName] = chainNonceData; - updateNonceFileData(nonceData); - } catch (error) { - printError(`Nonce updation failed with error: ${error.message}`); - printObj(error); +async function processCommand(config, chain, options) { + const { env, rpc, yes } = options; + let { addresses } = options; + const provider = rpc ? getDefaultProvider(rpc) : getDefaultProvider(chain.rpc); + + if (!yes) { + const anwser = readlineSync.question( + `Proceed with the nonces update on network ${chalk.green(chain.name)} ${chalk.green('(y/n)')} `, + ); + if (anwser !== 'y') return; } -} -async function main(options) { - const { env, chainNames, rpcUrl, addresses, yes } = options; - const config = loadConfig(env); - const chains = chainNames.split(',').map((str) => str.trim()); - - for (const chainName of chains) { - if (config.chains[chainName.toLowerCase()] === undefined) { - throw new Error(`Chain ${chainName} is not defined in the info file`); - } + const chainName = chain.name.toLowerCase(); + const nonceData = getNonceFileData(); + + if (!nonceData[env]) { + nonceData[env] = {}; } - for (const chainName of chains) { - const chain = config.chains[chainName.toLowerCase()]; - const provider = rpcUrl ? getDefaultProvider(rpcUrl) : getDefaultProvider(chain.rpc); - const network = await provider.getNetwork(); - - if (!yes) { - const anwser = readlineSync.question( - `Proceed with the nonces update on network ${chalk.green(network.name)} with chainId ${chalk.green( - network.chainId, - )} ${chalk.green('(y/n)')} `, - ); - if (anwser !== 'y') return; - } - - await updateNonce(provider, env, chain, addresses); + if (!nonceData[env][chainName]) { + nonceData[env][chainName] = {}; + } + + const chainNonceData = nonceData[env][chainName]; + + if (addresses) { + addresses = addresses.split(',').map((str) => str.trim()); + } else { + addresses = Object.keys(chainNonceData); } + + for (const address of addresses) { + const nonce = await getNonceFromProvider(provider, address); + chainNonceData[address] = nonce; + } + + updateNonceFileData(nonceData); +} + +async function main(options) { + await mainProcessor(options, processCommand); } const program = new Command(); -program.name('Update-Nonces').description('Offline sign all the unsigned transactions in the file'); +program.name('update-nonces').description('Update nonces for addresses'); program.addOption( new Option('-e, --env ', 'environment') @@ -87,8 +64,8 @@ program.addOption( .env('ENV'), ); program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions')); -program.addOption(new Option('-a --addresses ', 'The Array of addresses for which the nonces to update').env('ADDRESSES')); +program.addOption(new Option('-r, --rpc ', 'The rpc url for creating a provider to fetch gasOptions')); +program.addOption(new Option('--addresses ', 'The Array of addresses for which the nonces to update').env('ADDRESSES')); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { diff --git a/evm/update-static-gas-options.js b/evm/update-static-gas-options.js index a700c8cb9..019a46fa0 100644 --- a/evm/update-static-gas-options.js +++ b/evm/update-static-gas-options.js @@ -9,57 +9,34 @@ const { } = ethers; const readlineSync = require('readline-sync'); -const { printError, printInfo, printObj, saveConfig, loadConfig } = require('./utils'); +const { printInfo, mainProcessor } = require('./utils'); -async function updateStaticGasOptions(chain, options, filePath) { - const { rpcUrl, yes } = options; - const provider = rpcUrl ? getDefaultProvider(rpcUrl) : getDefaultProvider(chain.rpc); - const network = await provider.getNetwork(); +const defaultGasLimit = 3e6; +const gasPriceMultiplier = 5; + +async function processCommand(config, chain, options) { + const { rpc, yes } = options; + const provider = rpc ? getDefaultProvider(rpc) : getDefaultProvider(chain.rpc); if (!yes) { const anwser = readlineSync.question( - `Proceed with the static gasOption update on network ${chalk.green(network.name)} with chainId ${chalk.green( - network.chainId, - )} ${chalk.green('(y/n)')} `, + `Proceed with the static gasOption update on network ${chalk.green(chain.name)} ${chalk.green('(y/n)')} `, ); if (anwser !== 'y') return; } - try { - const gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'gwei') * 5; - - if (!(chain.staticGasOptions && chain.staticGasOptions.gasLimit !== undefined)) { - chain.staticGasOptions = { gasLimit: 3e6 }; - } + const gasPrice = parseUnits((await provider.getGasPrice()).toString(), 'wei') * gasPriceMultiplier; - chain.staticGasOptions.gasPrice = gasPrice; - printInfo(`GasOptions updated succesfully and stored in config file ${filePath}`); - } catch (error) { - printError(`GasOptions updation failed with error: ${error.message}`); - printObj(error); + if (!(chain.staticGasOptions && chain.staticGasOptions.gasLimit !== undefined)) { + chain.staticGasOptions = { gasLimit: defaultGasLimit }; } - return chain; + chain.staticGasOptions.gasPrice = gasPrice; + printInfo(`staticGasOptions updated succesfully and stored in config file`); } async function main(options) { - const { env, chainNames } = options; - const filePath = `${__dirname}/../axelar-chains-config/info/${env}.json`; - const config = loadConfig(env); - const chains = chainNames.split(',').map((str) => str.trim()); - - for (const chainName of chains) { - if (config.chains[chainName.toLowerCase()] === undefined) { - throw new Error(`Chain ${chainName} is not defined in the info file`); - } - } - - for (const chainName of chains) { - const chain = config.chains[chainName.toLowerCase()]; - config.chains[chainName.toLowerCase()] = await updateStaticGasOptions(chain, options, filePath); - } - - saveConfig(config, env); + await mainProcessor(options, processCommand, true); } const program = new Command(); @@ -74,7 +51,7 @@ program.addOption( .env('ENV'), ); program.addOption(new Option('-n, --chainNames ', 'chain names').makeOptionMandatory(true)); -program.addOption(new Option('-r, --rpcUrl ', 'The rpc url for creating a provider to fetch gasOptions')); +program.addOption(new Option('-r, --rpc ', 'The rpc url for creating a provider to fetch gasOptions')); program.addOption(new Option('-y, --yes', 'skip prompts')); program.action((options) => { diff --git a/evm/utils.js b/evm/utils.js index eaa472d7a..69eafe513 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -189,6 +189,10 @@ const isValidNumber = (arg) => { return !isNaN(parseInt(arg)) && isFinite(arg); }; +const isValidDecimal = (arg) => { + return !isNaN(parseFloat(arg)) && isFinite(arg); +}; + const isNumberArray = (arr) => { if (!Array.isArray(arr)) { return false; @@ -203,11 +207,11 @@ const isNumberArray = (arr) => { return true; }; -const isAddressArray = (arg) => { - if (!Array.isArray(arg)) return false; +const isAddressArray = (addresses) => { + if (!Array.isArray(addresses)) return false; - for (const ele of arg) { - if (!isAddress(ele)) { + for (const address of addresses) { + if (!isAddress(address)) { return false; } } @@ -451,17 +455,22 @@ function saveConfig(config, env) { writeJSON(config, `${__dirname}/../axelar-chains-config/info/${env}.json`); } -async function printWalletInfo(wallet) { - printInfo('Wallet address', await wallet.getAddress()); - const balance = await wallet.provider.getBalance(await wallet.getAddress()); - printInfo('Wallet balance', `${balance / 1e18}`); - printInfo('Wallet nonce', (await wallet.provider.getTransactionCount(await wallet.getAddress())).toString()); +async function printWalletInfo(wallet, options = {}) { + let balance = 0; + const address = await wallet.getAddress(); + printInfo('Wallet address', address); + + if (!options.offline) { + balance = await wallet.provider.getBalance(address); + printInfo('Wallet balance', `${balance / 1e18}`); + printInfo('Wallet nonce', (await wallet.provider.getTransactionCount(address)).toString()); - if (balance.isZero()) { - printError('Wallet balance is 0'); + if (balance.isZero()) { + printError('Wallet balance is 0'); + } } - return balance; + return { address, balance }; } const deployContract = async ( @@ -597,6 +606,43 @@ const isContract = async (address, provider) => { } }; +const mainProcessor = async (options, processCommand, save = false, catchErr = false) => { + if (!options.env) { + throw new Error('Environment was not provided'); + } + + if (!options.chainName && !options.chainNames) { + throw new Error('Chain names were not provided'); + } + + const config = loadConfig(options.env); + const chains = options.chainName ? [options.chainName] : options.chainNames.split(',').map((str) => str.trim()); + + for (const chainName of chains) { + if (config.chains[chainName.toLowerCase()] === undefined) { + throw new Error(`Chain ${chainName} is not defined in the info file`); + } + } + + for (const chainName of chains) { + const chain = config.chains[chainName.toLowerCase()]; + + try { + await processCommand(config, chain, options); + } catch (error) { + printError(`Failed with error on ${chain.name}`, error.message); + + if (!catchErr) { + throw error; + } + } + + if (save) { + saveConfig(config, options.env); + } + } +}; + module.exports = { deployCreate, deployCreate2, @@ -615,6 +661,7 @@ module.exports = { isString, isNumber, isValidNumber, + isValidDecimal, isNumberArray, isAddressArray, isKeccak256Hash, @@ -632,4 +679,5 @@ module.exports = { isContract, isValidPrivateKey, verifyContract, + mainProcessor, }; From 18a8d8366cbf0005926fd64f6c01dbad5cbc5661 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 26 Sep 2023 21:32:50 -0400 Subject: [PATCH 42/45] fix online signing --- evm/deploy-gateway-v5.0.x.js | 40 +++++++++++++++++------------------- evm/offline-sign-utils.js | 14 +++++++------ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/evm/deploy-gateway-v5.0.x.js b/evm/deploy-gateway-v5.0.x.js index 77b37009d..bbe531d22 100644 --- a/evm/deploy-gateway-v5.0.x.js +++ b/evm/deploy-gateway-v5.0.x.js @@ -95,17 +95,6 @@ async function deploy(config, options) { throw new Error('mintLimiter address is required'); } - const transactionCount = await wallet.getTransactionCount(); - const proxyAddress = getContractAddress({ - from: wallet.address, - nonce: transactionCount + 3, - }); - printInfo('Predicted proxy address', proxyAddress); - - const gasOptions = contractConfig.gasOptions || chain.gasOptions || {}; - printInfo('Gas override', JSON.stringify(gasOptions, null, 2)); - printInfo('Is verification enabled?', verify ? 'y' : 'n'); - const gatewayFactory = new ContractFactory(AxelarGateway.abi, AxelarGateway.bytecode, wallet); const authFactory = new ContractFactory(AxelarAuthWeighted.abi, AxelarAuthWeighted.bytecode, wallet); const tokenDeployerFactory = new ContractFactory(TokenDeployer.abi, TokenDeployer.bytecode, wallet); @@ -116,6 +105,23 @@ async function deploy(config, options) { let tokenDeployer; const contractsToVerify = []; + if (reuseProxy) { + const gatewayProxy = chain.contracts.AxelarGateway?.address || (await getProxy(config, chain.id)); + printInfo('Reusing Gateway Proxy address', gatewayProxy); + gateway = gatewayFactory.attach(gatewayProxy); + } else { + const transactionCount = await wallet.getTransactionCount(); + const proxyAddress = getContractAddress({ + from: wallet.address, + nonce: transactionCount + 3, + }); + printInfo('Predicted proxy address', proxyAddress); + } + + const gasOptions = contractConfig.gasOptions || chain.gasOptions || {}; + printInfo('Gas override', JSON.stringify(gasOptions, null, 2)); + printInfo('Is verification enabled?', verify ? 'y' : 'n'); + if (!yes) { console.log('Does this match any existing deployments?'); const anwser = readlineSync.question(`Proceed with deployment on ${chain.name}? ${chalk.green('(y/n)')} `); @@ -124,14 +130,6 @@ async function deploy(config, options) { contractConfig.deployer = wallet.address; - if (reuseProxy) { - printInfo('Reusing gateway proxy contract'); - - const gatewayProxy = chain.contracts.AxelarGateway?.address || (await getProxy(config, chain.id)); - printInfo('Proxy address', gatewayProxy); - gateway = gatewayFactory.attach(gatewayProxy); - } - if (reuseProxy && reuseHelpers) { auth = authFactory.attach(await gateway.authModule()); } else { @@ -214,7 +212,7 @@ async function deploy(config, options) { governanceModule = await gateway.governance(); } catch (e) { // this can fail when upgrading from an older version - printWarn(`WARN: Failed to retrieve governance address`); + printWarn(`WARN: Failed to retrieve governance address. Expected when reusing a gateway { }; } - if (options.offline) { + if (!options.offline) { + tx = await wallet.populateTransaction(tx); + } else { const address = options.signerAddress || (await wallet.getAddress()); tx = { ...chain.staticGasOptions, - nonce: options.nonce, chainId: chain.chainId, from: address, + nonce: options.nonce, ...tx, // prefer tx options if they were set }; @@ -98,7 +100,7 @@ const signTransaction = async (wallet, chain, tx, options = {}) => { } } - printInfo(JSON.stringify(tx, null, 2)); + printInfo('Transaction being signed', JSON.stringify(tx, null, 2)); let signedTx; @@ -109,9 +111,9 @@ const signTransaction = async (wallet, chain, tx, options = {}) => { await sendTransaction(signedTx, wallet.provider); } } else { - if (options.offline) { - signedTx = await wallet.signTransaction(tx); - } else { + signedTx = await wallet.signTransaction(tx); + + if (!options.offline) { await sendTransaction(signedTx, wallet.provider); } } From ae3b51a6a147177b12a4773622fcba0ed56ee738 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 26 Sep 2023 21:55:56 -0400 Subject: [PATCH 43/45] simplify broadcast --- evm/broadcast-transactions.js | 19 ++++++++----------- evm/offline-sign-utils.js | 19 +++---------------- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 50b45001e..9625a4dd1 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -11,7 +11,7 @@ const readlineSync = require('readline-sync'); const { printError, printInfo, mainProcessor } = require('./utils'); const { sendTransaction, getSignedTx, storeSignedTx } = require('./offline-sign-utils'); -async function processCommand(config, chain, options) { +async function processCommand(_, chain, options) { const { filePath, rpc } = options; const provider = getDefaultProvider(rpc || chain.rpc); @@ -35,22 +35,19 @@ async function processCommand(config, chain, options) { printInfo('Broadcasting transaction', JSON.stringify(transaction.unsignedTx, null, 2)); // Send the signed transaction - const { success, response } = await sendTransaction(transaction.signedTx, provider); - - if (success) { - // Update the transaction status and store transaction hash + try { + const { response } = await sendTransaction(transaction.signedTx, provider, chain.confirmations); transaction.status = 'SUCCESS'; - transaction.transactionHash = response.hash; - printInfo(`Transaction executed successfully ${response.hash}`); - } else { - // Update the transaction status and store error message + transaction.hash = response.hash; + printInfo('Transaction executed successfully', response.hash); + } catch (error) { transaction.status = 'FAILED'; - printError('Error broadcasting tx: ', transaction.signedTx); + printError('Error broadcasting tx', error); } storeSignedTx(filePath, transaction); } else { - printInfo('Skipping broadcast, transaction status is', transaction.status); + printWarn('Skipping broadcast, transaction status is', transaction.status); } } diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index d5bd06309..51b18f6ba 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -147,27 +147,14 @@ const ledgerSign = async (wallet, chain, baseTx) => { return signedTx; }; -const sendTransaction = async (tx, provider) => { - try { +const sendTransaction = async (tx, provider, confirmations = undefined) => { const response = await provider.sendTransaction(tx); - const receipt = await response.wait(); - - // if (response.error || !isValidJSON(response) || response.status !== 1) { - // const error = `Execution failed${ - // response.status ? ` with txHash: ${response.transactionHash}` : ` with msg: ${response.message}` - // }`; - // throw new Error(error); - // } + const receipt = await response.wait(confirmations); printInfo('Broadcasted tx', response.hash); printInfo('Tx receipt', JSON.stringify(receipt, null, 2)); - return { success: true, response, receipt }; - } catch (error) { - printError('Error while broadcasting signed tx', `${error}`); - - return { success: false, response: undefined, receipt: undefined }; - } + return { response, receipt }; }; function storeSignedTx(filePath, signedTx) { From 45fd73e8b3c6407e17f429eae7f8386f170c319b Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 26 Sep 2023 21:59:08 -0400 Subject: [PATCH 44/45] prettier --- evm/broadcast-transactions.js | 2 +- evm/offline-sign-utils.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/evm/broadcast-transactions.js b/evm/broadcast-transactions.js index 9625a4dd1..3413b5e19 100644 --- a/evm/broadcast-transactions.js +++ b/evm/broadcast-transactions.js @@ -8,7 +8,7 @@ const { } = ethers; const readlineSync = require('readline-sync'); -const { printError, printInfo, mainProcessor } = require('./utils'); +const { printError, printInfo, printWarn, mainProcessor } = require('./utils'); const { sendTransaction, getSignedTx, storeSignedTx } = require('./offline-sign-utils'); async function processCommand(_, chain, options) { diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index 51b18f6ba..e83d2a559 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -148,13 +148,13 @@ const ledgerSign = async (wallet, chain, baseTx) => { }; const sendTransaction = async (tx, provider, confirmations = undefined) => { - const response = await provider.sendTransaction(tx); - const receipt = await response.wait(confirmations); + const response = await provider.sendTransaction(tx); + const receipt = await response.wait(confirmations); - printInfo('Broadcasted tx', response.hash); - printInfo('Tx receipt', JSON.stringify(receipt, null, 2)); + printInfo('Broadcasted tx', response.hash); + printInfo('Tx receipt', JSON.stringify(receipt, null, 2)); - return { response, receipt }; + return { response, receipt }; }; function storeSignedTx(filePath, signedTx) { From 450ddd5b8e1c6635b5d0afebed26d7c10d424b34 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 26 Sep 2023 21:59:23 -0400 Subject: [PATCH 45/45] throw exception --- evm/offline-sign-utils.js | 12 ++++++------ evm/utils.js | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/evm/offline-sign-utils.js b/evm/offline-sign-utils.js index e83d2a559..1014d24ec 100644 --- a/evm/offline-sign-utils.js +++ b/evm/offline-sign-utils.js @@ -193,8 +193,8 @@ function getSignedTx(filePath) { return signedTx; } catch (error) { - printError(`Failed to get all signers data from the file ${filePath}`); - printObj(error); + printError(`Failed to get all signers data from the file ${filePath}`, error); + throw error; } } @@ -206,8 +206,8 @@ function getFileData(filePath) { const data = fs.readFileSync(filePath, 'utf-8'); return data; } catch (error) { - printError(`Failed to get data from the file ${filePath}`); - printObj(error); + printError(`Failed to get data from the file ${filePath}`, error); + throw error; } } @@ -233,8 +233,8 @@ async function getTransactions(filePath, signerAddress) { return transactions; } catch (error) { - printError(`Failed to get transactions for ${signerAddress}`); - printObj(error); + printError(`Failed to get transactions for ${signerAddress}`, error); + throw error; } } diff --git a/evm/utils.js b/evm/utils.js index 69eafe513..3bddd33e1 100644 --- a/evm/utils.js +++ b/evm/utils.js @@ -116,7 +116,7 @@ const printInfo = (msg, info = '') => { const printWarn = (msg, info = '') => { if (info) { - msg = msg + ': ' + info; + msg = `${msg}: ${info}`; } console.log(`${chalk.yellow(msg)}\n`); @@ -124,7 +124,7 @@ const printWarn = (msg, info = '') => { const printError = (msg, info = '') => { if (info) { - msg = msg + ': ' + info; + msg = `${msg}: ${info}`; } console.log(`${chalk.red(msg)}\n`);