From 88b04c1b204c3529858d3d9d46dabf3f4c5b6b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Fri, 12 Mar 2021 11:02:15 +0100 Subject: [PATCH 01/24] Rename signTransaction --- README.md | 4 ++-- src/EthersSafe.ts | 22 +++++++++++----------- src/Safe.ts | 4 ++-- tests/signatures.test.ts | 16 ++++++++-------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 9c0c6a050..b6f77cef4 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,10 @@ const tx = new SafeTransaction({ 3. Generate the signatures with both owners: ```js let safeSdk = new EthersSafe(ethers, wallet1, safeAddress) -await safeSdk.confirmTransaction(tx) +await safeSdk.signTransaction(tx) safeSdk = new EthersSafe(ethers, wallet2, safeAddress) -await safeSdk.confirmTransaction(tx) +await safeSdk.signTransaction(tx) ``` 4. Execute the transaction with the two signatures added: diff --git a/src/EthersSafe.ts b/src/EthersSafe.ts index 285a862ff..5aec60244 100644 --- a/src/EthersSafe.ts +++ b/src/EthersSafe.ts @@ -88,6 +88,16 @@ class EthersSafe implements Safe { return this.#contract.getModules() } + /** + * Checks if a specific Safe module is enabled for the current Safe. + * + * @param moduleAddress - The desired module address + * @returns TRUE if the module is enabled + */ + async isModuleEnabled(moduleAddress: string): Promise { + return this.#contract.isModuleEnabled(moduleAddress) + } + /** * Returns the transaction hash to be signed by the owners. * @@ -129,7 +139,7 @@ class EthersSafe implements Safe { * * @param safeTransaction - The Safe transaction to be signed */ - async confirmTransaction(safeTransaction: SafeTransaction): Promise { + async signTransaction(safeTransaction: SafeTransaction): Promise { const owners = await this.getOwners() if (!owners.find((owner: string) => areAddressesEqual(owner, this.#signer.address))) { throw new Error('Transactions can only be confirmed by Safe owners') @@ -193,16 +203,6 @@ class EthersSafe implements Safe { ) return txResponse } - - /** - * Checks if a specific Safe module is enabled for the current Safe. - * - * @param moduleAddress - The desired module address - * @returns TRUE if the module is enabled - */ - async isModuleEnabled(moduleAddress: string): Promise { - return this.#contract.isModuleEnabled(moduleAddress) - } } export default EthersSafe diff --git a/src/Safe.ts b/src/Safe.ts index ef11c0b6f..f595927e3 100644 --- a/src/Safe.ts +++ b/src/Safe.ts @@ -10,12 +10,12 @@ interface Safe { getNetworkId(): Promise getBalance(): Promise getModules(): Promise + isModuleEnabled(moduleAddress: string): Promise getTransactionHash(transaction: SafeTransaction): Promise signTransactionHash(hash: string): Promise - confirmTransaction(transaction: SafeTransaction): Promise + signTransaction(transaction: SafeTransaction): Promise encodeTransaction(transaction: SafeTransaction): Promise executeTransaction(transaction: SafeTransaction, options?: any): Promise - isModuleEnabled(moduleAddress: string): Promise } export default Safe diff --git a/tests/signatures.test.ts b/tests/signatures.test.ts index 6ca407265..e7f88efcc 100644 --- a/tests/signatures.test.ts +++ b/tests/signatures.test.ts @@ -16,7 +16,7 @@ describe('Safe Core SDK', () => { } }) - describe('confirmTransaction', async () => { + describe('signTransaction', async () => { it('should fail if signature is not added by an owner', async () => { const { safe } = await setupTests() const tx = new SafeTransaction({ @@ -27,7 +27,7 @@ describe('Safe Core SDK', () => { }) const safeSdk = new EthersSafe(ethers, user3, safe.address) chai - .expect(safeSdk.confirmTransaction(tx)) + .expect(safeSdk.signTransaction(tx)) .to.be.rejectedWith('Transactions can only be confirmed by Safe owners') }) @@ -41,9 +41,9 @@ describe('Safe Core SDK', () => { }) chai.expect(tx.signatures.size).to.be.eq(0) const safeSdk = new EthersSafe(ethers, user1, safe.address) - await safeSdk.confirmTransaction(tx) + await safeSdk.signTransaction(tx) chai.expect(tx.signatures.size).to.be.eq(1) - await safeSdk.confirmTransaction(tx) + await safeSdk.signTransaction(tx) chai.expect(tx.signatures.size).to.be.eq(1) }) @@ -57,7 +57,7 @@ describe('Safe Core SDK', () => { }) chai.expect(tx.signatures.size).to.be.eq(0) const safeSdk = new EthersSafe(ethers, user1, safe.address) - await safeSdk.confirmTransaction(tx) + await safeSdk.signTransaction(tx) chai.expect(tx.signatures.size).to.be.eq(1) }) }) @@ -72,7 +72,7 @@ describe('Safe Core SDK', () => { nonce: (await safe.nonce()).toString() }) const safeSdk = new EthersSafe(ethers, user1, safe.address) - await safeSdk.confirmTransaction(tx) + await safeSdk.signTransaction(tx) chai.expect(tx.signatures.size).to.be.eq(1) chai .expect( @@ -107,10 +107,10 @@ describe('Safe Core SDK', () => { }) chai.expect(tx.signatures.size).to.be.eq(0) let safeSdk = new EthersSafe(ethers, user1, safe.address) - await safeSdk.confirmTransaction(tx) + await safeSdk.signTransaction(tx) chai.expect(tx.signatures.size).to.be.eq(1) safeSdk = new EthersSafe(ethers, user2, safe.address) - await safeSdk.confirmTransaction(tx) + await safeSdk.signTransaction(tx) chai.expect(tx.signatures.size).to.be.eq(2) const txResponse = await safeSdk.executeTransaction(tx) chai.expect(txResponse.hash).not.to.be.null From 3ac3cd887359ff1faba1718f98c0ea5949549734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Fri, 12 Mar 2021 11:05:11 +0100 Subject: [PATCH 02/24] Fix typo --- tests/utils/setup.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/utils/setup.ts b/tests/utils/setup.ts index dccf9d4c6..001d5828b 100644 --- a/tests/utils/setup.ts +++ b/tests/utils/setup.ts @@ -22,11 +22,11 @@ export const getSafeTemplate = async () => { return Safe.attach(template) } -export const getSafeWithOwners = async (owners: string[], threhsold?: number) => { +export const getSafeWithOwners = async (owners: string[], threshold?: number) => { const template = await getSafeTemplate() await template.setup( owners, - threhsold || owners.length, + threshold || owners.length, AddressZero, '0x', AddressZero, From c4dde5d109670956e78ac803434005de6f5e27a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Fri, 12 Mar 2021 11:09:38 +0100 Subject: [PATCH 03/24] Update Safe ABI to handle on-chain signatures --- src/abis/SafeAbiV1-2-0.json | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/abis/SafeAbiV1-2-0.json b/src/abis/SafeAbiV1-2-0.json index a35618202..cb84c98be 100644 --- a/src/abis/SafeAbiV1-2-0.json +++ b/src/abis/SafeAbiV1-2-0.json @@ -257,5 +257,46 @@ "payable": false, "stateMutability": "view", "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "bytes32", + "name": "hashToApprove", + "type": "bytes32" + } + ], + "name": "approveHash", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "approvedHashes", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" } ] From 8b51eb268de614d261553666e9bf8ea0d7189e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Fri, 12 Mar 2021 12:24:56 +0100 Subject: [PATCH 04/24] Add test coverage --- .nycrc.json | 5 + package.json | 3 +- yarn.lock | 751 ++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 719 insertions(+), 40 deletions(-) create mode 100644 .nycrc.json diff --git a/.nycrc.json b/.nycrc.json new file mode 100644 index 000000000..913d652ef --- /dev/null +++ b/.nycrc.json @@ -0,0 +1,5 @@ +{ + "include": [ + "src/**/*.ts" + ] +} diff --git a/package.json b/package.json index bdba4434d..4e8cbc329 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ ], "scripts": { "build": "yarn rimraf dist && tsc && hardhat compile", - "test": "hardhat deploy && hardhat test", + "test": "hardhat deploy && nyc hardhat test", "format": "prettier --write \"{src,tests,hardhat}/**/*.ts\"", "lint": "tslint -p tsconfig.json" }, @@ -51,6 +51,7 @@ "husky": "^5.0.9", "lint-staged": "^10.5.4", "mocha": "^8.3.0", + "nyc": "^15.1.0", "prettier": "^2.2.1", "rimraf": "^3.0.2", "ts-generator": "^0.1.1", diff --git a/yarn.lock b/yarn.lock index 7e16bd8c0..cc9d561e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -115,7 +115,29 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.12.13", "@babel/generator@^7.13.0", "@babel/generator@^7.5.0": +"@babel/core@^7.7.5": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.10.tgz#07de050bbd8193fcd8a3c27918c0890613a94559" + integrity sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.9" + "@babel/helper-compilation-targets" "^7.13.10" + "@babel/helper-module-transforms" "^7.13.0" + "@babel/helpers" "^7.13.10" + "@babel/parser" "^7.13.10" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + lodash "^4.17.19" + semver "^6.3.0" + source-map "^0.5.0" + +"@babel/generator@^7.12.13", "@babel/generator@^7.13.0", "@babel/generator@^7.13.9", "@babel/generator@^7.5.0": version "7.13.9" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== @@ -141,6 +163,16 @@ browserslist "^4.14.5" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.13.10": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz#1310a1678cb8427c07a753750da4f8ce442bdd0c" + integrity sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA== + dependencies: + "@babel/compat-data" "^7.13.8" + "@babel/helper-validator-option" "^7.12.17" + browserslist "^4.14.5" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.13.0": version "7.13.8" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.8.tgz#0367bd0a7505156ce018ca464f7ac91ba58c1a04" @@ -273,6 +305,15 @@ "@babel/traverse" "^7.13.0" "@babel/types" "^7.13.0" +"@babel/helpers@^7.13.10": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.10.tgz#fd8e2ba7488533cdeac45cc158e9ebca5e3c7df8" + integrity sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== + dependencies: + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" + "@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.12.13.tgz#8ab538393e00370b26271b01fa08f7f27f2e795c" @@ -292,6 +333,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.9.tgz#ca34cb95e1c2dd126863a84465ae8ef66114be99" integrity sha512-nEUfRiARCcaVo3ny3ZQjURjHQZUo/JkEw7rLlSZy/psWGnvwXFtPcr6jb7Yb41DVW5LTe6KRq9LGleRNsg1Frw== +"@babel/parser@^7.13.10": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.12.tgz#ba320059420774394d3b0c0233ba40e4250b81d1" + integrity sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw== + "@babel/plugin-proposal-class-properties@^7.0.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz#146376000b94efd001e57a40a88a525afaab9f37" @@ -1651,6 +1697,22 @@ normalize-path "^2.0.1" through2 "^2.0.3" +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + "@nodelib/fs.scandir@2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" @@ -1918,6 +1980,11 @@ resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.11.1.tgz#fa840af64840c930f24a9c82c08d4a092a068add" integrity sha512-H8BSBoKE8EubJa0ONqecA2TviT3TnHeC4NpgnAHSUiuhZoQBfPB4L2P9bs8R6AoTW10Endvh3vc+fomVMIDIYQ== +"@solidity-parser/parser@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.12.0.tgz#18a0fb2a9d2484b23176f63b16093c64794fc323" + integrity sha512-DT3f/Aa4tQysZwUsuqBwvr8YRJzKkvPUKV/9o2/o5EVw3xqlbzmtx4O60lTUcZdCawL+N8bBLNUyOGpHjGlJVQ== + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -2067,7 +2134,7 @@ source-map-support "^0.5.19" web3 "1.2.9" -"@truffle/provider@^0.2.26": +"@truffle/provider@^0.2.24", "@truffle/provider@^0.2.26": version "0.2.26" resolved "https://registry.yarnpkg.com/@truffle/provider/-/provider-0.2.26.tgz#88e31b79973c2427c4a17d9a59411e6fbc810190" integrity sha512-YKPmhB9S9AQkT2ePGtadwjDduxU23DXXy+5zyM5fevw5GCbXSnf+jG6rICXjPkVFjuKBlXuq5JbuERZn43522Q== @@ -2284,6 +2351,14 @@ dependencies: "@types/node" "*" +"@types/glob@^7.1.1": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" + integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + "@types/http-assert@*": version "1.5.1" resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b" @@ -2340,6 +2415,11 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== +"@types/minimatch@*": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" + integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== + "@types/mkdirp@^0.5.2": version "0.5.2" resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.2.tgz#503aacfe5cc2703d5484326b1b27efa67a339c1f" @@ -2624,6 +2704,11 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abbrev@1.0.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= + abort-controller@3.0.0, abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -2724,6 +2809,11 @@ acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +address@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" + integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== + adm-zip@^0.4.16: version "0.4.16" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" @@ -2774,6 +2864,11 @@ ajv@^7.0.2: require-from-string "^2.0.2" uri-js "^4.2.2" +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= + ansi-colors@3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" @@ -3028,11 +3123,23 @@ app-module-path@^2.2.0: resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" integrity sha1-ZBqlXft9am8KgUHEucCqULbCTdU= +append-transform@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-2.0.0.tgz#99d9d29c7b38391e6f428d28ce136551f0b77e12" + integrity sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg== + dependencies: + default-require-extensions "^3.0.0" + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" @@ -3191,6 +3298,11 @@ async-retry@^1.2.1: dependencies: retry "0.12.0" +async@1.x, async@^1.4.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + async@2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" @@ -3198,11 +3310,6 @@ async@2.6.2: dependencies: lodash "^4.17.11" -async@^1.4.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= - async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: version "2.6.3" resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" @@ -4229,6 +4336,16 @@ cachedown@1.0.0: abstract-leveldown "^2.4.1" lru-cache "^3.2.0" +caching-transform@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" + integrity sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA== + dependencies: + hasha "^5.0.0" + make-dir "^3.0.0" + package-hash "^4.0.0" + write-file-atomic "^3.0.0" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -4699,6 +4816,11 @@ commander@^6.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + component-emitter@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" @@ -4909,7 +5031,7 @@ cross-fetch@^2.1.0, cross-fetch@^2.1.1: node-fetch "2.1.2" whatwg-fetch "2.0.4" -cross-spawn@^6.0.5: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -5013,6 +5135,11 @@ dataloader@2.0.0: resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.0.0.tgz#41eaf123db115987e21ca93c005cd7753c55fe6f" integrity sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ== +death@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/death/-/death-1.1.0.tgz#01aa9c401edd92750514470b8266390c66c67318" + integrity sha1-AaqcQB7dknUFFEcLgmY5DGbGcxg= + debug-fabulous@0.0.X: version "0.0.4" resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-0.0.4.tgz#fa071c5d87484685424807421ca4b16b0b1a0763" @@ -5022,7 +5149,7 @@ debug-fabulous@0.0.X: lazy-debug-legacy "0.0.X" object-assign "4.1.0" -debug@2.6.9, debug@2.X, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@2.X, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -5120,6 +5247,13 @@ deep-is@^0.1.3, deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +default-require-extensions@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.0.tgz#e03f93aac9b2b6443fc52e5e4a37b3ad9ad8df96" + integrity sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg== + dependencies: + strip-bom "^4.0.0" + defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -5247,6 +5381,14 @@ detect-newline@2.X: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= +detect-port@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.3.0.tgz#d9c40e9accadd4df5cac6a782aefd014d573d1f1" + integrity sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ== + dependencies: + address "^1.0.1" + debug "^2.6.0" + dicer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" @@ -5637,6 +5779,11 @@ es6-denodeify@^0.1.1: resolved "https://registry.yarnpkg.com/es6-denodeify/-/es6-denodeify-0.1.5.tgz#31d4d5fe9c5503e125460439310e16a2a3f39c1f" integrity sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8= +es6-error@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" @@ -5674,6 +5821,18 @@ escape-string-regexp@4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escodegen@1.8.x: + version "1.8.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + integrity sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg= + dependencies: + esprima "^2.7.1" + estraverse "^1.9.1" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.2.0" + escodegen@^1.6.1: version "1.14.3" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" @@ -5792,6 +5951,11 @@ espree@^7.3.0, espree@^7.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" +esprima@2.7.x, esprima@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE= + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -5811,6 +5975,11 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" +estraverse@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q= + estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" @@ -6426,6 +6595,19 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" @@ -6601,7 +6783,7 @@ fast-future@~1.0.2: resolved "https://registry.yarnpkg.com/fast-future/-/fast-future-1.0.2.tgz#8435a9aaa02d79248d17d704e76259301d99280a" integrity sha1-hDWpqqAteSSNF9cE52JZMB2ZKAo= -fast-glob@^3.1.1: +fast-glob@^3.0.3, fast-glob@^3.1.1: version "3.2.5" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== @@ -6747,6 +6929,15 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" +find-cache-dir@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" + integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + find-replace@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-1.0.3.tgz#b88e7364d2d9c959559f388c66670d6130441fa0" @@ -6785,7 +6976,7 @@ find-up@^2.1.0: dependencies: locate-path "^2.0.0" -find-up@^4.1.0: +find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -6872,6 +7063,14 @@ foreach@^2.0.4: resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^3.0.2" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -6940,6 +7139,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +fromentries@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" + integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== + fs-capacitor@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-2.0.4.tgz#5a22e72d40ae5078b4fe64fe4d08c0d3fc88ad3c" @@ -6988,6 +7192,15 @@ fs-extra@^7.0.0, fs-extra@^7.0.1: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^9.0.0, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -7030,6 +7243,15 @@ functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +ganache-cli@^6.11.0: + version "6.12.2" + resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.12.2.tgz#c0920f7db0d4ac062ffe2375cb004089806f627a" + integrity sha512-bnmwnJDBDsOWBUP8E/BExWf85TsdDEFelQSzihSJm9VChVO1SHp94YXLP5BlA4j/OTxp0wR4R1Tje9OHOuAJVw== + dependencies: + ethereumjs-util "6.2.1" + source-map-support "0.5.12" + yargs "13.2.4" + ganache-core@^2.10.2: version "2.13.2" resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.13.2.tgz#27e6fc5417c10e6e76e2e646671869d7665814a3" @@ -7115,6 +7337,11 @@ get-own-enumerable-property-symbols@^3.0.0: resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + get-params@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/get-params/-/get-params-0.1.2.tgz#bae0dfaba588a0c60d7834c0d8dc2ff60eeef2fe" @@ -7125,7 +7352,7 @@ get-stream@^3.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= -get-stream@^4.1.0: +get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== @@ -7151,6 +7378,14 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +ghost-testrpc@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz#c4de9557b1d1ae7b2d20bbe474a91378ca90ce92" + integrity sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ== + dependencies: + chalk "^2.4.2" + node-emoji "^1.10.0" + glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" @@ -7207,7 +7442,7 @@ glob@7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.1.6, glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.6: +glob@7.1.6, glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -7219,7 +7454,7 @@ glob@7.1.6, glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^5.0.3: +glob@^5.0.15, glob@^5.0.3: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= @@ -7230,6 +7465,22 @@ glob@^5.0.3: once "^1.3.0" path-is-absolute "^1.0.0" +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + global@~4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" @@ -7267,6 +7518,20 @@ globby@11.0.2, globby@^11.0.1: merge2 "^1.3.0" slash "^3.0.0" +globby@^10.0.1: + version "10.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" + integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.0.0" + got@9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -7304,7 +7569,7 @@ got@^7.1.0: url-parse-lax "^1.0.0" url-to-options "^1.0.1" -graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: +graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== @@ -7420,6 +7685,18 @@ gulp-sourcemaps@^1.5.2: through2 "2.X" vinyl "1.X" +handlebars@^4.0.1: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -7526,6 +7803,11 @@ has-bigints@^1.0.0: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -7621,6 +7903,14 @@ hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasha@^5.0.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" + integrity sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ== + dependencies: + is-stream "^2.0.0" + type-fest "^0.8.0" + he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -7668,6 +7958,11 @@ hosted-git-info@^2.1.4, hosted-git-info@^2.6.0: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + htmlparser2@^3.9.1: version "3.10.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" @@ -7807,7 +8102,7 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.4: +ignore@^5.1.1, ignore@^5.1.4: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== @@ -7885,7 +8180,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@~1.3.0: +ini@^1.3.5, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -7907,6 +8202,11 @@ invert-kv@^1.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + io-ts@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" @@ -8245,7 +8545,7 @@ is-set@^2.0.2: resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== -is-stream@^1.0.0, is-stream@^1.0.1: +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -8353,6 +8653,67 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.0-alpha.1: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== + +istanbul-lib-hook@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz#8f84c9434888cc6b1d0a9d7092a76d239ebf0cc6" + integrity sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ== + dependencies: + append-transform "^2.0.0" + +istanbul-lib-instrument@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-processinfo@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz#e1426514662244b2f25df728e8fd1ba35fe53b9c" + integrity sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw== + dependencies: + archy "^1.0.0" + cross-spawn "^7.0.0" + istanbul-lib-coverage "^3.0.0-alpha.1" + make-dir "^3.0.0" + p-map "^3.0.0" + rimraf "^3.0.0" + uuid "^3.3.3" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + isurl@^1.0.0-alpha5: version "1.0.0" resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" @@ -8415,14 +8776,7 @@ js-yaml@3.14.0: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" - integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== - dependencies: - argparse "^2.0.1" - -js-yaml@^3.13.1: +js-yaml@3.x, js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -8430,6 +8784,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" + integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== + dependencies: + argparse "^2.0.1" + jsan@^3.1.13: version "3.1.13" resolved "https://registry.yarnpkg.com/jsan/-/jsan-3.1.13.tgz#4de8c7bf8d1cfcd020c313d438f930cec4b91d86" @@ -8605,6 +8966,11 @@ jsonify@~0.0.0: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= +jsonschema@^1.2.4: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.0.tgz#1afa34c4bc22190d8e42271ec17ac8b3404f87b2" + integrity sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw== + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -8687,6 +9053,13 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + level-codec@9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.1.tgz#042f4aa85e56d4328ace368c950811ba802b7247" @@ -9082,6 +9455,11 @@ lodash.flatten@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= + lodash.isequal@^4.0.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" @@ -9136,6 +9514,11 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" +lodash.toarray@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" + integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= + lodash.tostring@^4.0.0: version "4.1.4" resolved "https://registry.yarnpkg.com/lodash.tostring/-/lodash.tostring-4.1.4.tgz#560c27d1f8eadde03c2cce198fef5c031d8298fb" @@ -9286,11 +9669,25 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" +make-dir@^3.0.0, make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -9337,6 +9734,15 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + memdown@1.4.1, memdown@^1.0.0: version "1.4.1" resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" @@ -9383,7 +9789,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0: +merge2@^1.2.3, merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -9508,7 +9914,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mimic-fn@^2.1.0: +mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -9599,7 +10005,7 @@ mkdirp@0.5.1: dependencies: minimist "0.0.8" -mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1: +mkdirp@0.5.5, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -9851,6 +10257,11 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +neo-async@^2.6.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + next-tick@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" @@ -9881,6 +10292,13 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== +node-emoji@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" + integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw== + dependencies: + lodash.toarray "^4.4.0" + node-environment-flags@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" @@ -9960,6 +10378,13 @@ node-pre-gyp@^0.11.0: semver "^5.3.0" tar "^4" +node-preload@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301" + integrity sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ== + dependencies: + process-on-spawn "^1.0.0" + node-releases@^1.1.70: version "1.1.71" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" @@ -9975,6 +10400,13 @@ noop-fn@^1.0.0: resolved "https://registry.yarnpkg.com/noop-fn/-/noop-fn-1.0.0.tgz#5f33d47f13d2150df93e0cb036699e982f78ffbf" integrity sha1-XzPUfxPSFQ35PgywNmmemC94/78= +nopt@3.x: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= + dependencies: + abbrev "1" + nopt@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" @@ -10031,6 +10463,13 @@ npm-packlist@^1.1.6: npm-bundled "^1.0.1" npm-normalize-package-bin "^1.0.1" +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + npm-run-path@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -10078,6 +10517,39 @@ number-to-bn@1.7.0: resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.4.tgz#2285631f34a95f0d0395cd900c96ed39b58f346e" integrity sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ== +nyc@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02" + integrity sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A== + dependencies: + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + caching-transform "^4.0.0" + convert-source-map "^1.7.0" + decamelize "^1.2.0" + find-cache-dir "^3.2.0" + find-up "^4.1.0" + foreground-child "^2.0.0" + get-package-type "^0.1.0" + glob "^7.1.6" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-hook "^3.0.0" + istanbul-lib-instrument "^4.0.0" + istanbul-lib-processinfo "^2.0.2" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + make-dir "^3.0.0" + node-preload "^0.2.1" + p-map "^3.0.0" + process-on-spawn "^1.0.0" + resolve-from "^5.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + spawn-wrap "^2.0.0" + test-exclude "^6.0.0" + yargs "^15.0.2" + oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" @@ -10200,7 +10672,7 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -10315,6 +10787,15 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" +os-locale@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -10338,11 +10819,21 @@ p-cancelable@^1.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + p-limit@3.1.0, p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -10392,6 +10883,13 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" @@ -10416,6 +10914,16 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-hash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" + integrity sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ== + dependencies: + graceful-fs "^4.1.15" + hasha "^5.0.0" + lodash.flattendeep "^4.4.0" + release-zalgo "^1.0.0" + param-case@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" @@ -10587,7 +11095,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^2.0.1: +path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= @@ -10657,6 +11165,11 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -10679,6 +11192,13 @@ pkg-conf@^1.1.2: object-assign "^4.0.1" symbol "^0.2.1" +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + please-upgrade-node@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" @@ -11030,6 +11550,13 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process-on-spawn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" + integrity sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg== + dependencies: + fromentries "^1.2.0" + process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -11418,6 +11945,13 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" +recursive-readdir@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" + integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== + dependencies: + minimatch "3.0.4" + redux-cli-logger@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/redux-cli-logger/-/redux-cli-logger-2.1.0.tgz#7e546502a4b08c7fac4fe2faee2326a6326cb4a1" @@ -11572,6 +12106,13 @@ relay-runtime@10.1.0: "@babel/runtime" "^7.0.0" fbjs "^3.0.0" +release-zalgo@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" + integrity sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA= + dependencies: + es6-error "^4.0.1" + remote-redux-devtools@^0.5.12: version "0.5.16" resolved "https://registry.yarnpkg.com/remote-redux-devtools/-/remote-redux-devtools-0.5.16.tgz#95b1a4a1988147ca04f3368f3573b661748b3717" @@ -11705,6 +12246,11 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve@1.1.x: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= + resolve@1.17.0, resolve@~1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" @@ -11772,7 +12318,7 @@ rimraf@^2.2.8, rimraf@^2.6.1, rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.2: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -11869,6 +12415,26 @@ sc-formatter@^3.0.1: resolved "https://registry.yarnpkg.com/sc-formatter/-/sc-formatter-3.0.2.tgz#9abdb14e71873ce7157714d3002477bbdb33c4e6" integrity sha512-9PbqYBpCq+OoEeRQ3QfFIGE6qwjjBcd2j7UjgDlhnZbtSnuGgHdcRklPKYGuYFH82V/dwd+AIpu8XvA1zqTd+A== +sc-istanbul@^0.4.5: + version "0.4.6" + resolved "https://registry.yarnpkg.com/sc-istanbul/-/sc-istanbul-0.4.6.tgz#cf6784355ff2076f92d70d59047d71c13703e839" + integrity sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g== + dependencies: + abbrev "1.0.x" + async "1.x" + escodegen "1.8.x" + esprima "2.7.x" + glob "^5.0.15" + handlebars "^4.0.1" + js-yaml "3.x" + mkdirp "0.5.x" + nopt "3.x" + once "1.x" + resolve "1.1.x" + supports-color "^3.1.0" + which "^1.1.1" + wordwrap "^1.0.0" + scrypt-js@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" @@ -11920,7 +12486,7 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -12249,6 +12815,31 @@ solc@^0.6.3: semver "^5.5.0" tmp "0.0.33" +solidity-coverage@^0.7.16: + version "0.7.16" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.7.16.tgz#c8c8c46baa361e2817bbf275116ddd2ec90a55fb" + integrity sha512-ttBOStywE6ZOTJmmABSg4b8pwwZfYKG8zxu40Nz+sRF5bQX7JULXWj/XbX0KXps3Fsp8CJXg8P29rH3W54ipxw== + dependencies: + "@solidity-parser/parser" "^0.12.0" + "@truffle/provider" "^0.2.24" + chalk "^2.4.2" + death "^1.1.0" + detect-port "^1.3.0" + fs-extra "^8.1.0" + ganache-cli "^6.11.0" + ghost-testrpc "^0.0.2" + global-modules "^2.0.0" + globby "^10.0.1" + jsonschema "^1.2.4" + lodash "^4.17.15" + node-emoji "^1.10.0" + pify "^4.0.1" + recursive-readdir "^2.2.2" + sc-istanbul "^0.4.5" + semver "^7.3.4" + shelljs "^0.8.3" + web3-utils "^1.3.0" + source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -12298,6 +12889,13 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +source-map@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + integrity sha1-2rc/vPwrqBm03gO9b26qSBZLP50= + dependencies: + amdefine ">=0.0.4" + spark-md5@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.0.tgz#3722227c54e2faf24b1dc6d933cc144e6f71bfef" @@ -12308,6 +12906,18 @@ spark-md5@3.0.1: resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.1.tgz#83a0e255734f2ab4e5c466e5a2cfc9ba2aa2124d" integrity sha512-0tF3AGSD1ppQeuffsLDIOWlKUd3lS92tFxcsrh5Pe3ZphhnoK+oXIBTzOAThZCiuINZLvpiLH/1VS1/ANEJVig== +spawn-wrap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" + integrity sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg== + dependencies: + foreground-child "^2.0.0" + is-windows "^1.0.2" + make-dir "^3.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + which "^2.0.1" + spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" @@ -12558,6 +13168,16 @@ strip-bom@2.X, strip-bom@^2.0.0: dependencies: is-utf8 "^0.2.0" +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" @@ -12632,6 +13252,13 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= +supports-color@^3.1.0: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= + dependencies: + has-flag "^1.0.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -12748,6 +13375,15 @@ tar@^4, tar@^4.0.2: safe-buffer "^5.1.2" yallist "^3.0.3" +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + test-value@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/test-value/-/test-value-2.1.0.tgz#11da6ff670f3471a73b625ca4f3fdcf7bb748291" @@ -13096,7 +13732,7 @@ type-fest@^0.7.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== -type-fest@^0.8.1: +type-fest@^0.8.0, type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== @@ -13208,6 +13844,11 @@ ua-parser-js@^0.7.18: resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c" integrity sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw== +uglify-js@^3.1.4: + version "3.13.2" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.2.tgz#fe10319861bccc8682bfe2e8151fbdd8aa921c44" + integrity sha512-SbMu4D2Vo95LMC/MetNaso1194M1htEA+JrqE9Hk+G2DhI+itfS9TRu9ZKeCahLDNa/J3n4MqUJ/fOHMzQpRWw== + ultron@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" @@ -13410,7 +14051,7 @@ uuid@8.1.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d" integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg== -uuid@^3.1.0, uuid@^3.3.2: +uuid@^3.1.0, uuid@^3.3.2, uuid@^3.3.3: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== @@ -13984,7 +14625,7 @@ web3-utils@1.2.9: underscore "1.9.1" utf8 "3.0.0" -web3-utils@^1.0.0-beta.31: +web3-utils@^1.0.0-beta.31, web3-utils@^1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.3.4.tgz#9b1aa30d7549f860b573e7bb7e690999e7192198" integrity sha512-/vC2v0MaZNpWooJfpRw63u0Y3ag2gNjAWiLtMSL6QQLmCqCy4SQIndMt/vRyx0uMoeGt1YTwSXEcHjUzOhLg0A== @@ -14097,7 +14738,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@1.3.1, which@^1.2.9: +which@1.3.1, which@^1.1.1, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -14128,6 +14769,11 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + workerpool@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.0.tgz#85aad67fa1a2c8ef9386a1b43539900f61d03d58" @@ -14187,6 +14833,16 @@ write-file-atomic@^2.0.0: imurmurhash "^0.1.4" signal-exit "^3.0.2" +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + write-stream@~0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/write-stream/-/write-stream-0.4.3.tgz#83cc8c0347d0af6057a93862b4e3ae01de5c81c1" @@ -14341,7 +14997,7 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== -yargs-parser@13.1.2, yargs-parser@^13.1.2: +yargs-parser@13.1.2, yargs-parser@^13.1.0, yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== @@ -14413,6 +15069,23 @@ yargs-unparser@2.0.0: flat "^5.0.2" is-plain-obj "^2.1.0" +yargs@13.2.4: + version "13.2.4" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" + integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + os-locale "^3.1.0" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.0" + yargs@13.3.2, yargs@^13.3.0: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" @@ -14477,7 +15150,7 @@ yargs@^14.2.3: y18n "^4.0.0" yargs-parser "^15.0.1" -yargs@^15.3.1: +yargs@^15.0.2, yargs@^15.3.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== From a37bc5fe1c16b3bee24f01dcb91c9230c6b49ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Fri, 12 Mar 2021 13:15:23 +0100 Subject: [PATCH 05/24] Refactor signatures --- .../{signatures.ts => signatures/SafeSignature.ts} | 11 +++-------- src/utils/signatures/index.ts | 11 +++++++++++ src/utils/transactions.ts | 8 ++++---- 3 files changed, 18 insertions(+), 12 deletions(-) rename src/utils/{signatures.ts => signatures/SafeSignature.ts} (73%) create mode 100644 src/utils/signatures/index.ts diff --git a/src/utils/signatures.ts b/src/utils/signatures/SafeSignature.ts similarity index 73% rename from src/utils/signatures.ts rename to src/utils/signatures/SafeSignature.ts index 23a213b57..8d25e8858 100644 --- a/src/utils/signatures.ts +++ b/src/utils/signatures/SafeSignature.ts @@ -18,18 +18,13 @@ export class EthSignSignature implements SafeSignature { */ constructor(signer: string, signature: string) { this.signer = signer - this.data = signature - .replace('0x', '') - .replace(/00$/, '1f') - .replace(/1b$/, '1f') - .replace(/01$/, '20') - .replace(/1c$/, '20') + this.data = signature.replace(/1b$/, '1f').replace(/1c$/, '20') } /** * Returns the static part of the Safe signature. * - * @Returns The static part of the Safe signature + * @returns The static part of the Safe signature */ staticPart(/* dynamicOffset: number */) { return this.data @@ -38,7 +33,7 @@ export class EthSignSignature implements SafeSignature { /** * Returns the dynamic part of the Safe signature. * - * @Returns The dynamic part of the Safe signature + * @returns The dynamic part of the Safe signature */ dynamicPart() { return '' diff --git a/src/utils/signatures/index.ts b/src/utils/signatures/index.ts new file mode 100644 index 000000000..2e0014ce3 --- /dev/null +++ b/src/utils/signatures/index.ts @@ -0,0 +1,11 @@ +import { EthSignSignature, SafeSignature } from './SafeSignature' + +export function generatePreValidatedSignature(ownerAddress: string): SafeSignature { + const signature = + '0x000000000000000000000000' + + ownerAddress.slice(2) + + '0000000000000000000000000000000000000000000000000000000000000000' + + '01' + + return new EthSignSignature(ownerAddress, signature) +} diff --git a/src/utils/transactions.ts b/src/utils/transactions.ts index da10f5127..fbe5f03a6 100644 --- a/src/utils/transactions.ts +++ b/src/utils/transactions.ts @@ -1,5 +1,5 @@ import { zeroAddress } from './constants' -import { SafeSignature } from './signatures' +import { SafeSignature } from './signatures/SafeSignature' export enum OperationType { Call, // 0 @@ -61,9 +61,9 @@ export class SafeTransaction { let staticParts = '' let dynamicParts = '' signers.forEach((signerAddress) => { - const signer = this.signatures.get(signerAddress)!! - staticParts += signer.staticPart(/*baseOffset + dynamicParts.length / 2*/) - dynamicParts += signer.dynamicPart() + const signature = this.signatures.get(signerAddress) + staticParts += signature?.staticPart(/*baseOffset + dynamicParts.length / 2*/).slice(2) + dynamicParts += signature?.dynamicPart() }) return '0x' + staticParts + dynamicParts } From 3f91116c9568afc253a447648e0fd067509d0e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Fri, 12 Mar 2021 13:59:07 +0100 Subject: [PATCH 06/24] Allow connect to SDK with signer or provider --- src/EthersSafe.ts | 114 +++++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 53 deletions(-) diff --git a/src/EthersSafe.ts b/src/EthersSafe.ts index 5aec60244..e68af97a3 100644 --- a/src/EthersSafe.ts +++ b/src/EthersSafe.ts @@ -1,28 +1,48 @@ -import { BigNumber, Wallet } from 'ethers' +import { Provider } from '@ethersproject/providers' +import { BigNumber, ContractTransaction, Wallet } from 'ethers' import { GnosisSafe } from '../typechain' import SafeAbi from './abis/SafeAbiV1-2-0.json' import Safe from './Safe' import { areAddressesEqual } from './utils' -import { EthSignSignature, SafeSignature } from './utils/signatures' +import { EthSignSignature, SafeSignature } from './utils/signatures/SafeSignature' import { SafeTransaction } from './utils/transactions' class EthersSafe implements Safe { - #contract: GnosisSafe + #contract!: GnosisSafe #ethers: any - #signer: Wallet + #provider!: Provider + #signer?: Wallet /** * Creates an instance of the Safe Core SDK. * * @param ethers - Ethers v5 library - * @param signer - Ethers signer + * @param providerOrSigner - Ethers provider or signer * @param safeAddress - The address of the Safe account to use * @returns The Safe Core SDK instance */ - constructor(ethers: any, signer: Wallet, safeAddress: string) { + constructor(ethers: any, safeAddress: string, providerOrSigner: Provider | Wallet) { this.#ethers = ethers - this.#signer = signer - this.#contract = new ethers.Contract(safeAddress, SafeAbi, signer) + this.connect(safeAddress, providerOrSigner) + } + + connect(safeAddress: string, providerOrSigner: Provider | Wallet): void { + this.#contract = new this.#ethers.Contract(safeAddress, SafeAbi, providerOrSigner) + if (Wallet.isSigner(providerOrSigner)) { + this.#signer = providerOrSigner + this.#provider = providerOrSigner.provider + return + } + this.#signer = undefined + this.#provider = providerOrSigner + } + + getProvider(): Provider { + return this.#provider + } + + getSigner(): Wallet | undefined { + return this.#signer } /** @@ -67,7 +87,7 @@ class EthersSafe implements Safe { * @returns The chainId of the connected network */ async getNetworkId(): Promise { - return (await this.#signer.provider.getNetwork()).chainId + return (await this.#provider.getNetwork()).chainId } /** @@ -76,7 +96,7 @@ class EthersSafe implements Safe { * @returns The ETH balance of the Safe */ async getBalance(): Promise { - return BigNumber.from(await this.#signer.provider.getBalance(this.getAddress())) + return BigNumber.from(await this.#provider.getBalance(this.getAddress())) } /** @@ -122,16 +142,26 @@ class EthersSafe implements Safe { } /** - * Signs data using the current owner account. + * Signs a hash using the current owner account. * - * @param hash - The data to sign + * @param hash - The hash to sign * @returns The Safe signature */ async signTransactionHash(hash: string): Promise { - const address = await this.#signer.address + if (!this.#signer) { + throw new Error('No signer provided') + } + const owners = await this.getOwners() + if ( + !owners.find( + (owner: string) => this.#signer && areAddressesEqual(owner, this.#signer.address) + ) + ) { + throw new Error('Transactions can only be signed by Safe owners') + } const messageArray = this.#ethers.utils.arrayify(hash) const signature = await this.#signer.signMessage(messageArray) - return new EthSignSignature(address, signature) + return new EthSignSignature(this.#signer.address, signature) } /** @@ -140,48 +170,26 @@ class EthersSafe implements Safe { * @param safeTransaction - The Safe transaction to be signed */ async signTransaction(safeTransaction: SafeTransaction): Promise { - const owners = await this.getOwners() - if (!owners.find((owner: string) => areAddressesEqual(owner, this.#signer.address))) { - throw new Error('Transactions can only be confirmed by Safe owners') - } const txHash = await this.getTransactionHash(safeTransaction) const signature = await this.signTransactionHash(txHash) safeTransaction.signatures.set(signature.signer, signature) } - /** - * Returns the encoding of a Safe transaction. - * - * @param transaction - The Safe transaction - * @returns The encoding of the Safe transaction - */ - async encodeTransaction(transaction: SafeTransaction): Promise { - const encodedTx = await this.#contract.interface.encodeFunctionData('execTransaction', [ - transaction.data.to, - transaction.data.value, - transaction.data.data, - transaction.data.operation, - transaction.data.safeTxGas, - transaction.data.baseGas, - transaction.data.gasPrice, - transaction.data.gasToken, - transaction.data.refundReceiver, - transaction.encodedSignatures() - ]) - return encodedTx - } /** * Executes a Safe transaction. * - * @param transaction - The Safe transaction to execute + * @param safeTransaction - The Safe transaction to execute * @param options - Execution configuration options * @returns The Safe transaction response */ - async executeTransaction(transaction: SafeTransaction, options?: any): Promise { + async executeTransaction( + safeTransaction: SafeTransaction, + options?: any + ): Promise { const threshold = await this.getThreshold() - if (threshold.gt(transaction.signatures.size)) { - const signaturesMissing = threshold.sub(transaction.signatures.size).toNumber() + if (threshold.gt(safeTransaction.signatures.size)) { + const signaturesMissing = threshold.sub(safeTransaction.signatures.size).toNumber() throw new Error( `There ${signaturesMissing > 1 ? 'are' : 'is'} ${signaturesMissing} signature${ signaturesMissing > 1 ? 's' : '' @@ -189,16 +197,16 @@ class EthersSafe implements Safe { ) } const txResponse = await this.#contract.execTransaction( - transaction.data.to, - transaction.data.value, - transaction.data.data, - transaction.data.operation, - transaction.data.safeTxGas, - transaction.data.baseGas, - transaction.data.gasPrice, - transaction.data.gasToken, - transaction.data.refundReceiver, - transaction.encodedSignatures(), + safeTransaction.data.to, + safeTransaction.data.value, + safeTransaction.data.data, + safeTransaction.data.operation, + safeTransaction.data.safeTxGas, + safeTransaction.data.baseGas, + safeTransaction.data.gasPrice, + safeTransaction.data.gasToken, + safeTransaction.data.refundReceiver, + safeTransaction.encodedSignatures(), { ...options } ) return txResponse From 32f5d455e417fb153c8026ce3fcbab8d123af20e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Tue, 16 Mar 2021 20:04:16 +0100 Subject: [PATCH 07/24] Add on-chain signatures logic --- src/EthersSafe.ts | 94 +++++++++++++++++++++++++++++++++++++++++++++++ src/Safe.ts | 19 +++++++--- 2 files changed, 107 insertions(+), 6 deletions(-) diff --git a/src/EthersSafe.ts b/src/EthersSafe.ts index e68af97a3..3bfe7a948 100644 --- a/src/EthersSafe.ts +++ b/src/EthersSafe.ts @@ -4,6 +4,7 @@ import { GnosisSafe } from '../typechain' import SafeAbi from './abis/SafeAbiV1-2-0.json' import Safe from './Safe' import { areAddressesEqual } from './utils' +import { generatePreValidatedSignature } from './utils/signatures' import { EthSignSignature, SafeSignature } from './utils/signatures/SafeSignature' import { SafeTransaction } from './utils/transactions' @@ -175,6 +176,51 @@ class EthersSafe implements Safe { safeTransaction.signatures.set(signature.signer, signature) } + /** + * Approves a hash using the current owner account. + * + * @param hash - The hash to approve + * @param skipOnChainApproval - TRUE to avoid the Safe transaction to be approved on-chain + * @returns The pre-validated signature + */ + async approveTransactionHash( + hash: string, + skipOnChainApproval?: boolean + ): Promise { + if (!this.#signer) { + throw new Error('No signer provided') + } + const owners = await this.getOwners() + if ( + !owners.find( + (owner: string) => this.#signer && areAddressesEqual(owner, this.#signer.address) + ) + ) { + throw new Error('Transaction hashes can only be approved by Safe owners') + } + if (!skipOnChainApproval) { + await this.#contract.approveHash(hash) + } + return generatePreValidatedSignature(this.#signer.address) + } + + /** + * Returns a list of owners who have approved a specific Safe transaction + * + * @param txHash - The Safe transaction hash + * @returns The list of owners + */ + async getOwnersWhoApprovedTx(txHash: string): Promise { + const owners = await this.getOwners() + let ownersWhoApproved: string[] = [] + for (const owner of owners) { + const approved = await this.#contract.approvedHashes(owner, txHash) + if (approved.gt(0)) { + ownersWhoApproved.push(owner) + } + } + return ownersWhoApproved + } /** * Executes a Safe transaction. @@ -196,6 +242,54 @@ class EthersSafe implements Safe { } missing` ) } + + const txResponse = await this.#contract.execTransaction( + safeTransaction.data.to, + safeTransaction.data.value, + safeTransaction.data.data, + safeTransaction.data.operation, + safeTransaction.data.safeTxGas, + safeTransaction.data.baseGas, + safeTransaction.data.gasPrice, + safeTransaction.data.gasToken, + safeTransaction.data.refundReceiver, + safeTransaction.encodedSignatures(), + { ...options } + ) + return txResponse + } + + async executeTransactionOnChain( + safeTransaction: SafeTransaction, + options?: any + ): Promise { + if (!this.#signer) { + throw new Error('No signer provided') + } + + const txHash = await this.getTransactionHash(safeTransaction) + const ownersWhoApprovedTx = await this.getOwnersWhoApprovedTx(txHash) + for (const owner of ownersWhoApprovedTx) { + safeTransaction.signatures.set(owner, generatePreValidatedSignature(owner)) + } + const owners = await this.getOwners() + if (owners.includes(this.#signer.address)) { + safeTransaction.signatures.set( + this.#signer.address, + generatePreValidatedSignature(this.#signer.address) + ) + } + + const threshold = await this.getThreshold() + if (threshold.gt(safeTransaction.signatures.size)) { + const signaturesMissing = threshold.sub(safeTransaction.signatures.size).toNumber() + throw new Error( + `There ${signaturesMissing > 1 ? 'are' : 'is'} ${signaturesMissing} signature${ + signaturesMissing > 1 ? 's' : '' + } missing` + ) + } + const txResponse = await this.#contract.execTransaction( safeTransaction.data.to, safeTransaction.data.value, diff --git a/src/Safe.ts b/src/Safe.ts index f595927e3..de6db59b7 100644 --- a/src/Safe.ts +++ b/src/Safe.ts @@ -1,8 +1,10 @@ -import { BigNumber } from 'ethers' -import { SafeSignature } from 'utils/signatures' +import { Provider } from '@ethersproject/providers' +import { BigNumber, ContractTransaction, Wallet } from 'ethers' +import { SafeSignature } from 'utils/signatures/SafeSignature' import { SafeTransaction } from './utils/transactions' interface Safe { + connect(safeAddress: string, providerOrSigner: Provider | Wallet): void getAddress(): string getContractVersion(): Promise getOwners(): Promise @@ -11,11 +13,16 @@ interface Safe { getBalance(): Promise getModules(): Promise isModuleEnabled(moduleAddress: string): Promise - getTransactionHash(transaction: SafeTransaction): Promise + getTransactionHash(safeTransaction: SafeTransaction): Promise signTransactionHash(hash: string): Promise - signTransaction(transaction: SafeTransaction): Promise - encodeTransaction(transaction: SafeTransaction): Promise - executeTransaction(transaction: SafeTransaction, options?: any): Promise + signTransaction(safeTransaction: SafeTransaction): Promise + approveTransactionHash(hash: string, skipOnChainApproval: boolean): Promise + getOwnersWhoApprovedTx(txHash: string): Promise + executeTransaction(safeTransaction: SafeTransaction, options?: any): Promise + executeTransactionOnChain( + safeTransaction: SafeTransaction, + options?: any + ): Promise } export default Safe From d5e79abe617adb1d729535f8c93b6785a8be1a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Tue, 16 Mar 2021 20:17:32 +0100 Subject: [PATCH 08/24] Update core tests --- tests/core.test.ts | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tests/core.test.ts b/tests/core.test.ts index b1e6c5b05..c14328b93 100644 --- a/tests/core.test.ts +++ b/tests/core.test.ts @@ -17,10 +17,26 @@ describe('Safe Core SDK', () => { } }) + describe('connect', async () => { + it('connect signer', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1) + chai.expect(safeSdk.getProvider()).to.be.eq(user1.provider) + chai.expect(safeSdk.getSigner()).to.be.eq(user1) + }) + + it('connect provider', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1.provider) + chai.expect(safeSdk.getProvider()).to.be.eq(user1.provider) + chai.expect(safeSdk.getSigner()).to.be.undefined + }) + }) + describe('getContractVersion', async () => { it('should return the Safe contract version', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, user1, safe.address) + const safeSdk = new EthersSafe(ethers, safe.address, user1) const contractVersion = await safeSdk.getContractVersion() chai.expect(contractVersion).to.be.eq('1.2.0') }) @@ -29,7 +45,7 @@ describe('Safe Core SDK', () => { describe('getAddress', async () => { it('should return the Safe contract address', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, user1, safe.address) + const safeSdk = new EthersSafe(ethers, safe.address, user1) chai.expect(safeSdk.getAddress()).to.be.eq(safe.address) }) }) @@ -37,7 +53,7 @@ describe('Safe Core SDK', () => { describe('getOwners', async () => { it('should return the list of Safe owners', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, user1, safe.address) + const safeSdk = new EthersSafe(ethers, safe.address, user1) const owners = await safeSdk.getOwners() chai.expect(owners.length).to.be.eq(2) chai.expect(owners[0]).to.be.eq(user1.address) @@ -48,7 +64,7 @@ describe('Safe Core SDK', () => { describe('getThreshold', async () => { it('should return the Safe threshold', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, user1, safe.address) + const safeSdk = new EthersSafe(ethers, safe.address, user1) chai.expect(await safeSdk.getThreshold()).to.be.eq(2) }) }) @@ -56,7 +72,7 @@ describe('Safe Core SDK', () => { describe('getNetworkId', async () => { it('should return the chainId of the current network', async () => { const { safe, chainId } = await setupTests() - const safeSdk = new EthersSafe(ethers, user1, safe.address) + const safeSdk = new EthersSafe(ethers, safe.address, user1) chai.expect(await safeSdk.getNetworkId()).to.be.eq(chainId) }) }) @@ -64,7 +80,7 @@ describe('Safe Core SDK', () => { describe('getBalance', async () => { it('should return the balance of the Safe contract', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, user1, safe.address) + const safeSdk = new EthersSafe(ethers, safe.address, user1) chai.expect(await safeSdk.getBalance()).to.be.eq(0) await user1.sendTransaction({ to: safe.address, @@ -77,7 +93,7 @@ describe('Safe Core SDK', () => { describe('getModules', async () => { it('should return an empty array if there are no modules enabled', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, user1, safe.address) + const safeSdk = new EthersSafe(ethers, safe.address, user1) chai.expect((await safeSdk.getModules()).length).to.be.eq(0) }) @@ -89,7 +105,7 @@ describe('Safe Core SDK', () => { describe('isModuleEnabled', async () => { it('should return false if a module is not enabled', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, user1, safe.address) + const safeSdk = new EthersSafe(ethers, safe.address, user1) chai.expect(await safeSdk.isModuleEnabled(user1.address)).to.be.false }) From 38d9ca4a09324c049bb7fc29c00dd471f60621ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Tue, 16 Mar 2021 21:00:45 +0100 Subject: [PATCH 09/24] Update off-chain signatures tests --- ...s.test.ts => off-chain-signatures.test.ts} | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) rename tests/{signatures.test.ts => off-chain-signatures.test.ts} (72%) diff --git a/tests/signatures.test.ts b/tests/off-chain-signatures.test.ts similarity index 72% rename from tests/signatures.test.ts rename to tests/off-chain-signatures.test.ts index e7f88efcc..6091b43f4 100644 --- a/tests/signatures.test.ts +++ b/tests/off-chain-signatures.test.ts @@ -1,62 +1,76 @@ import chai from 'chai' import chaiAsPromised from 'chai-as-promised' import { deployments, ethers, waffle } from 'hardhat' -import EthersSafe, { SafeTransaction } from '../src/index' +import EthersSafe, { SafeTransaction } from '../src' import { getSafeWithOwners } from './utils/setup' chai.use(chaiAsPromised) -describe('Safe Core SDK', () => { +describe('Off-chain signatures', () => { const [user1, user2, user3] = waffle.provider.getWallets() const setupTests = deployments.createFixture(async ({ deployments }) => { await deployments.fixture() return { - safe: await getSafeWithOwners([user1.address, user2.address]), - chainId: (await waffle.provider.getNetwork()).chainId + safe: await getSafeWithOwners([user1.address, user2.address]) } }) + describe('signTransactionHash', async () => { + it('should fail if signer is not provided', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1.provider) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + const txHash = await safeSdk.getTransactionHash(tx) + chai.expect(safeSdk.signTransactionHash(txHash)).to.be.rejectedWith('No signer provided') + }) + }) + describe('signTransaction', async () => { - it('should fail if signature is not added by an owner', async () => { + it('should fail if signature is added by an account that is not an owner', async () => { const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user3) const tx = new SafeTransaction({ to: user1.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - const safeSdk = new EthersSafe(ethers, user3, safe.address) chai .expect(safeSdk.signTransaction(tx)) - .to.be.rejectedWith('Transactions can only be confirmed by Safe owners') + .to.be.rejectedWith('Transactions can only be signed by Safe owners') }) - it('should ignore duplicated signatures', async () => { + it('should add owner signature', async () => { const { safe } = await setupTests() const tx = new SafeTransaction({ to: user1.address, value: '0', data: '0x', - nonce: await safe.nonce() + nonce: (await safe.nonce()).toString() }) chai.expect(tx.signatures.size).to.be.eq(0) - const safeSdk = new EthersSafe(ethers, user1, safe.address) - await safeSdk.signTransaction(tx) - chai.expect(tx.signatures.size).to.be.eq(1) + const safeSdk = new EthersSafe(ethers, safe.address, user1) await safeSdk.signTransaction(tx) chai.expect(tx.signatures.size).to.be.eq(1) }) - it('should add owner signature', async () => { + it('should ignore duplicated signatures', async () => { const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ to: user1.address, value: '0', data: '0x', - nonce: (await safe.nonce()).toString() + nonce: await safe.nonce() }) chai.expect(tx.signatures.size).to.be.eq(0) - const safeSdk = new EthersSafe(ethers, user1, safe.address) + await safeSdk.signTransaction(tx) + chai.expect(tx.signatures.size).to.be.eq(1) await safeSdk.signTransaction(tx) chai.expect(tx.signatures.size).to.be.eq(1) }) @@ -65,13 +79,13 @@ describe('Safe Core SDK', () => { describe('execTransaction', async () => { it('should fail if there are not enough signatures (1 missing)', async () => { const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - const safeSdk = new EthersSafe(ethers, user1, safe.address) await safeSdk.signTransaction(tx) chai.expect(tx.signatures.size).to.be.eq(1) chai @@ -85,13 +99,13 @@ describe('Safe Core SDK', () => { it('should fail if there are not enough signatures (>1 missing)', async () => { const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - const safeSdk = new EthersSafe(ethers, user1, safe.address) chai .expect(safeSdk.executeTransaction(tx)) .to.be.rejectedWith('There are 2 signatures missing') @@ -99,6 +113,7 @@ describe('Safe Core SDK', () => { it('should execute transaction when there are enough signatures', async () => { const { safe } = await setupTests() + let safeSdk = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ to: safe.address, value: '0', @@ -106,10 +121,9 @@ describe('Safe Core SDK', () => { nonce: (await safe.nonce()).toString() }) chai.expect(tx.signatures.size).to.be.eq(0) - let safeSdk = new EthersSafe(ethers, user1, safe.address) await safeSdk.signTransaction(tx) chai.expect(tx.signatures.size).to.be.eq(1) - safeSdk = new EthersSafe(ethers, user2, safe.address) + safeSdk.connect(safe.address, user2) await safeSdk.signTransaction(tx) chai.expect(tx.signatures.size).to.be.eq(2) const txResponse = await safeSdk.executeTransaction(tx) From 0d321244f047c0cdc90d20fd02afe79b64e4da9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Thu, 18 Mar 2021 09:46:13 +0100 Subject: [PATCH 10/24] Add on-chain signatures tests --- tests/on-chain-signatures.test.ts | 203 ++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 tests/on-chain-signatures.test.ts diff --git a/tests/on-chain-signatures.test.ts b/tests/on-chain-signatures.test.ts new file mode 100644 index 000000000..ae5fa86b1 --- /dev/null +++ b/tests/on-chain-signatures.test.ts @@ -0,0 +1,203 @@ +import chai from 'chai' +import chaiAsPromised from 'chai-as-promised' +import { deployments, ethers, waffle } from 'hardhat' +import EthersSafe, { SafeTransaction } from '../src' +import { getSafeWithOwners } from './utils/setup' +chai.use(chaiAsPromised) + +describe('On-chain signatures', () => { + const [user1, user2, user3] = waffle.provider.getWallets() + + const setupTests = deployments.createFixture(async ({ deployments }) => { + await deployments.fixture() + return { + safe: await getSafeWithOwners([user1.address, user2.address]) + } + }) + + describe('approveTransactionHash', async () => { + it('should fail if signer is not provided', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1.provider) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + const txHash = await safeSdk.getTransactionHash(tx) + chai.expect(safeSdk.approveTransactionHash(txHash)).to.be.rejectedWith('No signer provided') + }) + + it('should fail if a transaction hash is approved by an account that is not an owner', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user3) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + const hash = await safeSdk.getTransactionHash(tx) + chai + .expect(safeSdk.approveTransactionHash(hash)) + .to.be.rejectedWith('Transaction hashes can only be approved by Safe owners') + }) + + it('should return signature without approving the transaction hash on-chain if specified', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + const txHash = await safeSdk.getTransactionHash(tx) + await safeSdk.approveTransactionHash(txHash, true) + chai.expect(await safe.approvedHashes(user1.address, txHash)).to.be.equal(0) + }) + + it('should approve a transaction hash', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + const hash = await safeSdk.getTransactionHash(tx) + await safeSdk.approveTransactionHash(hash) + chai.expect(await safe.approvedHashes(user1.address, hash)).to.be.equal(1) + }) + + it('should ignore a duplicated signatures', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + const hash = await safeSdk.getTransactionHash(tx) + chai.expect(await safe.approvedHashes(user1.address, hash)).to.be.equal(0) + await safeSdk.approveTransactionHash(hash) + chai.expect(await safe.approvedHashes(user1.address, hash)).to.be.equal(1) + await safeSdk.approveTransactionHash(hash) + chai.expect(await safe.approvedHashes(user1.address, hash)).to.be.equal(1) + }) + }) + + describe('getOwnersWhoApprovedTx', async () => { + it('should return the list of owners who approved a transaction hash', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + const txHash = await safeSdk.getTransactionHash(tx) + const ownersWhoApproved0 = await safeSdk.getOwnersWhoApprovedTx(txHash) + chai.expect(ownersWhoApproved0.length).to.be.eq(0) + await safeSdk.approveTransactionHash(txHash) + const ownersWhoApproved1 = await safeSdk.getOwnersWhoApprovedTx(txHash) + chai.expect(ownersWhoApproved1.length).to.be.eq(1) + safeSdk.connect(safe.address, user2) + await safeSdk.approveTransactionHash(txHash) + const ownersWhoApproved2 = await safeSdk.getOwnersWhoApprovedTx(txHash) + chai.expect(ownersWhoApproved2.length).to.be.eq(2) + }) + }) + + describe('execTransaction', async () => { + it('should fail if signer is not provided', async () => { + const safe = await getSafeWithOwners([user1.address]) + const safeSdk = new EthersSafe(ethers, safe.address, user1.provider) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + chai + .expect(safeSdk.executeTransactionOnChain(tx, { gasLimit: 10000000 })) + .rejectedWith('No signer provided') + }) + + it('should fail if there are not enough signatures (1 missing)', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + await chai + .expect(safeSdk.executeTransactionOnChain(tx, { gasLimit: 10000000 })) + .to.be.rejectedWith('There is 1 signature missing') + }) + + it('should fail if there are not enough signatures (>1 missing)', async () => { + const safe = await getSafeWithOwners([user1.address, user2.address, user3.address]) + const safeSdk = new EthersSafe(ethers, safe.address, user2) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + await chai + .expect(safeSdk.executeTransactionOnChain(tx, { gasLimit: 10000000 })) + .to.be.rejectedWith('There are 2 signatures missing') + }) + + it('should execute a transaction with threshold 1', async () => { + const safe = await getSafeWithOwners([user1.address]) + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + const txResponse = await safeSdk.executeTransactionOnChain(tx, { gasLimit: 10000000 }) + chai.expect(txResponse.hash).not.to.be.null + }) + + it('should execute a transaction with threshold 2', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + const txHash = await safeSdk.getTransactionHash(tx) + await safeSdk.approveTransactionHash(txHash) + safeSdk.connect(safe.address, user2) + const txResponse = await safeSdk.executeTransactionOnChain(tx, { gasLimit: 10000000 }) + chai.expect(txResponse.hash).not.to.be.null + }) + + it('should execute a transaction when is not submitted by an owner', async () => { + const safe = await getSafeWithOwners([user1.address]) + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + await safeSdk.signTransaction(tx) + safeSdk.connect(safe.address, user2) + const txResponse = await safeSdk.executeTransactionOnChain(tx, { gasLimit: 10000000 }) + chai.expect(txResponse.hash).not.to.be.null + }) + }) +}) From c67036a70a1e4ffc4263c1418238dcb9ccb45826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Thu, 18 Mar 2021 11:34:40 +0100 Subject: [PATCH 11/24] Refactor signatures test --- tests/off-chain-signatures.test.ts | 76 ++++++++++------------- tests/on-chain-signatures.test.ts | 98 ++---------------------------- 2 files changed, 37 insertions(+), 137 deletions(-) diff --git a/tests/off-chain-signatures.test.ts b/tests/off-chain-signatures.test.ts index 6091b43f4..23327c143 100644 --- a/tests/off-chain-signatures.test.ts +++ b/tests/off-chain-signatures.test.ts @@ -28,10 +28,8 @@ describe('Off-chain signatures', () => { const txHash = await safeSdk.getTransactionHash(tx) chai.expect(safeSdk.signTransactionHash(txHash)).to.be.rejectedWith('No signer provided') }) - }) - describe('signTransaction', async () => { - it('should fail if signature is added by an account that is not an owner', async () => { + it('should fail if signer is not an owner', async () => { const { safe } = await setupTests() const safeSdk = new EthersSafe(ethers, safe.address, user3) const tx = new SafeTransaction({ @@ -40,94 +38,82 @@ describe('Off-chain signatures', () => { data: '0x', nonce: (await safe.nonce()).toString() }) + const txHash = await safeSdk.getTransactionHash(tx) chai - .expect(safeSdk.signTransaction(tx)) + .expect(safeSdk.signTransactionHash(txHash)) .to.be.rejectedWith('Transactions can only be signed by Safe owners') }) - it('should add owner signature', async () => { + it('should sign a transaction hash with the current signer', async () => { const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ to: user1.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - chai.expect(tx.signatures.size).to.be.eq(0) - const safeSdk = new EthersSafe(ethers, safe.address, user1) - await safeSdk.signTransaction(tx) - chai.expect(tx.signatures.size).to.be.eq(1) + const txHash = await safeSdk.getTransactionHash(tx) + const signature = await safeSdk.signTransactionHash(txHash) + chai.expect(signature.staticPart().length).to.be.eq(132) }) + }) - it('should ignore duplicated signatures', async () => { + describe('signTransaction', async () => { + it('should fail if signer is not provided', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user1) + const safeSdk = new EthersSafe(ethers, safe.address, user1.provider) const tx = new SafeTransaction({ - to: user1.address, + to: safe.address, value: '0', data: '0x', - nonce: await safe.nonce() + nonce: (await safe.nonce()).toString() }) - chai.expect(tx.signatures.size).to.be.eq(0) - await safeSdk.signTransaction(tx) - chai.expect(tx.signatures.size).to.be.eq(1) - await safeSdk.signTransaction(tx) - chai.expect(tx.signatures.size).to.be.eq(1) + chai.expect(safeSdk.signTransaction(tx)).to.be.rejectedWith('No signer provided') }) - }) - describe('execTransaction', async () => { - it('should fail if there are not enough signatures (1 missing)', async () => { + it('should fail if signature is added by an account that is not an owner', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user1) + const safeSdk = new EthersSafe(ethers, safe.address, user3) const tx = new SafeTransaction({ - to: safe.address, + to: user1.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - await safeSdk.signTransaction(tx) - chai.expect(tx.signatures.size).to.be.eq(1) chai - .expect( - safeSdk.executeTransaction(tx, { - gasLimit: 10000000 - }) - ) - .to.be.rejectedWith('There is 1 signature missing') + .expect(safeSdk.signTransaction(tx)) + .to.be.rejectedWith('Transactions can only be signed by Safe owners') }) - it('should fail if there are not enough signatures (>1 missing)', async () => { + it('should add the signature of the current signer', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ - to: safe.address, + to: user1.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - chai - .expect(safeSdk.executeTransaction(tx)) - .to.be.rejectedWith('There are 2 signatures missing') + chai.expect(tx.signatures.size).to.be.eq(0) + const safeSdk = new EthersSafe(ethers, safe.address, user1) + await safeSdk.signTransaction(tx) + chai.expect(tx.signatures.size).to.be.eq(1) }) - it('should execute transaction when there are enough signatures', async () => { + it('should ignore duplicated signatures', async () => { const { safe } = await setupTests() - let safeSdk = new EthersSafe(ethers, safe.address, user1) + const safeSdk = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ - to: safe.address, + to: user1.address, value: '0', data: '0x', - nonce: (await safe.nonce()).toString() + nonce: await safe.nonce() }) chai.expect(tx.signatures.size).to.be.eq(0) await safeSdk.signTransaction(tx) chai.expect(tx.signatures.size).to.be.eq(1) - safeSdk.connect(safe.address, user2) await safeSdk.signTransaction(tx) - chai.expect(tx.signatures.size).to.be.eq(2) - const txResponse = await safeSdk.executeTransaction(tx) - chai.expect(txResponse.hash).not.to.be.null + chai.expect(tx.signatures.size).to.be.eq(1) }) }) }) diff --git a/tests/on-chain-signatures.test.ts b/tests/on-chain-signatures.test.ts index ae5fa86b1..b2746e842 100644 --- a/tests/on-chain-signatures.test.ts +++ b/tests/on-chain-signatures.test.ts @@ -44,7 +44,7 @@ describe('On-chain signatures', () => { .to.be.rejectedWith('Transaction hashes can only be approved by Safe owners') }) - it('should return signature without approving the transaction hash on-chain if specified', async () => { + it('should return the pre-validated signature without approving the transaction hash on-chain if specified', async () => { const { safe } = await setupTests() const safeSdk = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ @@ -54,11 +54,12 @@ describe('On-chain signatures', () => { nonce: (await safe.nonce()).toString() }) const txHash = await safeSdk.getTransactionHash(tx) - await safeSdk.approveTransactionHash(txHash, true) + const signature = await safeSdk.approveTransactionHash(txHash, true) chai.expect(await safe.approvedHashes(user1.address, txHash)).to.be.equal(0) + chai.expect(signature.staticPart().length).to.be.eq(132) }) - it('should approve a transaction hash', async () => { + it('should return the pre-validated signature and approve the transaction hash', async () => { const { safe } = await setupTests() const safeSdk = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ @@ -68,8 +69,9 @@ describe('On-chain signatures', () => { nonce: (await safe.nonce()).toString() }) const hash = await safeSdk.getTransactionHash(tx) - await safeSdk.approveTransactionHash(hash) + const signature = await safeSdk.approveTransactionHash(hash) chai.expect(await safe.approvedHashes(user1.address, hash)).to.be.equal(1) + chai.expect(signature.staticPart().length).to.be.eq(132) }) it('should ignore a duplicated signatures', async () => { @@ -112,92 +114,4 @@ describe('On-chain signatures', () => { chai.expect(ownersWhoApproved2.length).to.be.eq(2) }) }) - - describe('execTransaction', async () => { - it('should fail if signer is not provided', async () => { - const safe = await getSafeWithOwners([user1.address]) - const safeSdk = new EthersSafe(ethers, safe.address, user1.provider) - const tx = new SafeTransaction({ - to: safe.address, - value: '0', - data: '0x', - nonce: (await safe.nonce()).toString() - }) - chai - .expect(safeSdk.executeTransactionOnChain(tx, { gasLimit: 10000000 })) - .rejectedWith('No signer provided') - }) - - it('should fail if there are not enough signatures (1 missing)', async () => { - const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user1) - const tx = new SafeTransaction({ - to: safe.address, - value: '0', - data: '0x', - nonce: (await safe.nonce()).toString() - }) - await chai - .expect(safeSdk.executeTransactionOnChain(tx, { gasLimit: 10000000 })) - .to.be.rejectedWith('There is 1 signature missing') - }) - - it('should fail if there are not enough signatures (>1 missing)', async () => { - const safe = await getSafeWithOwners([user1.address, user2.address, user3.address]) - const safeSdk = new EthersSafe(ethers, safe.address, user2) - const tx = new SafeTransaction({ - to: safe.address, - value: '0', - data: '0x', - nonce: (await safe.nonce()).toString() - }) - await chai - .expect(safeSdk.executeTransactionOnChain(tx, { gasLimit: 10000000 })) - .to.be.rejectedWith('There are 2 signatures missing') - }) - - it('should execute a transaction with threshold 1', async () => { - const safe = await getSafeWithOwners([user1.address]) - const safeSdk = new EthersSafe(ethers, safe.address, user1) - const tx = new SafeTransaction({ - to: safe.address, - value: '0', - data: '0x', - nonce: (await safe.nonce()).toString() - }) - const txResponse = await safeSdk.executeTransactionOnChain(tx, { gasLimit: 10000000 }) - chai.expect(txResponse.hash).not.to.be.null - }) - - it('should execute a transaction with threshold 2', async () => { - const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user1) - const tx = new SafeTransaction({ - to: safe.address, - value: '0', - data: '0x', - nonce: (await safe.nonce()).toString() - }) - const txHash = await safeSdk.getTransactionHash(tx) - await safeSdk.approveTransactionHash(txHash) - safeSdk.connect(safe.address, user2) - const txResponse = await safeSdk.executeTransactionOnChain(tx, { gasLimit: 10000000 }) - chai.expect(txResponse.hash).not.to.be.null - }) - - it('should execute a transaction when is not submitted by an owner', async () => { - const safe = await getSafeWithOwners([user1.address]) - const safeSdk = new EthersSafe(ethers, safe.address, user1) - const tx = new SafeTransaction({ - to: safe.address, - value: '0', - data: '0x', - nonce: (await safe.nonce()).toString() - }) - await safeSdk.signTransaction(tx) - safeSdk.connect(safe.address, user2) - const txResponse = await safeSdk.executeTransactionOnChain(tx, { gasLimit: 10000000 }) - chai.expect(txResponse.hash).not.to.be.null - }) - }) }) From 0db60eddd1ddccfe4e477b4b43975ee3d49728e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Thu, 18 Mar 2021 12:28:19 +0100 Subject: [PATCH 12/24] Add transactions execution tests --- src/EthersSafe.ts | 30 ----------- src/Safe.ts | 4 -- tests/execution.test.ts | 114 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 34 deletions(-) create mode 100644 tests/execution.test.ts diff --git a/src/EthersSafe.ts b/src/EthersSafe.ts index 3bfe7a948..0e13b6e1f 100644 --- a/src/EthersSafe.ts +++ b/src/EthersSafe.ts @@ -232,36 +232,6 @@ class EthersSafe implements Safe { async executeTransaction( safeTransaction: SafeTransaction, options?: any - ): Promise { - const threshold = await this.getThreshold() - if (threshold.gt(safeTransaction.signatures.size)) { - const signaturesMissing = threshold.sub(safeTransaction.signatures.size).toNumber() - throw new Error( - `There ${signaturesMissing > 1 ? 'are' : 'is'} ${signaturesMissing} signature${ - signaturesMissing > 1 ? 's' : '' - } missing` - ) - } - - const txResponse = await this.#contract.execTransaction( - safeTransaction.data.to, - safeTransaction.data.value, - safeTransaction.data.data, - safeTransaction.data.operation, - safeTransaction.data.safeTxGas, - safeTransaction.data.baseGas, - safeTransaction.data.gasPrice, - safeTransaction.data.gasToken, - safeTransaction.data.refundReceiver, - safeTransaction.encodedSignatures(), - { ...options } - ) - return txResponse - } - - async executeTransactionOnChain( - safeTransaction: SafeTransaction, - options?: any ): Promise { if (!this.#signer) { throw new Error('No signer provided') diff --git a/src/Safe.ts b/src/Safe.ts index de6db59b7..5e9f8561e 100644 --- a/src/Safe.ts +++ b/src/Safe.ts @@ -19,10 +19,6 @@ interface Safe { approveTransactionHash(hash: string, skipOnChainApproval: boolean): Promise getOwnersWhoApprovedTx(txHash: string): Promise executeTransaction(safeTransaction: SafeTransaction, options?: any): Promise - executeTransactionOnChain( - safeTransaction: SafeTransaction, - options?: any - ): Promise } export default Safe diff --git a/tests/execution.test.ts b/tests/execution.test.ts new file mode 100644 index 000000000..116e82ea2 --- /dev/null +++ b/tests/execution.test.ts @@ -0,0 +1,114 @@ +import chai from 'chai' +import chaiAsPromised from 'chai-as-promised' +import { deployments, ethers, waffle } from 'hardhat' +import EthersSafe, { SafeTransaction } from '../src' +import { getSafeWithOwners } from './utils/setup' +chai.use(chaiAsPromised) + +describe('Transactions execution', () => { + const [user1, user2, user3] = waffle.provider.getWallets() + + const setupTests = deployments.createFixture(async ({ deployments }) => { + await deployments.fixture() + return { + safe: await getSafeWithOwners([user1.address, user2.address]) + } + }) + + describe('execTransaction', async () => { + it('should fail if signer is not provided', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1.provider) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + await chai + .expect(safeSdk.executeTransaction(tx, { gasLimit: 10000000 })) + .rejectedWith('No signer provided') + }) + + it('should fail if there are not enough signatures (1 missing)', async () => { + const safe = await getSafeWithOwners([user1.address, user2.address, user3.address]) + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + await safeSdk.signTransaction(tx) + safeSdk.connect(safe.address, user2) + const txHash = await safeSdk.getTransactionHash(tx) + await safeSdk.approveTransactionHash(txHash) + await chai + .expect(safeSdk.executeTransaction(tx, { gasLimit: 10000000 })) + .to.be.rejectedWith('There is 1 signature missing') + }) + + it('should fail if there are not enough signatures (>1 missing)', async () => { + const safe = await getSafeWithOwners([user1.address, user2.address, user3.address]) + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + await chai + .expect(safeSdk.executeTransaction(tx)) + .to.be.rejectedWith('There are 2 signatures missing') + }) + + it('should execute a transaction with threshold 1', async () => { + const safe = await getSafeWithOwners([user1.address]) + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + const txResponse = await safeSdk.executeTransaction(tx, { gasLimit: 10000000 }) + chai.expect(txResponse.hash.length).to.be.eq(66) + }) + + it('should execute a transaction with threshold >1', async () => { + const safe = await getSafeWithOwners([user1.address, user2.address, user3.address]) + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + await safeSdk.signTransaction(tx) + safeSdk.connect(safe.address, user2) + const txHash = await safeSdk.getTransactionHash(tx) + await safeSdk.approveTransactionHash(txHash) + safeSdk.connect(safe.address, user3) + const txResponse = await safeSdk.executeTransaction(tx, { gasLimit: 10000000 }) + chai.expect(txResponse.hash.length).to.be.eq(66) + }) + + it('should execute a transaction when is not submitted by an owner', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address, user1) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + await safeSdk.signTransaction(tx) + safeSdk.connect(safe.address, user2) + const txHash = await safeSdk.getTransactionHash(tx) + await safeSdk.approveTransactionHash(txHash) + safeSdk.connect(safe.address, user3) + const txResponse = await safeSdk.executeTransaction(tx, { gasLimit: 10000000 }) + chai.expect(txResponse.hash.length).to.be.eq(66) + }) + }) +}) From 02a06ed7577d1e519b224b4dabcb67ad37245366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Thu, 18 Mar 2021 12:31:34 +0100 Subject: [PATCH 13/24] Fix async tests when catching errors --- tests/off-chain-signatures.test.ts | 20 +++++++++++--------- tests/on-chain-signatures.test.ts | 6 ++++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/off-chain-signatures.test.ts b/tests/off-chain-signatures.test.ts index 23327c143..54a74d09e 100644 --- a/tests/off-chain-signatures.test.ts +++ b/tests/off-chain-signatures.test.ts @@ -26,20 +26,22 @@ describe('Off-chain signatures', () => { nonce: (await safe.nonce()).toString() }) const txHash = await safeSdk.getTransactionHash(tx) - chai.expect(safeSdk.signTransactionHash(txHash)).to.be.rejectedWith('No signer provided') + await chai + .expect(safeSdk.signTransactionHash(txHash)) + .to.be.rejectedWith('No signer provided') }) it('should fail if signer is not an owner', async () => { const { safe } = await setupTests() const safeSdk = new EthersSafe(ethers, safe.address, user3) const tx = new SafeTransaction({ - to: user1.address, + to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) const txHash = await safeSdk.getTransactionHash(tx) - chai + await chai .expect(safeSdk.signTransactionHash(txHash)) .to.be.rejectedWith('Transactions can only be signed by Safe owners') }) @@ -48,7 +50,7 @@ describe('Off-chain signatures', () => { const { safe } = await setupTests() const safeSdk = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ - to: user1.address, + to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() @@ -69,19 +71,19 @@ describe('Off-chain signatures', () => { data: '0x', nonce: (await safe.nonce()).toString() }) - chai.expect(safeSdk.signTransaction(tx)).to.be.rejectedWith('No signer provided') + await chai.expect(safeSdk.signTransaction(tx)).to.be.rejectedWith('No signer provided') }) it('should fail if signature is added by an account that is not an owner', async () => { const { safe } = await setupTests() const safeSdk = new EthersSafe(ethers, safe.address, user3) const tx = new SafeTransaction({ - to: user1.address, + to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - chai + await chai .expect(safeSdk.signTransaction(tx)) .to.be.rejectedWith('Transactions can only be signed by Safe owners') }) @@ -89,7 +91,7 @@ describe('Off-chain signatures', () => { it('should add the signature of the current signer', async () => { const { safe } = await setupTests() const tx = new SafeTransaction({ - to: user1.address, + to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() @@ -104,7 +106,7 @@ describe('Off-chain signatures', () => { const { safe } = await setupTests() const safeSdk = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ - to: user1.address, + to: safe.address, value: '0', data: '0x', nonce: await safe.nonce() diff --git a/tests/on-chain-signatures.test.ts b/tests/on-chain-signatures.test.ts index b2746e842..2e0f79a6d 100644 --- a/tests/on-chain-signatures.test.ts +++ b/tests/on-chain-signatures.test.ts @@ -26,7 +26,9 @@ describe('On-chain signatures', () => { nonce: (await safe.nonce()).toString() }) const txHash = await safeSdk.getTransactionHash(tx) - chai.expect(safeSdk.approveTransactionHash(txHash)).to.be.rejectedWith('No signer provided') + await chai + .expect(safeSdk.approveTransactionHash(txHash)) + .to.be.rejectedWith('No signer provided') }) it('should fail if a transaction hash is approved by an account that is not an owner', async () => { @@ -39,7 +41,7 @@ describe('On-chain signatures', () => { nonce: (await safe.nonce()).toString() }) const hash = await safeSdk.getTransactionHash(tx) - chai + await chai .expect(safeSdk.approveTransactionHash(hash)) .to.be.rejectedWith('Transaction hashes can only be approved by Safe owners') }) From 42d1fb50dd6ea16735e0444504b9cace066b6321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Thu, 18 Mar 2021 14:15:51 +0100 Subject: [PATCH 14/24] Add coveralls --- README.md | 2 + package.json | 2 + yarn.lock | 426 ++++++--------------------------------------------- 3 files changed, 55 insertions(+), 375 deletions(-) diff --git a/README.md b/README.md index b6f77cef4..112ccb852 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Safe Core SDK +[![Coverage Status](https://coveralls.io/repos/github/gnosis/safe-core-sdk/badge.svg?branch=main)](https://coveralls.io/github/gnosis/safe-core-sdk?branch=main) + Software development kit that facilitates the interaction with the [Gnosis Safe contracts](https://github.com/gnosis/safe-contracts). ## Install diff --git a/package.json b/package.json index 4e8cbc329..9bab5a7ca 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "scripts": { "build": "yarn rimraf dist && tsc && hardhat compile", "test": "hardhat deploy && nyc hardhat test", + "coverage": "nyc report --reporter=text-lcov | coveralls", "format": "prettier --write \"{src,tests,hardhat}/**/*.ts\"", "lint": "tslint -p tsconfig.json" }, @@ -39,6 +40,7 @@ "@typescript-eslint/parser": "^4.15.0", "chai": "^4.3.1", "chai-as-promised": "^7.1.1", + "coveralls": "^3.1.0", "dotenv": "^8.2.0", "eslint": "^7.20.0", "eslint-config-prettier": "^7.2.0", diff --git a/yarn.lock b/yarn.lock index cc9d561e7..100451d2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1980,11 +1980,6 @@ resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.11.1.tgz#fa840af64840c930f24a9c82c08d4a092a068add" integrity sha512-H8BSBoKE8EubJa0ONqecA2TviT3TnHeC4NpgnAHSUiuhZoQBfPB4L2P9bs8R6AoTW10Endvh3vc+fomVMIDIYQ== -"@solidity-parser/parser@^0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.12.0.tgz#18a0fb2a9d2484b23176f63b16093c64794fc323" - integrity sha512-DT3f/Aa4tQysZwUsuqBwvr8YRJzKkvPUKV/9o2/o5EVw3xqlbzmtx4O60lTUcZdCawL+N8bBLNUyOGpHjGlJVQ== - "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -2134,7 +2129,7 @@ source-map-support "^0.5.19" web3 "1.2.9" -"@truffle/provider@^0.2.24", "@truffle/provider@^0.2.26": +"@truffle/provider@^0.2.26": version "0.2.26" resolved "https://registry.yarnpkg.com/@truffle/provider/-/provider-0.2.26.tgz#88e31b79973c2427c4a17d9a59411e6fbc810190" integrity sha512-YKPmhB9S9AQkT2ePGtadwjDduxU23DXXy+5zyM5fevw5GCbXSnf+jG6rICXjPkVFjuKBlXuq5JbuERZn43522Q== @@ -2351,14 +2346,6 @@ dependencies: "@types/node" "*" -"@types/glob@^7.1.1": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" - integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" - "@types/http-assert@*": version "1.5.1" resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b" @@ -2415,11 +2402,6 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== -"@types/minimatch@*": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" - integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== - "@types/mkdirp@^0.5.2": version "0.5.2" resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.2.tgz#503aacfe5cc2703d5484326b1b27efa67a339c1f" @@ -2704,11 +2686,6 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -abbrev@1.0.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" - integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= - abort-controller@3.0.0, abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -2809,11 +2786,6 @@ acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -address@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" - integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== - adm-zip@^0.4.16: version "0.4.16" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" @@ -2864,11 +2836,6 @@ ajv@^7.0.2: require-from-string "^2.0.2" uri-js "^4.2.2" -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= - ansi-colors@3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" @@ -3298,11 +3265,6 @@ async-retry@^1.2.1: dependencies: retry "0.12.0" -async@1.x, async@^1.4.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= - async@2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" @@ -3310,6 +3272,11 @@ async@2.6.2: dependencies: lodash "^4.17.11" +async@^1.4.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: version "2.6.3" resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" @@ -4980,6 +4947,17 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +coveralls@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.1.0.tgz#13c754d5e7a2dd8b44fe5269e21ca394fb4d615b" + integrity sha512-sHxOu2ELzW8/NC1UP5XVLbZDzO4S3VxfFye3XYCznopHy02YjNkHcj5bKaVw2O7hVaBdBjEdQGpie4II1mWhuQ== + dependencies: + js-yaml "^3.13.1" + lcov-parse "^1.0.0" + log-driver "^1.2.7" + minimist "^1.2.5" + request "^2.88.2" + create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -5031,7 +5009,7 @@ cross-fetch@^2.1.0, cross-fetch@^2.1.1: node-fetch "2.1.2" whatwg-fetch "2.0.4" -cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -5135,11 +5113,6 @@ dataloader@2.0.0: resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.0.0.tgz#41eaf123db115987e21ca93c005cd7753c55fe6f" integrity sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ== -death@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/death/-/death-1.1.0.tgz#01aa9c401edd92750514470b8266390c66c67318" - integrity sha1-AaqcQB7dknUFFEcLgmY5DGbGcxg= - debug-fabulous@0.0.X: version "0.0.4" resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-0.0.4.tgz#fa071c5d87484685424807421ca4b16b0b1a0763" @@ -5149,7 +5122,7 @@ debug-fabulous@0.0.X: lazy-debug-legacy "0.0.X" object-assign "4.1.0" -debug@2.6.9, debug@2.X, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@2.X, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -5381,14 +5354,6 @@ detect-newline@2.X: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= -detect-port@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.3.0.tgz#d9c40e9accadd4df5cac6a782aefd014d573d1f1" - integrity sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ== - dependencies: - address "^1.0.1" - debug "^2.6.0" - dicer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" @@ -5821,18 +5786,6 @@ escape-string-regexp@4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escodegen@1.8.x: - version "1.8.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" - integrity sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg= - dependencies: - esprima "^2.7.1" - estraverse "^1.9.1" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.2.0" - escodegen@^1.6.1: version "1.14.3" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" @@ -5951,11 +5904,6 @@ espree@^7.3.0, espree@^7.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" -esprima@2.7.x, esprima@^2.7.1: - version "2.7.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" - integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE= - esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -5975,11 +5923,6 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^1.9.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" - integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q= - estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" @@ -6595,19 +6538,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" @@ -6783,7 +6713,7 @@ fast-future@~1.0.2: resolved "https://registry.yarnpkg.com/fast-future/-/fast-future-1.0.2.tgz#8435a9aaa02d79248d17d704e76259301d99280a" integrity sha1-hDWpqqAteSSNF9cE52JZMB2ZKAo= -fast-glob@^3.0.3, fast-glob@^3.1.1: +fast-glob@^3.1.1: version "3.2.5" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== @@ -7192,15 +7122,6 @@ fs-extra@^7.0.0, fs-extra@^7.0.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^9.0.0, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -7243,15 +7164,6 @@ functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -ganache-cli@^6.11.0: - version "6.12.2" - resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.12.2.tgz#c0920f7db0d4ac062ffe2375cb004089806f627a" - integrity sha512-bnmwnJDBDsOWBUP8E/BExWf85TsdDEFelQSzihSJm9VChVO1SHp94YXLP5BlA4j/OTxp0wR4R1Tje9OHOuAJVw== - dependencies: - ethereumjs-util "6.2.1" - source-map-support "0.5.12" - yargs "13.2.4" - ganache-core@^2.10.2: version "2.13.2" resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.13.2.tgz#27e6fc5417c10e6e76e2e646671869d7665814a3" @@ -7352,7 +7264,7 @@ get-stream@^3.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= -get-stream@^4.0.0, get-stream@^4.1.0: +get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== @@ -7378,14 +7290,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -ghost-testrpc@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz#c4de9557b1d1ae7b2d20bbe474a91378ca90ce92" - integrity sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ== - dependencies: - chalk "^2.4.2" - node-emoji "^1.10.0" - glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" @@ -7454,7 +7358,7 @@ glob@7.1.6, glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glo once "^1.3.0" path-is-absolute "^1.0.0" -glob@^5.0.15, glob@^5.0.3: +glob@^5.0.3: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= @@ -7465,22 +7369,6 @@ glob@^5.0.15, glob@^5.0.3: once "^1.3.0" path-is-absolute "^1.0.0" -global-modules@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" - integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== - dependencies: - global-prefix "^3.0.0" - -global-prefix@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" - integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== - dependencies: - ini "^1.3.5" - kind-of "^6.0.2" - which "^1.3.1" - global@~4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" @@ -7518,20 +7406,6 @@ globby@11.0.2, globby@^11.0.1: merge2 "^1.3.0" slash "^3.0.0" -globby@^10.0.1: - version "10.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" - integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== - dependencies: - "@types/glob" "^7.1.1" - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.0.3" - glob "^7.1.3" - ignore "^5.1.1" - merge2 "^1.2.3" - slash "^3.0.0" - got@9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -7685,18 +7559,6 @@ gulp-sourcemaps@^1.5.2: through2 "2.X" vinyl "1.X" -handlebars@^4.0.1: - version "4.7.7" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== - dependencies: - minimist "^1.2.5" - neo-async "^2.6.0" - source-map "^0.6.1" - wordwrap "^1.0.0" - optionalDependencies: - uglify-js "^3.1.4" - har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -7803,11 +7665,6 @@ has-bigints@^1.0.0: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" - integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -8102,7 +7959,7 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.1, ignore@^5.1.4: +ignore@^5.1.4: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== @@ -8180,7 +8037,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.5, ini@~1.3.0: +ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -8202,11 +8059,6 @@ invert-kv@^1.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" - integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== - io-ts@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" @@ -8545,7 +8397,7 @@ is-set@^2.0.2: resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== -is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.0.0, is-stream@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -8776,14 +8628,6 @@ js-yaml@3.14.0: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@3.x, js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js-yaml@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" @@ -8791,6 +8635,14 @@ js-yaml@4.0.0: dependencies: argparse "^2.0.1" +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsan@^3.1.13: version "3.1.13" resolved "https://registry.yarnpkg.com/jsan/-/jsan-3.1.13.tgz#4de8c7bf8d1cfcd020c313d438f930cec4b91d86" @@ -8966,11 +8818,6 @@ jsonify@~0.0.0: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= -jsonschema@^1.2.4: - version "1.4.0" - resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.0.tgz#1afa34c4bc22190d8e42271ec17ac8b3404f87b2" - integrity sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw== - jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -9053,12 +8900,10 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== - dependencies: - invert-kv "^2.0.0" +lcov-parse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0" + integrity sha1-6w1GtUER68VhrLTECO+TY73I9+A= level-codec@9.0.1: version "9.0.1" @@ -9514,11 +9359,6 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" -lodash.toarray@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" - integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= - lodash.tostring@^4.0.0: version "4.1.4" resolved "https://registry.yarnpkg.com/lodash.tostring/-/lodash.tostring-4.1.4.tgz#560c27d1f8eadde03c2cce198fef5c031d8298fb" @@ -9539,6 +9379,11 @@ lodash@4.17.21, lodash@^4.1.0, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.14, resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-driver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" + integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== + log-symbols@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" @@ -9681,13 +9526,6 @@ make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" - integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== - dependencies: - p-defer "^1.0.0" - map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -9734,15 +9572,6 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -mem@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" - integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" - memdown@1.4.1, memdown@^1.0.0: version "1.4.1" resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" @@ -9789,7 +9618,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.2.3, merge2@^1.3.0: +merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -9914,7 +9743,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mimic-fn@^2.0.0, mimic-fn@^2.1.0: +mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -10005,7 +9834,7 @@ mkdirp@0.5.1: dependencies: minimist "0.0.8" -mkdirp@0.5.5, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1: +mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -10257,11 +10086,6 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -neo-async@^2.6.0: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - next-tick@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" @@ -10292,13 +10116,6 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== -node-emoji@^1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" - integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw== - dependencies: - lodash.toarray "^4.4.0" - node-environment-flags@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" @@ -10400,13 +10217,6 @@ noop-fn@^1.0.0: resolved "https://registry.yarnpkg.com/noop-fn/-/noop-fn-1.0.0.tgz#5f33d47f13d2150df93e0cb036699e982f78ffbf" integrity sha1-XzPUfxPSFQ35PgywNmmemC94/78= -nopt@3.x: - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= - dependencies: - abbrev "1" - nopt@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" @@ -10463,13 +10273,6 @@ npm-packlist@^1.1.6: npm-bundled "^1.0.1" npm-normalize-package-bin "^1.0.1" -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - npm-run-path@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -10672,7 +10475,7 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -10787,15 +10590,6 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" -os-locale@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" - integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== - dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" - os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -10819,21 +10613,11 @@ p-cancelable@^1.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" - integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" - integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== - p-limit@3.1.0, p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -11095,7 +10879,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^2.0.0, path-key@^2.0.1: +path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= @@ -11165,11 +10949,6 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -11945,13 +11724,6 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" -recursive-readdir@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" - integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== - dependencies: - minimatch "3.0.4" - redux-cli-logger@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/redux-cli-logger/-/redux-cli-logger-2.1.0.tgz#7e546502a4b08c7fac4fe2faee2326a6326cb4a1" @@ -12159,7 +11931,7 @@ replace-ext@0.0.1: resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= -request@^2.55.0, request@^2.79.0, request@^2.85.0: +request@^2.55.0, request@^2.79.0, request@^2.85.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -12246,11 +12018,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@1.1.x: - version "1.1.7" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" - integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= - resolve@1.17.0, resolve@~1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" @@ -12415,26 +12182,6 @@ sc-formatter@^3.0.1: resolved "https://registry.yarnpkg.com/sc-formatter/-/sc-formatter-3.0.2.tgz#9abdb14e71873ce7157714d3002477bbdb33c4e6" integrity sha512-9PbqYBpCq+OoEeRQ3QfFIGE6qwjjBcd2j7UjgDlhnZbtSnuGgHdcRklPKYGuYFH82V/dwd+AIpu8XvA1zqTd+A== -sc-istanbul@^0.4.5: - version "0.4.6" - resolved "https://registry.yarnpkg.com/sc-istanbul/-/sc-istanbul-0.4.6.tgz#cf6784355ff2076f92d70d59047d71c13703e839" - integrity sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g== - dependencies: - abbrev "1.0.x" - async "1.x" - escodegen "1.8.x" - esprima "2.7.x" - glob "^5.0.15" - handlebars "^4.0.1" - js-yaml "3.x" - mkdirp "0.5.x" - nopt "3.x" - once "1.x" - resolve "1.1.x" - supports-color "^3.1.0" - which "^1.1.1" - wordwrap "^1.0.0" - scrypt-js@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" @@ -12815,31 +12562,6 @@ solc@^0.6.3: semver "^5.5.0" tmp "0.0.33" -solidity-coverage@^0.7.16: - version "0.7.16" - resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.7.16.tgz#c8c8c46baa361e2817bbf275116ddd2ec90a55fb" - integrity sha512-ttBOStywE6ZOTJmmABSg4b8pwwZfYKG8zxu40Nz+sRF5bQX7JULXWj/XbX0KXps3Fsp8CJXg8P29rH3W54ipxw== - dependencies: - "@solidity-parser/parser" "^0.12.0" - "@truffle/provider" "^0.2.24" - chalk "^2.4.2" - death "^1.1.0" - detect-port "^1.3.0" - fs-extra "^8.1.0" - ganache-cli "^6.11.0" - ghost-testrpc "^0.0.2" - global-modules "^2.0.0" - globby "^10.0.1" - jsonschema "^1.2.4" - lodash "^4.17.15" - node-emoji "^1.10.0" - pify "^4.0.1" - recursive-readdir "^2.2.2" - sc-istanbul "^0.4.5" - semver "^7.3.4" - shelljs "^0.8.3" - web3-utils "^1.3.0" - source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -12889,13 +12611,6 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" - integrity sha1-2rc/vPwrqBm03gO9b26qSBZLP50= - dependencies: - amdefine ">=0.0.4" - spark-md5@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.0.tgz#3722227c54e2faf24b1dc6d933cc144e6f71bfef" @@ -13173,11 +12888,6 @@ strip-bom@^4.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" @@ -13252,13 +12962,6 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -supports-color@^3.1.0: - version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" - integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= - dependencies: - has-flag "^1.0.0" - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -13844,11 +13547,6 @@ ua-parser-js@^0.7.18: resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c" integrity sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw== -uglify-js@^3.1.4: - version "3.13.2" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.2.tgz#fe10319861bccc8682bfe2e8151fbdd8aa921c44" - integrity sha512-SbMu4D2Vo95LMC/MetNaso1194M1htEA+JrqE9Hk+G2DhI+itfS9TRu9ZKeCahLDNa/J3n4MqUJ/fOHMzQpRWw== - ultron@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" @@ -14625,7 +14323,7 @@ web3-utils@1.2.9: underscore "1.9.1" utf8 "3.0.0" -web3-utils@^1.0.0-beta.31, web3-utils@^1.3.0: +web3-utils@^1.0.0-beta.31: version "1.3.4" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.3.4.tgz#9b1aa30d7549f860b573e7bb7e690999e7192198" integrity sha512-/vC2v0MaZNpWooJfpRw63u0Y3ag2gNjAWiLtMSL6QQLmCqCy4SQIndMt/vRyx0uMoeGt1YTwSXEcHjUzOhLg0A== @@ -14738,7 +14436,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@1.3.1, which@^1.1.1, which@^1.2.9, which@^1.3.1: +which@1.3.1, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -14769,11 +14467,6 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wordwrap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - workerpool@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.0.tgz#85aad67fa1a2c8ef9386a1b43539900f61d03d58" @@ -14997,7 +14690,7 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== -yargs-parser@13.1.2, yargs-parser@^13.1.0, yargs-parser@^13.1.2: +yargs-parser@13.1.2, yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== @@ -15069,23 +14762,6 @@ yargs-unparser@2.0.0: flat "^5.0.2" is-plain-obj "^2.1.0" -yargs@13.2.4: - version "13.2.4" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" - integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - os-locale "^3.1.0" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.0" - yargs@13.3.2, yargs@^13.3.0: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" From f5091e69e73e32f2a7557690f821d50922a2658f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Thu, 18 Mar 2021 16:02:48 +0100 Subject: [PATCH 15/24] Fix documentation --- src/EthersSafe.ts | 28 ++++++++++++++++++++++------ src/Safe.ts | 2 ++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/EthersSafe.ts b/src/EthersSafe.ts index 0e13b6e1f..916b073ab 100644 --- a/src/EthersSafe.ts +++ b/src/EthersSafe.ts @@ -18,8 +18,8 @@ class EthersSafe implements Safe { * Creates an instance of the Safe Core SDK. * * @param ethers - Ethers v5 library - * @param providerOrSigner - Ethers provider or signer * @param safeAddress - The address of the Safe account to use + * @param providerOrSigner - Ethers provider or signer * @returns The Safe Core SDK instance */ constructor(ethers: any, safeAddress: string, providerOrSigner: Provider | Wallet) { @@ -27,6 +27,12 @@ class EthersSafe implements Safe { this.connect(safeAddress, providerOrSigner) } + /** + * Initializes the Safe Core SDK connecting the providerOrSigner to the safeAddress. + * + * @param safeAddress - The address of the Safe account to use + * @param providerOrSigner - Ethers provider or signer + */ connect(safeAddress: string, providerOrSigner: Provider | Wallet): void { this.#contract = new this.#ethers.Contract(safeAddress, SafeAbi, providerOrSigner) if (Wallet.isSigner(providerOrSigner)) { @@ -38,10 +44,20 @@ class EthersSafe implements Safe { this.#provider = providerOrSigner } + /** + * Returns the connected provider. + * + * @returns The connected provider + */ getProvider(): Provider { return this.#provider } + /** + * Returns the connected signer. + * + * @returns The connected signer + */ getSigner(): Wallet | undefined { return this.#signer } @@ -120,7 +136,7 @@ class EthersSafe implements Safe { } /** - * Returns the transaction hash to be signed by the owners. + * Returns the transaction hash of a Safe transaction. * * @param safeTransaction - The Safe transaction * @returns The transaction hash of the Safe transaction @@ -143,7 +159,7 @@ class EthersSafe implements Safe { } /** - * Signs a hash using the current owner account. + * Signs a hash using the current signer account. * * @param hash - The hash to sign * @returns The Safe signature @@ -166,7 +182,7 @@ class EthersSafe implements Safe { } /** - * Adds the signature of the current owner to the Safe transaction object. + * Adds the signature of the current signer to the Safe transaction object. * * @param safeTransaction - The Safe transaction to be signed */ @@ -177,7 +193,7 @@ class EthersSafe implements Safe { } /** - * Approves a hash using the current owner account. + * Approves on-chain a hash using the current signer account. * * @param hash - The hash to approve * @param skipOnChainApproval - TRUE to avoid the Safe transaction to be approved on-chain @@ -205,7 +221,7 @@ class EthersSafe implements Safe { } /** - * Returns a list of owners who have approved a specific Safe transaction + * Returns a list of owners who have approved a specific Safe transaction. * * @param txHash - The Safe transaction hash * @returns The list of owners diff --git a/src/Safe.ts b/src/Safe.ts index 5e9f8561e..219985576 100644 --- a/src/Safe.ts +++ b/src/Safe.ts @@ -5,6 +5,8 @@ import { SafeTransaction } from './utils/transactions' interface Safe { connect(safeAddress: string, providerOrSigner: Provider | Wallet): void + getProvider(): Provider + getSigner(): Wallet | undefined getAddress(): string getContractVersion(): Promise getOwners(): Promise From ae1cd0815193f3adc19d07653bcdfd62f25f6ce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Thu, 18 Mar 2021 16:33:00 +0100 Subject: [PATCH 16/24] Add API reference and improve the example in the README file --- README.md | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 194 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 112ccb852..19c304a52 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Software development kit that facilitates the interaction with the [Gnosis Safe contracts](https://github.com/gnosis/safe-contracts). -## Install +## Installation Install the package with yarn or npm: @@ -22,27 +22,33 @@ yarn build npm build ``` -## Documentation +## Getting Started -### Getting Started +A Safe account with three owners and threshold equal three will be used as the starting point for this example but any Safe configuration is valid. -This is how executing a Safe transaction with off-chain signatures looks like. - -1. Create a Safe account with two owners and threshold equal two (this configuration is just an example): ```js -import { ethers } from 'ethers') +import { ethers } from 'ethers' import EthersSafe, { SafeTransaction } from 'safe-core-sdk' const provider = ethers.getDefaultProvider('homestead') const wallet1 = ethers.Wallet.createRandom().connect(provider) const wallet2 = ethers.Wallet.createRandom().connect(provider) +const wallet3 = ethers.Wallet.createRandom().connect(provider) -// Existing Safe address (e.g. Safe created via https://app.gnosis-safe.io +// Existing Safe address (e.g. Safe created via https://app.gnosis-safe.io) +// Where wallet1.address, wallet2.address and wallet3.address are the Safe owners const safeAddress = "0x" const safeNonce = ``` -2. Create a Safe transaction: +Create an instance of the Safe Core SDK with wallet1 connected as the signer. + +```js +const safeSdk = new EthersSafe(ethers, safeAddress, wallet1) +``` + +### 1. Create a Safe transaction + ```js const tx = new SafeTransaction({ to: safeAddress, @@ -52,19 +58,192 @@ const tx = new SafeTransaction({ }) ``` -3. Generate the signatures with both owners: +Before executing this transaction, it must be signed by the owners and this can be done off-chain or on-chain. In this example the owner `wallet1` will sign it off-chain and the owner `wallet2` will sign it on-chain. It is not needed that `wallet3` signs the transaction explicitly because it will be the one executing the transaction. If an account that is not an owner executes the transaction, `wallet3` would have to explicitly sign it too. + +### 2.a. Off-chain signatures + +The owner `wallet1` signs the transaction off-chain. + ```js -let safeSdk = new EthersSafe(ethers, wallet1, safeAddress) -await safeSdk.signTransaction(tx) +const wallet1Signature = await safeSdk.signTransaction(tx) +``` -safeSdk = new EthersSafe(ethers, wallet2, safeAddress) -await safeSdk.signTransaction(tx) +Because the signature is off-chain, there is no interaction with the contract and the signature is available at `tx.signatures`. + +### 2.b. On-chain signatures + +After `wallet2` account is connected to the SDK as the signer the transaction hash is approved on-chain. + +```js +safeSdk.connect(safeAddress, wallet2) +const txHash = await safeSdk.getTransactionHash(tx) +const wallet2Signature = await safeSdk.approveTransactionHash(txHash) ``` -4. Execute the transaction with the two signatures added: +### 3. Transaction execution + +Lastly, `wallet3` account is connected to the SDK as the signer and executor of the Safe transaction to execute it. + ```js +safeSdk.connect(safeAddress, wallet3) const txResponse = await safeSdk.executeTransaction(tx) +``` + +All the signatures used to execute the transaction are available at `tx.signatures`. + +## API Reference + +### connect + +Initializes the Safe Core SDK connecting the providerOrSigner to the safeAddress. + +```js +safeSdk.connect(safeAddress, providerOrSigner) +``` + +### getProvider + +Returns the connected provider. + +```js +const provider = safeSdk.getProvider() +``` + +### getSigner + +Returns the connected signer. + +```js +const signer = safeSdk.getSigner() +``` + +### getAddress + +Returns the address of the current Safe Proxy contract. + +```js +const address = safeSdk.getAddress() +``` + +### getContractVersion + +Returns the Safe Master Copy contract version. + +```js +const contractVersion = await safeSdk.getContractVersion() +``` + +### getOwners + +Returns the list of Safe owner accounts. + +```js +const owners = await safeSdk.getOwners() +``` + +### getThreshold + +Returns the Safe threshold. + +```js +const threshold = await safeSdk.getThreshold() +``` + +### getNetworkId + +Returns the chainId of the connected network. +```js +const networkId = await safeSdk.getNetworkId() +``` + +### getBalance + +Returns the ETH balance of the Safe. + +```js +const balance = await safeSdk.getBalance() +``` + +### getModules + +Returns the list of addresses of all the enabled Safe modules. + +```js +const modules = await safeSdk.getModules() +``` + +### isModuleEnabled + +Checks if a specific Safe module is enabled for the current Safe. + +```js +const isEnabled = await safeSdk.isModuleEnabled(moduleAddress) +``` + +### getTransactionHash + +Returns the transaction hash of a Safe transaction. + +```js +const tx = new SafeTransaction({ + // ... +}) +const txHash = await safeSdk.getTransactionHash(tx) +``` + +### signTransactionHash + +Signs a hash using the current signer account. + +```js +const tx = new SafeTransaction({ + // ... +}) +const txHash = await safeSdk.getTransactionHash(tx) +const signature = await safeSdk.signTransactionHash(txHash) +``` + +### signTransaction + +Adds the signature of the current signer to the Safe transaction object. + +```js +const tx = new SafeTransaction({ + // ... +}) +await safeSdk.signTransaction(tx) +``` + +### approveTransactionHash + +Approves on-chain a hash using the current signer account. + +```js +const tx = new SafeTransaction({ + // ... +}) +const txHash = await safeSdk.getTransactionHash(tx) +const signature = await safeSdk.approveTransactionHash(txHash) +``` + +### getOwnersWhoApprovedTx + +Returns a list of owners who have approved a specific Safe transaction. + +```js +const owners = await safeSdk.getOwnersWhoApproved() +``` + +### executeTransaction + +Executes a Safe transaction. + +```js +const tx = new SafeTransaction({ + // ... +}) +const txResponse = await safeSdk.executeTransaction(tx) ``` ## License From f16a0ab0b3a58cbc63c06a0bad3d7a4a37d6d106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Mon, 29 Mar 2021 15:15:09 +0200 Subject: [PATCH 17/24] Update node version to v14 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 42c10e7d6..4a1dd67bf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,7 +5,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [12.x] + node-version: [14.x] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} From c66e6229d00ece67fbb0ef7074dd1479f42eb147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Wed, 31 Mar 2021 10:29:44 +0200 Subject: [PATCH 18/24] Rename getChainId method --- README.md | 10 +++++++--- src/EthersSafe.ts | 2 +- src/Safe.ts | 2 +- tests/core.test.ts | 4 ++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 19c304a52..adc82a41a 100644 --- a/README.md +++ b/README.md @@ -149,12 +149,12 @@ Returns the Safe threshold. const threshold = await safeSdk.getThreshold() ``` -### getNetworkId +### getChainId Returns the chainId of the connected network. ```js -const networkId = await safeSdk.getNetworkId() +const chainId = await safeSdk.getChainId() ``` ### getBalance @@ -232,7 +232,11 @@ const signature = await safeSdk.approveTransactionHash(txHash) Returns a list of owners who have approved a specific Safe transaction. ```js -const owners = await safeSdk.getOwnersWhoApproved() +const tx = new SafeTransaction({ + // ... +}) +const txHash = await safeSdk.getTransactionHash(tx) +const owners = await safeSdk.getOwnersWhoApprovedTx(txHash) ``` ### executeTransaction diff --git a/src/EthersSafe.ts b/src/EthersSafe.ts index 916b073ab..a77f19bc1 100644 --- a/src/EthersSafe.ts +++ b/src/EthersSafe.ts @@ -103,7 +103,7 @@ class EthersSafe implements Safe { * * @returns The chainId of the connected network */ - async getNetworkId(): Promise { + async getChainId(): Promise { return (await this.#provider.getNetwork()).chainId } diff --git a/src/Safe.ts b/src/Safe.ts index 219985576..d09066f5e 100644 --- a/src/Safe.ts +++ b/src/Safe.ts @@ -11,7 +11,7 @@ interface Safe { getContractVersion(): Promise getOwners(): Promise getThreshold(): Promise - getNetworkId(): Promise + getChainId(): Promise getBalance(): Promise getModules(): Promise isModuleEnabled(moduleAddress: string): Promise diff --git a/tests/core.test.ts b/tests/core.test.ts index c14328b93..437bc748e 100644 --- a/tests/core.test.ts +++ b/tests/core.test.ts @@ -69,11 +69,11 @@ describe('Safe Core SDK', () => { }) }) - describe('getNetworkId', async () => { + describe('getChainId', async () => { it('should return the chainId of the current network', async () => { const { safe, chainId } = await setupTests() const safeSdk = new EthersSafe(ethers, safe.address, user1) - chai.expect(await safeSdk.getNetworkId()).to.be.eq(chainId) + chai.expect(await safeSdk.getChainId()).to.be.eq(chainId) }) }) From 4782d6f9d69c46de02bcfd7d9a1338f991bb73dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Wed, 31 Mar 2021 20:18:01 +0200 Subject: [PATCH 19/24] Update logic to add a signature to a SafeTransaction --- src/utils/transactions.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/utils/transactions.ts b/src/utils/transactions.ts index fbe5f03a6..b3b859f08 100644 --- a/src/utils/transactions.ts +++ b/src/utils/transactions.ts @@ -49,19 +49,27 @@ export function standardizeSafeTransaction(tx: SafeTransactionDataPartial): Safe export class SafeTransaction { data: SafeTransactionData - signatures: Map = new Map() + #signatures: Map = new Map() constructor(data: SafeTransactionDataPartial) { this.data = standardizeSafeTransaction(data) } + get signatures(): Map { + return new Map(this.#signatures) + } + + addSignature(signature: SafeSignature) { + this.#signatures.set(signature.signer, signature) + } + encodedSignatures(): string { - const signers = Array.from(this.signatures.keys()).sort() + const signers = Array.from(this.#signatures.keys()).sort() const baseOffset = signers.length * 65 let staticParts = '' let dynamicParts = '' signers.forEach((signerAddress) => { - const signature = this.signatures.get(signerAddress) + const signature = this.#signatures.get(signerAddress) staticParts += signature?.staticPart(/*baseOffset + dynamicParts.length / 2*/).slice(2) dynamicParts += signature?.dynamicPart() }) From a52fb84223ffde81e9029e85aaf99a27df05107f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Wed, 31 Mar 2021 20:18:18 +0200 Subject: [PATCH 20/24] Update logic to connect a signer or provider --- src/EthersSafe.ts | 40 +++++++++++++++++++--------------------- src/Safe.ts | 2 +- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/EthersSafe.ts b/src/EthersSafe.ts index a77f19bc1..26ecbd5e2 100644 --- a/src/EthersSafe.ts +++ b/src/EthersSafe.ts @@ -9,9 +9,9 @@ import { EthSignSignature, SafeSignature } from './utils/signatures/SafeSignatur import { SafeTransaction } from './utils/transactions' class EthersSafe implements Safe { - #contract!: GnosisSafe + #contract: GnosisSafe #ethers: any - #provider!: Provider + #provider: Provider #signer?: Wallet /** @@ -19,29 +19,30 @@ class EthersSafe implements Safe { * * @param ethers - Ethers v5 library * @param safeAddress - The address of the Safe account to use - * @param providerOrSigner - Ethers provider or signer + * @param providerOrSigner - Ethers provider or signer. If this parameter is not passed, Ethers defaultProvider will be used. * @returns The Safe Core SDK instance */ - constructor(ethers: any, safeAddress: string, providerOrSigner: Provider | Wallet) { + constructor(ethers: any, safeAddress: string, providerOrSigner?: Provider | Wallet) { + const currentProviderOrSigner = providerOrSigner || (ethers.getDefaultProvider() as Provider) this.#ethers = ethers - this.connect(safeAddress, providerOrSigner) + this.#contract = new this.#ethers.Contract(safeAddress, SafeAbi, currentProviderOrSigner) + if (Wallet.isSigner(currentProviderOrSigner)) { + this.#signer = currentProviderOrSigner + this.#provider = currentProviderOrSigner.provider + return + } + this.#signer = undefined + this.#provider = currentProviderOrSigner } /** * Initializes the Safe Core SDK connecting the providerOrSigner to the safeAddress. * - * @param safeAddress - The address of the Safe account to use * @param providerOrSigner - Ethers provider or signer + * @param safeAddress - The address of the Safe account to use */ - connect(safeAddress: string, providerOrSigner: Provider | Wallet): void { - this.#contract = new this.#ethers.Contract(safeAddress, SafeAbi, providerOrSigner) - if (Wallet.isSigner(providerOrSigner)) { - this.#signer = providerOrSigner - this.#provider = providerOrSigner.provider - return - } - this.#signer = undefined - this.#provider = providerOrSigner + connect(providerOrSigner: Provider | Wallet, safeAddress?: string): EthersSafe { + return new EthersSafe(this.#ethers, safeAddress || this.#contract.address, providerOrSigner) } /** @@ -189,7 +190,7 @@ class EthersSafe implements Safe { async signTransaction(safeTransaction: SafeTransaction): Promise { const txHash = await this.getTransactionHash(safeTransaction) const signature = await this.signTransactionHash(txHash) - safeTransaction.signatures.set(signature.signer, signature) + safeTransaction.addSignature(signature) } /** @@ -256,14 +257,11 @@ class EthersSafe implements Safe { const txHash = await this.getTransactionHash(safeTransaction) const ownersWhoApprovedTx = await this.getOwnersWhoApprovedTx(txHash) for (const owner of ownersWhoApprovedTx) { - safeTransaction.signatures.set(owner, generatePreValidatedSignature(owner)) + safeTransaction.addSignature(generatePreValidatedSignature(owner)) } const owners = await this.getOwners() if (owners.includes(this.#signer.address)) { - safeTransaction.signatures.set( - this.#signer.address, - generatePreValidatedSignature(this.#signer.address) - ) + safeTransaction.addSignature(generatePreValidatedSignature(this.#signer.address)) } const threshold = await this.getThreshold() diff --git a/src/Safe.ts b/src/Safe.ts index d09066f5e..b32474e46 100644 --- a/src/Safe.ts +++ b/src/Safe.ts @@ -4,7 +4,7 @@ import { SafeSignature } from 'utils/signatures/SafeSignature' import { SafeTransaction } from './utils/transactions' interface Safe { - connect(safeAddress: string, providerOrSigner: Provider | Wallet): void + connect(providerOrSigner: Provider | Wallet, safeAddress?: string): void getProvider(): Provider getSigner(): Wallet | undefined getAddress(): string From 20d3ef5c5af4ac1092f410722ad6691be8b8f89d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Wed, 31 Mar 2021 20:23:03 +0200 Subject: [PATCH 21/24] Update tests --- tests/core.test.ts | 9 +++++ tests/execution.test.ts | 66 ++++++++++++++++++------------- tests/on-chain-signatures.test.ts | 48 +++++++++++----------- 3 files changed, 72 insertions(+), 51 deletions(-) diff --git a/tests/core.test.ts b/tests/core.test.ts index 437bc748e..cdef02224 100644 --- a/tests/core.test.ts +++ b/tests/core.test.ts @@ -31,6 +31,15 @@ describe('Safe Core SDK', () => { chai.expect(safeSdk.getProvider()).to.be.eq(user1.provider) chai.expect(safeSdk.getSigner()).to.be.undefined }) + + it('connect default provider', async () => { + const { safe } = await setupTests() + const safeSdk = new EthersSafe(ethers, safe.address) + const defaultProvider = safeSdk.getProvider() + chai.expect(ethers.providers.Provider.isProvider(defaultProvider)).to.be.true + chai.expect((await defaultProvider.getNetwork()).chainId).to.be.eq(1) + chai.expect(safeSdk.getSigner()).to.be.undefined + }) }) describe('getContractVersion', async () => { diff --git a/tests/execution.test.ts b/tests/execution.test.ts index 116e82ea2..b2ab1faea 100644 --- a/tests/execution.test.ts +++ b/tests/execution.test.ts @@ -16,9 +16,9 @@ describe('Transactions execution', () => { }) describe('execTransaction', async () => { - it('should fail if signer is not provided', async () => { + it('should fail if a provider is provided', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user1.provider) + const safeSdk1 = new EthersSafe(ethers, safe.address, user1.provider) const tx = new SafeTransaction({ to: safe.address, value: '0', @@ -26,31 +26,43 @@ describe('Transactions execution', () => { nonce: (await safe.nonce()).toString() }) await chai - .expect(safeSdk.executeTransaction(tx, { gasLimit: 10000000 })) + .expect(safeSdk1.executeTransaction(tx, { gasLimit: 10000000 })) .rejectedWith('No signer provided') }) + it('should fail if no provider or signer is provided', async () => { + const { safe } = await setupTests() + const safeSdk1 = new EthersSafe(ethers, safe.address) + const tx = new SafeTransaction({ + to: safe.address, + value: '0', + data: '0x', + nonce: (await safe.nonce()).toString() + }) + await chai.expect(safeSdk1.executeTransaction(tx)).rejectedWith('No signer provided') + }) + it('should fail if there are not enough signatures (1 missing)', async () => { const safe = await getSafeWithOwners([user1.address, user2.address, user3.address]) - const safeSdk = new EthersSafe(ethers, safe.address, user1) + const safeSdk1 = new EthersSafe(ethers, safe.address, user1) + const safeSdk2 = safeSdk1.connect(user2) const tx = new SafeTransaction({ to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - await safeSdk.signTransaction(tx) - safeSdk.connect(safe.address, user2) - const txHash = await safeSdk.getTransactionHash(tx) - await safeSdk.approveTransactionHash(txHash) + await safeSdk1.signTransaction(tx) + const txHash = await safeSdk2.getTransactionHash(tx) + await safeSdk2.approveTransactionHash(txHash) await chai - .expect(safeSdk.executeTransaction(tx, { gasLimit: 10000000 })) + .expect(safeSdk2.executeTransaction(tx, { gasLimit: 10000000 })) .to.be.rejectedWith('There is 1 signature missing') }) it('should fail if there are not enough signatures (>1 missing)', async () => { const safe = await getSafeWithOwners([user1.address, user2.address, user3.address]) - const safeSdk = new EthersSafe(ethers, safe.address, user1) + const safeSdk1 = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ to: safe.address, value: '0', @@ -58,56 +70,56 @@ describe('Transactions execution', () => { nonce: (await safe.nonce()).toString() }) await chai - .expect(safeSdk.executeTransaction(tx)) + .expect(safeSdk1.executeTransaction(tx)) .to.be.rejectedWith('There are 2 signatures missing') }) it('should execute a transaction with threshold 1', async () => { const safe = await getSafeWithOwners([user1.address]) - const safeSdk = new EthersSafe(ethers, safe.address, user1) + const safeSdk1 = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - const txResponse = await safeSdk.executeTransaction(tx, { gasLimit: 10000000 }) + const txResponse = await safeSdk1.executeTransaction(tx, { gasLimit: 10000000 }) chai.expect(txResponse.hash.length).to.be.eq(66) }) it('should execute a transaction with threshold >1', async () => { const safe = await getSafeWithOwners([user1.address, user2.address, user3.address]) - const safeSdk = new EthersSafe(ethers, safe.address, user1) + const safeSdk1 = new EthersSafe(ethers, safe.address, user1) + const safeSdk2 = safeSdk1.connect(user2) + const safeSdk3 = safeSdk1.connect(user3) const tx = new SafeTransaction({ to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - await safeSdk.signTransaction(tx) - safeSdk.connect(safe.address, user2) - const txHash = await safeSdk.getTransactionHash(tx) - await safeSdk.approveTransactionHash(txHash) - safeSdk.connect(safe.address, user3) - const txResponse = await safeSdk.executeTransaction(tx, { gasLimit: 10000000 }) + await safeSdk1.signTransaction(tx) + const txHash = await safeSdk2.getTransactionHash(tx) + await safeSdk2.approveTransactionHash(txHash) + const txResponse = await safeSdk3.executeTransaction(tx, { gasLimit: 10000000 }) chai.expect(txResponse.hash.length).to.be.eq(66) }) it('should execute a transaction when is not submitted by an owner', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user1) + const safeSdk1 = new EthersSafe(ethers, safe.address, user1) + const safeSdk2 = safeSdk1.connect(user2) + const safeSdk3 = safeSdk1.connect(user3) const tx = new SafeTransaction({ to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - await safeSdk.signTransaction(tx) - safeSdk.connect(safe.address, user2) - const txHash = await safeSdk.getTransactionHash(tx) - await safeSdk.approveTransactionHash(txHash) - safeSdk.connect(safe.address, user3) - const txResponse = await safeSdk.executeTransaction(tx, { gasLimit: 10000000 }) + await safeSdk1.signTransaction(tx) + const txHash = await safeSdk2.getTransactionHash(tx) + await safeSdk2.approveTransactionHash(txHash) + const txResponse = await safeSdk3.executeTransaction(tx, { gasLimit: 10000000 }) chai.expect(txResponse.hash.length).to.be.eq(66) }) }) diff --git a/tests/on-chain-signatures.test.ts b/tests/on-chain-signatures.test.ts index 2e0f79a6d..118fc8ee0 100644 --- a/tests/on-chain-signatures.test.ts +++ b/tests/on-chain-signatures.test.ts @@ -18,78 +18,78 @@ describe('On-chain signatures', () => { describe('approveTransactionHash', async () => { it('should fail if signer is not provided', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user1.provider) + const safeSdk1 = new EthersSafe(ethers, safe.address, user1.provider) const tx = new SafeTransaction({ to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - const txHash = await safeSdk.getTransactionHash(tx) + const txHash = await safeSdk1.getTransactionHash(tx) await chai - .expect(safeSdk.approveTransactionHash(txHash)) + .expect(safeSdk1.approveTransactionHash(txHash)) .to.be.rejectedWith('No signer provided') }) it('should fail if a transaction hash is approved by an account that is not an owner', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user3) + const safeSdk1 = new EthersSafe(ethers, safe.address, user3) const tx = new SafeTransaction({ to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - const hash = await safeSdk.getTransactionHash(tx) + const hash = await safeSdk1.getTransactionHash(tx) await chai - .expect(safeSdk.approveTransactionHash(hash)) + .expect(safeSdk1.approveTransactionHash(hash)) .to.be.rejectedWith('Transaction hashes can only be approved by Safe owners') }) it('should return the pre-validated signature without approving the transaction hash on-chain if specified', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user1) + const safeSdk1 = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - const txHash = await safeSdk.getTransactionHash(tx) - const signature = await safeSdk.approveTransactionHash(txHash, true) + const txHash = await safeSdk1.getTransactionHash(tx) + const signature = await safeSdk1.approveTransactionHash(txHash, true) chai.expect(await safe.approvedHashes(user1.address, txHash)).to.be.equal(0) chai.expect(signature.staticPart().length).to.be.eq(132) }) it('should return the pre-validated signature and approve the transaction hash', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user1) + const safeSdk1 = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - const hash = await safeSdk.getTransactionHash(tx) - const signature = await safeSdk.approveTransactionHash(hash) + const hash = await safeSdk1.getTransactionHash(tx) + const signature = await safeSdk1.approveTransactionHash(hash) chai.expect(await safe.approvedHashes(user1.address, hash)).to.be.equal(1) chai.expect(signature.staticPart().length).to.be.eq(132) }) it('should ignore a duplicated signatures', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user1) + const safeSdk1 = new EthersSafe(ethers, safe.address, user1) const tx = new SafeTransaction({ to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - const hash = await safeSdk.getTransactionHash(tx) + const hash = await safeSdk1.getTransactionHash(tx) chai.expect(await safe.approvedHashes(user1.address, hash)).to.be.equal(0) - await safeSdk.approveTransactionHash(hash) + await safeSdk1.approveTransactionHash(hash) chai.expect(await safe.approvedHashes(user1.address, hash)).to.be.equal(1) - await safeSdk.approveTransactionHash(hash) + await safeSdk1.approveTransactionHash(hash) chai.expect(await safe.approvedHashes(user1.address, hash)).to.be.equal(1) }) }) @@ -97,22 +97,22 @@ describe('On-chain signatures', () => { describe('getOwnersWhoApprovedTx', async () => { it('should return the list of owners who approved a transaction hash', async () => { const { safe } = await setupTests() - const safeSdk = new EthersSafe(ethers, safe.address, user1) + const safeSdk1 = new EthersSafe(ethers, safe.address, user1) + const safeSdk2 = safeSdk1.connect(user2) const tx = new SafeTransaction({ to: safe.address, value: '0', data: '0x', nonce: (await safe.nonce()).toString() }) - const txHash = await safeSdk.getTransactionHash(tx) - const ownersWhoApproved0 = await safeSdk.getOwnersWhoApprovedTx(txHash) + const txHash = await safeSdk1.getTransactionHash(tx) + const ownersWhoApproved0 = await safeSdk1.getOwnersWhoApprovedTx(txHash) chai.expect(ownersWhoApproved0.length).to.be.eq(0) - await safeSdk.approveTransactionHash(txHash) - const ownersWhoApproved1 = await safeSdk.getOwnersWhoApprovedTx(txHash) + await safeSdk1.approveTransactionHash(txHash) + const ownersWhoApproved1 = await safeSdk1.getOwnersWhoApprovedTx(txHash) chai.expect(ownersWhoApproved1.length).to.be.eq(1) - safeSdk.connect(safe.address, user2) - await safeSdk.approveTransactionHash(txHash) - const ownersWhoApproved2 = await safeSdk.getOwnersWhoApprovedTx(txHash) + await safeSdk2.approveTransactionHash(txHash) + const ownersWhoApproved2 = await safeSdk2.getOwnersWhoApprovedTx(txHash) chai.expect(ownersWhoApproved2.length).to.be.eq(2) }) }) From 0499351323d6935229bcd41f499320be858064ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Wed, 31 Mar 2021 20:27:45 +0200 Subject: [PATCH 22/24] Update README file --- README.md | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index adc82a41a..444eafdca 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ const safeNonce = Create an instance of the Safe Core SDK with wallet1 connected as the signer. ```js -const safeSdk = new EthersSafe(ethers, safeAddress, wallet1) +const safeSdk1 = new EthersSafe(ethers, safeAddress, wallet1) ``` ### 1. Create a Safe transaction @@ -65,7 +65,7 @@ Before executing this transaction, it must be signed by the owners and this can The owner `wallet1` signs the transaction off-chain. ```js -const wallet1Signature = await safeSdk.signTransaction(tx) +const wallet1Signature = await safeSdk1.signTransaction(tx) ``` Because the signature is off-chain, there is no interaction with the contract and the signature is available at `tx.signatures`. @@ -75,9 +75,9 @@ Because the signature is off-chain, there is no interaction with the contract an After `wallet2` account is connected to the SDK as the signer the transaction hash is approved on-chain. ```js -safeSdk.connect(safeAddress, wallet2) -const txHash = await safeSdk.getTransactionHash(tx) -const wallet2Signature = await safeSdk.approveTransactionHash(txHash) +const safeSdk2 = safeSdk1.connect(wallet2) +const txHash = await safeSdk2.getTransactionHash(tx) +const wallet2Signature = await safeSdk2.approveTransactionHash(txHash) ``` ### 3. Transaction execution @@ -85,20 +85,40 @@ const wallet2Signature = await safeSdk.approveTransactionHash(txHash) Lastly, `wallet3` account is connected to the SDK as the signer and executor of the Safe transaction to execute it. ```js -safeSdk.connect(safeAddress, wallet3) -const txResponse = await safeSdk.executeTransaction(tx) +const safeSdk3 = safeSdk2.connect(wallet3) +const txResponse = await safeSdk3.executeTransaction(tx) ``` All the signatures used to execute the transaction are available at `tx.signatures`. ## API Reference +### constructor + +Returns an instance of the Safe Core SDK with the `providerOrSigner` connected to the `safeAddress`. + +```js +const safeSdk = safeSdk(ethers, safeAddress, providerOrSigner) +``` + +If `providerOrSigner` is not provided, `ethers` default provider will be used. + +```js +const safeSdk = safeSdk(ethers, safeAddress) +``` + ### connect -Initializes the Safe Core SDK connecting the providerOrSigner to the safeAddress. +Returns a new instance of the Safe Core SDK with the `providerOrSigner` connected to the `safeAddress`. + +```js +safeSdk.connect(providerOrSigner, safeAddress) +``` + +If `safeAddress` is not provided, the `providerOrSigner` will be connected to the previous Safe. ```js -safeSdk.connect(safeAddress, providerOrSigner) +safeSdk.connect(providerOrSigner) ``` ### getProvider From 55e4c9944a2ceca93e8cfef10996a334ae827915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Wed, 31 Mar 2021 20:35:23 +0200 Subject: [PATCH 23/24] Fix constructor in README file --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 444eafdca..ccc493f61 100644 --- a/README.md +++ b/README.md @@ -98,13 +98,13 @@ All the signatures used to execute the transaction are available at `tx.signatur Returns an instance of the Safe Core SDK with the `providerOrSigner` connected to the `safeAddress`. ```js -const safeSdk = safeSdk(ethers, safeAddress, providerOrSigner) +const safeSdk = new EthersSafe(ethers, safeAddress, providerOrSigner) ``` If `providerOrSigner` is not provided, `ethers` default provider will be used. ```js -const safeSdk = safeSdk(ethers, safeAddress) +const safeSdk = new EthersSafe(ethers, safeAddress) ``` ### connect @@ -112,13 +112,13 @@ const safeSdk = safeSdk(ethers, safeAddress) Returns a new instance of the Safe Core SDK with the `providerOrSigner` connected to the `safeAddress`. ```js -safeSdk.connect(providerOrSigner, safeAddress) +const safeSdk2 = safeSdk.connect(providerOrSigner, safeAddress) ``` If `safeAddress` is not provided, the `providerOrSigner` will be connected to the previous Safe. ```js -safeSdk.connect(providerOrSigner) +const safeSdk2 = safeSdk.connect(providerOrSigner) ``` ### getProvider From 968ece591c9275510380298efd411b95d409ff82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Mart=C3=ADnez?= Date: Tue, 6 Apr 2021 12:07:30 +0200 Subject: [PATCH 24/24] Review PRs comments on-chain signatures --- src/EthersSafe.ts | 18 ++++++++---------- tests/core.test.ts | 6 +++--- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/EthersSafe.ts b/src/EthersSafe.ts index 26ecbd5e2..94b1cae7d 100644 --- a/src/EthersSafe.ts +++ b/src/EthersSafe.ts @@ -170,11 +170,10 @@ class EthersSafe implements Safe { throw new Error('No signer provided') } const owners = await this.getOwners() - if ( - !owners.find( - (owner: string) => this.#signer && areAddressesEqual(owner, this.#signer.address) - ) - ) { + const addressIsOwner = owners.find( + (owner: string) => this.#signer && areAddressesEqual(owner, this.#signer.address) + ) + if (!addressIsOwner) { throw new Error('Transactions can only be signed by Safe owners') } const messageArray = this.#ethers.utils.arrayify(hash) @@ -208,11 +207,10 @@ class EthersSafe implements Safe { throw new Error('No signer provided') } const owners = await this.getOwners() - if ( - !owners.find( - (owner: string) => this.#signer && areAddressesEqual(owner, this.#signer.address) - ) - ) { + const addressIsOwner = owners.find( + (owner: string) => this.#signer && areAddressesEqual(owner, this.#signer.address) + ) + if (!addressIsOwner) { throw new Error('Transaction hashes can only be approved by Safe owners') } if (!skipOnChainApproval) { diff --git a/tests/core.test.ts b/tests/core.test.ts index cdef02224..b1bce8076 100644 --- a/tests/core.test.ts +++ b/tests/core.test.ts @@ -18,21 +18,21 @@ describe('Safe Core SDK', () => { }) describe('connect', async () => { - it('connect signer', async () => { + it('should connect with signer', async () => { const { safe } = await setupTests() const safeSdk = new EthersSafe(ethers, safe.address, user1) chai.expect(safeSdk.getProvider()).to.be.eq(user1.provider) chai.expect(safeSdk.getSigner()).to.be.eq(user1) }) - it('connect provider', async () => { + it('should connect with provider', async () => { const { safe } = await setupTests() const safeSdk = new EthersSafe(ethers, safe.address, user1.provider) chai.expect(safeSdk.getProvider()).to.be.eq(user1.provider) chai.expect(safeSdk.getSigner()).to.be.undefined }) - it('connect default provider', async () => { + it('should connect with default provider', async () => { const { safe } = await setupTests() const safeSdk = new EthersSafe(ethers, safe.address) const defaultProvider = safeSdk.getProvider()