From 05500ab20c9ca94ad652758ba216276bde6bddce Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Apr 2021 08:20:50 -0700 Subject: [PATCH 01/10] @metamask/eslint-config@6.0.0 --- .eslintrc.js | 13 ++-- package.json | 13 ++-- yarn.lock | 174 +++++++++++++++++++++++++++++++++------------------ 3 files changed, 128 insertions(+), 72 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index f9635aabf9..be1b8ce35b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,26 +1,27 @@ module.exports = { root: true, - extends: ['@metamask/eslint-config', '@metamask/eslint-config/config/jest', '@metamask/eslint-config/config/nodejs'], + extends: ['@metamask/eslint-config', '@metamask/eslint-config-nodejs'], ignorePatterns: ['!.eslintrc.js', '!jest.config.js', 'node_modules', 'dist', 'docs', 'coverage', '*.d.ts'], overrides: [ + { + files: ['*.test.ts', '*.test.js'], + extends: ['@metamask/eslint-config-jest'] + }, { files: ['*.js'], parserOptions: { - ecmaVersion: '2018', sourceType: 'script', }, }, { files: ['*.ts'], - extends: ['@metamask/eslint-config/config/typescript'], + extends: ['@metamask/eslint-config-typescript'], rules: { // `no-shadow` has incompatibilities with TypeScript + // TODO: Migrate this into @metamask/eslint-config? 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'error', - // Prettier handles indentation. This rule conflicts with prettier in some cases - '@typescript-eslint/indent': 'off', - // disabled due to incompatibility with Record // See https://github.com/Microsoft/TypeScript/issues/15300#issuecomment-702872440 '@typescript-eslint/consistent-type-definitions': 'off', diff --git a/package.json b/package.json index 6cf3711bf8..aa52990323 100644 --- a/package.json +++ b/package.json @@ -67,18 +67,23 @@ "web3-provider-engine": "^16.0.1" }, "devDependencies": { - "@metamask/eslint-config": "^5.0.0", + "@metamask/eslint-config": "^6.0.0", + "@metamask/eslint-config-jest": "^6.0.0", + "@metamask/eslint-config-nodejs": "^6.0.0", + "@metamask/eslint-config-typescript": "^6.0.0", "@types/jest": "^22.2.3", "@types/node": "^14.14.31", "@types/sinon": "^9.0.10", "@types/web3": "^1.0.6", - "@typescript-eslint/eslint-plugin": "^4.18.0", - "@typescript-eslint/parser": "^4.18.0", - "eslint": "^7.20.0", + "@typescript-eslint/eslint-plugin": "^4.22.0", + "@typescript-eslint/parser": "^4.22.0", + "eslint": "^7.24.0", + "eslint-config-prettier": "^8.1.0", "eslint-import-resolver-typescript": "^2.4.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jest": "^24.1.5", "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^3.3.1", "ethjs-provider-http": "^0.1.6", "jest": "^26.4.2", "jest-environment-jsdom": "^25.0.0", diff --git a/yarn.lock b/yarn.lock index c691cea9a7..cf223302a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -416,10 +416,10 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@eslint/eslintrc@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.3.0.tgz#d736d6963d7003b6514e6324bec9c602ac340318" - integrity sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg== +"@eslint/eslintrc@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.0.tgz#99cc0a0584d72f1df38b900fb062ba995f395547" + integrity sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog== dependencies: ajv "^6.12.4" debug "^4.1.1" @@ -428,7 +428,6 @@ ignore "^4.0.6" import-fresh "^3.2.1" js-yaml "^3.13.1" - lodash "^4.17.20" minimatch "^3.0.4" strip-json-comments "^3.1.1" @@ -664,10 +663,25 @@ resolved "https://registry.yarnpkg.com/@metamask/contract-metadata/-/contract-metadata-1.24.0.tgz#9f9c50378e805be082745f5b470b6819221af3c7" integrity sha512-0/DIOU+vCnayfJarLHv8LrYyXl+JZwUlpRuESXtAjY7TT74Yi6rEpEQK15PE/VY415XrmJ80EG9ZP6NJULK6wg== -"@metamask/eslint-config@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@metamask/eslint-config/-/eslint-config-5.0.0.tgz#70c1ca854ce9b3b1cabd89cb736e8bb36127d164" - integrity sha512-eZt17NofPMmtoNjmBGOhUdAmyL0C+2/smtqAkVhpzZsU2ZGv+4Kekn8p8gcNONOYN8EotpWUxGkN1CTdVLdWZw== +"@metamask/eslint-config-jest@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eslint-config-jest/-/eslint-config-jest-6.0.0.tgz#9e10cfbca31236afd7be2058be70365084e540d6" + integrity sha512-C0sXmyp5Hnp5IHVYXaW2TJAo/E9UiS192CwyUcw2qU1Ck7lj4z/wHdgROaH5F6rInqBO3afkDaqnArqvoxvO5Q== + +"@metamask/eslint-config-nodejs@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eslint-config-nodejs/-/eslint-config-nodejs-6.0.0.tgz#df77bb35b91556030f1b23ad4ff51c1caf033339" + integrity sha512-nx7VhJRpJKQrcdDvy2bLCSWqBmWftgqxyG+BUw06XcWQzbmZTn94EXdLlH6zTQxmR4C+m+AOy5ung0NSUwmY3g== + +"@metamask/eslint-config-typescript@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eslint-config-typescript/-/eslint-config-typescript-6.0.0.tgz#c7bee90a48ed3c49f9f786c53dcdfea4cfd2a6db" + integrity sha512-pfchVgPz03jHvBMu+/wRNVVf4gIIgjZ+CtfsoZF6PkxzD3h0T5e+7aGcgDET7SlkOVnAySMQxr39lSnYXHQPvQ== + +"@metamask/eslint-config@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eslint-config/-/eslint-config-6.0.0.tgz#ec53e8ab278073e882411ed89705bc7d06b78c81" + integrity sha512-LyakGYGwM8UQOGhwWa+5erAI1hXuiTgf/y7USzOomX6H9KiuY09IAUYnPh7ToPG2sedD2F48UF1bUm8yvCoZOw== "@metamask/safe-event-emitter@^2.0.0": version "2.0.0" @@ -920,13 +934,13 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.18.0.tgz#50fbce93211b5b690895d20ebec6fe8db48af1f6" - integrity sha512-Lzkc/2+7EoH7+NjIWLS2lVuKKqbEmJhtXe3rmfA8cyiKnZm3IfLf51irnBcmow8Q/AptVV0XBZmBJKuUJTe6cQ== +"@typescript-eslint/eslint-plugin@^4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz#3d5f29bb59e61a9dba1513d491b059e536e16dbc" + integrity sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA== dependencies: - "@typescript-eslint/experimental-utils" "4.18.0" - "@typescript-eslint/scope-manager" "4.18.0" + "@typescript-eslint/experimental-utils" "4.22.0" + "@typescript-eslint/scope-manager" "4.22.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" lodash "^4.17.15" @@ -934,15 +948,15 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.18.0.tgz#ed6c955b940334132b17100d2917449b99a91314" - integrity sha512-92h723Kblt9JcT2RRY3QS2xefFKar4ZQFVs3GityOKWQYgtajxt/tuXIzL7sVCUlM1hgreiV5gkGYyBpdOwO6A== +"@typescript-eslint/experimental-utils@4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz#68765167cca531178e7b650a53456e6e0bef3b1f" + integrity sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.18.0" - "@typescript-eslint/types" "4.18.0" - "@typescript-eslint/typescript-estree" "4.18.0" + "@typescript-eslint/scope-manager" "4.22.0" + "@typescript-eslint/types" "4.22.0" + "@typescript-eslint/typescript-estree" "4.22.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" @@ -958,14 +972,14 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@^4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.18.0.tgz#a211edb14a69fc5177054bec04c95b185b4dde21" - integrity sha512-W3z5S0ZbecwX3PhJEAnq4mnjK5JJXvXUDBYIYGoweCyWyuvAKfGHvzmpUzgB5L4cRBb+cTu9U/ro66dx7dIimA== +"@typescript-eslint/parser@^4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.22.0.tgz#e1637327fcf796c641fe55f73530e90b16ac8fe8" + integrity sha512-z/bGdBJJZJN76nvAY9DkJANYgK3nlRstRRi74WHm3jjgf2I8AglrSY+6l7ogxOmn55YJ6oKZCLLy+6PW70z15Q== dependencies: - "@typescript-eslint/scope-manager" "4.18.0" - "@typescript-eslint/types" "4.18.0" - "@typescript-eslint/typescript-estree" "4.18.0" + "@typescript-eslint/scope-manager" "4.22.0" + "@typescript-eslint/types" "4.22.0" + "@typescript-eslint/typescript-estree" "4.22.0" debug "^4.1.1" "@typescript-eslint/scope-manager@4.15.2": @@ -976,23 +990,23 @@ "@typescript-eslint/types" "4.15.2" "@typescript-eslint/visitor-keys" "4.15.2" -"@typescript-eslint/scope-manager@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.18.0.tgz#d75b55234c35d2ff6ac945758d6d9e53be84a427" - integrity sha512-olX4yN6rvHR2eyFOcb6E4vmhDPsfdMyfQ3qR+oQNkAv8emKKlfxTWUXU5Mqxs2Fwe3Pf1BoPvrwZtwngxDzYzQ== +"@typescript-eslint/scope-manager@4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz#ed411545e61161a8d702e703a4b7d96ec065b09a" + integrity sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q== dependencies: - "@typescript-eslint/types" "4.18.0" - "@typescript-eslint/visitor-keys" "4.18.0" + "@typescript-eslint/types" "4.22.0" + "@typescript-eslint/visitor-keys" "4.22.0" "@typescript-eslint/types@4.15.2": version "4.15.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.15.2.tgz#04acf3a2dc8001a88985291744241e732ef22c60" integrity sha512-r7lW7HFkAarfUylJ2tKndyO9njwSyoy6cpfDKWPX6/ctZA+QyaYscAHXVAfJqtnY6aaTwDYrOhp+ginlbc7HfQ== -"@typescript-eslint/types@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.18.0.tgz#bebe323f81f2a7e2e320fac9415e60856267584a" - integrity sha512-/BRociARpj5E+9yQ7cwCF/SNOWwXJ3qhjurMuK2hIFUbr9vTuDeu476Zpu+ptxY2kSxUHDGLLKy+qGq2sOg37A== +"@typescript-eslint/types@4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.0.tgz#0ca6fde5b68daf6dba133f30959cc0688c8dd0b6" + integrity sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA== "@typescript-eslint/typescript-estree@4.15.2": version "4.15.2" @@ -1007,13 +1021,13 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/typescript-estree@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.18.0.tgz#756d3e61da8c16ab99185532c44872f4cd5538cb" - integrity sha512-wt4xvF6vvJI7epz+rEqxmoNQ4ZADArGQO9gDU+cM0U5fdVv7N+IAuVoVAoZSOZxzGHBfvE3XQMLdy+scsqFfeg== +"@typescript-eslint/typescript-estree@4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz#b5d95d6d366ff3b72f5168c75775a3e46250d05c" + integrity sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg== dependencies: - "@typescript-eslint/types" "4.18.0" - "@typescript-eslint/visitor-keys" "4.18.0" + "@typescript-eslint/types" "4.22.0" + "@typescript-eslint/visitor-keys" "4.22.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" @@ -1028,12 +1042,12 @@ "@typescript-eslint/types" "4.15.2" eslint-visitor-keys "^2.0.0" -"@typescript-eslint/visitor-keys@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.18.0.tgz#4e6fe2a175ee33418318a029610845a81e2ff7b6" - integrity sha512-Q9t90JCvfYaN0OfFUgaLqByOfz8yPeTAdotn/XYNm5q9eHax90gzdb+RJ6E9T5s97Kv/UHWKERTmqA0jTKAEHw== +"@typescript-eslint/visitor-keys@4.22.0": + version "4.22.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz#169dae26d3c122935da7528c839f42a8a42f6e47" + integrity sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw== dependencies: - "@typescript-eslint/types" "4.18.0" + "@typescript-eslint/types" "4.22.0" eslint-visitor-keys "^2.0.0" abab@^2.0.0: @@ -2344,6 +2358,11 @@ escodegen@^1.11.1, escodegen@^1.14.1: optionalDependencies: source-map "~0.6.1" +eslint-config-prettier@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz#4ef1eaf97afe5176e6a75ddfb57c335121abc5a6" + integrity sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw== + eslint-import-resolver-node@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" @@ -2417,6 +2436,13 @@ eslint-plugin-node@^11.1.0: resolve "^1.10.1" semver "^6.1.0" +eslint-plugin-prettier@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz#7079cfa2497078905011e6f82e8dd8453d1371b7" + integrity sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-scope@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5" @@ -2450,13 +2476,13 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@^7.20.0: - version "7.20.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.20.0.tgz#db07c4ca4eda2e2316e7aa57ac7fc91ec550bdc7" - integrity sha512-qGi0CTcOGP2OtCQBgWZlQjcTuP0XkIpYFj25XtRTQSHC+umNnp7UMshr2G8SLsRFYDdAPFeHOsiteadmMH02Yw== +eslint@^7.24.0: + version "7.24.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.24.0.tgz#2e44fa62d93892bfdb100521f17345ba54b8513a" + integrity sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ== dependencies: "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.3.0" + "@eslint/eslintrc" "^0.4.0" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -2469,10 +2495,10 @@ eslint@^7.20.0: espree "^7.3.1" esquery "^1.4.0" esutils "^2.0.2" - file-entry-cache "^6.0.0" + file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" - globals "^12.1.0" + globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" @@ -2480,7 +2506,7 @@ eslint@^7.20.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.20" + lodash "^4.17.21" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -3164,6 +3190,11 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-glob@^3.1.1: version "3.2.4" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" @@ -3213,10 +3244,10 @@ figures@^1.7.0: escape-string-regexp "^1.0.5" object-assign "^4.1.0" -file-entry-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.0.tgz#7921a89c391c6d93efec2169ac6bf300c527ea0a" - integrity sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA== +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" @@ -3435,6 +3466,13 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globals@^13.6.0: + version "13.8.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.8.0.tgz#3e20f504810ce87a8d72e55aecf8435b50f4c1b3" + integrity sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q== + dependencies: + type-fest "^0.20.2" + globby@^11.0.1: version "11.0.1" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" @@ -5831,6 +5869,13 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + prettier@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" @@ -7070,6 +7115,11 @@ type-fest@^0.11.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" From b99cdb03efea1a8a3a17d7d8e224753e49505c9d Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Apr 2021 08:22:30 -0700 Subject: [PATCH 02/10] Update package scripts, remove format script --- .circleci/config.yml | 15 --------------- package.json | 7 ++++--- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ec4c0ce4b4..cd6c328a5e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,14 +13,10 @@ workflows: - test-unit: requires: - prep-build - - test-format: - requires: - - prep-build - all-tests-pass: requires: - test-lint - test-unit - - test-format jobs: prep-deps: @@ -57,17 +53,6 @@ jobs: paths: - dist - test-format: - docker: - - image: circleci/node:12 - steps: - - checkout - - attach_workspace: - at: . - - run: - name: Format - command: yarn format - test-lint: docker: - image: circleci/node:12 diff --git a/package.json b/package.json index aa52990323..6aeeb4006d 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,11 @@ }, "scripts": { "prepublishOnly": "yarn build", - "lint": "eslint --ext js,ts .", + "lint:eslint": "eslint . --cache --ext js,ts", + "lint:json": "prettier '**/*.json' --ignore-path .gitignore", + "lint": "yarn lint:eslint && yarn lint:json --check", + "lint:fix": "yarn lint:eslint --fix && yarn lint:json --write", "test": "jest --coverage", - "format": "prettier '**/*.ts' '**/*.js' --check --ignore-path=.gitignore", - "format:fix": "prettier '**/*.ts' '**/*.js' --write --ignore-path=.gitignore", "build": "rm -rf dist && tsc", "build:watch": "yarn build && tsc -w", "build:link": "yarn build && cd dist && yarn link && rm -rf node_modules && cd ..", From b8fa351550db0af3a9eb98cf5eb1a1b41b6a9358 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Apr 2021 08:34:52 -0700 Subject: [PATCH 03/10] yarn lint:fix, use .toStrictEqual in tests --- .eslintrc.js | 6 +- .gitignore | 3 + package.json | 2 +- src/BaseController.test.ts | 20 +-- src/BaseControllerV2.test.ts | 56 ++++----- src/ControllerMessenger.test.ts | 36 +++--- src/apis/crypto-compare.test.ts | 18 +-- src/approval/ApprovalController.test.js | 8 +- src/approval/ApprovalController.test.ts | 116 +++++++++--------- src/assets/AccountTrackerController.test.ts | 4 +- src/assets/AssetsContractController.test.ts | 22 ++-- src/assets/AssetsController.test.ts | 48 ++++---- src/assets/AssetsDetectionController.test.ts | 28 ++--- src/assets/CurrencyRateController.test.ts | 10 +- src/assets/TokenBalancesController.test.ts | 4 +- src/assets/TokenRatesController.test.ts | 8 +- src/keyring/KeyringController.test.ts | 20 +-- .../AbstractMessageManager.test.ts | 16 +-- src/message-manager/MessageManager.test.ts | 18 +-- .../PersonalMessageManager.test.ts | 18 +-- .../TypedMessageManager.test.ts | 20 +-- src/network/NetworkController.test.ts | 2 +- .../NotificationController.test.ts | 2 +- src/third-party/EnsController.test.ts | 32 ++--- src/third-party/PhishingController.test.ts | 2 +- src/transaction/TransactionController.test.ts | 10 +- src/user/AddressBookController.test.ts | 24 ++-- src/user/PreferencesController.test.ts | 52 ++++---- src/util.test.ts | 36 +++--- yarn.lock | 41 ++++++- 30 files changed, 360 insertions(+), 322 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index be1b8ce35b..a648a54f99 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { overrides: [ { files: ['*.test.ts', '*.test.js'], - extends: ['@metamask/eslint-config-jest'] + extends: ['@metamask/eslint-config-jest'], }, { files: ['*.js'], @@ -39,7 +39,7 @@ module.exports = { // Left disabled because various properties throughough this repo are snake_case because the // names come from external sources or must comply with standards // e.g. `txreceipt_status`, `signTypedData_v4`, `token_id` - camelcase: 'off', + 'camelcase': 'off', // TODO: re-enble most of these rules 'function-paren-newline': 'off', @@ -54,7 +54,7 @@ module.exports = { 'no-negated-condition': 'off', 'no-new': 'off', 'no-param-reassign': 'off', - radix: 'off', + 'radix': 'off', 'require-atomic-updates': 'off', 'jest/no-conditional-expect': 'off', diff --git a/.gitignore b/.gitignore index c7a4175bd3..8c57c00a02 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ package-lock.json # tarball from `yarn pack` *.tgz +# eslint cache +.eslintcache + .DS_STORE coverage dist diff --git a/package.json b/package.json index 6aeeb4006d..0e065f1eb6 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "@metamask/eslint-config-jest": "^6.0.0", "@metamask/eslint-config-nodejs": "^6.0.0", "@metamask/eslint-config-typescript": "^6.0.0", - "@types/jest": "^22.2.3", + "@types/jest": "^26.0.22", "@types/node": "^14.14.31", "@types/sinon": "^9.0.10", "@types/web3": "^1.0.6", diff --git a/src/BaseController.test.ts b/src/BaseController.test.ts index 7df1ac1f0c..c06c7d21e6 100644 --- a/src/BaseController.test.ts +++ b/src/BaseController.test.ts @@ -14,33 +14,33 @@ class TestController extends BaseController { describe('BaseController', () => { it('should set initial state', () => { const controller = new TestController(undefined, STATE); - expect(controller.state).toEqual(STATE); + expect(controller.state).toStrictEqual(STATE); }); it('should set initial config', () => { const controller = new TestController(CONFIG); - expect(controller.config).toEqual(CONFIG); + expect(controller.config).toStrictEqual(CONFIG); }); it('should overwrite state', () => { const controller = new TestController(); - expect(controller.state).toEqual({}); + expect(controller.state).toStrictEqual({}); controller.update(STATE, true); - expect(controller.state).toEqual(STATE); + expect(controller.state).toStrictEqual(STATE); }); it('should overwrite config', () => { const controller = new TestController(); - expect(controller.config).toEqual({}); + expect(controller.config).toStrictEqual({}); controller.configure(CONFIG, true); - expect(controller.config).toEqual(CONFIG); + expect(controller.config).toStrictEqual(CONFIG); }); it('should be able to partially update the config', () => { const controller = new TestController(CONFIG); - expect(controller.config).toEqual(CONFIG); + expect(controller.config).toStrictEqual(CONFIG); controller.configure({ disabled: false }, false, false); - expect(controller.config).toEqual({ disabled: false }); + expect(controller.config).toStrictEqual({ disabled: false }); }); it('should notify all listeners', () => { @@ -52,8 +52,8 @@ describe('BaseController', () => { controller.notify(); expect(listenerOne.calledOnce).toBe(true); expect(listenerTwo.calledOnce).toBe(true); - expect(listenerOne.getCall(0).args[0]).toEqual(STATE); - expect(listenerTwo.getCall(0).args[0]).toEqual(STATE); + expect(listenerOne.getCall(0).args[0]).toStrictEqual(STATE); + expect(listenerTwo.getCall(0).args[0]).toStrictEqual(STATE); }); it('should not notify unsubscribed listeners', () => { diff --git a/src/BaseControllerV2.test.ts b/src/BaseControllerV2.test.ts index df5f86ad13..0e8cc10616 100644 --- a/src/BaseControllerV2.test.ts +++ b/src/BaseControllerV2.test.ts @@ -45,7 +45,7 @@ describe('BaseController', () => { metadata: countControllerStateMetadata, }); - expect(controller.state).toEqual({ count: 0 }); + expect(controller.state).toStrictEqual({ count: 0 }); }); it('should set initial schema', () => { @@ -62,7 +62,7 @@ describe('BaseController', () => { metadata: countControllerStateMetadata, }); - expect(controller.metadata).toEqual(countControllerStateMetadata); + expect(controller.metadata).toStrictEqual(CountControllerStateMetadata); }); it('should not allow mutating state directly', () => { @@ -102,7 +102,7 @@ describe('BaseController', () => { draft.count += 1; }); - expect(controller.state).toEqual({ count: 1 }); + expect(controller.state).toStrictEqual({ count: 1 }); }); it('should allow updating state by return a value', () => { @@ -123,7 +123,7 @@ describe('BaseController', () => { return { count: 1 }; }); - expect(controller.state).toEqual({ count: 1 }); + expect(controller.state).toStrictEqual({ count: 1 }); }); it('should throw an error if update callback modifies draft and returns value', () => { @@ -172,10 +172,10 @@ describe('BaseController', () => { return { count: 1 }; }); - expect(listener1.callCount).toEqual(1); - expect(listener1.firstCall.args).toEqual([{ count: 1 }, [{ op: 'replace', path: [], value: { count: 1 } }]]); - expect(listener2.callCount).toEqual(1); - expect(listener2.firstCall.args).toEqual([{ count: 1 }, [{ op: 'replace', path: [], value: { count: 1 } }]]); + expect(listener1.callCount).toStrictEqual(1); + expect(listener1.firstCall.args).toStrictEqual([{ count: 1 }, [{ op: 'replace', path: [], value: { count: 1 } }]]); + expect(listener2.callCount).toStrictEqual(1); + expect(listener2.firstCall.args).toStrictEqual([{ count: 1 }, [{ op: 'replace', path: [], value: { count: 1 } }]]); }); it('should inform a subscriber of each state change once even after multiple subscriptions', () => { @@ -200,8 +200,8 @@ describe('BaseController', () => { return { count: 1 }; }); - expect(listener1.callCount).toEqual(1); - expect(listener1.firstCall.args).toEqual([{ count: 1 }, [{ op: 'replace', path: [], value: { count: 1 } }]]); + expect(listener1.callCount).toStrictEqual(1); + expect(listener1.firstCall.args).toStrictEqual([{ count: 1 }, [{ op: 'replace', path: [], value: { count: 1 } }]]); }); it('should no longer inform a subscriber about state changes after unsubscribing', () => { @@ -225,7 +225,7 @@ describe('BaseController', () => { return { count: 1 }; }); - expect(listener1.callCount).toEqual(0); + expect(listener1.callCount).toStrictEqual(0); }); it('should no longer inform a subscriber about state changes after unsubscribing once, even if they subscribed many times', () => { @@ -250,7 +250,7 @@ describe('BaseController', () => { return { count: 1 }; }); - expect(listener1.callCount).toEqual(0); + expect(listener1.callCount).toStrictEqual(0); }); it('should throw when unsubscribing listener who was never subscribed', () => { @@ -296,19 +296,19 @@ describe('BaseController', () => { return { count: 1 }; }); - expect(listener1.callCount).toEqual(0); - expect(listener2.callCount).toEqual(0); + expect(listener1.callCount).toStrictEqual(0); + expect(listener2.callCount).toStrictEqual(0); }); }); describe('getAnonymizedState', () => { it('should return empty state', () => { - expect(getAnonymizedState({}, {})).toEqual({}); + expect(getAnonymizedState({}, {})).toStrictEqual({}); }); it('should return empty state when no properties are anonymized', () => { const anonymizedState = getAnonymizedState({ count: 1 }, { count: { anonymous: false, persist: false } }); - expect(anonymizedState).toEqual({}); + expect(anonymizedState).toStrictEqual({}); }); it('should return state that is already anonymized', () => { @@ -338,7 +338,7 @@ describe('getAnonymizedState', () => { }, }, ); - expect(anonymizedState).toEqual({ network: 'mainnet', tokens: ['DAI', 'USDC'] }); + expect(anonymizedState).toStrictEqual({ network: 'mainnet', tokens: ['DAI', 'USDC'] }); }); it('should use anonymizing function to anonymize state', () => { @@ -358,7 +358,7 @@ describe('getAnonymizedState', () => { }, ); - expect(anonymizedState).toEqual({ transactionHash: '4321x0' }); + expect(anonymizedState).toStrictEqual({ transactionHash: '4321x0' }); }); it('should allow returning a partial object from an anonymizing function', () => { @@ -381,7 +381,7 @@ describe('getAnonymizedState', () => { }, ); - expect(anonymizedState).toEqual({ txMeta: { value: 10 } }); + expect(anonymizedState).toStrictEqual({ txMeta: { value: 10 } }); }); it('should allow returning a nested partial object from an anonymizing function', () => { @@ -415,7 +415,7 @@ describe('getAnonymizedState', () => { }, ); - expect(anonymizedState).toEqual({ txMeta: { history: [{ value: 9 }], value: 10 } }); + expect(anonymizedState).toStrictEqual({ txMeta: { history: [{ value: 9 }], value: 10 } }); }); it('should allow transforming types in an anonymizing function', () => { @@ -431,18 +431,18 @@ describe('getAnonymizedState', () => { }, ); - expect(anonymizedState).toEqual({ count: 1 }); + expect(anonymizedState).toStrictEqual({ count: 1 }); }); }); describe('getPersistentState', () => { it('should return empty state', () => { - expect(getPersistentState({}, {})).toEqual({}); + expect(getPersistentState({}, {})).toStrictEqual({}); }); it('should return empty state when no properties are persistent', () => { const persistentState = getPersistentState({ count: 1 }, { count: { anonymous: false, persist: false } }); - expect(persistentState).toEqual({}); + expect(persistentState).toStrictEqual({}); }); it('should return persistent state', () => { @@ -472,7 +472,7 @@ describe('getPersistentState', () => { }, }, ); - expect(persistentState).toEqual({ password: 'secret password', privateKey: '123' }); + expect(persistentState).toStrictEqual({ password: 'secret password', privateKey: '123' }); }); it('should use function to derive persistent state', () => { @@ -492,7 +492,7 @@ describe('getPersistentState', () => { }, ); - expect(persistentState).toEqual({ transactionHash: '0x1234' }); + expect(persistentState).toStrictEqual({ transactionHash: '0x1234' }); }); it('should allow returning a partial object from a persist function', () => { @@ -515,7 +515,7 @@ describe('getPersistentState', () => { }, ); - expect(persistentState).toEqual({ txMeta: { value: 10 } }); + expect(persistentState).toStrictEqual({ txMeta: { value: 10 } }); }); it('should allow returning a nested partial object from a persist function', () => { @@ -553,7 +553,7 @@ describe('getPersistentState', () => { }, ); - expect(persistentState).toEqual({ txMeta: { history: [{ value: 9 }], value: 10 } }); + expect(persistentState).toStrictEqual({ txMeta: { history: [{ value: 9 }], value: 10 } }); }); it('should allow transforming types in a persist function', () => { @@ -569,7 +569,7 @@ describe('getPersistentState', () => { }, ); - expect(persistentState).toEqual({ count: 1 }); + expect(persistentState).toStrictEqual({ count: 1 }); }); describe('inter-controller communication', () => { diff --git a/src/ControllerMessenger.test.ts b/src/ControllerMessenger.test.ts index 0fbcbe85c0..7eff43022d 100644 --- a/src/ControllerMessenger.test.ts +++ b/src/ControllerMessenger.test.ts @@ -17,7 +17,7 @@ describe('ControllerMessenger', () => { }); controllerMessenger.call('count', 1); - expect(count).toEqual(1); + expect(count).toStrictEqual(1); }); it('should allow registering and calling multiple different action handlers', () => { @@ -37,7 +37,7 @@ describe('ControllerMessenger', () => { controllerMessenger.call('reset', 'hello'); controllerMessenger.call('concat', ', world'); - expect(message).toEqual('hello, world'); + expect(message).toStrictEqual('hello, world'); }); it('should allow registering and calling an action handler with no parameters', () => { @@ -50,7 +50,7 @@ describe('ControllerMessenger', () => { }); controllerMessenger.call('increment'); - expect(count).toEqual(1); + expect(count).toStrictEqual(1); }); it('should allow registering and calling an action handler with multiple parameters', () => { @@ -63,7 +63,7 @@ describe('ControllerMessenger', () => { }); controllerMessenger.call('message', '0x123', 'hello'); - expect(messages['0x123']).toEqual('hello'); + expect(messages['0x123']).toStrictEqual('hello'); }); it('should allow registering and calling an action handler with a return value', () => { @@ -75,7 +75,7 @@ describe('ControllerMessenger', () => { }); const result = controllerMessenger.call('add', 5, 10); - expect(result).toEqual(15); + expect(result).toStrictEqual(15); }); it('should not allow registering multiple action handlers under the same name', () => { @@ -116,7 +116,7 @@ describe('ControllerMessenger', () => { expect(() => { controllerMessenger.call('ping'); }).toThrow('A handler for ping has not been registered'); - expect(pingCount).toEqual(0); + expect(pingCount).toStrictEqual(0); }); it('should throw when calling an action after actions have been reset', () => { @@ -137,7 +137,7 @@ describe('ControllerMessenger', () => { expect(() => { controllerMessenger.call('ping'); }).toThrow('A handler for ping has not been registered'); - expect(pingCount).toEqual(0); + expect(pingCount).toStrictEqual(0); }); it('should publish event to subscriber', () => { @@ -149,7 +149,7 @@ describe('ControllerMessenger', () => { controllerMessenger.publish('message', 'hello'); expect(handler.calledWithExactly('hello')).toBeTruthy(); - expect(handler.callCount).toEqual(1); + expect(handler.callCount).toStrictEqual(1); }); it('should allow publishing multiple different events to subscriber', () => { @@ -165,9 +165,9 @@ describe('ControllerMessenger', () => { controllerMessenger.publish('ping'); expect(messageHandler.calledWithExactly('hello')).toBeTruthy(); - expect(messageHandler.callCount).toEqual(1); + expect(messageHandler.callCount).toStrictEqual(1); expect(pingHandler.calledWithExactly()).toBeTruthy(); - expect(pingHandler.callCount).toEqual(1); + expect(pingHandler.callCount).toStrictEqual(1); }); it('should publish event with no payload to subscriber', () => { @@ -179,7 +179,7 @@ describe('ControllerMessenger', () => { controllerMessenger.publish('ping'); expect(handler.calledWithExactly()).toBeTruthy(); - expect(handler.callCount).toEqual(1); + expect(handler.callCount).toStrictEqual(1); }); it('should publish event with multiple payload parameters to subscriber', () => { @@ -191,7 +191,7 @@ describe('ControllerMessenger', () => { controllerMessenger.publish('message', 'hello', 'there'); expect(handler.calledWithExactly('hello', 'there')).toBeTruthy(); - expect(handler.callCount).toEqual(1); + expect(handler.callCount).toStrictEqual(1); }); it('should publish event once to subscriber even if subscribed multiple times', () => { @@ -204,7 +204,7 @@ describe('ControllerMessenger', () => { controllerMessenger.publish('message', 'hello'); expect(handler.calledWithExactly('hello')).toBeTruthy(); - expect(handler.callCount).toEqual(1); + expect(handler.callCount).toStrictEqual(1); }); it('should publish event to many subscribers', () => { @@ -218,9 +218,9 @@ describe('ControllerMessenger', () => { controllerMessenger.publish('message', 'hello'); expect(handler1.calledWithExactly('hello')).toBeTruthy(); - expect(handler1.callCount).toEqual(1); + expect(handler1.callCount).toStrictEqual(1); expect(handler2.calledWithExactly('hello')).toBeTruthy(); - expect(handler2.callCount).toEqual(1); + expect(handler2.callCount).toStrictEqual(1); }); it('should not call subscriber after unsubscribing', () => { @@ -232,7 +232,7 @@ describe('ControllerMessenger', () => { controllerMessenger.unsubscribe('message', handler); controllerMessenger.publish('message', 'hello'); - expect(handler.callCount).toEqual(0); + expect(handler.callCount).toStrictEqual(0); }); it('should throw when unsubscribing when there are no subscriptions', () => { @@ -267,7 +267,7 @@ describe('ControllerMessenger', () => { controllerMessenger.clearEventSubscriptions('message'); controllerMessenger.publish('message', 'hello'); - expect(handler.callCount).toEqual(0); + expect(handler.callCount).toStrictEqual(0); }); it('should not throw when clearing event that has no subscriptions', () => { @@ -286,7 +286,7 @@ describe('ControllerMessenger', () => { controllerMessenger.clearSubscriptions(); controllerMessenger.publish('message', 'hello'); - expect(handler.callCount).toEqual(0); + expect(handler.callCount).toStrictEqual(0); }); }); diff --git a/src/apis/crypto-compare.test.ts b/src/apis/crypto-compare.test.ts index df018c4360..72510884b6 100644 --- a/src/apis/crypto-compare.test.ts +++ b/src/apis/crypto-compare.test.ts @@ -22,7 +22,7 @@ describe('CryptoCompare', () => { const { conversionRate } = await fetchExchangeRate('CAD', 'ETH'); - expect(conversionRate).toEqual(2000.42); + expect(conversionRate).toStrictEqual(2000.42); }); it('should return conversion date', async () => { @@ -41,7 +41,7 @@ describe('CryptoCompare', () => { const { conversionRate } = await fetchExchangeRate('cad', 'ETH'); - expect(conversionRate).toEqual(2000.42); + expect(conversionRate).toStrictEqual(2000.42); }); it('should return CAD conversion rate given lower-cased native currency', async () => { @@ -49,7 +49,7 @@ describe('CryptoCompare', () => { const { conversionRate } = await fetchExchangeRate('CAD', 'eth'); - expect(conversionRate).toEqual(2000.42); + expect(conversionRate).toStrictEqual(2000.42); }); it('should not return USD conversion rate when fetching just CAD conversion rate', async () => { @@ -65,8 +65,8 @@ describe('CryptoCompare', () => { const { conversionRate, usdConversionRate } = await fetchExchangeRate('USD', 'ETH', false); - expect(conversionRate).toEqual(1000.42); - expect(usdConversionRate).toEqual(1000.42); + expect(conversionRate).toStrictEqual(1000.42); + expect(usdConversionRate).toStrictEqual(1000.42); }); it('should return USD conversion rate for USD when includeUSD is enabled', async () => { @@ -74,8 +74,8 @@ describe('CryptoCompare', () => { const { conversionRate, usdConversionRate } = await fetchExchangeRate('USD', 'ETH', true); - expect(conversionRate).toEqual(1000.42); - expect(usdConversionRate).toEqual(1000.42); + expect(conversionRate).toStrictEqual(1000.42); + expect(usdConversionRate).toStrictEqual(1000.42); }); it('should return CAD and USD conversion rate', async () => { @@ -83,8 +83,8 @@ describe('CryptoCompare', () => { const { conversionRate, usdConversionRate } = await fetchExchangeRate('CAD', 'ETH', true); - expect(conversionRate).toEqual(2000.42); - expect(usdConversionRate).toEqual(1000.42); + expect(conversionRate).toStrictEqual(2000.42); + expect(usdConversionRate).toStrictEqual(1000.42); }); it('should throw if fetch throws', async () => { diff --git a/src/approval/ApprovalController.test.js b/src/approval/ApprovalController.test.js index 2b04adb0d2..efa1651310 100644 --- a/src/approval/ApprovalController.test.js +++ b/src/approval/ApprovalController.test.js @@ -104,7 +104,7 @@ describe('ApprovalController: Input Validation', () => { !approvalController.has({ type: 'type' }) && !approvalController.has({ origin: 'bar.baz' }) && !approvalController.state[STORE_KEY].foo, - ).toEqual(true); + ).toStrictEqual(true); }); it('deletes one entry out of many without side-effects', () => { @@ -115,9 +115,11 @@ describe('ApprovalController: Input Validation', () => { expect( !approvalController.has({ id: 'fizz' }) && !approvalController.has({ origin: 'bar.baz', type: 'type2' }), - ).toEqual(true); + ).toStrictEqual(true); - expect(approvalController.has({ id: 'foo' }) && approvalController.has({ origin: 'bar.baz' })).toEqual(true); + expect(approvalController.has({ id: 'foo' }) && approvalController.has({ origin: 'bar.baz' })).toStrictEqual( + true, + ); }); }); diff --git a/src/approval/ApprovalController.test.ts b/src/approval/ApprovalController.test.ts index 499f85960a..a9c3466c93 100644 --- a/src/approval/ApprovalController.test.ts +++ b/src/approval/ApprovalController.test.ts @@ -24,9 +24,9 @@ describe('approval controller', () => { it('adds correctly specified entry', () => { expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE })).not.toThrow(); - expect(approvalController.has({ id: 'foo' })).toEqual(true); - expect(approvalController.has({ origin: 'bar.baz', type: TYPE })).toEqual(true); - expect(approvalController.state[STORE_KEY]).toEqual({ + expect(approvalController.has({ id: 'foo' })).toStrictEqual(true); + expect(approvalController.has({ origin: 'bar.baz', type: TYPE })).toStrictEqual(true); + expect(approvalController.state[STORE_KEY]).toStrictEqual({ foo: { id: 'foo', origin: 'bar.baz', time: 1, type: TYPE }, }); }); @@ -48,10 +48,10 @@ describe('approval controller', () => { }), ).not.toThrow(); - expect(approvalController.has({ id: 'foo' })).toEqual(true); - expect(approvalController.has({ origin: 'bar.baz' })).toEqual(true); - expect(approvalController.has({ type: 'myType' })).toEqual(true); - expect(approvalController.state[STORE_KEY].foo.requestData).toEqual({ foo: 'bar' }); + expect(approvalController.has({ id: 'foo' })).toStrictEqual(true); + expect(approvalController.has({ origin: 'bar.baz' })).toStrictEqual(true); + expect(approvalController.has({ type: 'myType' })).toStrictEqual(true); + expect(approvalController.state[STORE_KEY].foo.requestData).toStrictEqual({ foo: 'bar' }); }); it('adds multiple entries for same origin with different types and ids', () => { @@ -60,12 +60,12 @@ describe('approval controller', () => { expect(() => approvalController.add({ id: 'foo1', origin: ORIGIN, type: 'myType1' })).not.toThrow(); expect(() => approvalController.add({ id: 'foo2', origin: ORIGIN, type: 'myType2' })).not.toThrow(); - expect(approvalController.has({ id: 'foo1' }) && approvalController.has({ id: 'foo2' })).toEqual(true); + expect(approvalController.has({ id: 'foo1' }) && approvalController.has({ id: 'foo2' })).toStrictEqual(true); expect( approvalController.has({ origin: ORIGIN }) && approvalController.has({ origin: ORIGIN, type: 'myType1' }) && approvalController.has({ origin: ORIGIN, type: 'myType2' }), - ).toEqual(true); + ).toStrictEqual(true); }); it('throws on id collision', () => { @@ -100,8 +100,8 @@ describe('approval controller', () => { type: 'myType', requestData: { foo: 'bar' }, }); - expect(result instanceof Promise).toEqual(true); - expect(showApprovalSpy.calledOnce).toEqual(true); + expect(result instanceof Promise).toStrictEqual(true); + expect(showApprovalSpy.calledOnce).toStrictEqual(true); }); }); @@ -109,7 +109,7 @@ describe('approval controller', () => { it('gets entry', () => { const approvalController = new ApprovalController({ ...defaultConfig }); approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); - expect(approvalController.get('foo')).toEqual({ id: 'foo', origin: 'bar.baz', type: 'myType', time: 1 }); + expect(approvalController.get('foo')).toStrictEqual({ id: 'foo', origin: 'bar.baz', type: 'myType', time: 1 }); }); }); @@ -127,17 +127,17 @@ describe('approval controller', () => { addWithCatch({ id: '2', origin: 'origin1', type: 'type1' }); addWithCatch({ id: '3', origin: 'origin2', type: 'type1' }); - expect(approvalController.getApprovalCount({ origin: 'origin1', type: TYPE })).toEqual(1); - expect(approvalController.getApprovalCount({ origin: 'origin1', type: 'type1' })).toEqual(1); - expect(approvalController.getApprovalCount({ origin: 'origin1', type: 'type2' })).toEqual(0); + expect(approvalController.getApprovalCount({ origin: 'origin1', type: TYPE })).toStrictEqual(1); + expect(approvalController.getApprovalCount({ origin: 'origin1', type: 'type1' })).toStrictEqual(1); + expect(approvalController.getApprovalCount({ origin: 'origin1', type: 'type2' })).toStrictEqual(0); - expect(approvalController.getApprovalCount({ origin: 'origin2', type: TYPE })).toEqual(0); - expect(approvalController.getApprovalCount({ origin: 'origin2', type: 'type1' })).toEqual(1); - expect(approvalController.getApprovalCount({ origin: 'origin2', type: 'type2' })).toEqual(0); + expect(approvalController.getApprovalCount({ origin: 'origin2', type: TYPE })).toStrictEqual(0); + expect(approvalController.getApprovalCount({ origin: 'origin2', type: 'type1' })).toStrictEqual(1); + expect(approvalController.getApprovalCount({ origin: 'origin2', type: 'type2' })).toStrictEqual(0); - expect(approvalController.getApprovalCount({ origin: 'origin3', type: TYPE })).toEqual(0); - expect(approvalController.getApprovalCount({ origin: 'origin3', type: 'type1' })).toEqual(0); - expect(approvalController.getApprovalCount({ origin: 'origin3', type: 'type2' })).toEqual(0); + expect(approvalController.getApprovalCount({ origin: 'origin3', type: TYPE })).toStrictEqual(0); + expect(approvalController.getApprovalCount({ origin: 'origin3', type: 'type1' })).toStrictEqual(0); + expect(approvalController.getApprovalCount({ origin: 'origin3', type: 'type2' })).toStrictEqual(0); }); it('gets the count when specifying origin only', () => { @@ -145,11 +145,11 @@ describe('approval controller', () => { addWithCatch({ id: '2', origin: 'origin1', type: 'type1' }); addWithCatch({ id: '3', origin: 'origin2', type: 'type1' }); - expect(approvalController.getApprovalCount({ origin: 'origin1' })).toEqual(2); + expect(approvalController.getApprovalCount({ origin: 'origin1' })).toStrictEqual(2); - expect(approvalController.getApprovalCount({ origin: 'origin2' })).toEqual(1); + expect(approvalController.getApprovalCount({ origin: 'origin2' })).toStrictEqual(1); - expect(approvalController.getApprovalCount({ origin: 'origin3' })).toEqual(0); + expect(approvalController.getApprovalCount({ origin: 'origin3' })).toStrictEqual(0); }); it('gets the count when specifying type only', () => { @@ -157,35 +157,35 @@ describe('approval controller', () => { addWithCatch({ id: '3', origin: 'origin2', type: 'type1' }); addWithCatch({ id: '4', origin: 'origin2', type: 'type2' }); - expect(approvalController.getApprovalCount({ type: 'type1' })).toEqual(2); + expect(approvalController.getApprovalCount({ type: 'type1' })).toStrictEqual(2); - expect(approvalController.getApprovalCount({ type: 'type2' })).toEqual(1); + expect(approvalController.getApprovalCount({ type: 'type2' })).toStrictEqual(1); - expect(approvalController.getApprovalCount({ type: 'type3' })).toEqual(0); + expect(approvalController.getApprovalCount({ type: 'type3' })).toStrictEqual(0); }); }); describe('getTotalApprovalCount', () => { it('gets the total approval count', () => { const approvalController = new ApprovalController({ ...defaultConfig }); - expect(approvalController.getTotalApprovalCount()).toEqual(0); + expect(approvalController.getTotalApprovalCount()).toStrictEqual(0); const addWithCatch = (args: any) => approvalController.add(args).catch(() => undefined); addWithCatch({ id: '1', origin: 'origin1', type: 'type0' }); - expect(approvalController.getTotalApprovalCount()).toEqual(1); + expect(approvalController.getTotalApprovalCount()).toStrictEqual(1); addWithCatch({ id: '2', origin: 'origin1', type: 'type1' }); - expect(approvalController.getTotalApprovalCount()).toEqual(2); + expect(approvalController.getTotalApprovalCount()).toStrictEqual(2); addWithCatch({ id: '3', origin: 'origin2', type: 'type1' }); - expect(approvalController.getTotalApprovalCount()).toEqual(3); + expect(approvalController.getTotalApprovalCount()).toStrictEqual(3); approvalController.reject('2', new Error('foo')); - expect(approvalController.getTotalApprovalCount()).toEqual(2); + expect(approvalController.getTotalApprovalCount()).toStrictEqual(2); approvalController.clear(); - expect(approvalController.getTotalApprovalCount()).toEqual(0); + expect(approvalController.getTotalApprovalCount()).toStrictEqual(0); }); }); @@ -199,55 +199,55 @@ describe('approval controller', () => { it('returns true for existing entry by id', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); - expect(approvalController.has({ id: 'foo' })).toEqual(true); + expect(approvalController.has({ id: 'foo' })).toStrictEqual(true); }); it('returns true for existing entry by origin', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); - expect(approvalController.has({ origin: 'bar.baz' })).toEqual(true); + expect(approvalController.has({ origin: 'bar.baz' })).toStrictEqual(true); }); it('returns true for existing entry by origin and type', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); - expect(approvalController.has({ origin: 'bar.baz', type: 'myType' })).toEqual(true); + expect(approvalController.has({ origin: 'bar.baz', type: 'myType' })).toStrictEqual(true); }); it('returns true for existing type', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); - expect(approvalController.has({ type: 'myType' })).toEqual(true); + expect(approvalController.has({ type: 'myType' })).toStrictEqual(true); }); it('returns false for non-existing entry by id', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); - expect(approvalController.has({ id: 'fizz' })).toEqual(false); + expect(approvalController.has({ id: 'fizz' })).toStrictEqual(false); }); it('returns false for non-existing entry by origin', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); - expect(approvalController.has({ origin: 'fizz.buzz' })).toEqual(false); + expect(approvalController.has({ origin: 'fizz.buzz' })).toStrictEqual(false); }); it('returns false for non-existing entry by existing origin and non-existing type', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); - expect(approvalController.has({ origin: 'bar.baz', type: 'myType' })).toEqual(false); + expect(approvalController.has({ origin: 'bar.baz', type: 'myType' })).toStrictEqual(false); }); it('returns false for non-existing entry by non-existing origin and existing type', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); - expect(approvalController.has({ origin: 'fizz.buzz', type: 'myType' })).toEqual(false); + expect(approvalController.has({ origin: 'fizz.buzz', type: 'myType' })).toStrictEqual(false); }); it('returns false for non-existing entry by type', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType1' }); - expect(approvalController.has({ type: 'myType2' })).toEqual(false); + expect(approvalController.has({ type: 'myType2' })).toStrictEqual(false); }); }); @@ -270,8 +270,8 @@ describe('approval controller', () => { approvalController.resolve('foo', 'success'); const result = await approvalPromise; - expect(result).toEqual('success'); - expect(deleteSpy.callCount).toEqual(numDeletions); + expect(result).toStrictEqual('success'); + expect(deleteSpy.callCount).toStrictEqual(numDeletions); }); it('resolves multiple approval promises out of order', async () => { @@ -283,18 +283,18 @@ describe('approval controller', () => { approvalController.resolve('foo2', 'success2'); let result = await approvalPromise2; - expect(result).toEqual('success2'); + expect(result).toStrictEqual('success2'); approvalController.resolve('foo1', 'success1'); result = await approvalPromise1; - expect(result).toEqual('success1'); - expect(deleteSpy.callCount).toEqual(numDeletions); + expect(result).toStrictEqual('success1'); + expect(deleteSpy.callCount).toStrictEqual(numDeletions); }); it('throws on unknown id', () => { expect(() => approvalController.resolve('foo')).toThrow(getIdNotFoundError('foo')); - expect(deleteSpy.callCount).toEqual(numDeletions); + expect(deleteSpy.callCount).toStrictEqual(numDeletions); }); }); @@ -315,7 +315,7 @@ describe('approval controller', () => { const approvalPromise = approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); approvalController.reject('foo', new Error('failure')); await expect(approvalPromise).rejects.toThrow('failure'); - expect(deleteSpy.callCount).toEqual(numDeletions); + expect(deleteSpy.callCount).toStrictEqual(numDeletions); }); it('rejects multiple approval promises out of order', async () => { @@ -328,12 +328,12 @@ describe('approval controller', () => { approvalController.reject('foo1', new Error('failure1')); await expect(rejectionPromise2).rejects.toThrow('failure2'); await expect(rejectionPromise1).rejects.toThrow('failure1'); - expect(deleteSpy.callCount).toEqual(numDeletions); + expect(deleteSpy.callCount).toStrictEqual(numDeletions); }); it('throws on unknown id', () => { expect(() => approvalController.reject('foo', new Error('bar'))).toThrow(getIdNotFoundError('foo')); - expect(deleteSpy.callCount).toEqual(numDeletions); + expect(deleteSpy.callCount).toStrictEqual(numDeletions); }); }); @@ -349,7 +349,7 @@ describe('approval controller', () => { approvalController.resolve('foo2', 'success2'); let result = await promise2; - expect(result).toEqual('success2'); + expect(result).toStrictEqual('success2'); approvalController.reject('foo4', new Error('failure4')); await expect(promise4).rejects.toThrow('failure4'); @@ -357,15 +357,15 @@ describe('approval controller', () => { approvalController.reject('foo3', new Error('failure3')); await expect(promise3).rejects.toThrow('failure3'); - expect(approvalController.has({ origin: 'fizz.buzz' })).toEqual(false); - expect(approvalController.has({ origin: 'bar.baz' })).toEqual(true); + expect(approvalController.has({ origin: 'fizz.buzz' })).toStrictEqual(false); + expect(approvalController.has({ origin: 'bar.baz' })).toStrictEqual(true); approvalController.resolve('foo1', 'success1'); result = await promise1; - expect(result).toEqual('success1'); + expect(result).toStrictEqual('success1'); - expect(approvalController.has({ origin: 'bar.baz' })).toEqual(false); + expect(approvalController.has({ origin: 'bar.baz' })).toStrictEqual(false); }); }); @@ -388,8 +388,8 @@ describe('approval controller', () => { approvalController.clear(); - expect(approvalController.state[STORE_KEY]).toEqual({}); - expect(rejectSpy.callCount).toEqual(2); + expect(approvalController.state[STORE_KEY]).toStrictEqual({}); + expect(rejectSpy.callCount).toStrictEqual(2); }); }); }); diff --git a/src/assets/AccountTrackerController.test.ts b/src/assets/AccountTrackerController.test.ts index 8500d5b566..95dd54fe75 100644 --- a/src/assets/AccountTrackerController.test.ts +++ b/src/assets/AccountTrackerController.test.ts @@ -12,7 +12,7 @@ describe('AccountTrackerController', () => { onPreferencesStateChange: stub(), getIdentities: () => ({}), }); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ accounts: {}, }); }); @@ -57,7 +57,7 @@ describe('AccountTrackerController', () => { }, ); controller.refresh(); - expect(controller.state.accounts).toEqual({ baz: { balance: '0x0' } }); + expect(controller.state.accounts).toStrictEqual({ baz: { balance: '0x0' } }); }); it('should subscribe to new sibling preference controllers', async () => { diff --git a/src/assets/AssetsContractController.test.ts b/src/assets/AssetsContractController.test.ts index 8b80ad44f2..1e63ee3e36 100644 --- a/src/assets/AssetsContractController.test.ts +++ b/src/assets/AssetsContractController.test.ts @@ -14,7 +14,7 @@ describe('AssetsContractController', () => { }); it('should set default config', () => { - expect(assetsContract.config).toEqual({ + expect(assetsContract.config).toStrictEqual({ provider: undefined, }); }); @@ -35,8 +35,8 @@ describe('AssetsContractController', () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); const CKBalance = await assetsContract.getBalanceOf(CKADDRESS, '0xb1690c08e213a35ed9bab7b318de14420fb57d8c'); const CKNoBalance = await assetsContract.getBalanceOf(CKADDRESS, '0xb1690c08e213a35ed9bab7b318de14420fb57d81'); - expect(CKBalance.toNumber()).not.toEqual(0); - expect(CKNoBalance.toNumber()).toEqual(0); + expect(CKBalance.toNumber()).not.toStrictEqual(0); + expect(CKNoBalance.toNumber()).toStrictEqual(0); }); it('should get collectible tokenId correctly', async () => { @@ -46,47 +46,47 @@ describe('AssetsContractController', () => { '0x9a90bd8d1149a88b42a99cf62215ad955d6f498a', 0, ); - expect(tokenId).not.toEqual(0); + expect(tokenId).not.toStrictEqual(0); }); it('should get collectible tokenURI correctly', async () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); const tokenId = await assetsContract.getCollectibleTokenURI(GODSADDRESS, 0); - expect(tokenId).toEqual('https://api.godsunchained.com/card/0'); + expect(tokenId).toStrictEqual('https://api.godsunchained.com/card/0'); }); it('should return empty string as URI when address given is not an NFT', async () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); const tokenId = await assetsContract.getCollectibleTokenURI('0x0000000000000000000000000000000000000000', 0); - expect(tokenId).toEqual(''); + expect(tokenId).toStrictEqual(''); }); it('should get collectible name', async () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); const name = await assetsContract.getAssetName(GODSADDRESS); - expect(name).toEqual('Gods Unchained'); + expect(name).toStrictEqual('Gods Unchained'); }); it('should get collectible symbol', async () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); const symbol = await assetsContract.getAssetSymbol(GODSADDRESS); - expect(symbol).toEqual('GODS'); + expect(symbol).toStrictEqual('GODS'); }); it('should get token decimals', async () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); const symbol = await assetsContract.getTokenDecimals(SAI_ADDRESS); - expect(Number(symbol)).toEqual(18); + expect(Number(symbol)).toStrictEqual(18); }); it('should get collectible ownership', async () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); const tokenId = await assetsContract.getOwnerOf(GODSADDRESS, 148332); - expect(tokenId).not.toEqual(''); + expect(tokenId).not.toStrictEqual(''); }); it('should get balances in a single call', async () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); const balances = await assetsContract.getBalancesInSingleCall(SAI_ADDRESS, [SAI_ADDRESS]); - expect(balances[SAI_ADDRESS]).not.toEqual(0); + expect(balances[SAI_ADDRESS]).not.toStrictEqual(0); }); }); diff --git a/src/assets/AssetsController.test.ts b/src/assets/AssetsController.test.ts index 1d0dd9190a..fdf7b33833 100644 --- a/src/assets/AssetsController.test.ts +++ b/src/assets/AssetsController.test.ts @@ -84,7 +84,7 @@ describe('AssetsController', () => { }); it('should set default state', () => { - expect(assetsController.state).toEqual({ + expect(assetsController.state).toStrictEqual({ allCollectibleContracts: {}, allCollectibles: {}, allTokens: {}, @@ -99,13 +99,13 @@ describe('AssetsController', () => { it('should add token', async () => { await assetsController.addToken('foo', 'bar', 2); - expect(assetsController.state.tokens[0]).toEqual({ + expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xfoO', decimals: 2, symbol: 'bar', }); await assetsController.addToken('foo', 'baz', 2); - expect(assetsController.state.tokens[0]).toEqual({ + expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xfoO', decimals: 2, symbol: 'baz', @@ -117,12 +117,12 @@ describe('AssetsController', () => { { address: 'addressA', symbol: 'barA', decimals: 2 }, { address: 'addressB', symbol: 'barB', decimals: 2 }, ]); - expect(assetsController.state.tokens[0]).toEqual({ + expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xAdDRessA', decimals: 2, symbol: 'barA', }); - expect(assetsController.state.tokens[1]).toEqual({ + expect(assetsController.state.tokens[1]).toStrictEqual({ address: '0xAddReSSB', decimals: 2, symbol: 'barB', @@ -131,12 +131,12 @@ describe('AssetsController', () => { { address: 'addressA', symbol: 'bazA', decimals: 2 }, { address: 'addressB', symbol: 'bazB', decimals: 2 }, ]); - expect(assetsController.state.tokens[0]).toEqual({ + expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xAdDRessA', decimals: 2, symbol: 'bazA', }); - expect(assetsController.state.tokens[1]).toEqual({ + expect(assetsController.state.tokens[1]).toStrictEqual({ address: '0xAddReSSB', decimals: 2, symbol: 'bazB', @@ -152,7 +152,7 @@ describe('AssetsController', () => { preferences.update({ selectedAddress: secondAddress }); expect(assetsController.state.tokens).toHaveLength(0); preferences.update({ selectedAddress: firstAddress }); - expect(assetsController.state.tokens[0]).toEqual({ + expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xfoO', decimals: 2, symbol: 'bar', @@ -167,7 +167,7 @@ describe('AssetsController', () => { network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType] } }); expect(assetsController.state.tokens).toHaveLength(0); network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); - expect(assetsController.state.tokens[0]).toEqual({ + expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xfoO', decimals: 2, symbol: 'bar', @@ -190,7 +190,7 @@ describe('AssetsController', () => { assetsController.removeToken('0xfoO'); expect(assetsController.state.tokens).toHaveLength(0); preferences.update({ selectedAddress: firstAddress }); - expect(assetsController.state.tokens[0]).toEqual({ + expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xFOu', decimals: 2, symbol: 'baz', @@ -207,7 +207,7 @@ describe('AssetsController', () => { assetsController.removeToken('0xfoO'); expect(assetsController.state.tokens).toHaveLength(0); network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); - expect(assetsController.state.tokens[0]).toEqual({ + expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xFOu', decimals: 2, symbol: 'baz', @@ -216,14 +216,14 @@ describe('AssetsController', () => { it('should add collectible and collectible contract', async () => { await assetsController.addCollectible('foo', 1, { name: 'name', image: 'image', description: 'description' }); - expect(assetsController.state.collectibles[0]).toEqual({ + expect(assetsController.state.collectibles[0]).toStrictEqual({ address: '0xfoO', description: 'description', image: 'image', name: 'name', tokenId: 1, }); - expect(assetsController.state.collectibleContracts[0]).toEqual({ + expect(assetsController.state.collectibleContracts[0]).toStrictEqual({ address: '0xfoO', description: 'Description', logo: 'url', @@ -249,7 +249,7 @@ describe('AssetsController', () => { it('should add collectible and get information from OpenSea', async () => { await assetsController.addCollectible('foo', 1); - expect(assetsController.state.collectibles[0]).toEqual({ + expect(assetsController.state.collectibles[0]).toStrictEqual({ address: '0xfoO', description: 'Description', image: 'url', @@ -263,14 +263,14 @@ describe('AssetsController', () => { sandbox.stub(assetsController, 'getCollectibleContractInformationFromApi' as any).returns(undefined); sandbox.stub(assetsController, 'getCollectibleInformationFromApi' as any).returns(undefined); await assetsController.addCollectible(KUDOSADDRESS, 1203); - expect(assetsController.state.collectibles[0]).toEqual({ + expect(assetsController.state.collectibles[0]).toStrictEqual({ address: '0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163', description: undefined, image: 'Kudos Image', name: 'Kudos Name', tokenId: 1203, }); - expect(assetsController.state.collectibleContracts[0]).toEqual({ + expect(assetsController.state.collectibleContracts[0]).toStrictEqual({ address: '0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163', description: undefined, logo: undefined, @@ -291,7 +291,7 @@ describe('AssetsController', () => { preferences.update({ selectedAddress: secondAddress }); await assetsController.addCollectible('fou', 4321); preferences.update({ selectedAddress: firstAddress }); - expect(assetsController.state.collectibles[0]).toEqual({ + expect(assetsController.state.collectibles[0]).toStrictEqual({ address: '0xfoO', description: 'description', image: 'url', @@ -311,7 +311,7 @@ describe('AssetsController', () => { network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType] } }); expect(assetsController.state.collectibles).toHaveLength(0); network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); - expect(assetsController.state.collectibles[0]).toEqual({ + expect(assetsController.state.collectibles[0]).toStrictEqual({ address: '0xfoO', description: 'description', image: 'url', @@ -322,10 +322,10 @@ describe('AssetsController', () => { it('should not add collectibles with no contract information when auto detecting', async () => { await assetsController.addCollectible('0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab', 123, undefined, true); - expect(assetsController.state.collectibles).toEqual([]); - expect(assetsController.state.collectibleContracts).toEqual([]); + expect(assetsController.state.collectibles).toStrictEqual([]); + expect(assetsController.state.collectibleContracts).toStrictEqual([]); await assetsController.addCollectible('0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163', 1203, undefined, true); - expect(assetsController.state.collectibles).toEqual([ + expect(assetsController.state.collectibles).toStrictEqual([ { address: '0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163', description: 'Kudos Description', @@ -334,7 +334,7 @@ describe('AssetsController', () => { tokenId: 1203, }, ]); - expect(assetsController.state.collectibleContracts).toEqual([ + expect(assetsController.state.collectibleContracts).toStrictEqual([ { address: '0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163', description: 'Kudos Description', @@ -374,7 +374,7 @@ describe('AssetsController', () => { assetsController.removeCollectible('0xfoO', 1234); expect(assetsController.state.collectibles).toHaveLength(0); preferences.update({ selectedAddress: firstAddress }); - expect(assetsController.state.collectibles[0]).toEqual({ + expect(assetsController.state.collectibles[0]).toStrictEqual({ address: '0xFOu', description: 'description', image: 'url', @@ -397,7 +397,7 @@ describe('AssetsController', () => { assetsController.removeCollectible('0xfoO', 1234); expect(assetsController.state.collectibles).toHaveLength(0); network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); - expect(assetsController.state.collectibles[0]).toEqual({ + expect(assetsController.state.collectibles[0]).toStrictEqual({ address: '0xFOu', description: 'description', image: 'url', diff --git a/src/assets/AssetsDetectionController.test.ts b/src/assets/AssetsDetectionController.test.ts index 8c0f95be4e..fe7dce1fd4 100644 --- a/src/assets/AssetsDetectionController.test.ts +++ b/src/assets/AssetsDetectionController.test.ts @@ -124,7 +124,7 @@ describe('AssetsDetectionController', () => { }); it('should set default config', () => { - expect(assetsDetection.config).toEqual({ + expect(assetsDetection.config).toStrictEqual({ interval: DEFAULT_INTERVAL, networkType: 'mainnet', selectedAddress: '', @@ -163,9 +163,9 @@ describe('AssetsDetectionController', () => { it('should detect mainnet correctly', () => { assetsDetection.configure({ networkType: MAINNET }); - expect(assetsDetection.isMainnet()).toEqual(true); + expect(assetsDetection.isMainnet()).toStrictEqual(true); assetsDetection.configure({ networkType: ROPSTEN }); - expect(assetsDetection.isMainnet()).toEqual(false); + expect(assetsDetection.isMainnet()).toStrictEqual(false); }); it('should not autodetect while not on mainnet', async () => { @@ -196,7 +196,7 @@ describe('AssetsDetectionController', () => { it('should detect and add collectibles correctly', async () => { assetsDetection.configure({ networkType: MAINNET, selectedAddress: '0x1' }); await assetsDetection.detectCollectibles(); - expect(assets.state.collectibles).toEqual([ + expect(assets.state.collectibles).toStrictEqual([ { address: '0x1D963688FE2209A98db35c67A041524822cf04Hh', description: 'Description 2574', @@ -215,7 +215,7 @@ describe('AssetsDetectionController', () => { name: 'ID 2573', }); await assetsDetection.detectCollectibles(); - expect(assets.state.collectibles).toEqual([ + expect(assets.state.collectibles).toStrictEqual([ { address: '0x1D963688FE2209A98db35c67A041524822cf04Hh', description: 'Description 2573', @@ -247,7 +247,7 @@ describe('AssetsDetectionController', () => { it('should not detect and add collectibles if there is no selectedAddress', async () => { assetsDetection.configure({ networkType: MAINNET }); await assetsDetection.detectCollectibles(); - expect(assets.state.collectibles).toEqual([]); + expect(assets.state.collectibles).toStrictEqual([]); }); it('should not add collectible if collectible or collectible contract has no information to display', async () => { @@ -299,8 +299,8 @@ describe('AssetsDetectionController', () => { assetsDetection.configure({ selectedAddress: '0x1', networkType: MAINNET }); await assetsDetection.detectCollectibles(); // First fetch to API, only gets information from contract ending in HH - expect(assets.state.collectibles).toEqual([collectibleHH2574]); - expect(assets.state.collectibleContracts).toEqual([collectibleContractHH]); + expect(assets.state.collectibles).toStrictEqual([collectibleHH2574]); + expect(assets.state.collectibleContracts).toStrictEqual([collectibleContractHH]); // During next call of assets detection, API succeds returning contract ending in gg information nock(OPEN_SEA_HOST) @@ -355,19 +355,19 @@ describe('AssetsDetectionController', () => { // Now user should have respective collectibles await assetsDetection.detectCollectibles(); - expect(assets.state.collectibleContracts).toEqual([ + expect(assets.state.collectibleContracts).toStrictEqual([ collectibleContractHH, collectibleContractII, collectibleContractGG, ]); - expect(assets.state.collectibles).toEqual([collectibleHH2574, collectibleII2577, collectibleGG2574]); + expect(assets.state.collectibles).toStrictEqual([collectibleHH2574, collectibleII2577, collectibleGG2574]); }); it('should detect tokens correctly', async () => { assetsDetection.configure({ networkType: MAINNET, selectedAddress: '0x1' }); getBalancesInSingleCall.resolves({ '0x6810e776880C02933D47DB1b9fc05908e5386b96': new BN(1) }); await assetsDetection.detectTokens(); - expect(assets.state.tokens).toEqual([ + expect(assets.state.tokens).toStrictEqual([ { address: '0x6810e776880C02933D47DB1b9fc05908e5386b96', decimals: 18, @@ -383,14 +383,14 @@ describe('AssetsDetectionController', () => { assets.removeAndIgnoreToken('0x6810e776880C02933D47DB1b9fc05908e5386b96'); await assetsDetection.detectTokens(); - expect(assets.state.tokens).toEqual([]); + expect(assets.state.tokens).toStrictEqual([]); }); it('should not detect tokens if there is no selectedAddress set', async () => { assetsDetection.configure({ networkType: MAINNET }); getBalancesInSingleCall.resolves({ '0x6810e776880C02933D47DB1b9fc05908e5386b96': new BN(1) }); await assetsDetection.detectTokens(); - expect(assets.state.tokens).toEqual([]); + expect(assets.state.tokens).toStrictEqual([]); }); it('should subscribe to new sibling detecting assets when account changes', async () => { @@ -410,6 +410,6 @@ describe('AssetsDetectionController', () => { network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); expect(network.state.provider.type).toEqual(firstNetworkType); assets.update({ tokens: TOKENS }); - expect(assetsDetection.config.tokens).toEqual(TOKENS); + expect(assetsDetection.config.tokens).toStrictEqual(TOKENS); }); }); diff --git a/src/assets/CurrencyRateController.test.ts b/src/assets/CurrencyRateController.test.ts index b98ba0a100..ea39ac8070 100644 --- a/src/assets/CurrencyRateController.test.ts +++ b/src/assets/CurrencyRateController.test.ts @@ -6,7 +6,7 @@ describe('CurrencyRateController', () => { it('should set default state', () => { const fetchExchangeRateStub = stub(); const controller = new CurrencyRateController({}, {}, fetchExchangeRateStub); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ conversionDate: 0, conversionRate: 0, currentCurrency: 'usd', @@ -20,7 +20,7 @@ describe('CurrencyRateController', () => { it('should initialize with the default config', () => { const fetchExchangeRateStub = stub(); const controller = new CurrencyRateController({}, {}, fetchExchangeRateStub); - expect(controller.config).toEqual({ + expect(controller.config).toStrictEqual({ currentCurrency: 'usd', disabled: false, interval: 180000, @@ -35,7 +35,7 @@ describe('CurrencyRateController', () => { const fetchExchangeRateStub = stub(); const existingState = { currentCurrency: 'rep' }; const controller = new CurrencyRateController({}, existingState, fetchExchangeRateStub); - expect(controller.config).toEqual({ + expect(controller.config).toStrictEqual({ currentCurrency: 'rep', disabled: false, interval: 180000, @@ -99,9 +99,9 @@ describe('CurrencyRateController', () => { it('should update currency', async () => { const fetchExchangeRateStub = stub().resolves({ conversionRate: 10 }); const controller = new CurrencyRateController({ interval: 10 }, {}, fetchExchangeRateStub); - expect(controller.state.conversionRate).toEqual(0); + expect(controller.state.conversionRate).toStrictEqual(0); await controller.updateExchangeRate(); - expect(controller.state.conversionRate).toEqual(10); + expect(controller.state.conversionRate).toStrictEqual(10); controller.disabled = true; }); diff --git a/src/assets/TokenBalancesController.test.ts b/src/assets/TokenBalancesController.test.ts index eb3b194114..f222ec86a7 100644 --- a/src/assets/TokenBalancesController.test.ts +++ b/src/assets/TokenBalancesController.test.ts @@ -23,7 +23,7 @@ describe('TokenBalancesController', () => { }); it('should re-export BN', () => { - expect(exportedBn).toEqual(BN); + expect(exportedBn).toStrictEqual(BN); }); it('should set default state', () => { @@ -163,7 +163,7 @@ describe('TokenBalancesController', () => { const mytoken = getToken(tokenBalances, address); expect(mytoken?.balanceError).toBeInstanceOf(Error); expect(mytoken?.balanceError?.message).toBe(errorMsg); - expect(tokenBalances.state.contractBalances[address].toNumber()).toEqual(0); + expect(tokenBalances.state.contractBalances[address].toNumber()).toStrictEqual(0); getBalanceOfStub.returns(new BN(1)); await tokenBalances.updateBalances(); diff --git a/src/assets/TokenRatesController.test.ts b/src/assets/TokenRatesController.test.ts index f601eccf53..78e05ab309 100644 --- a/src/assets/TokenRatesController.test.ts +++ b/src/assets/TokenRatesController.test.ts @@ -123,7 +123,7 @@ describe('TokenRatesController', () => { ); const address = '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359'; const address2 = '0xfoO'; - expect(controller.state.contractExchangeRates).toEqual({}); + expect(controller.state.contractExchangeRates).toStrictEqual({}); controller.tokens = [ { address, decimals: 18, symbol: 'DAI' }, { address: address2, decimals: 0, symbol: '' }, @@ -132,7 +132,7 @@ describe('TokenRatesController', () => { expect(Object.keys(controller.state.contractExchangeRates)).toContain(address); expect(controller.state.contractExchangeRates[address]).toBeGreaterThan(0); expect(Object.keys(controller.state.contractExchangeRates)).toContain(address2); - expect(controller.state.contractExchangeRates[address2]).toEqual(0); + expect(controller.state.contractExchangeRates[address2]).toStrictEqual(0); }); it('should handle balance not found in API', async () => { @@ -141,7 +141,7 @@ describe('TokenRatesController', () => { { interval: 10 }, ); stub(controller, 'fetchExchangeRate').throws({ error: 'Not Found', message: 'Not Found' }); - expect(controller.state.contractExchangeRates).toEqual({}); + expect(controller.state.contractExchangeRates).toStrictEqual({}); controller.tokens = [{ address: 'bar', decimals: 0, symbol: '' }]; const mock = stub(controller, 'updateExchangeRates'); await controller.updateExchangeRates(); @@ -172,6 +172,6 @@ describe('TokenRatesController', () => { const { tokens } = assets.state; const found = tokens.filter((token: Token) => token.address === '0xfoO'); expect(found.length > 0).toBe(true); - expect(controller.config.nativeCurrency).toEqual('gno'); + expect(controller.config.nativeCurrency).toStrictEqual('gno'); }); }); diff --git a/src/keyring/KeyringController.test.ts b/src/keyring/KeyringController.test.ts index 5f592b2897..3def52a3b9 100644 --- a/src/keyring/KeyringController.test.ts +++ b/src/keyring/KeyringController.test.ts @@ -47,11 +47,11 @@ describe('KeyringController', () => { }); it('should set default state', () => { - expect(keyringController.state.keyrings).not.toEqual([]); + expect(keyringController.state.keyrings).not.toStrictEqual([]); const keyring = keyringController.state.keyrings[0]; - expect(keyring.accounts).not.toEqual([]); - expect(keyring.index).toEqual(0); - expect(keyring.type).toEqual('HD Key Tree'); + expect(keyring.accounts).not.toStrictEqual([]); + expect(keyring.index).toStrictEqual(0); + expect(keyring.type).toStrictEqual('HD Key Tree'); }); it('should add new account', async () => { @@ -71,7 +71,7 @@ describe('KeyringController', () => { expect(initialState.keyrings[0].accounts).not.toBe(currentKeyringMemState.keyrings); expect(currentKeyringMemState.keyrings[0].accounts).toHaveLength(2); const identitiesLength = Object.keys(preferences.state.identities).length; - expect(identitiesLength).toEqual(initialIdentitiesLength); + expect(identitiesLength).toStrictEqual(initialIdentitiesLength); }); it('should create new vault and keychain', async () => { @@ -105,7 +105,7 @@ describe('KeyringController', () => { it('should get accounts', async () => { const initialAccount = initialState.keyrings[0].accounts; const accounts = await keyringController.getAccounts(); - expect(accounts).toEqual(initialAccount); + expect(accounts).toStrictEqual(initialAccount); }); it('should import account with strategy privateKey', async () => { @@ -127,7 +127,7 @@ describe('KeyringController', () => { const newKeyring = { accounts: [address], type: 'Simple Key Pair' }; const obj = await keyringController.importAccountWithStrategy(AccountImportStrategy.privateKey, [privateKey]); const modifiedState = { ...initialState, keyrings: [initialState.keyrings[0], newKeyring] }; - expect(obj).toEqual(modifiedState); + expect(obj).toStrictEqual(modifiedState); }); it('should import account with strategy json', async () => { @@ -136,7 +136,7 @@ describe('KeyringController', () => { const obj = await keyringController.importAccountWithStrategy(AccountImportStrategy.json, [input, somePassword]); const newKeyring = { accounts: [address], type: 'Simple Key Pair' }; const modifiedState = { ...initialState, keyrings: [initialState.keyrings[0], newKeyring] }; - expect(obj).toEqual(modifiedState); + expect(obj).toStrictEqual(modifiedState); }); it('should throw when passed an unrecognized strategy', async () => { @@ -160,7 +160,7 @@ describe('KeyringController', () => { it('should remove account', async () => { await keyringController.importAccountWithStrategy(AccountImportStrategy.privateKey, [privateKey]); const finalState = await keyringController.removeAccount('0x51253087e6f8358b5f10c0a94315d69db3357859'); - expect(finalState).toEqual(initialState); + expect(finalState).toStrictEqual(initialState); }); it('should sign message', async () => { @@ -355,7 +355,7 @@ describe('KeyringController', () => { it('should submit password and decrypt', async () => { const state = await keyringController.submitPassword(password); - expect(state).toEqual(initialState); + expect(state).toStrictEqual(initialState); }); it('should subscribe and unsubscribe', async () => { diff --git a/src/message-manager/AbstractMessageManager.test.ts b/src/message-manager/AbstractMessageManager.test.ts index 4af05d8154..b8e4f2a200 100644 --- a/src/message-manager/AbstractMessageManager.test.ts +++ b/src/message-manager/AbstractMessageManager.test.ts @@ -34,12 +34,12 @@ const messageData = typedMessage; describe('AbstractTestManager', () => { it('should set default state', () => { const controller = new AbstractTestManager(); - expect(controller.state).toEqual({ unapprovedMessages: {}, unapprovedMessagesCount: 0 }); + expect(controller.state).toStrictEqual({ unapprovedMessages: {}, unapprovedMessagesCount: 0 }); }); it('should set default config', () => { const controller = new AbstractTestManager(); - expect(controller.config).toEqual({}); + expect(controller.config).toStrictEqual({}); }); it('should add a valid message', async () => { @@ -149,8 +149,8 @@ describe('AbstractTestManager', () => { const controller = new AbstractTestManager(); controller.addMessage(firstMessage); controller.addMessage(secondMessage); - expect(controller.getUnapprovedMessagesCount()).toEqual(2); - expect(controller.getUnapprovedMessages()).toEqual({ + expect(controller.getUnapprovedMessagesCount()).toStrictEqual(2); + expect(controller.getUnapprovedMessages()).toStrictEqual({ [firstMessage.id]: firstMessage, [secondMessage.id]: secondMessage, }); @@ -169,10 +169,10 @@ describe('AbstractTestManager', () => { }); const messageParams = await controller.approveMessage({ ...firstMessage, metamaskId: messageId, version }); const message = controller.getMessage(messageId); - expect(messageParams).toEqual(firstMessage); + expect(messageParams).toStrictEqual(firstMessage); expect(message).not.toBeUndefined(); if (message) { - expect(message.status).toEqual('approved'); + expect(message.status).toStrictEqual('approved'); } }); @@ -187,11 +187,11 @@ describe('AbstractTestManager', () => { type: 'type', }); const messageBefore = controller.getMessage(messageId); - expect(messageBefore?.status).toEqual('status'); + expect(messageBefore?.status).toStrictEqual('status'); controller.setMessageStatus(messageId, 'newstatus'); const messageAfter = controller.getMessage(messageId); - expect(messageAfter?.status).toEqual('newstatus'); + expect(messageAfter?.status).toStrictEqual('newstatus'); }); it('should throw an error if message is not found', () => { diff --git a/src/message-manager/MessageManager.test.ts b/src/message-manager/MessageManager.test.ts index 55d9c0c656..db969b25ae 100644 --- a/src/message-manager/MessageManager.test.ts +++ b/src/message-manager/MessageManager.test.ts @@ -3,12 +3,12 @@ import MessageManager from './MessageManager'; describe('PersonalMessageManager', () => { it('should set default state', () => { const controller = new MessageManager(); - expect(controller.state).toEqual({ unapprovedMessages: {}, unapprovedMessagesCount: 0 }); + expect(controller.state).toStrictEqual({ unapprovedMessages: {}, unapprovedMessagesCount: 0 }); }); it('should set default config', () => { const controller = new MessageManager(); - expect(controller.config).toEqual({}); + expect(controller.config).toStrictEqual({}); }); it('should add a valid message', async () => { @@ -144,8 +144,8 @@ describe('PersonalMessageManager', () => { const controller = new MessageManager(); controller.addMessage(firstMessage); controller.addMessage(secondMessage); - expect(controller.getUnapprovedMessagesCount()).toEqual(2); - expect(controller.getUnapprovedMessages()).toEqual({ + expect(controller.getUnapprovedMessagesCount()).toStrictEqual(2); + expect(controller.getUnapprovedMessages()).toStrictEqual({ [firstMessage.id]: firstMessage, [secondMessage.id]: secondMessage, }); @@ -157,10 +157,10 @@ describe('PersonalMessageManager', () => { const messageId = controller.addUnapprovedMessage(firstMessage); const messageParams = await controller.approveMessage({ ...firstMessage, metamaskId: messageId }); const message = controller.getMessage(messageId); - expect(messageParams).toEqual(firstMessage); + expect(messageParams).toStrictEqual(firstMessage); expect(message).not.toBeUndefined(); if (message) { - expect(message.status).toEqual('approved'); + expect(message.status).toStrictEqual('approved'); } }); @@ -174,8 +174,8 @@ describe('PersonalMessageManager', () => { const message = controller.getMessage(messageId); expect(message).not.toBeUndefined(); if (message) { - expect(message.rawSig).toEqual(rawSig); - expect(message.status).toEqual('signed'); + expect(message.rawSig).toStrictEqual(rawSig); + expect(message.status).toStrictEqual('signed'); } }); @@ -187,7 +187,7 @@ describe('PersonalMessageManager', () => { const message = controller.getMessage(messageId); expect(message).not.toBeUndefined(); if (message) { - expect(message.status).toEqual('rejected'); + expect(message.status).toStrictEqual('rejected'); } }); }); diff --git a/src/message-manager/PersonalMessageManager.test.ts b/src/message-manager/PersonalMessageManager.test.ts index 994b6650ab..34ecdf4a4b 100644 --- a/src/message-manager/PersonalMessageManager.test.ts +++ b/src/message-manager/PersonalMessageManager.test.ts @@ -3,12 +3,12 @@ import PersonalMessageManager from './PersonalMessageManager'; describe('PersonalMessageManager', () => { it('should set default state', () => { const controller = new PersonalMessageManager(); - expect(controller.state).toEqual({ unapprovedMessages: {}, unapprovedMessagesCount: 0 }); + expect(controller.state).toStrictEqual({ unapprovedMessages: {}, unapprovedMessagesCount: 0 }); }); it('should set default config', () => { const controller = new PersonalMessageManager(); - expect(controller.config).toEqual({}); + expect(controller.config).toStrictEqual({}); }); it('should add a valid message', async () => { @@ -144,8 +144,8 @@ describe('PersonalMessageManager', () => { const controller = new PersonalMessageManager(); controller.addMessage(firstMessage); controller.addMessage(secondMessage); - expect(controller.getUnapprovedMessagesCount()).toEqual(2); - expect(controller.getUnapprovedMessages()).toEqual({ + expect(controller.getUnapprovedMessagesCount()).toStrictEqual(2); + expect(controller.getUnapprovedMessages()).toStrictEqual({ [firstMessage.id]: firstMessage, [secondMessage.id]: secondMessage, }); @@ -157,10 +157,10 @@ describe('PersonalMessageManager', () => { const messageId = controller.addUnapprovedMessage(firstMessage); const messageParams = await controller.approveMessage({ ...firstMessage, metamaskId: messageId }); const message = controller.getMessage(messageId); - expect(messageParams).toEqual(firstMessage); + expect(messageParams).toStrictEqual(firstMessage); expect(message).not.toBeUndefined(); if (message) { - expect(message.status).toEqual('approved'); + expect(message.status).toStrictEqual('approved'); } }); @@ -174,8 +174,8 @@ describe('PersonalMessageManager', () => { const message = controller.getMessage(messageId); expect(message).not.toBeUndefined(); if (message) { - expect(message.rawSig).toEqual(rawSig); - expect(message.status).toEqual('signed'); + expect(message.rawSig).toStrictEqual(rawSig); + expect(message.status).toStrictEqual('signed'); } }); @@ -187,7 +187,7 @@ describe('PersonalMessageManager', () => { const message = controller.getMessage(messageId); expect(message).not.toBeUndefined(); if (message) { - expect(message.status).toEqual('rejected'); + expect(message.status).toStrictEqual('rejected'); } }); }); diff --git a/src/message-manager/TypedMessageManager.test.ts b/src/message-manager/TypedMessageManager.test.ts index 4994a41f8a..03d68ee2eb 100644 --- a/src/message-manager/TypedMessageManager.test.ts +++ b/src/message-manager/TypedMessageManager.test.ts @@ -15,12 +15,12 @@ const typedMessage = [ describe('TypedMessageManager', () => { it('should set default state', () => { const controller = new TypedMessageManager(); - expect(controller.state).toEqual({ unapprovedMessages: {}, unapprovedMessagesCount: 0 }); + expect(controller.state).toStrictEqual({ unapprovedMessages: {}, unapprovedMessagesCount: 0 }); }); it('should set default config', () => { const controller = new TypedMessageManager(); - expect(controller.config).toEqual({}); + expect(controller.config).toStrictEqual({}); }); it('should add a valid message', async () => { @@ -236,8 +236,8 @@ describe('TypedMessageManager', () => { const controller = new TypedMessageManager(); controller.addMessage(firstMessage); controller.addMessage(secondMessage); - expect(controller.getUnapprovedMessagesCount()).toEqual(2); - expect(controller.getUnapprovedMessages()).toEqual({ + expect(controller.getUnapprovedMessagesCount()).toStrictEqual(2); + expect(controller.getUnapprovedMessages()).toStrictEqual({ [firstMessage.id]: firstMessage, [secondMessage.id]: secondMessage, }); @@ -251,10 +251,10 @@ describe('TypedMessageManager', () => { const messageId = controller.addUnapprovedMessage(firstMessage, version); const messageParams = await controller.approveMessage({ ...firstMessage, metamaskId: messageId, version }); const message = controller.getMessage(messageId); - expect(messageParams).toEqual(firstMessage); + expect(messageParams).toStrictEqual(firstMessage); expect(message).not.toBeUndefined(); if (message) { - expect(message.status).toEqual('approved'); + expect(message.status).toStrictEqual('approved'); } }); @@ -269,8 +269,8 @@ describe('TypedMessageManager', () => { const message = controller.getMessage(messageId); expect(message).not.toBeUndefined(); if (message) { - expect(message.rawSig).toEqual(rawSig); - expect(message.status).toEqual('signed'); + expect(message.rawSig).toStrictEqual(rawSig); + expect(message.status).toStrictEqual('signed'); } }); @@ -284,7 +284,7 @@ describe('TypedMessageManager', () => { const message = controller.getMessage(messageId); expect(message).not.toBeUndefined(); if (message) { - expect(message.status).toEqual('rejected'); + expect(message.status).toStrictEqual('rejected'); } }); @@ -298,7 +298,7 @@ describe('TypedMessageManager', () => { const message = controller.getMessage(messageId); expect(message).not.toBeUndefined(); if (message) { - expect(message.status).toEqual('errored'); + expect(message.status).toStrictEqual('errored'); } }); }); diff --git a/src/network/NetworkController.test.ts b/src/network/NetworkController.test.ts index 81fb7e253e..3b29354556 100644 --- a/src/network/NetworkController.test.ts +++ b/src/network/NetworkController.test.ts @@ -7,7 +7,7 @@ const RPC_TARGET = 'http://foo'; describe('NetworkController', () => { it('should set default state', () => { const controller = new NetworkController(); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ network: 'loading', provider: { type: 'mainnet', diff --git a/src/notification/NotificationController.test.ts b/src/notification/NotificationController.test.ts index 8283d59996..b5b978c1b5 100644 --- a/src/notification/NotificationController.test.ts +++ b/src/notification/NotificationController.test.ts @@ -64,7 +64,7 @@ describe('notification controller', () => { isShown: false, }, }; - expect(controller.state.notifications).toEqual(expectedStateNotifications); + expect(controller.state.notifications).toStrictEqual(expectedStateNotifications); }); it('should add new notifcation to state', () => { diff --git a/src/third-party/EnsController.test.ts b/src/third-party/EnsController.test.ts index 944cc03d34..9015c90703 100644 --- a/src/third-party/EnsController.test.ts +++ b/src/third-party/EnsController.test.ts @@ -15,13 +15,13 @@ const address3Checksum = toChecksumAddress(address3); describe('EnsController', () => { it('should set default state', () => { const controller = new EnsController(); - expect(controller.state).toEqual({ ensEntries: {} }); + expect(controller.state).toStrictEqual({ ensEntries: {} }); }); it('should add a new ENS entry and return true', () => { const controller = new EnsController(); expect(controller.set('1', name1, address1)).toBeTruthy(); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ ensEntries: { 1: { [name1]: { @@ -37,7 +37,7 @@ describe('EnsController', () => { it('should add a new ENS entry with null address and return true', () => { const controller = new EnsController(); expect(controller.set('1', name1, null)).toBeTruthy(); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ ensEntries: { 1: { [name1]: { @@ -54,7 +54,7 @@ describe('EnsController', () => { const controller = new EnsController(); expect(controller.set('1', name1, address1)).toBeTruthy(); expect(controller.set('1', name1, address2)).toBeTruthy(); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ ensEntries: { 1: { [name1]: { @@ -71,7 +71,7 @@ describe('EnsController', () => { const controller = new EnsController(); expect(controller.set('1', name1, address1)).toBeTruthy(); expect(controller.set('1', name1, null)).toBeTruthy(); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ ensEntries: { 1: { [name1]: { @@ -88,7 +88,7 @@ describe('EnsController', () => { const controller = new EnsController(); expect(controller.set('1', name1, address1)).toBeTruthy(); expect(controller.set('1', name1, address1)).toBeFalsy(); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ ensEntries: { 1: { [name1]: { @@ -105,7 +105,7 @@ describe('EnsController', () => { const controller = new EnsController(); expect(controller.set('1', name1, null)).toBeTruthy(); expect(controller.set('1', name1, null)).toBeFalsy(); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ ensEntries: { 1: { [name1]: { @@ -124,7 +124,7 @@ describe('EnsController', () => { expect(controller.set('1', name2, address2)).toBeTruthy(); expect(controller.set('2', name1, address1)).toBeTruthy(); expect(controller.set('1', name1, address3)).toBeTruthy(); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ ensEntries: { 1: { [name1]: { @@ -152,7 +152,7 @@ describe('EnsController', () => { it('should get ENS entry by chainId and ensName', () => { const controller = new EnsController(); expect(controller.set('1', name1, address1)).toBeTruthy(); - expect(controller.get('1', name1)).toEqual({ + expect(controller.get('1', name1)).toStrictEqual({ address: address1Checksum, chainId: '1', ensName: name1, @@ -178,7 +178,7 @@ describe('EnsController', () => { }).toThrow( 'Invalid ENS entry: { chainId:a, ensName:foobarb.eth, address:0x32Be343B94f860124dC4fEe278FDCBD38C102D88}', ); - expect(controller.state).toEqual({ ensEntries: {} }); + expect(controller.state).toStrictEqual({ ensEntries: {} }); }); it('should throw on attempt to set invalid ENS entry: ENS name', () => { @@ -186,7 +186,7 @@ describe('EnsController', () => { expect(() => { controller.set('1', 'foo.eth', address1); }).toThrow('Invalid ENS name: foo.eth'); - expect(controller.state).toEqual({ ensEntries: {} }); + expect(controller.state).toStrictEqual({ ensEntries: {} }); }); it('should throw on attempt to set invalid ENS entry: address', () => { @@ -194,14 +194,14 @@ describe('EnsController', () => { expect(() => { controller.set('1', name1, 'foo'); }).toThrow('Invalid ENS entry: { chainId:1, ensName:foobarb.eth, address:foo}'); - expect(controller.state).toEqual({ ensEntries: {} }); + expect(controller.state).toStrictEqual({ ensEntries: {} }); }); it('should remove an ENS entry and return true', () => { const controller = new EnsController(); expect(controller.set('1', name1, address1)).toBeTruthy(); expect(controller.delete('1', name1)).toBeTruthy(); - expect(controller.state).toEqual({ ensEntries: {} }); + expect(controller.state).toStrictEqual({ ensEntries: {} }); }); it('should return false if an ENS entry was NOT deleted', () => { @@ -209,7 +209,7 @@ describe('EnsController', () => { controller.set('1', name1, address1); expect(controller.delete('1', 'bar')).toBeFalsy(); expect(controller.delete('2', 'bar')).toBeFalsy(); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ ensEntries: { 1: { [name1]: { @@ -228,7 +228,7 @@ describe('EnsController', () => { expect(controller.set('1', name2, address2)).toBeTruthy(); expect(controller.set('2', name1, address1)).toBeTruthy(); expect(controller.delete('1', name1)).toBeTruthy(); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ ensEntries: { 1: { [name2]: { @@ -254,6 +254,6 @@ describe('EnsController', () => { expect(controller.set('1', name2, address2)).toBeTruthy(); expect(controller.set('2', name1, address1)).toBeTruthy(); controller.clear(); - expect(controller.state).toEqual({ ensEntries: {} }); + expect(controller.state).toStrictEqual({ ensEntries: {} }); }); }); diff --git a/src/third-party/PhishingController.test.ts b/src/third-party/PhishingController.test.ts index 0bb975f5ab..f3ce494291 100644 --- a/src/third-party/PhishingController.test.ts +++ b/src/third-party/PhishingController.test.ts @@ -17,7 +17,7 @@ describe('PhishingController', () => { it('should set default config', () => { const controller = new PhishingController(); - expect(controller.config).toEqual({ interval: 3_600_000 }); + expect(controller.config).toStrictEqual({ interval: 3_600_000 }); }); it('should poll and update rate in the right interval', async () => { diff --git a/src/transaction/TransactionController.test.ts b/src/transaction/TransactionController.test.ts index 746e6d7052..23590d9cd2 100644 --- a/src/transaction/TransactionController.test.ts +++ b/src/transaction/TransactionController.test.ts @@ -944,8 +944,8 @@ describe('TransactionController', () => { const ethTransaction = controller.state.transactions.find( ({ transactionHash }) => transactionHash === ETHER_TRANSACTION_HASH, ) || { id: '' }; - expect(tokenTransaction?.id).toEqual('token-transaction-id'); - expect(ethTransaction?.id).toEqual('eth-transaction-id'); + expect(tokenTransaction?.id).toStrictEqual('token-transaction-id'); + expect(ethTransaction?.id).toStrictEqual('eth-transaction-id'); }); it('should fetch all the transactions from an address, including incoming transactions, in mainnet from block', async () => { @@ -990,11 +990,11 @@ describe('TransactionController', () => { {}, ); const registry = await controller.handleMethodData('0xf39b5b9b'); - expect(registry.parsedRegistryMethod).toEqual({ + expect(registry.parsedRegistryMethod).toStrictEqual({ args: [{ type: 'uint256' }, { type: 'uint256' }], name: 'Eth To Token Swap Input', }); - expect(registry.registryMethod).toEqual('ethToTokenSwapInput(uint256,uint256)'); + expect(registry.registryMethod).toStrictEqual('ethToTokenSwapInput(uint256,uint256)'); }); it('should handle known method data', async () => { @@ -1007,7 +1007,7 @@ describe('TransactionController', () => { {}, ); const registry = await controller.handleMethodData('0xf39b5b9b'); - expect(registry.parsedRegistryMethod).toEqual({ + expect(registry.parsedRegistryMethod).toStrictEqual({ args: [{ type: 'uint256' }, { type: 'uint256' }], name: 'Eth To Token Swap Input', }); diff --git a/src/user/AddressBookController.test.ts b/src/user/AddressBookController.test.ts index 35e1d51a7b..b1ba0031ba 100644 --- a/src/user/AddressBookController.test.ts +++ b/src/user/AddressBookController.test.ts @@ -3,13 +3,13 @@ import AddressBookController from './AddressBookController'; describe('AddressBookController', () => { it('should set default state', () => { const controller = new AddressBookController(); - expect(controller.state).toEqual({ addressBook: {} }); + expect(controller.state).toStrictEqual({ addressBook: {} }); }); it('should add a contact entry', () => { const controller = new AddressBookController(); controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo'); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ addressBook: { 1: { '0x32Be343B94f860124dC4fEe278FDCBD38C102D88': { @@ -27,7 +27,7 @@ describe('AddressBookController', () => { it('should add a contact entry with chainId and memo', () => { const controller = new AddressBookController(); controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo', '1', 'account 1'); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ addressBook: { 1: { '0x32Be343B94f860124dC4fEe278FDCBD38C102D88': { @@ -47,7 +47,7 @@ describe('AddressBookController', () => { controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo', '1', 'account 2'); controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo', '2', 'account 2'); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ addressBook: { 1: { '0x32Be343B94f860124dC4fEe278FDCBD38C102D88': { @@ -75,7 +75,7 @@ describe('AddressBookController', () => { const controller = new AddressBookController(); controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo'); controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'bar'); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ addressBook: { 1: { '0x32Be343B94f860124dC4fEe278FDCBD38C102D88': { @@ -93,7 +93,7 @@ describe('AddressBookController', () => { it('should not add invalid contact entry', () => { const controller = new AddressBookController(); controller.set('1337', 'foo'); - expect(controller.state).toEqual({ addressBook: {} }); + expect(controller.state).toStrictEqual({ addressBook: {} }); }); it('should remove one contact entry', () => { @@ -101,7 +101,7 @@ describe('AddressBookController', () => { controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo'); controller.delete('1', '0x32Be343B94f860124dC4fEe278FDCBD38C102D88'); - expect(controller.state).toEqual({ addressBook: {} }); + expect(controller.state).toStrictEqual({ addressBook: {} }); }); it('should remove only one contact entry', () => { @@ -110,7 +110,7 @@ describe('AddressBookController', () => { controller.set('0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d', 'bar'); controller.delete('1', '0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d'); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ addressBook: { 1: { '0x32Be343B94f860124dC4fEe278FDCBD38C102D88': { @@ -130,7 +130,7 @@ describe('AddressBookController', () => { controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo'); controller.set('0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d', 'bar'); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ addressBook: { 1: { '0x32Be343B94f860124dC4fEe278FDCBD38C102D88': { @@ -155,7 +155,7 @@ describe('AddressBookController', () => { it('should correctly mark ens entries', () => { const controller = new AddressBookController(); controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'metamask.eth'); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ addressBook: { 1: { '0x32Be343B94f860124dC4fEe278FDCBD38C102D88': { @@ -175,7 +175,7 @@ describe('AddressBookController', () => { controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo'); controller.set('0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d', 'bar'); controller.clear(); - expect(controller.state).toEqual({ addressBook: {} }); + expect(controller.state).toStrictEqual({ addressBook: {} }); }); it('should return true to indicate an address book entry has been added', () => { @@ -206,7 +206,7 @@ describe('AddressBookController', () => { controller.set('0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d', 'bar'); controller.delete('1', '0xC38BF1AD06EF69F0C04E29DBEB4152B4175F0A8D'); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ addressBook: { 1: { '0x32Be343B94f860124dC4fEe278FDCBD38C102D88': { diff --git a/src/user/PreferencesController.test.ts b/src/user/PreferencesController.test.ts index 657ceaac61..c3fa947c9d 100644 --- a/src/user/PreferencesController.test.ts +++ b/src/user/PreferencesController.test.ts @@ -3,7 +3,7 @@ import PreferencesController from './PreferencesController'; describe('PreferencesController', () => { it('should set default state', () => { const controller = new PreferencesController(); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ featureFlags: {}, frequentRpcList: [], identities: {}, @@ -17,8 +17,8 @@ describe('PreferencesController', () => { const controller = new PreferencesController(); controller.addIdentities(['foo']); controller.addIdentities(['foo']); - expect(controller.state.identities['0xfoO'].address).toEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toEqual('Account 1'); + expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); + expect(controller.state.identities['0xfoO'].name).toStrictEqual('Account 1'); expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual(Date.now()); }); @@ -46,26 +46,26 @@ describe('PreferencesController', () => { const controller = new PreferencesController(); controller.addIdentities(['foo', 'bar']); controller.syncIdentities(['foo', 'bar']); - expect(controller.state.identities['0xfoO'].address).toEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toEqual('Account 1'); + expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); + expect(controller.state.identities['0xfoO'].name).toStrictEqual('Account 1'); expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual(Date.now()); - expect(controller.state.identities['0xbar'].address).toEqual('0xbar'); - expect(controller.state.identities['0xbar'].name).toEqual('Account 2'); + expect(controller.state.identities['0xbar'].address).toStrictEqual('0xbar'); + expect(controller.state.identities['0xbar'].name).toStrictEqual('Account 2'); expect(controller.state.identities['0xbar'].importTime).toBeLessThanOrEqual(Date.now()); controller.syncIdentities(['foo']); - expect(controller.state.identities['0xfoO'].address).toEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toEqual('Account 1'); + expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); + expect(controller.state.identities['0xfoO'].name).toStrictEqual('Account 1'); expect(controller.state.selectedAddress).toBe('0xfoO'); }); it('should add new identities', () => { const controller = new PreferencesController(); controller.updateIdentities(['foo', 'bar']); - expect(controller.state.identities['0xfoO'].address).toEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toEqual('Account 1'); + expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); + expect(controller.state.identities['0xfoO'].name).toStrictEqual('Account 1'); expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual(Date.now()); - expect(controller.state.identities['0xbar'].address).toEqual('0xbar'); - expect(controller.state.identities['0xbar'].name).toEqual('Account 2'); + expect(controller.state.identities['0xbar'].address).toStrictEqual('0xbar'); + expect(controller.state.identities['0xbar'].name).toStrictEqual('Account 2'); expect(controller.state.identities['0xbar'].importTime).toBeLessThanOrEqual(Date.now()); }); @@ -75,11 +75,11 @@ describe('PreferencesController', () => { { identities: { '0xbar': { address: '0xbar', name: 'Custom name' } } }, ); controller.updateIdentities(['foo', 'bar']); - expect(controller.state.identities['0xfoO'].address).toEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toEqual('Account 1'); + expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); + expect(controller.state.identities['0xfoO'].name).toStrictEqual('Account 1'); expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual(Date.now()); - expect(controller.state.identities['0xbar'].address).toEqual('0xbar'); - expect(controller.state.identities['0xbar'].name).toEqual('Custom name'); + expect(controller.state.identities['0xbar'].address).toStrictEqual('0xbar'); + expect(controller.state.identities['0xbar'].name).toStrictEqual('Custom name'); expect(controller.state.identities['0xbar'].importTime).toBeUndefined(); }); @@ -94,7 +94,7 @@ describe('PreferencesController', () => { }, ); controller.updateIdentities(['foo']); - expect(controller.state.identities).toEqual({ + expect(controller.state.identities).toStrictEqual({ '0xfoO': { address: '0xfoO', name: 'Account 1' }, }); }); @@ -111,7 +111,7 @@ describe('PreferencesController', () => { }, ); controller.updateIdentities(['foo', 'bar']); - expect(controller.state.selectedAddress).toEqual('0xbar'); + expect(controller.state.selectedAddress).toStrictEqual('0xbar'); }); it('should update selected address to first identity if it was removed from identities', () => { @@ -127,7 +127,7 @@ describe('PreferencesController', () => { }, ); controller.updateIdentities(['foo', 'bar']); - expect(controller.state.selectedAddress).toEqual('0xfoO'); + expect(controller.state.selectedAddress).toStrictEqual('0xfoO'); }); it('should add custom rpc url', () => { @@ -148,9 +148,9 @@ describe('PreferencesController', () => { }; controller.addToFrequentRpcList('rpc_url', undefined, 'RPC', 'RPC'); controller.addToFrequentRpcList('http://localhost:8545', undefined, 'LOCAL'); - expect(controller.state.frequentRpcList).toEqual([rpcUrlNetwork, localhostNetwork]); + expect(controller.state.frequentRpcList).toStrictEqual([rpcUrlNetwork, localhostNetwork]); controller.addToFrequentRpcList('rpc_url'); - expect(controller.state.frequentRpcList).toEqual([ + expect(controller.state.frequentRpcList).toStrictEqual([ localhostNetwork, { ...rpcUrlNetwork, nickname: undefined, ticker: undefined }, ]); @@ -166,21 +166,21 @@ describe('PreferencesController', () => { ticker: undefined, }; controller.addToFrequentRpcList('rpc_url'); - expect(controller.state.frequentRpcList).toEqual([rpcUrlNetwork]); + expect(controller.state.frequentRpcList).toStrictEqual([rpcUrlNetwork]); controller.removeFromFrequentRpcList('other_rpc_url'); controller.removeFromFrequentRpcList('rpc_url'); - expect(controller.state.frequentRpcList).toEqual([]); + expect(controller.state.frequentRpcList).toStrictEqual([]); }); it('should set IPFS gateway', () => { const controller = new PreferencesController(); controller.setIpfsGateway('https://ipfs.infura.io/ipfs/'); - expect(controller.state.ipfsGateway).toEqual('https://ipfs.infura.io/ipfs/'); + expect(controller.state.ipfsGateway).toStrictEqual('https://ipfs.infura.io/ipfs/'); }); it('should update selected address as checksummed', () => { const controller = new PreferencesController(); controller.setSelectedAddress('0x95d2bc047b0ddec1e4a178eeb64d59f5e735cd0a'); - expect(controller.state.selectedAddress).toEqual('0x95D2bC047B0dDEc1E4A178EeB64d59F5E735cd0A'); + expect(controller.state.selectedAddress).toStrictEqual('0x95D2bC047B0dDEc1E4A178EeB64d59F5E735cd0A'); }); }); diff --git a/src/util.test.ts b/src/util.test.ts index ef93b808ac..b492aa1379 100644 --- a/src/util.test.ts +++ b/src/util.test.ts @@ -87,7 +87,7 @@ describe('util', () => { to: 'TO', value: 'value', }); - expect(normalized).toEqual({ + expect(normalized).toStrictEqual({ data: '0xdata', from: '0xfrom', gas: '0xgas', @@ -135,7 +135,7 @@ describe('util', () => { const response = await util.safelyExecuteWithTimeout(() => { return new Promise((res) => setTimeout(() => res('response'), 200)); }); - expect(response).toEqual('response'); + expect(response).toStrictEqual('response'); }); it('should timeout', async () => { @@ -269,13 +269,13 @@ describe('util', () => { '879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0', ); const secondNormalized = util.normalizeMessageData('somedata'); - expect(firstNormalized).toEqual('0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0'); - expect(secondNormalized).toEqual('0x736f6d6564617461'); + expect(firstNormalized).toStrictEqual('0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0'); + expect(secondNormalized).toStrictEqual('0x736f6d6564617461'); }); it('messageHexToString', () => { const str = util.hexToText('68656c6c6f207468657265'); - expect(str).toEqual('hello there'); + expect(str).toStrictEqual('hello there'); }); describe('validateSignMessageData', () => { @@ -582,7 +582,7 @@ describe('util', () => { it('should return successful fetch response', async () => { const res = await util.successfulFetch(SOME_API); const parsed = await res.json(); - expect(parsed).toEqual({ foo: 'bar' }); + expect(parsed).toStrictEqual({ foo: 'bar' }); }); it('should throw error for an unsuccessful fetch', async () => { @@ -604,7 +604,7 @@ describe('util', () => { it('should fetch first if response is faster than timeout', async () => { const res = await util.timeoutFetch(SOME_API); const parsed = await res.json(); - expect(parsed).toEqual({}); + expect(parsed).toStrictEqual({}); }); it('should fail fetch with timeout', async () => { @@ -621,31 +621,31 @@ describe('util', () => { describe('normalizeEnsName', () => { it('should normalize with valid 2LD', async () => { let valid = util.normalizeEnsName('metamask.eth'); - expect(valid).toEqual('metamask.eth'); + expect(valid).toStrictEqual('metamask.eth'); valid = util.normalizeEnsName('foobar1.eth'); - expect(valid).toEqual('foobar1.eth'); + expect(valid).toStrictEqual('foobar1.eth'); valid = util.normalizeEnsName('foo-bar.eth'); - expect(valid).toEqual('foo-bar.eth'); + expect(valid).toStrictEqual('foo-bar.eth'); valid = util.normalizeEnsName('1-foo-bar.eth'); - expect(valid).toEqual('1-foo-bar.eth'); + expect(valid).toStrictEqual('1-foo-bar.eth'); }); it('should normalize with valid 2LD and "test" TLD', async () => { const valid = util.normalizeEnsName('metamask.test'); - expect(valid).toEqual('metamask.test'); + expect(valid).toStrictEqual('metamask.test'); }); it('should normalize with valid 2LD and 3LD', async () => { let valid = util.normalizeEnsName('a.metamask.eth'); - expect(valid).toEqual('a.metamask.eth'); + expect(valid).toStrictEqual('a.metamask.eth'); valid = util.normalizeEnsName('aa.metamask.eth'); - expect(valid).toEqual('aa.metamask.eth'); + expect(valid).toStrictEqual('aa.metamask.eth'); valid = util.normalizeEnsName('a-a.metamask.eth'); - expect(valid).toEqual('a-a.metamask.eth'); + expect(valid).toStrictEqual('a-a.metamask.eth'); valid = util.normalizeEnsName('1-a.metamask.eth'); - expect(valid).toEqual('1-a.metamask.eth'); + expect(valid).toStrictEqual('1-a.metamask.eth'); valid = util.normalizeEnsName('1-2.metamask.eth'); - expect(valid).toEqual('1-2.metamask.eth'); + expect(valid).toStrictEqual('1-2.metamask.eth'); }); it('should return null with invalid 2LD', async () => { @@ -701,7 +701,7 @@ describe('util', () => { it('should query and resolve', async () => { const ethQuery = new EthQuery(PROVIDER); const gasPrice = await util.query(ethQuery, 'gasPrice', []); - expect(gasPrice).toEqual('0x0'); + expect(gasPrice).toStrictEqual('0x0'); }); it('should query and reject if error', async () => { diff --git a/yarn.lock b/yarn.lock index cf223302a8..00b35c5cde 100644 --- a/yarn.lock +++ b/yarn.lock @@ -838,10 +838,13 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" -"@types/jest@^22.2.3": - version "22.2.3" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-22.2.3.tgz#0157c0316dc3722c43a7b71de3fdf3acbccef10d" - integrity sha512-e74sM9W/4qqWB6D4TWV9FQk0WoHtX1X4FJpbjxucMSVJHtFjbQOH3H6yp+xno4br0AKG0wz/kPtaN599GUOvAg== +"@types/jest@^26.0.22": + version "26.0.22" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.22.tgz#8308a1debdf1b807aa47be2838acdcd91e88fbe6" + integrity sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw== + dependencies: + jest-diff "^26.0.0" + pretty-format "^26.0.0" "@types/json-schema@^7.0.3": version "7.0.6" @@ -2170,6 +2173,11 @@ diff-sequences@^26.3.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.3.0.tgz#62a59b1b29ab7fd27cef2a33ae52abe73042d0a2" integrity sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig== +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== + diff@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -4179,6 +4187,16 @@ jest-diff@^25.2.1: jest-get-type "^25.2.6" pretty-format "^25.5.0" +jest-diff@^26.0.0: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + jest-diff@^26.4.2: version "26.4.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.4.2.tgz#a1b7b303bcc534aabdb3bd4a7caf594ac059f5aa" @@ -5899,6 +5917,16 @@ pretty-format@^25.2.1, pretty-format@^25.5.0: ansi-styles "^4.0.0" react-is "^16.12.0" +pretty-format@^26.0.0, pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + pretty-format@^26.4.2: version "26.4.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.4.2.tgz#d081d032b398e801e2012af2df1214ef75a81237" @@ -6005,6 +6033,11 @@ react-is@^16.12.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527" integrity sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA== +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + read-pkg-up@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" From 2fa8725cc0f6bb90da91437ea8fca9a83f0d8959 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Apr 2021 08:35:06 -0700 Subject: [PATCH 04/10] Fix toStrictEqual test failures --- src/assets/AssetsController.test.ts | 10 ++++++++++ src/assets/AssetsDetectionController.test.ts | 1 + 2 files changed, 11 insertions(+) diff --git a/src/assets/AssetsController.test.ts b/src/assets/AssetsController.test.ts index fdf7b33833..301bcbe1d9 100644 --- a/src/assets/AssetsController.test.ts +++ b/src/assets/AssetsController.test.ts @@ -102,12 +102,14 @@ describe('AssetsController', () => { expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xfoO', decimals: 2, + image: undefined, symbol: 'bar', }); await assetsController.addToken('foo', 'baz', 2); expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xfoO', decimals: 2, + image: undefined, symbol: 'baz', }); }); @@ -120,11 +122,13 @@ describe('AssetsController', () => { expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xAdDRessA', decimals: 2, + image: undefined, symbol: 'barA', }); expect(assetsController.state.tokens[1]).toStrictEqual({ address: '0xAddReSSB', decimals: 2, + image: undefined, symbol: 'barB', }); await assetsController.addTokens([ @@ -134,11 +138,13 @@ describe('AssetsController', () => { expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xAdDRessA', decimals: 2, + image: undefined, symbol: 'bazA', }); expect(assetsController.state.tokens[1]).toStrictEqual({ address: '0xAddReSSB', decimals: 2, + image: undefined, symbol: 'bazB', }); }); @@ -155,6 +161,7 @@ describe('AssetsController', () => { expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xfoO', decimals: 2, + image: undefined, symbol: 'bar', }); }); @@ -170,6 +177,7 @@ describe('AssetsController', () => { expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xfoO', decimals: 2, + image: undefined, symbol: 'bar', }); }); @@ -193,6 +201,7 @@ describe('AssetsController', () => { expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xFOu', decimals: 2, + image: undefined, symbol: 'baz', }); }); @@ -210,6 +219,7 @@ describe('AssetsController', () => { expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xFOu', decimals: 2, + image: undefined, symbol: 'baz', }); }); diff --git a/src/assets/AssetsDetectionController.test.ts b/src/assets/AssetsDetectionController.test.ts index fe7dce1fd4..08bf85a744 100644 --- a/src/assets/AssetsDetectionController.test.ts +++ b/src/assets/AssetsDetectionController.test.ts @@ -371,6 +371,7 @@ describe('AssetsDetectionController', () => { { address: '0x6810e776880C02933D47DB1b9fc05908e5386b96', decimals: 18, + image: undefined, symbol: 'GNO', }, ]); From 257fc97a731e80b7172a57f6e67f5344270e4bcb Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Apr 2021 08:39:13 -0700 Subject: [PATCH 05/10] Remove jest 'resolves' matcher --- src/util.test.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/util.test.ts b/src/util.test.ts index b492aa1379..185a406954 100644 --- a/src/util.test.ts +++ b/src/util.test.ts @@ -100,11 +100,11 @@ describe('util', () => { describe('safelyExecute', () => { it('should swallow errors', async () => { - await expect( - util.safelyExecute(() => { + expect( + await util.safelyExecute(() => { throw new Error('ahh'); }), - ).resolves.toBeUndefined(); + ).toBeUndefined(); }); it('should call retry function', async () => { @@ -124,11 +124,11 @@ describe('util', () => { describe('safelyExecuteWithTimeout', () => { it('should swallow errors', async () => { - await expect( - util.safelyExecuteWithTimeout(() => { + expect( + await util.safelyExecuteWithTimeout(() => { throw new Error('ahh'); }), - ).resolves.toBeUndefined(); + ).toBeUndefined(); }); it('should resolve', async () => { @@ -139,11 +139,11 @@ describe('util', () => { }); it('should timeout', async () => { - await expect( - util.safelyExecuteWithTimeout(() => { + expect( + await util.safelyExecuteWithTimeout(() => { return new Promise((res) => setTimeout(res, 800)); }), - ).resolves.toBeUndefined(); + ).toBeUndefined(); }); }); From 5f3d81933790fd678ace8b2142dd71cf09d63750 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Apr 2021 08:43:34 -0700 Subject: [PATCH 06/10] Fix jest rules --- .eslintrc.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index a648a54f99..6da50fdb48 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -6,6 +6,20 @@ module.exports = { { files: ['*.test.ts', '*.test.js'], extends: ['@metamask/eslint-config-jest'], + rules: { + 'jest/no-conditional-expect': 'off', + 'jest/no-restricted-matchers': [ + 'error', + { + resolves: 'Use `expect(await promise)` instead.', + // 'toBeFalsy': 'Avoid `toBeFalsy`', + // 'toBeTruthy': 'Avoid `toBeTruthy`', + toMatchSnapshot: 'Use `toMatchInlineSnapshot()` instead', + toThrowErrorMatchingSnapshot: 'Use `toThrowErrorMatchingInlineSnapshot()` instead', + }, + ], + 'jest/no-test-return-statement': 'off', + }, }, { files: ['*.js'], @@ -56,11 +70,6 @@ module.exports = { 'no-param-reassign': 'off', 'radix': 'off', 'require-atomic-updates': 'off', - - 'jest/no-conditional-expect': 'off', - 'jest/no-restricted-matchers': 'off', - 'jest/no-test-return-statement': 'off', - 'jest/prefer-strict-equal': 'off', }, settings: { 'import/resolver': { From e5090fb014c843789b21e223c40fa13bb5094ea1 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Apr 2021 08:44:35 -0700 Subject: [PATCH 07/10] Remove lint-staged --- .eslintrc.js | 20 +- package.json | 15 -- tsconfig.json | 8 +- yarn.lock | 516 +------------------------------------------------- 4 files changed, 26 insertions(+), 533 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 6da50fdb48..46373ae7e3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,15 @@ module.exports = { root: true, extends: ['@metamask/eslint-config', '@metamask/eslint-config-nodejs'], - ignorePatterns: ['!.eslintrc.js', '!jest.config.js', 'node_modules', 'dist', 'docs', 'coverage', '*.d.ts'], + ignorePatterns: [ + '!.eslintrc.js', + '!jest.config.js', + 'node_modules', + 'dist', + 'docs', + 'coverage', + '*.d.ts', + ], overrides: [ { files: ['*.test.ts', '*.test.js'], @@ -15,7 +23,8 @@ module.exports = { // 'toBeFalsy': 'Avoid `toBeFalsy`', // 'toBeTruthy': 'Avoid `toBeTruthy`', toMatchSnapshot: 'Use `toMatchInlineSnapshot()` instead', - toThrowErrorMatchingSnapshot: 'Use `toThrowErrorMatchingInlineSnapshot()` instead', + toThrowErrorMatchingSnapshot: + 'Use `toThrowErrorMatchingInlineSnapshot()` instead', }, ], 'jest/no-test-return-statement': 'off', @@ -44,7 +53,12 @@ module.exports = { // TODO: Migrate this rule change back into `@metamask/eslint-config` '@typescript-eslint/no-unused-vars': [ 'error', - { vars: 'all', args: 'all', argsIgnorePattern: '[_]+', ignoreRestSiblings: true }, + { + vars: 'all', + args: 'all', + argsIgnorePattern: '[_]+', + ignoreRestSiblings: true, + }, ], }, }, diff --git a/package.json b/package.json index 0e065f1eb6..a03d5a6ccf 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,6 @@ "ethjs-provider-http": "^0.1.6", "jest": "^26.4.2", "jest-environment-jsdom": "^25.0.0", - "lint-staged": "^6.1.0", "nock": "^13.0.7", "prettier": "^2.2.1", "sinon": "^9.2.4", @@ -125,19 +124,5 @@ "setupFiles": [ "./tests/setupTests.ts" ] - }, - "prettier": { - "arrowParens": "always", - "parser": "typescript", - "printWidth": 120, - "singleQuote": true, - "trailingComma": "all" - }, - "lint-staged": { - "*.ts": [ - "prettier --ignore-path=.gitignore --write **/*.ts", - "yarn lint", - "git add" - ] } } diff --git a/tsconfig.json b/tsconfig.json index fe73d82e9b..12c8c1bd69 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,10 +14,6 @@ "strict": true, "target": "es6" }, - "include": [ - "src/**/*.ts" - ], - "exclude": [ - "**/*.test.ts" - ] + "include": ["src/**/*.ts"], + "exclude": ["**/*.test.ts"] } diff --git a/yarn.lock b/yarn.lock index 00b35c5cde..7ad8f9bd6b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1173,11 +1173,6 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-escapes@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= - ansi-escapes@^4.2.1: version "4.3.1" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" @@ -1185,27 +1180,12 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.11.0" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - ansi-regex@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -1220,11 +1200,6 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: "@types/color-name" "^1.1.1" color-convert "^2.0.1" -any-observable@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.2.0.tgz#c67870058003579009083f54ac0abafb5c33d242" - integrity sha1-xnhwBYADV5AJCD9UrAq6+1wz0kI= - anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1241,11 +1216,6 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" -app-root-path@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" - integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== - argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1712,18 +1682,7 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0: +chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1783,26 +1742,6 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -cli-cursor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= - dependencies: - restore-cursor "^1.0.1" - -cli-spinners@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" - integrity sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw= - -cli-truncate@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" - integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= - dependencies: - slice-ansi "0.0.4" - string-width "^1.0.1" - cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -1822,11 +1761,6 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - collect-v8-coverage@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.0.tgz#150ee634ac3650b71d9c985eb7f608942334feb1" @@ -1876,11 +1810,6 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.11.0, commander@^2.9.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" - integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== - commander@~2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -1935,16 +1864,6 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cosmiconfig@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-4.0.0.tgz#760391549580bbd2df1e562bc177b13c290972dc" - integrity sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ== - dependencies: - is-directory "^0.3.1" - js-yaml "^3.9.0" - parse-json "^4.0.0" - require-from-string "^2.0.1" - create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" @@ -1976,15 +1895,6 @@ cross-fetch@^2.1.0: node-fetch "2.1.2" whatwg-fetch "2.0.4" -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -2061,11 +1971,6 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -date-fns@^1.27.2: - version "1.30.1" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" - integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== - debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2073,13 +1978,6 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" -debug@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -2102,11 +2000,6 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= - deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -2241,11 +2134,6 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -elegant-spinner@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" - integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= - elliptic@^6.4.0, elliptic@^6.4.1, elliptic@^6.5.2: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -2344,7 +2232,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -3066,19 +2954,6 @@ exec-sh@^0.3.2: resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b" integrity sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg== -execa@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" - integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.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@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -3107,11 +2982,6 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= - exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -3244,14 +3114,6 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" -figures@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -3281,11 +3143,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-parent-dir@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54" - integrity sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ= - find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -3387,16 +3244,6 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-own-enumerable-property-symbols@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz#b877b49a5c16aefac3655f2ed2ea5b684df8d203" - integrity sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg== - -get-stream@^3.0.0: - version "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: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -3533,13 +3380,6 @@ har-validator@~5.1.0, har-validator@~5.1.3: ajv "^6.5.5" har-schema "^2.0.0" -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -3739,18 +3579,6 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" - -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -3859,11 +3687,6 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= - is-docker@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" @@ -3886,25 +3709,11 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= - dependencies: - number-is-nan "^1.0.0" - is-fn@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" integrity sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw= -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -3944,18 +3753,6 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= - -is-observable@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-0.2.0.tgz#b361311d83c6e5d726cabf5e250b0237106f5ae2" - integrity sha1-s2ExHYPG5dcmyr9eJQsCNxBvWuI= - dependencies: - symbol-observable "^0.2.2" - is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -3968,11 +3765,6 @@ is-potential-custom-element-name@^1.0.0: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= - is-regex@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" @@ -3987,11 +3779,6 @@ is-regex@^1.1.0: dependencies: has-symbols "^1.0.1" -is-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" - integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= - is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4262,11 +4049,6 @@ jest-environment-node@^26.3.0: jest-mock "^26.3.0" jest-util "^26.3.0" -jest-get-type@^21.2.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-21.2.0.tgz#f6376ab9db4b60d81e39f30749c6c466f40d4a23" - integrity sha512-y2fFw3C+D0yjNSDp7ab1kcd6NUYfy3waPTlD8yWkAtiocJdBRQqNoRqVfMNxgj+IjT0V5cBIHJO0z9vuSSZ43Q== - jest-get-type@^25.2.6: version "25.2.6" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" @@ -4538,16 +4320,6 @@ jest-util@^26.3.0: is-ci "^2.0.0" micromatch "^4.0.2" -jest-validate@^21.1.0: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-21.2.1.tgz#cc0cbca653cd54937ba4f2a111796774530dd3c7" - integrity sha512-k4HLI1rZQjlU+EC682RlQ6oZvLrE5SCh3brseQc24vbZTxzT/k/3urar5QMCVgjadmSO7lECeGdc6YxnM3yEGg== - dependencies: - chalk "^2.0.1" - jest-get-type "^21.2.0" - leven "^2.1.0" - pretty-format "^21.2.1" - jest-validate@^26.4.2: version "26.4.2" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.4.2.tgz#e871b0dfe97747133014dcf6445ee8018398f39c" @@ -4611,7 +4383,7 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1, js-yaml@^3.9.0: +js-yaml@^3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -4693,11 +4465,6 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - json-parse-even-better-errors@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -4902,11 +4669,6 @@ levelup@^1.2.1: semver "~5.4.1" xtend "~4.0.0" -leven@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" - integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA= - leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -4933,84 +4695,6 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -lint-staged@^6.1.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-6.1.1.tgz#cd08c4d9b8ccc2d37198d1c47ce77d22be6cf324" - integrity sha512-M/7bwLdXbeG7ZNLcasGeLMBDg60/w6obj3KOtINwJyxAxb53XGY0yH5FSZlWklEzuVbTtqtIfAajh6jYIN90AA== - dependencies: - app-root-path "^2.0.0" - chalk "^2.1.0" - commander "^2.11.0" - cosmiconfig "^4.0.0" - debug "^3.1.0" - dedent "^0.7.0" - execa "^0.8.0" - find-parent-dir "^0.3.0" - is-glob "^4.0.0" - jest-validate "^21.1.0" - listr "^0.13.0" - lodash "^4.17.4" - log-symbols "^2.0.0" - minimatch "^3.0.0" - npm-which "^3.0.1" - p-map "^1.1.1" - path-is-inside "^1.0.2" - pify "^3.0.0" - staged-git-files "1.0.0" - stringify-object "^3.2.0" - -listr-silent-renderer@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" - integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= - -listr-update-renderer@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz#344d980da2ca2e8b145ba305908f32ae3f4cc8a7" - integrity sha1-NE2YDaLKLosUW6MFkI8yrj9MyKc= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - strip-ansi "^3.0.1" - -listr-verbose-renderer@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" - integrity sha1-ggb0z21S3cWCfl/RSYng6WWTOjU= - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - -listr@^0.13.0: - version "0.13.0" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.13.0.tgz#20bb0ba30bae660ee84cc0503df4be3d5623887d" - integrity sha1-ILsLowuuZg7oTMBQPfS+PVYjiH0= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - figures "^1.7.0" - indent-string "^2.1.0" - is-observable "^0.2.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.4.0" - listr-verbose-renderer "^0.4.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - ora "^0.2.3" - p-map "^1.1.1" - rxjs "^5.4.2" - stream-to-observable "^0.2.0" - strip-ansi "^3.0.1" - load-json-file@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" @@ -5056,7 +4740,7 @@ lodash@4.x, lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4: +lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: version "4.17.19" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== @@ -5066,28 +4750,6 @@ lodash@^4.17.19, lodash@^4.17.20: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== -log-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" - integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= - dependencies: - chalk "^1.0.0" - -log-symbols@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - -log-update@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" - integrity sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE= - dependencies: - ansi-escapes "^1.0.0" - cli-cursor "^1.0.2" - loglevel@^1.5.0: version "1.6.4" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.4.tgz#f408f4f006db8354d0577dcf6d33485b3cb90d56" @@ -5100,14 +4762,6 @@ lolex@^5.0.0: dependencies: "@sinonjs/commons" "^1.7.0" -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -5441,13 +5095,6 @@ normalize-path@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -npm-path@^2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.4.tgz#c641347a5ff9d6a09e4d9bce5580c4f505278e64" - integrity sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw== - dependencies: - which "^1.2.10" - 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" @@ -5462,20 +5109,6 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -npm-which@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa" - integrity sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo= - dependencies: - commander "^2.9.0" - npm-path "^2.0.2" - which "^1.2.10" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - number-to-bn@1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" @@ -5494,11 +5127,6 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" @@ -5574,11 +5202,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= - onetime@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" @@ -5617,16 +5240,6 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -ora@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" - integrity sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q= - dependencies: - chalk "^1.1.1" - cli-cursor "^1.0.2" - cli-spinners "^0.1.2" - object-assign "^4.0.1" - p-each-series@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" @@ -5665,11 +5278,6 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-map@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" - integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== - p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -5702,14 +5310,6 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - parse-json@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646" @@ -5750,11 +5350,6 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - 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" @@ -5899,14 +5494,6 @@ prettier@^2.2.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== -pretty-format@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-21.2.1.tgz#ae5407f3cf21066cd011aa1ba5fce7b6a2eddb36" - integrity sha512-ZdWPGYAnYfcVP8yKA3zFjCn8s4/17TeYH28MXuC8vTp0o21eXjbFGcOAXZEaDaOFJjc3h2qa7HQNHNshhvoh2A== - dependencies: - ansi-regex "^3.0.0" - ansi-styles "^3.2.0" - pretty-format@^25.2.1, pretty-format@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a" @@ -5978,11 +5565,6 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - psl@^1.1.24: version "1.3.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.3.0.tgz#e1ebf6a3b5564fa8376f3da2275da76d875ca1bd" @@ -6152,13 +5734,6 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - request-promise-core@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" @@ -6232,7 +5807,7 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^2.0.1, require-from-string@^2.0.2: +require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== @@ -6286,14 +5861,6 @@ resolve@^1.10.1: is-core-module "^2.1.0" path-parse "^1.0.6" -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -6349,13 +5916,6 @@ rustbn.js@~0.2.0: resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== -rxjs@^5.4.2: - version "5.5.12" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" - integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== - dependencies: - symbol-observable "1.0.1" - safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" @@ -6594,11 +6154,6 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= - slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -6742,11 +6297,6 @@ stack-utils@^2.0.2: dependencies: escape-string-regexp "^2.0.0" -staged-git-files@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.0.0.tgz#cdb847837c1fcc52c08a872d4883cc0877668a80" - integrity sha1-zbhHg3wfzFLAioctSIPMCHdmioA= - static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -6760,13 +6310,6 @@ stealthy-require@^1.1.1: resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= -stream-to-observable@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.2.0.tgz#59d6ea393d87c2c0ddac10aa0d561bc6ba6f0e10" - integrity sha1-WdbqOT2HwsDdrBCqDVYbxrpvDhA= - dependencies: - any-observable "^0.2.0" - string-length@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz#4a973bf31ef77c4edbceadd6af2611996985f8a1" @@ -6775,15 +6318,6 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - string-width@^4.1.0, string-width@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" @@ -6830,22 +6364,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-object@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" - integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== - dependencies: - get-own-enumerable-property-symbols "^3.0.0" - is-obj "^1.0.1" - is-regexp "^1.0.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -6885,11 +6403,6 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -6912,16 +6425,6 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" -symbol-observable@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" - integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= - -symbol-observable@^0.2.2: - version "0.2.4" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-0.2.4.tgz#95a83db26186d6af7e7a18dbd9760a2f86d08f40" - integrity sha1-lag9smGG1q9+ehjb2XYKL4bQj0A= - symbol-tree@^3.2.2, symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -7440,7 +6943,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.2.10, which@^1.2.9: +which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -7554,11 +7057,6 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" From fdec72369c3803b786ef301c37677abc03c031b9 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Tue, 13 Apr 2021 08:52:06 -0700 Subject: [PATCH 08/10] lint --fix all prettier violations --- src/BaseController.ts | 8 +- src/BaseControllerV2.test.ts | 183 ++++++-- src/BaseControllerV2.ts | 62 ++- src/ComposableController.test.ts | 58 ++- src/ControllerMessenger.test.ts | 438 +++++++++++++----- src/ControllerMessenger.ts | 105 ++++- src/apis/crypto-compare.test.ts | 76 ++- src/apis/crypto-compare.ts | 22 +- src/approval/ApprovalController.test.js | 89 ++-- src/approval/ApprovalController.test.ts | 257 +++++++--- src/approval/ApprovalController.ts | 58 ++- src/assets/AccountTrackerController.test.ts | 15 +- src/assets/AccountTrackerController.ts | 17 +- src/assets/AssetsContractController.test.ts | 35 +- src/assets/AssetsContractController.ts | 128 +++-- src/assets/AssetsController.test.ts | 237 ++++++++-- src/assets/AssetsController.ts | 327 ++++++++++--- src/assets/AssetsDetectionController.test.ts | 134 ++++-- src/assets/AssetsDetectionController.ts | 103 ++-- src/assets/CurrencyRateController.test.ts | 66 ++- src/assets/CurrencyRateController.ts | 15 +- src/assets/TokenBalancesController.test.ts | 58 ++- src/assets/TokenBalancesController.ts | 14 +- src/assets/TokenRatesController.test.ts | 58 ++- src/assets/TokenRatesController.ts | 17 +- src/keyring/KeyringController.test.ts | 177 +++++-- src/keyring/KeyringController.ts | 81 +++- .../AbstractMessageManager.test.ts | 27 +- src/message-manager/AbstractMessageManager.ts | 8 +- src/message-manager/MessageManager.test.ts | 15 +- src/message-manager/MessageManager.ts | 34 +- .../PersonalMessageManager.test.ts | 15 +- src/message-manager/PersonalMessageManager.ts | 34 +- .../TypedMessageManager.test.ts | 21 +- src/message-manager/TypedMessageManager.ts | 40 +- src/network/NetworkController.test.ts | 18 +- src/network/NetworkController.ts | 56 ++- .../NotificationController.test.ts | 4 +- src/notification/NotificationController.ts | 25 +- src/third-party/EnsController.test.ts | 4 +- src/third-party/EnsController.ts | 15 +- src/third-party/PhishingController.test.ts | 4 +- src/third-party/PhishingController.ts | 21 +- src/transaction/TransactionController.test.ts | 205 +++++--- src/transaction/TransactionController.ts | 237 +++++++--- src/user/AddressBookController.test.ts | 34 +- src/user/AddressBookController.ts | 11 +- src/user/PreferencesController.test.ts | 75 ++- src/user/PreferencesController.ts | 51 +- src/util.test.ts | 49 +- src/util.ts | 172 +++++-- 51 files changed, 3042 insertions(+), 971 deletions(-) diff --git a/src/BaseController.ts b/src/BaseController.ts index 645faa563e..a262bed28d 100644 --- a/src/BaseController.ts +++ b/src/BaseController.ts @@ -114,7 +114,9 @@ export class BaseController { */ configure(config: Partial, overwrite = false, fullUpdate = true) { if (fullUpdate) { - this.internalConfig = overwrite ? (config as C) : Object.assign(this.internalConfig, config); + this.internalConfig = overwrite + ? (config as C) + : Object.assign(this.internalConfig, config); for (const key in this.internalConfig) { if (typeof this.internalConfig[key] !== 'undefined') { @@ -172,7 +174,9 @@ export class BaseController { * @param overwrite - Overwrite state instead of merging */ update(state: Partial, overwrite = false) { - this.internalState = overwrite ? Object.assign({}, state as S) : Object.assign({}, this.internalState, state); + this.internalState = overwrite + ? Object.assign({}, state as S) + : Object.assign({}, this.internalState, state); this.notify(); } } diff --git a/src/BaseControllerV2.test.ts b/src/BaseControllerV2.test.ts index 0e8cc10616..ec3439e1cc 100644 --- a/src/BaseControllerV2.test.ts +++ b/src/BaseControllerV2.test.ts @@ -1,8 +1,15 @@ import type { Draft, Patch } from 'immer'; import sinon from 'sinon'; -import { BaseController, getAnonymizedState, getPersistentState } from './BaseControllerV2'; -import { ControllerMessenger, RestrictedControllerMessenger } from './ControllerMessenger'; +import { + BaseController, + getAnonymizedState, + getPersistentState, +} from './BaseControllerV2'; +import { + ControllerMessenger, + RestrictedControllerMessenger, +} from './ControllerMessenger'; type CountControllerState = { count: number; @@ -20,8 +27,15 @@ const countControllerStateMetadata = { }, }; -class CountController extends BaseController<'CountController', CountControllerState> { - update(callback: (state: Draft) => void | CountControllerState) { +class CountController extends BaseController< + 'CountController', + CountControllerState +> { + update( + callback: ( + state: Draft, + ) => void | CountControllerState, + ) { super.update(callback); } @@ -32,7 +46,10 @@ class CountController extends BaseController<'CountController', CountControllerS describe('BaseController', () => { it('should set initial state', () => { - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + CountControllerEvent + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: [], @@ -49,7 +66,10 @@ describe('BaseController', () => { }); it('should set initial schema', () => { - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + CountControllerEvent + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: [], @@ -62,11 +82,14 @@ describe('BaseController', () => { metadata: countControllerStateMetadata, }); - expect(controller.metadata).toStrictEqual(CountControllerStateMetadata); + expect(controller.metadata).toStrictEqual(countControllerStateMetadata); }); it('should not allow mutating state directly', () => { - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + CountControllerEvent + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: [], @@ -81,11 +104,16 @@ describe('BaseController', () => { expect(() => { controller.state = { count: 1 }; - }).toThrow("Controller state cannot be directly mutated; use 'update' method instead."); + }).toThrow( + "Controller state cannot be directly mutated; use 'update' method instead.", + ); }); it('should allow updating state by modifying draft', () => { - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + CountControllerEvent + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: [], @@ -106,7 +134,10 @@ describe('BaseController', () => { }); it('should allow updating state by return a value', () => { - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + CountControllerEvent + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: [], @@ -127,7 +158,10 @@ describe('BaseController', () => { }); it('should throw an error if update callback modifies draft and returns value', () => { - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + CountControllerEvent + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: [], @@ -151,7 +185,10 @@ describe('BaseController', () => { }); it('should inform subscribers of state changes', () => { - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + CountControllerEvent + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: [], @@ -173,13 +210,22 @@ describe('BaseController', () => { }); expect(listener1.callCount).toStrictEqual(1); - expect(listener1.firstCall.args).toStrictEqual([{ count: 1 }, [{ op: 'replace', path: [], value: { count: 1 } }]]); + expect(listener1.firstCall.args).toStrictEqual([ + { count: 1 }, + [{ op: 'replace', path: [], value: { count: 1 } }], + ]); expect(listener2.callCount).toStrictEqual(1); - expect(listener2.firstCall.args).toStrictEqual([{ count: 1 }, [{ op: 'replace', path: [], value: { count: 1 } }]]); + expect(listener2.firstCall.args).toStrictEqual([ + { count: 1 }, + [{ op: 'replace', path: [], value: { count: 1 } }], + ]); }); it('should inform a subscriber of each state change once even after multiple subscriptions', () => { - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + CountControllerEvent + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: [], @@ -201,11 +247,17 @@ describe('BaseController', () => { }); expect(listener1.callCount).toStrictEqual(1); - expect(listener1.firstCall.args).toStrictEqual([{ count: 1 }, [{ op: 'replace', path: [], value: { count: 1 } }]]); + expect(listener1.firstCall.args).toStrictEqual([ + { count: 1 }, + [{ op: 'replace', path: [], value: { count: 1 } }], + ]); }); it('should no longer inform a subscriber about state changes after unsubscribing', () => { - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + CountControllerEvent + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: [], @@ -229,7 +281,10 @@ describe('BaseController', () => { }); it('should no longer inform a subscriber about state changes after unsubscribing once, even if they subscribed many times', () => { - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + CountControllerEvent + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: [], @@ -254,7 +309,10 @@ describe('BaseController', () => { }); it('should throw when unsubscribing listener who was never subscribed', () => { - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + CountControllerEvent + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: [], @@ -270,11 +328,16 @@ describe('BaseController', () => { expect(() => { controllerMessenger.unsubscribe('CountController:stateChange', listener1); - }).toThrow("Subscription not found for event: 'CountController:stateChange'"); + }).toThrow( + "Subscription not found for event: 'CountController:stateChange'", + ); }); it('should no longer update subscribers after being destroyed', () => { - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + CountControllerEvent + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: [], @@ -307,7 +370,10 @@ describe('getAnonymizedState', () => { }); it('should return empty state when no properties are anonymized', () => { - const anonymizedState = getAnonymizedState({ count: 1 }, { count: { anonymous: false, persist: false } }); + const anonymizedState = getAnonymizedState( + { count: 1 }, + { count: { anonymous: false, persist: false } }, + ); expect(anonymizedState).toStrictEqual({}); }); @@ -338,7 +404,10 @@ describe('getAnonymizedState', () => { }, }, ); - expect(anonymizedState).toStrictEqual({ network: 'mainnet', tokens: ['DAI', 'USDC'] }); + expect(anonymizedState).toStrictEqual({ + network: 'mainnet', + tokens: ['DAI', 'USDC'], + }); }); it('should use anonymizing function to anonymize state', () => { @@ -385,7 +454,11 @@ describe('getAnonymizedState', () => { }); it('should allow returning a nested partial object from an anonymizing function', () => { - const anonymizeTxMeta = (txMeta: { hash: string; value: number; history: { hash: string; value: number }[] }) => { + const anonymizeTxMeta = (txMeta: { + hash: string; + value: number; + history: { hash: string; value: number }[]; + }) => { return { history: txMeta.history.map((entry) => { return { value: entry.value }; @@ -415,7 +488,9 @@ describe('getAnonymizedState', () => { }, ); - expect(anonymizedState).toStrictEqual({ txMeta: { history: [{ value: 9 }], value: 10 } }); + expect(anonymizedState).toStrictEqual({ + txMeta: { history: [{ value: 9 }], value: 10 }, + }); }); it('should allow transforming types in an anonymizing function', () => { @@ -441,7 +516,10 @@ describe('getPersistentState', () => { }); it('should return empty state when no properties are persistent', () => { - const persistentState = getPersistentState({ count: 1 }, { count: { anonymous: false, persist: false } }); + const persistentState = getPersistentState( + { count: 1 }, + { count: { anonymous: false, persist: false } }, + ); expect(persistentState).toStrictEqual({}); }); @@ -472,7 +550,10 @@ describe('getPersistentState', () => { }, }, ); - expect(persistentState).toStrictEqual({ password: 'secret password', privateKey: '123' }); + expect(persistentState).toStrictEqual({ + password: 'secret password', + privateKey: '123', + }); }); it('should use function to derive persistent state', () => { @@ -553,7 +634,9 @@ describe('getPersistentState', () => { }, ); - expect(persistentState).toStrictEqual({ txMeta: { history: [{ value: 9 }], value: 10 } }); + expect(persistentState).toStrictEqual({ + txMeta: { history: [{ value: 9 }], value: 10 }, + }); }); it('should allow transforming types in a persist function', () => { @@ -597,7 +680,10 @@ describe('getPersistentState', () => { }, }; - class VisitorController extends BaseController<'VisitorController', VisitorControllerState> { + class VisitorController extends BaseController< + 'VisitorController', + VisitorControllerState + > { constructor( messagingSystem: RestrictedControllerMessenger< 'VisitorController', @@ -613,7 +699,10 @@ describe('getPersistentState', () => { name: 'VisitorController', state: { visitors: [] }, }); - messagingSystem.registerActionHandler('VisitorController:clear', this.clear); + messagingSystem.registerActionHandler( + 'VisitorController:clear', + this.clear, + ); } clear = () => { @@ -671,8 +760,14 @@ describe('getPersistentState', () => { name: 'VisitorOverflowController', state: { maxVisitors: 5 }, }); - messagingSystem.registerActionHandler('VisitorOverflowController:updateMax', this.updateMax); - messagingSystem.subscribe('VisitorController:stateChange', this.onVisit); + messagingSystem.registerActionHandler( + 'VisitorOverflowController:updateMax', + this.updateMax, + ); + messagingSystem.subscribe( + 'VisitorController:stateChange', + this.onVisit, + ); } onVisit = ({ visitors }: VisitorControllerState) => { @@ -702,20 +797,26 @@ describe('getPersistentState', () => { allowedActions: [], allowedEvents: [], }); - const visitorController = new VisitorController(visitorControllerMessenger); - const visitorOverflowControllerMessenger = controllerMessenger.getRestricted({ - name: 'VisitorOverflowController', - allowedActions: ['VisitorController:clear'], - allowedEvents: ['VisitorController:stateChange'], - }); - const visitorOverflowController = new VisitorOverflowController(visitorOverflowControllerMessenger); + const visitorController = new VisitorController( + visitorControllerMessenger, + ); + const visitorOverflowControllerMessenger = controllerMessenger.getRestricted( + { + name: 'VisitorOverflowController', + allowedActions: ['VisitorController:clear'], + allowedEvents: ['VisitorController:stateChange'], + }, + ); + const visitorOverflowController = new VisitorOverflowController( + visitorOverflowControllerMessenger, + ); controllerMessenger.call('VisitorOverflowController:updateMax', 2); visitorController.addVisitor('A'); visitorController.addVisitor('B'); visitorController.addVisitor('C'); // this should trigger an overflow - expect(visitorOverflowController.state.maxVisitors).toEqual(2); + expect(visitorOverflowController.state.maxVisitors).toStrictEqual(2); expect(visitorController.state.visitors).toHaveLength(0); }); }); diff --git a/src/BaseControllerV2.ts b/src/BaseControllerV2.ts index 5a7ca0545f..d803163a75 100644 --- a/src/BaseControllerV2.ts +++ b/src/BaseControllerV2.ts @@ -4,7 +4,10 @@ import { enablePatches, produceWithPatches } from 'immer'; // eslint-disable-next-line no-duplicate-imports import type { Draft, Patch } from 'immer'; -import type { RestrictedControllerMessenger, Namespaced } from './ControllerMessenger'; +import type { + RestrictedControllerMessenger, + Namespaced, +} from './ControllerMessenger'; enablePatches(); @@ -94,19 +97,37 @@ export interface StatePropertyMetadata { anonymous: boolean | StateDeriver; } -type Json = null | boolean | number | string | Json[] | { [prop: string]: Json }; +type Json = + | null + | boolean + | number + | string + | Json[] + | { [prop: string]: Json }; -type StateChangeEvent = E extends { type: `${N}:stateChange`; payload: [S, Patch[]] } +type StateChangeEvent = E extends { + type: `${N}:stateChange`; + payload: [S, Patch[]]; +} ? E : never; /** * Controller class that provides state management, subscriptions, and state metadata */ -export class BaseController> { +export class BaseController< + N extends string, + S extends Record +> { private internalState: IsJsonable; - protected messagingSystem: RestrictedControllerMessenger, string, string>; + protected messagingSystem: RestrictedControllerMessenger< + N, + any, + StateChangeEvent, + string, + string + >; private name: N; @@ -128,7 +149,13 @@ export class BaseController> name, state, }: { - messenger: RestrictedControllerMessenger, string, string>; + messenger: RestrictedControllerMessenger< + N, + any, + StateChangeEvent, + string, + string + >; metadata: StateMetadata; name: N; state: IsJsonable; @@ -149,7 +176,9 @@ export class BaseController> } set state(_) { - throw new Error(`Controller state cannot be directly mutated; use 'update' method instead.`); + throw new Error( + `Controller state cannot be directly mutated; use 'update' method instead.`, + ); } /** @@ -161,10 +190,19 @@ export class BaseController> * @param callback - Callback for updating state, passed a draft state * object. Return a new state object or mutate the draft to update state. */ - protected update(callback: (state: Draft>) => void | IsJsonable) { - const [nextState, patches] = produceWithPatches(this.internalState, callback); + protected update( + callback: (state: Draft>) => void | IsJsonable, + ) { + const [nextState, patches] = produceWithPatches( + this.internalState, + callback, + ); this.internalState = nextState as IsJsonable; - this.messagingSystem.publish(`${this.name}:stateChange` as Namespaced, nextState as S, patches); + this.messagingSystem.publish( + `${this.name}:stateChange` as Namespaced, + nextState as S, + patches, + ); } /** @@ -177,7 +215,9 @@ export class BaseController> * listeners from being garbage collected. */ protected destroy() { - this.messagingSystem.clearEventSubscriptions(`${this.name}:stateChange` as Namespaced); + this.messagingSystem.clearEventSubscriptions( + `${this.name}:stateChange` as Namespaced, + ); } } diff --git a/src/ComposableController.test.ts b/src/ComposableController.test.ts index ea68b17fa7..d23144de27 100644 --- a/src/ComposableController.test.ts +++ b/src/ComposableController.test.ts @@ -5,7 +5,10 @@ import ComposableController from './ComposableController'; import PreferencesController from './user/PreferencesController'; import TokenRatesController from './assets/TokenRatesController'; import { AssetsController } from './assets/AssetsController'; -import { NetworkController, NetworksChainId } from './network/NetworkController'; +import { + NetworkController, + NetworksChainId, +} from './network/NetworkController'; import { AssetsContractController } from './assets/AssetsContractController'; import CurrencyRateController from './assets/CurrencyRateController'; @@ -15,11 +18,18 @@ describe('ComposableController', () => { const networkController = new NetworkController(); const assetContractController = new AssetsContractController(); const assetController = new AssetsController({ - onPreferencesStateChange: (listener) => preferencesController.subscribe(listener), + onPreferencesStateChange: (listener) => + preferencesController.subscribe(listener), onNetworkStateChange: (listener) => networkController.subscribe(listener), - getAssetName: assetContractController.getAssetName.bind(assetContractController), - getAssetSymbol: assetContractController.getAssetSymbol.bind(assetContractController), - getCollectibleTokenURI: assetContractController.getCollectibleTokenURI.bind(assetContractController), + getAssetName: assetContractController.getAssetName.bind( + assetContractController, + ), + getAssetSymbol: assetContractController.getAssetSymbol.bind( + assetContractController, + ), + getCollectibleTokenURI: assetContractController.getCollectibleTokenURI.bind( + assetContractController, + ), }); const currencyRateController = new CurrencyRateController(); const controller = new ComposableController([ @@ -32,10 +42,11 @@ describe('ComposableController', () => { preferencesController, new TokenRatesController({ onAssetsStateChange: (listener) => assetController.subscribe(listener), - onCurrencyRateStateChange: (listener) => currencyRateController.subscribe(listener), + onCurrencyRateStateChange: (listener) => + currencyRateController.subscribe(listener), }), ]); - expect(controller.state).toEqual({ + expect(controller.state).toStrictEqual({ AddressBookController: { addressBook: {} }, AssetsContractController: {}, AssetsController: { @@ -80,11 +91,18 @@ describe('ComposableController', () => { const networkController = new NetworkController(); const assetContractController = new AssetsContractController(); const assetController = new AssetsController({ - onPreferencesStateChange: (listener) => preferencesController.subscribe(listener), + onPreferencesStateChange: (listener) => + preferencesController.subscribe(listener), onNetworkStateChange: (listener) => networkController.subscribe(listener), - getAssetName: assetContractController.getAssetName.bind(assetContractController), - getAssetSymbol: assetContractController.getAssetSymbol.bind(assetContractController), - getCollectibleTokenURI: assetContractController.getCollectibleTokenURI.bind(assetContractController), + getAssetName: assetContractController.getAssetName.bind( + assetContractController, + ), + getAssetSymbol: assetContractController.getAssetSymbol.bind( + assetContractController, + ), + getCollectibleTokenURI: assetContractController.getCollectibleTokenURI.bind( + assetContractController, + ), }); const currencyRateController = new CurrencyRateController(); const controller = new ComposableController([ @@ -97,10 +115,11 @@ describe('ComposableController', () => { preferencesController, new TokenRatesController({ onAssetsStateChange: (listener) => assetController.subscribe(listener), - onCurrencyRateStateChange: (listener) => currencyRateController.subscribe(listener), + onCurrencyRateStateChange: (listener) => + currencyRateController.subscribe(listener), }), ]); - expect(controller.flatState).toEqual({ + expect(controller.flatState).toStrictEqual({ addressBook: {}, allCollectibleContracts: {}, allCollectibles: {}, @@ -143,8 +162,10 @@ describe('ComposableController', () => { }, }, }; - const controller = new ComposableController([new AddressBookController(undefined, state)]); - expect(controller.state).toEqual({ AddressBookController: state }); + const controller = new ComposableController([ + new AddressBookController(undefined, state), + ]); + expect(controller.state).toStrictEqual({ AddressBookController: state }); }); it('should notify listeners of nested state change', () => { @@ -152,9 +173,12 @@ describe('ComposableController', () => { const controller = new ComposableController([addressBookController]); const listener = stub(); controller.subscribe(listener); - addressBookController.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo'); + addressBookController.set( + '0x32Be343B94f860124dC4fEe278FDCBD38C102D88', + 'foo', + ); expect(listener.calledOnce).toBe(true); - expect(listener.getCall(0).args[0]).toEqual({ + expect(listener.getCall(0).args[0]).toStrictEqual({ AddressBookController: { addressBook: { 1: { diff --git a/src/ControllerMessenger.test.ts b/src/ControllerMessenger.test.ts index 7eff43022d..b29f64755c 100644 --- a/src/ControllerMessenger.test.ts +++ b/src/ControllerMessenger.test.ts @@ -27,9 +27,12 @@ describe('ControllerMessenger', () => { const controllerMessenger = new ControllerMessenger(); let message = ''; - controllerMessenger.registerActionHandler('reset', (initialMessage: string) => { - message = initialMessage; - }); + controllerMessenger.registerActionHandler( + 'reset', + (initialMessage: string) => { + message = initialMessage; + }, + ); controllerMessenger.registerActionHandler('concat', (s: string) => { message += s; }); @@ -42,7 +45,10 @@ describe('ControllerMessenger', () => { it('should allow registering and calling an action handler with no parameters', () => { type IncrementAction = { type: 'increment'; handler: () => void }; - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + IncrementAction, + never + >(); let count = 0; controllerMessenger.registerActionHandler('increment', () => { @@ -54,7 +60,10 @@ describe('ControllerMessenger', () => { }); it('should allow registering and calling an action handler with multiple parameters', () => { - type MessageAction = { type: 'message'; handler: (to: string, message: string) => void }; + type MessageAction = { + type: 'message'; + handler: (to: string, message: string) => void; + }; const controllerMessenger = new ControllerMessenger(); const messages: Record = {}; @@ -153,7 +162,9 @@ describe('ControllerMessenger', () => { }); it('should allow publishing multiple different events to subscriber', () => { - type MessageEvent = { type: 'message'; payload: [string] } | { type: 'ping'; payload: [] }; + type MessageEvent = + | { type: 'message'; payload: [string] } + | { type: 'ping'; payload: [] }; const controllerMessenger = new ControllerMessenger(); const messageHandler = sinon.stub(); @@ -274,7 +285,9 @@ describe('ControllerMessenger', () => { type MessageEvent = { type: 'message'; payload: [string] }; const controllerMessenger = new ControllerMessenger(); - expect(() => controllerMessenger.clearEventSubscriptions('message')).not.toThrow(); + expect(() => + controllerMessenger.clearEventSubscriptions('message'), + ).not.toThrow(); }); it('should not call subscriber after resetting subscriptions', () => { @@ -292,7 +305,10 @@ describe('ControllerMessenger', () => { describe('RestrictedControllerMessenger', () => { it('should allow registering and calling an action handler', () => { - type CountAction = { type: 'CountController:count'; handler: (increment: number) => void }; + type CountAction = { + type: 'CountController:count'; + handler: (increment: number) => void; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', @@ -301,18 +317,24 @@ describe('RestrictedControllerMessenger', () => { }); let count = 0; - restrictedControllerMessenger.registerActionHandler('CountController:count', (increment: number) => { - count += increment; - }); + restrictedControllerMessenger.registerActionHandler( + 'CountController:count', + (increment: number) => { + count += increment; + }, + ); restrictedControllerMessenger.call('CountController:count', 1); - expect(count).toEqual(1); + expect(count).toStrictEqual(1); }); it('should allow registering and calling multiple different action handlers', () => { type MessageAction = | { type: 'MessageController:concat'; handler: (message: string) => void } - | { type: 'MessageController:reset'; handler: (initialMessage: string) => void }; + | { + type: 'MessageController:reset'; + handler: (initialMessage: string) => void; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -321,22 +343,34 @@ describe('RestrictedControllerMessenger', () => { }); let message = ''; - restrictedControllerMessenger.registerActionHandler('MessageController:reset', (initialMessage: string) => { - message = initialMessage; - }); - restrictedControllerMessenger.registerActionHandler('MessageController:concat', (s: string) => { - message += s; - }); + restrictedControllerMessenger.registerActionHandler( + 'MessageController:reset', + (initialMessage: string) => { + message = initialMessage; + }, + ); + restrictedControllerMessenger.registerActionHandler( + 'MessageController:concat', + (s: string) => { + message += s; + }, + ); restrictedControllerMessenger.call('MessageController:reset', 'hello'); restrictedControllerMessenger.call('MessageController:concat', ', world'); - expect(message).toEqual('hello, world'); + expect(message).toStrictEqual('hello, world'); }); it('should allow registering and calling an action handler with no parameters', () => { - type IncrementAction = { type: 'CountController:increment'; handler: () => void }; - const controllerMessenger = new ControllerMessenger(); + type IncrementAction = { + type: 'CountController:increment'; + handler: () => void; + }; + const controllerMessenger = new ControllerMessenger< + IncrementAction, + never + >(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'CountController', allowedActions: ['CountController:increment'], @@ -344,16 +378,22 @@ describe('RestrictedControllerMessenger', () => { }); let count = 0; - restrictedControllerMessenger.registerActionHandler('CountController:increment', () => { - count += 1; - }); + restrictedControllerMessenger.registerActionHandler( + 'CountController:increment', + () => { + count += 1; + }, + ); restrictedControllerMessenger.call('CountController:increment'); - expect(count).toEqual(1); + expect(count).toStrictEqual(1); }); it('should allow registering and calling an action handler with multiple parameters', () => { - type MessageAction = { type: 'MessageController:message'; handler: (to: string, message: string) => void }; + type MessageAction = { + type: 'MessageController:message'; + handler: (to: string, message: string) => void; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -362,16 +402,26 @@ describe('RestrictedControllerMessenger', () => { }); const messages: Record = {}; - restrictedControllerMessenger.registerActionHandler('MessageController:message', (to, message) => { - messages[to] = message; - }); - restrictedControllerMessenger.call('MessageController:message', '0x123', 'hello'); + restrictedControllerMessenger.registerActionHandler( + 'MessageController:message', + (to, message) => { + messages[to] = message; + }, + ); + restrictedControllerMessenger.call( + 'MessageController:message', + '0x123', + 'hello', + ); - expect(messages['0x123']).toEqual('hello'); + expect(messages['0x123']).toStrictEqual('hello'); }); it('should allow registering and calling an action handler with a return value', () => { - type AddAction = { type: 'MathController:add'; handler: (a: number, b: number) => number }; + type AddAction = { + type: 'MathController:add'; + handler: (a: number, b: number) => number; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'MathController', @@ -379,12 +429,19 @@ describe('RestrictedControllerMessenger', () => { allowedEvents: [], }); - restrictedControllerMessenger.registerActionHandler('MathController:add', (a, b) => { - return a + b; - }); - const result = restrictedControllerMessenger.call('MathController:add', 5, 10); + restrictedControllerMessenger.registerActionHandler( + 'MathController:add', + (a, b) => { + return a + b; + }, + ); + const result = restrictedControllerMessenger.call( + 'MathController:add', + 5, + 10, + ); - expect(result).toEqual(15); + expect(result).toStrictEqual(15); }); it('should not allow registering multiple action handlers under the same name', () => { @@ -396,10 +453,16 @@ describe('RestrictedControllerMessenger', () => { allowedEvents: [], }); - restrictedControllerMessenger.registerActionHandler('PingController:ping', () => undefined); + restrictedControllerMessenger.registerActionHandler( + 'PingController:ping', + () => undefined, + ); expect(() => { - restrictedControllerMessenger.registerActionHandler('PingController:ping', () => undefined); + restrictedControllerMessenger.registerActionHandler( + 'PingController:ping', + () => undefined, + ); }).toThrow('A handler for PingController:ping has already been registered'); }); @@ -431,20 +494,28 @@ describe('RestrictedControllerMessenger', () => { }).toThrow('A handler for PingController:ping has not been registered'); let pingCount = 0; - restrictedControllerMessenger.registerActionHandler('PingController:ping', () => { - pingCount += 1; - }); + restrictedControllerMessenger.registerActionHandler( + 'PingController:ping', + () => { + pingCount += 1; + }, + ); - restrictedControllerMessenger.unregisterActionHandler('PingController:ping'); + restrictedControllerMessenger.unregisterActionHandler( + 'PingController:ping', + ); expect(() => { restrictedControllerMessenger.call('PingController:ping'); }).toThrow('A handler for PingController:ping has not been registered'); - expect(pingCount).toEqual(0); + expect(pingCount).toStrictEqual(0); }); it('should publish event to subscriber', () => { - type MessageEvent = { type: 'MessageController:message'; payload: [string] }; + type MessageEvent = { + type: 'MessageController:message'; + payload: [string]; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -453,11 +524,14 @@ describe('RestrictedControllerMessenger', () => { }); const handler = sinon.stub(); - restrictedControllerMessenger.subscribe('MessageController:message', handler); + restrictedControllerMessenger.subscribe( + 'MessageController:message', + handler, + ); restrictedControllerMessenger.publish('MessageController:message', 'hello'); expect(handler.calledWithExactly('hello')).toBeTruthy(); - expect(handler.callCount).toEqual(1); + expect(handler.callCount).toStrictEqual(1); }); it('should allow publishing multiple different events to subscriber', () => { @@ -473,16 +547,22 @@ describe('RestrictedControllerMessenger', () => { const messageHandler = sinon.stub(); const pingHandler = sinon.stub(); - restrictedControllerMessenger.subscribe('MessageController:message', messageHandler); - restrictedControllerMessenger.subscribe('MessageController:ping', pingHandler); + restrictedControllerMessenger.subscribe( + 'MessageController:message', + messageHandler, + ); + restrictedControllerMessenger.subscribe( + 'MessageController:ping', + pingHandler, + ); restrictedControllerMessenger.publish('MessageController:message', 'hello'); restrictedControllerMessenger.publish('MessageController:ping'); expect(messageHandler.calledWithExactly('hello')).toBeTruthy(); - expect(messageHandler.callCount).toEqual(1); + expect(messageHandler.callCount).toStrictEqual(1); expect(pingHandler.calledWithExactly()).toBeTruthy(); - expect(pingHandler.callCount).toEqual(1); + expect(pingHandler.callCount).toStrictEqual(1); }); it('should publish event with no payload to subscriber', () => { @@ -499,11 +579,14 @@ describe('RestrictedControllerMessenger', () => { restrictedControllerMessenger.publish('PingController:ping'); expect(handler.calledWithExactly()).toBeTruthy(); - expect(handler.callCount).toEqual(1); + expect(handler.callCount).toStrictEqual(1); }); it('should publish event with multiple payload parameters to subscriber', () => { - type MessageEvent = { type: 'MessageController:message'; payload: [string, string] }; + type MessageEvent = { + type: 'MessageController:message'; + payload: [string, string]; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -512,15 +595,25 @@ describe('RestrictedControllerMessenger', () => { }); const handler = sinon.stub(); - restrictedControllerMessenger.subscribe('MessageController:message', handler); - restrictedControllerMessenger.publish('MessageController:message', 'hello', 'there'); + restrictedControllerMessenger.subscribe( + 'MessageController:message', + handler, + ); + restrictedControllerMessenger.publish( + 'MessageController:message', + 'hello', + 'there', + ); expect(handler.calledWithExactly('hello', 'there')).toBeTruthy(); - expect(handler.callCount).toEqual(1); + expect(handler.callCount).toStrictEqual(1); }); it('should publish event once to subscriber even if subscribed multiple times', () => { - type MessageEvent = { type: 'MessageController:message'; payload: [string] }; + type MessageEvent = { + type: 'MessageController:message'; + payload: [string]; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -529,16 +622,25 @@ describe('RestrictedControllerMessenger', () => { }); const handler = sinon.stub(); - restrictedControllerMessenger.subscribe('MessageController:message', handler); - restrictedControllerMessenger.subscribe('MessageController:message', handler); + restrictedControllerMessenger.subscribe( + 'MessageController:message', + handler, + ); + restrictedControllerMessenger.subscribe( + 'MessageController:message', + handler, + ); restrictedControllerMessenger.publish('MessageController:message', 'hello'); expect(handler.calledWithExactly('hello')).toBeTruthy(); - expect(handler.callCount).toEqual(1); + expect(handler.callCount).toStrictEqual(1); }); it('should publish event to many subscribers', () => { - type MessageEvent = { type: 'MessageController:message'; payload: [string] }; + type MessageEvent = { + type: 'MessageController:message'; + payload: [string]; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -548,18 +650,27 @@ describe('RestrictedControllerMessenger', () => { const handler1 = sinon.stub(); const handler2 = sinon.stub(); - restrictedControllerMessenger.subscribe('MessageController:message', handler1); - restrictedControllerMessenger.subscribe('MessageController:message', handler2); + restrictedControllerMessenger.subscribe( + 'MessageController:message', + handler1, + ); + restrictedControllerMessenger.subscribe( + 'MessageController:message', + handler2, + ); restrictedControllerMessenger.publish('MessageController:message', 'hello'); expect(handler1.calledWithExactly('hello')).toBeTruthy(); - expect(handler1.callCount).toEqual(1); + expect(handler1.callCount).toStrictEqual(1); expect(handler2.calledWithExactly('hello')).toBeTruthy(); - expect(handler2.callCount).toEqual(1); + expect(handler2.callCount).toStrictEqual(1); }); it('should not call subscriber after unsubscribing', () => { - type MessageEvent = { type: 'MessageController:message'; payload: [string] }; + type MessageEvent = { + type: 'MessageController:message'; + payload: [string]; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -568,15 +679,24 @@ describe('RestrictedControllerMessenger', () => { }); const handler = sinon.stub(); - restrictedControllerMessenger.subscribe('MessageController:message', handler); - restrictedControllerMessenger.unsubscribe('MessageController:message', handler); + restrictedControllerMessenger.subscribe( + 'MessageController:message', + handler, + ); + restrictedControllerMessenger.unsubscribe( + 'MessageController:message', + handler, + ); restrictedControllerMessenger.publish('MessageController:message', 'hello'); - expect(handler.callCount).toEqual(0); + expect(handler.callCount).toStrictEqual(0); }); it('should throw when unsubscribing when there are no subscriptions', () => { - type MessageEvent = { type: 'MessageController:message'; payload: [string] }; + type MessageEvent = { + type: 'MessageController:message'; + payload: [string]; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -585,13 +705,19 @@ describe('RestrictedControllerMessenger', () => { }); const handler = sinon.stub(); - expect(() => restrictedControllerMessenger.unsubscribe('MessageController:message', handler)).toThrow( - `Subscription not found for event: 'MessageController:message'`, - ); + expect(() => + restrictedControllerMessenger.unsubscribe( + 'MessageController:message', + handler, + ), + ).toThrow(`Subscription not found for event: 'MessageController:message'`); }); it('should throw when unsubscribing a handler that is not subscribed', () => { - type MessageEvent = { type: 'MessageController:message'; payload: [string] }; + type MessageEvent = { + type: 'MessageController:message'; + payload: [string]; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -601,15 +727,24 @@ describe('RestrictedControllerMessenger', () => { const handler1 = sinon.stub(); const handler2 = sinon.stub(); - restrictedControllerMessenger.subscribe('MessageController:message', handler1); - - expect(() => restrictedControllerMessenger.unsubscribe('MessageController:message', handler2)).toThrow( - `Subscription not found for event: 'MessageController:message'`, + restrictedControllerMessenger.subscribe( + 'MessageController:message', + handler1, ); + + expect(() => + restrictedControllerMessenger.unsubscribe( + 'MessageController:message', + handler2, + ), + ).toThrow(`Subscription not found for event: 'MessageController:message'`); }); it('should not call subscriber after clearing event subscriptions', () => { - type MessageEvent = { type: 'MessageController:message'; payload: [string] }; + type MessageEvent = { + type: 'MessageController:message'; + payload: [string]; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -618,15 +753,23 @@ describe('RestrictedControllerMessenger', () => { }); const handler = sinon.stub(); - restrictedControllerMessenger.subscribe('MessageController:message', handler); - restrictedControllerMessenger.clearEventSubscriptions('MessageController:message'); + restrictedControllerMessenger.subscribe( + 'MessageController:message', + handler, + ); + restrictedControllerMessenger.clearEventSubscriptions( + 'MessageController:message', + ); restrictedControllerMessenger.publish('MessageController:message', 'hello'); - expect(handler.callCount).toEqual(0); + expect(handler.callCount).toStrictEqual(0); }); it('should not throw when clearing event that has no subscriptions', () => { - type MessageEvent = { type: 'MessageController:message'; payload: [string] }; + type MessageEvent = { + type: 'MessageController:message'; + payload: [string]; + }; const controllerMessenger = new ControllerMessenger(); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -634,17 +777,26 @@ describe('RestrictedControllerMessenger', () => { allowedEvents: ['MessageController:message'], }); - expect(() => restrictedControllerMessenger.clearEventSubscriptions('MessageController:message')).not.toThrow(); + expect(() => + restrictedControllerMessenger.clearEventSubscriptions( + 'MessageController:message', + ), + ).not.toThrow(); }); it('should allow calling an external action', () => { - type CountAction = { type: 'CountController:count'; handler: (increment: number) => void }; + type CountAction = { + type: 'CountController:count'; + handler: (increment: number) => void; + }; const controllerMessenger = new ControllerMessenger(); - const externalRestrictedControllerMessenger = controllerMessenger.getRestricted({ - name: 'CountController', - allowedActions: [], - allowedEvents: [], - }); + const externalRestrictedControllerMessenger = controllerMessenger.getRestricted( + { + name: 'CountController', + allowedActions: [], + allowedEvents: [], + }, + ); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'OtherController', allowedActions: ['CountController:count'], @@ -652,22 +804,30 @@ describe('RestrictedControllerMessenger', () => { }); let count = 0; - externalRestrictedControllerMessenger.registerActionHandler('CountController:count', (increment: number) => { - count += increment; - }); + externalRestrictedControllerMessenger.registerActionHandler( + 'CountController:count', + (increment: number) => { + count += increment; + }, + ); restrictedControllerMessenger.call('CountController:count', 1); - expect(count).toEqual(1); + expect(count).toStrictEqual(1); }); it('should allow subscribing to an external event', () => { - type MessageEvent = { type: 'MessageController:message'; payload: [string] }; + type MessageEvent = { + type: 'MessageController:message'; + payload: [string]; + }; const controllerMessenger = new ControllerMessenger(); - const externalRestrictedControllerMessenger = controllerMessenger.getRestricted({ - name: 'MessageController', - allowedActions: [], - allowedEvents: [], - }); + const externalRestrictedControllerMessenger = controllerMessenger.getRestricted( + { + name: 'MessageController', + allowedActions: [], + allowedEvents: [], + }, + ); const restrictedControllerMessenger = controllerMessenger.getRestricted({ name: 'OtherController', allowedActions: [], @@ -675,19 +835,34 @@ describe('RestrictedControllerMessenger', () => { }); const handler = sinon.stub(); - restrictedControllerMessenger.subscribe('MessageController:message', handler); - externalRestrictedControllerMessenger.publish('MessageController:message', 'hello'); + restrictedControllerMessenger.subscribe( + 'MessageController:message', + handler, + ); + externalRestrictedControllerMessenger.publish( + 'MessageController:message', + 'hello', + ); expect(handler.calledWithExactly('hello')).toBeTruthy(); - expect(handler.callCount).toEqual(1); + expect(handler.callCount).toStrictEqual(1); }); it('should allow interacting with internal and external actions', () => { type MessageAction = | { type: 'MessageController:concat'; handler: (message: string) => void } - | { type: 'MessageController:reset'; handler: (initialMessage: string) => void }; - type CountAction = { type: 'CountController:count'; handler: (increment: number) => void }; - const controllerMessenger = new ControllerMessenger(); + | { + type: 'MessageController:reset'; + handler: (initialMessage: string) => void; + }; + type CountAction = { + type: 'CountController:count'; + handler: (increment: number) => void; + }; + const controllerMessenger = new ControllerMessenger< + MessageAction | CountAction, + never + >(); const messageControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -701,23 +876,32 @@ describe('RestrictedControllerMessenger', () => { }); let count = 0; - countControllerMessenger.registerActionHandler('CountController:count', (increment: number) => { - count += increment; - }); + countControllerMessenger.registerActionHandler( + 'CountController:count', + (increment: number) => { + count += increment; + }, + ); let fullMessage = ''; - messageControllerMessenger.registerActionHandler('MessageController:concat', (message: string) => { - fullMessage += message; - }); - messageControllerMessenger.registerActionHandler('MessageController:reset', (message: string) => { - fullMessage = message; - }); + messageControllerMessenger.registerActionHandler( + 'MessageController:concat', + (message: string) => { + fullMessage += message; + }, + ); + messageControllerMessenger.registerActionHandler( + 'MessageController:reset', + (message: string) => { + fullMessage = message; + }, + ); messageControllerMessenger.call('MessageController:reset', 'hello'); messageControllerMessenger.call('CountController:count', 1); - expect(fullMessage).toEqual('hello'); - expect(count).toEqual(1); + expect(fullMessage).toStrictEqual('hello'); + expect(count).toStrictEqual(1); }); it('should allow interacting with internal and external events', () => { @@ -725,7 +909,10 @@ describe('RestrictedControllerMessenger', () => { | { type: 'MessageController:message'; payload: [string] } | { type: 'MessageController:ping'; payload: [] }; type CountEvent = { type: 'CountController:update'; payload: [number] }; - const controllerMessenger = new ControllerMessenger(); + const controllerMessenger = new ControllerMessenger< + never, + MessageEvent | CountEvent + >(); const messageControllerMessenger = controllerMessenger.getRestricted({ name: 'MessageController', @@ -743,13 +930,16 @@ describe('RestrictedControllerMessenger', () => { pings += 1; }); let currentCount; - messageControllerMessenger.subscribe('CountController:update', (newCount: number) => { - currentCount = newCount; - }); + messageControllerMessenger.subscribe( + 'CountController:update', + (newCount: number) => { + currentCount = newCount; + }, + ); messageControllerMessenger.publish('MessageController:ping'); countControllerMessenger.publish('CountController:update', 10); - expect(pings).toEqual(1); - expect(currentCount).toEqual(10); + expect(pings).toStrictEqual(1); + expect(currentCount).toStrictEqual(10); }); }); diff --git a/src/ControllerMessenger.ts b/src/ControllerMessenger.ts index 406129a6de..d5e4836ba8 100644 --- a/src/ControllerMessenger.ts +++ b/src/ControllerMessenger.ts @@ -1,15 +1,27 @@ type ActionHandler = ( ...args: ExtractActionParameters ) => ExtractActionResponse; -type ExtractActionParameters = Action extends { type: T; handler: (...args: infer H) => any } ? H : never; -type ExtractActionResponse = Action extends { type: T; handler: (...args: any) => infer H } ? H : never; +type ExtractActionParameters = Action extends { + type: T; + handler: (...args: infer H) => any; +} + ? H + : never; +type ExtractActionResponse = Action extends { + type: T; + handler: (...args: any) => infer H; +} + ? H + : never; type ExtractEvenHandler = Event extends { type: T; payload: infer P } ? P extends any[] ? (...payload: P) => void : never : never; -type ExtractEventPayload = Event extends { type: T; payload: infer P } ? P : never; +type ExtractEventPayload = Event extends { type: T; payload: infer P } + ? P + : never; type ActionConstraint = { type: string; handler: (...args: any) => unknown }; type EventConstraint = { type: string; payload: unknown[] }; @@ -19,7 +31,9 @@ type EventConstraint = { type: string; payload: unknown[] }; * * This type verifies that the string T is prefixed by the string Name followed by a colon. */ -export type Namespaced = T extends `${Name}:${string}` ? T : never; +export type Namespaced = T extends `${Name}:${string}` + ? T + : never; /** * A restricted controller messenger. @@ -89,10 +103,15 @@ export class RestrictedControllerMessenger< * invoked with the given action type. * @throws Will throw when a handler has been registered for this action type already. */ - registerActionHandler>(action: T, handler: ActionHandler) { + registerActionHandler>( + action: T, + handler: ActionHandler, + ) { /* istanbul ignore if */ // Branch unreachable with valid types if (!action.startsWith(`${this.controllerName}:`)) { - throw new Error(`Only allowed registering action handlers prefixed by '${this.controllerName}:'`); + throw new Error( + `Only allowed registering action handlers prefixed by '${this.controllerName}:'`, + ); } return this.controllerMessenger.registerActionHandler(action, handler); } @@ -109,7 +128,9 @@ export class RestrictedControllerMessenger< unregisterActionHandler>(action: T) { /* istanbul ignore if */ // Branch unreachable with valid types if (!action.startsWith(`${this.controllerName}:`)) { - throw new Error(`Only allowed unregistering action handlers prefixed by '${this.controllerName}:'`); + throw new Error( + `Only allowed unregistering action handlers prefixed by '${this.controllerName}:'`, + ); } return this.controllerMessenger.unregisterActionHandler(action); } @@ -149,10 +170,15 @@ export class RestrictedControllerMessenger< * @param payload - The event payload. The type of the parameters for each event handler must * match the type of this payload. */ - publish>(event: E, ...payload: ExtractEventPayload) { + publish>( + event: E, + ...payload: ExtractEventPayload + ) { /* istanbul ignore if */ // Branch unreachable with valid types if (!event.startsWith(`${this.controllerName}:`)) { - throw new Error(`Only allowed publishing events prefixed by '${this.controllerName}:'`); + throw new Error( + `Only allowed publishing events prefixed by '${this.controllerName}:'`, + ); } return this.controllerMessenger.publish(event, ...payload); } @@ -168,7 +194,10 @@ export class RestrictedControllerMessenger< * @param handler - The event handler. The type of the parameters for this event handler must * match the type of the payload for this event type. */ - subscribe(event: E, handler: ExtractEvenHandler) { + subscribe( + event: E, + handler: ExtractEvenHandler, + ) { /* istanbul ignore if */ // Branch unreachable with valid types if (!this.allowedEvents.includes(event)) { throw new Error(`Event missing from allow list: ${event}`); @@ -187,7 +216,10 @@ export class RestrictedControllerMessenger< * @param handler - The event handler to unregister. * @throws Will throw when the given event handler is not registered for this event. */ - unsubscribe(event: E, handler: ExtractEvenHandler) { + unsubscribe( + event: E, + handler: ExtractEvenHandler, + ) { /* istanbul ignore if */ // Branch unreachable with valid types if (!this.allowedEvents.includes(event)) { throw new Error(`Event missing from allow list: ${event}`); @@ -207,7 +239,9 @@ export class RestrictedControllerMessenger< clearEventSubscriptions>(event: E) { /* istanbul ignore if */ // Branch unreachable with valid types if (!event.startsWith(`${this.controllerName}:`)) { - throw new Error(`Only allowed clearing events prefixed by '${this.controllerName}:'`); + throw new Error( + `Only allowed clearing events prefixed by '${this.controllerName}:'`, + ); } return this.controllerMessenger.clearEventSubscriptions(event); } @@ -220,7 +254,10 @@ export class RestrictedControllerMessenger< * and it allows publishing and subscribing to events. Both actions and events are identified by * unique strings. */ -export class ControllerMessenger { +export class ControllerMessenger< + Action extends ActionConstraint, + Event extends EventConstraint +> { private actions = new Map(); private events = new Map>(); @@ -235,9 +272,14 @@ export class ControllerMessenger(actionType: T, handler: ActionHandler) { + registerActionHandler( + actionType: T, + handler: ActionHandler, + ) { if (this.actions.has(actionType)) { - throw new Error(`A handler for ${actionType} has already been registered`); + throw new Error( + `A handler for ${actionType} has already been registered`, + ); } this.actions.set(actionType, handler); } @@ -293,8 +335,13 @@ export class ControllerMessenger(eventType: E, ...payload: ExtractEventPayload) { - const subscribers = this.events.get(eventType) as Set>; + publish( + eventType: E, + ...payload: ExtractEventPayload + ) { + const subscribers = this.events.get(eventType) as Set< + ExtractEvenHandler + >; if (subscribers) { for (const eventHandler of subscribers) { @@ -312,7 +359,10 @@ export class ControllerMessenger(eventType: E, handler: ExtractEvenHandler) { + subscribe( + eventType: E, + handler: ExtractEvenHandler, + ) { let subscribers = this.events.get(eventType); if (!subscribers) { subscribers = new Set(); @@ -330,7 +380,10 @@ export class ControllerMessenger(eventType: E, handler: ExtractEvenHandler) { + unsubscribe( + eventType: E, + handler: ExtractEvenHandler, + ) { const subscribers = this.events.get(eventType); if (!subscribers || !subscribers.has(handler)) { @@ -379,7 +432,11 @@ export class ControllerMessenger({ + getRestricted< + N extends string, + AllowedAction extends string, + AllowedEvent extends string + >({ name, allowedActions, allowedEvents, @@ -388,7 +445,13 @@ export class ControllerMessenger[] | []; allowedEvents: Extract[] | []; }) { - return new RestrictedControllerMessenger({ + return new RestrictedControllerMessenger< + N, + Action, + Event, + AllowedAction, + AllowedEvent + >({ controllerMessenger: this, name, allowedActions, diff --git a/src/apis/crypto-compare.test.ts b/src/apis/crypto-compare.test.ts index 72510884b6..1e297ed819 100644 --- a/src/apis/crypto-compare.test.ts +++ b/src/apis/crypto-compare.test.ts @@ -18,7 +18,9 @@ describe('CryptoCompare', () => { }); it('should return CAD conversion rate', async () => { - nock(cryptoCompareHost).get('/data/price?fsym=ETH&tsyms=CAD').reply(200, { CAD: 2000.42 }); + nock(cryptoCompareHost) + .get('/data/price?fsym=ETH&tsyms=CAD') + .reply(200, { CAD: 2000.42 }); const { conversionRate } = await fetchExchangeRate('CAD', 'ETH'); @@ -26,7 +28,9 @@ describe('CryptoCompare', () => { }); it('should return conversion date', async () => { - nock(cryptoCompareHost).get('/data/price?fsym=ETH&tsyms=CAD').reply(200, { CAD: 2000.42 }); + nock(cryptoCompareHost) + .get('/data/price?fsym=ETH&tsyms=CAD') + .reply(200, { CAD: 2000.42 }); const before = Date.now() / 1000; const { conversionDate } = await fetchExchangeRate('CAD', 'ETH'); @@ -37,7 +41,9 @@ describe('CryptoCompare', () => { }); it('should return CAD conversion rate given lower-cased currency', async () => { - nock(cryptoCompareHost).get('/data/price?fsym=ETH&tsyms=CAD').reply(200, { CAD: 2000.42 }); + nock(cryptoCompareHost) + .get('/data/price?fsym=ETH&tsyms=CAD') + .reply(200, { CAD: 2000.42 }); const { conversionRate } = await fetchExchangeRate('cad', 'ETH'); @@ -45,7 +51,9 @@ describe('CryptoCompare', () => { }); it('should return CAD conversion rate given lower-cased native currency', async () => { - nock(cryptoCompareHost).get('/data/price?fsym=ETH&tsyms=CAD').reply(200, { CAD: 2000.42 }); + nock(cryptoCompareHost) + .get('/data/price?fsym=ETH&tsyms=CAD') + .reply(200, { CAD: 2000.42 }); const { conversionRate } = await fetchExchangeRate('CAD', 'eth'); @@ -53,7 +61,9 @@ describe('CryptoCompare', () => { }); it('should not return USD conversion rate when fetching just CAD conversion rate', async () => { - nock(cryptoCompareHost).get('/data/price?fsym=ETH&tsyms=CAD').reply(200, { CAD: 1000.42 }); + nock(cryptoCompareHost) + .get('/data/price?fsym=ETH&tsyms=CAD') + .reply(200, { CAD: 1000.42 }); const { usdConversionRate } = await fetchExchangeRate('CAD', 'ETH'); @@ -61,36 +71,58 @@ describe('CryptoCompare', () => { }); it('should return USD conversion rate for USD even when includeUSD is disabled', async () => { - nock(cryptoCompareHost).get('/data/price?fsym=ETH&tsyms=USD').reply(200, { USD: 1000.42 }); - - const { conversionRate, usdConversionRate } = await fetchExchangeRate('USD', 'ETH', false); + nock(cryptoCompareHost) + .get('/data/price?fsym=ETH&tsyms=USD') + .reply(200, { USD: 1000.42 }); + + const { conversionRate, usdConversionRate } = await fetchExchangeRate( + 'USD', + 'ETH', + false, + ); expect(conversionRate).toStrictEqual(1000.42); expect(usdConversionRate).toStrictEqual(1000.42); }); it('should return USD conversion rate for USD when includeUSD is enabled', async () => { - nock(cryptoCompareHost).get('/data/price?fsym=ETH&tsyms=USD').reply(200, { USD: 1000.42 }); - - const { conversionRate, usdConversionRate } = await fetchExchangeRate('USD', 'ETH', true); + nock(cryptoCompareHost) + .get('/data/price?fsym=ETH&tsyms=USD') + .reply(200, { USD: 1000.42 }); + + const { conversionRate, usdConversionRate } = await fetchExchangeRate( + 'USD', + 'ETH', + true, + ); expect(conversionRate).toStrictEqual(1000.42); expect(usdConversionRate).toStrictEqual(1000.42); }); it('should return CAD and USD conversion rate', async () => { - nock(cryptoCompareHost).get('/data/price?fsym=ETH&tsyms=CAD,USD').reply(200, { CAD: 2000.42, USD: 1000.42 }); - - const { conversionRate, usdConversionRate } = await fetchExchangeRate('CAD', 'ETH', true); + nock(cryptoCompareHost) + .get('/data/price?fsym=ETH&tsyms=CAD,USD') + .reply(200, { CAD: 2000.42, USD: 1000.42 }); + + const { conversionRate, usdConversionRate } = await fetchExchangeRate( + 'CAD', + 'ETH', + true, + ); expect(conversionRate).toStrictEqual(2000.42); expect(usdConversionRate).toStrictEqual(1000.42); }); it('should throw if fetch throws', async () => { - nock(cryptoCompareHost).get('/data/price?fsym=ETH&tsyms=CAD').replyWithError('Example network error'); + nock(cryptoCompareHost) + .get('/data/price?fsym=ETH&tsyms=CAD') + .replyWithError('Example network error'); - await expect(fetchExchangeRate('CAD', 'ETH')).rejects.toThrow('Example network error'); + await expect(fetchExchangeRate('CAD', 'ETH')).rejects.toThrow( + 'Example network error', + ); }); it('should throw if fetch returns unsuccessful response', async () => { @@ -102,13 +134,19 @@ describe('CryptoCompare', () => { }); it('should throw if conversion rate is invalid', async () => { - nock(cryptoCompareHost).get('/data/price?fsym=ETH&tsyms=CAD').reply(200, { CAD: 'invalid' }); + nock(cryptoCompareHost) + .get('/data/price?fsym=ETH&tsyms=CAD') + .reply(200, { CAD: 'invalid' }); - await expect(fetchExchangeRate('CAD', 'ETH')).rejects.toThrow('Invalid response for CAD: invalid'); + await expect(fetchExchangeRate('CAD', 'ETH')).rejects.toThrow( + 'Invalid response for CAD: invalid', + ); }); it('should throw if USD conversion rate is invalid', async () => { - nock(cryptoCompareHost).get('/data/price?fsym=ETH&tsyms=CAD,USD').reply(200, { CAD: 2000.47, USD: 'invalid' }); + nock(cryptoCompareHost) + .get('/data/price?fsym=ETH&tsyms=CAD,USD') + .reply(200, { CAD: 2000.47, USD: 'invalid' }); await expect(fetchExchangeRate('CAD', 'ETH', true)).rejects.toThrow( 'Invalid response for usdConversionRate: invalid', diff --git a/src/apis/crypto-compare.ts b/src/apis/crypto-compare.ts index 7ed644ffce..4e1f292b91 100644 --- a/src/apis/crypto-compare.ts +++ b/src/apis/crypto-compare.ts @@ -1,6 +1,10 @@ import { handleFetch } from '../util'; -function getPricingURL(currentCurrency: string, nativeCurrency: string, includeUSDRate?: boolean) { +function getPricingURL( + currentCurrency: string, + nativeCurrency: string, + includeUSDRate?: boolean, +) { return ( `https://min-api.cryptocompare.com/data/price?fsym=` + `${nativeCurrency.toUpperCase()}&tsyms=${currentCurrency.toUpperCase()}` + @@ -20,12 +24,22 @@ export async function fetchExchangeRate( currency: string, nativeCurrency: string, includeUSDRate?: boolean, -): Promise<{ conversionDate: number; conversionRate: number; usdConversionRate: number }> { - const json = await handleFetch(getPricingURL(currency, nativeCurrency, includeUSDRate)); +): Promise<{ + conversionDate: number; + conversionRate: number; + usdConversionRate: number; +}> { + const json = await handleFetch( + getPricingURL(currency, nativeCurrency, includeUSDRate), + ); const conversionRate = Number(json[currency.toUpperCase()]); const usdConversionRate = Number(json.USD); if (!Number.isFinite(conversionRate)) { - throw new Error(`Invalid response for ${currency.toUpperCase()}: ${json[currency.toUpperCase()]}`); + throw new Error( + `Invalid response for ${currency.toUpperCase()}: ${ + json[currency.toUpperCase()] + }`, + ); } if (includeUSDRate && !Number.isFinite(usdConversionRate)) { throw new Error(`Invalid response for usdConversionRate: ${json.USD}`); diff --git a/src/approval/ApprovalController.test.js b/src/approval/ApprovalController.test.js index efa1651310..e540129f04 100644 --- a/src/approval/ApprovalController.test.js +++ b/src/approval/ApprovalController.test.js @@ -5,17 +5,20 @@ const defaultConfig = { showApprovalRequest: () => undefined, }; -const getApprovalController = () => new ApprovalController({ ...defaultConfig }); +const getApprovalController = () => + new ApprovalController({ ...defaultConfig }); const STORE_KEY = 'pendingApprovals'; describe('ApprovalController: Input Validation', () => { describe('constructor', () => { it('throws on invalid input', () => { - expect(() => new ApprovalController({})).toThrow(getInvalidShowApprovalRequestError()); - expect(() => new ApprovalController({ showApprovalRequest: 'bar' })).toThrow( + expect(() => new ApprovalController({})).toThrow( getInvalidShowApprovalRequestError(), ); + expect( + () => new ApprovalController({ showApprovalRequest: 'bar' }), + ).toThrow(getInvalidShowApprovalRequestError()); }); }); @@ -23,20 +26,26 @@ describe('ApprovalController: Input Validation', () => { it('validates input', () => { const approvalController = getApprovalController(); - expect(() => approvalController.add({ id: null, origin: 'bar.baz' })).toThrow(getInvalidIdError()); - - expect(() => approvalController.add({ id: 'foo' })).toThrow(getInvalidOriginError()); - - expect(() => approvalController.add({ id: 'foo', origin: true })).toThrow(getInvalidOriginError()); + expect(() => + approvalController.add({ id: null, origin: 'bar.baz' }), + ).toThrow(getInvalidIdError()); - expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz', type: {} })).toThrow( - getInvalidTypeError(errorCodes.rpc.internal), + expect(() => approvalController.add({ id: 'foo' })).toThrow( + getInvalidOriginError(), ); - expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz', type: '' })).toThrow( - getInvalidTypeError(errorCodes.rpc.internal), + expect(() => approvalController.add({ id: 'foo', origin: true })).toThrow( + getInvalidOriginError(), ); + expect(() => + approvalController.add({ id: 'foo', origin: 'bar.baz', type: {} }), + ).toThrow(getInvalidTypeError(errorCodes.rpc.internal)); + + expect(() => + approvalController.add({ id: 'foo', origin: 'bar.baz', type: '' }), + ).toThrow(getInvalidTypeError(errorCodes.rpc.internal)); + expect(() => approvalController.add({ id: 'foo', @@ -66,10 +75,18 @@ describe('ApprovalController: Input Validation', () => { it('validates input', () => { const approvalController = getApprovalController(); - expect(() => approvalController.getApprovalCount()).toThrow(getApprovalCountParamsError()); - expect(() => approvalController.getApprovalCount({})).toThrow(getApprovalCountParamsError()); - expect(() => approvalController.getApprovalCount({ origin: null })).toThrow(getApprovalCountParamsError()); - expect(() => approvalController.getApprovalCount({ type: false })).toThrow(getApprovalCountParamsError()); + expect(() => approvalController.getApprovalCount()).toThrow( + getApprovalCountParamsError(), + ); + expect(() => approvalController.getApprovalCount({})).toThrow( + getApprovalCountParamsError(), + ); + expect(() => + approvalController.getApprovalCount({ origin: null }), + ).toThrow(getApprovalCountParamsError()); + expect(() => + approvalController.getApprovalCount({ type: false }), + ).toThrow(getApprovalCountParamsError()); }); }); @@ -77,11 +94,21 @@ describe('ApprovalController: Input Validation', () => { it('validates input', () => { const approvalController = getApprovalController(); - expect(() => approvalController.has()).toThrow(getInvalidHasParamsError()); - expect(() => approvalController.has({})).toThrow(getInvalidHasParamsError()); - expect(() => approvalController.has({ id: true })).toThrow(getInvalidHasIdError()); - expect(() => approvalController.has({ origin: true })).toThrow(getInvalidHasOriginError()); - expect(() => approvalController.has({ type: true })).toThrow(getInvalidHasTypeError()); + expect(() => approvalController.has()).toThrow( + getInvalidHasParamsError(), + ); + expect(() => approvalController.has({})).toThrow( + getInvalidHasParamsError(), + ); + expect(() => approvalController.has({ id: true })).toThrow( + getInvalidHasIdError(), + ); + expect(() => approvalController.has({ origin: true })).toThrow( + getInvalidHasOriginError(), + ); + expect(() => approvalController.has({ type: true })).toThrow( + getInvalidHasTypeError(), + ); }); }); @@ -114,12 +141,14 @@ describe('ApprovalController: Input Validation', () => { approvalController._delete('fizz'); expect( - !approvalController.has({ id: 'fizz' }) && !approvalController.has({ origin: 'bar.baz', type: 'type2' }), + !approvalController.has({ id: 'fizz' }) && + !approvalController.has({ origin: 'bar.baz', type: 'type2' }), ).toStrictEqual(true); - expect(approvalController.has({ id: 'foo' }) && approvalController.has({ origin: 'bar.baz' })).toStrictEqual( - true, - ); + expect( + approvalController.has({ id: 'foo' }) && + approvalController.has({ origin: 'bar.baz' }), + ).toStrictEqual(true); }); }); @@ -154,11 +183,17 @@ function getInvalidHasTypeError() { } function getInvalidOriginError() { - return getError('Must specify non-empty string origin.', errorCodes.rpc.internal); + return getError( + 'Must specify non-empty string origin.', + errorCodes.rpc.internal, + ); } function getInvalidRequestDataError() { - return getError('Request data must be a plain object if specified.', errorCodes.rpc.internal); + return getError( + 'Request data must be a plain object if specified.', + errorCodes.rpc.internal, + ); } function getInvalidTypeError(code) { diff --git a/src/approval/ApprovalController.test.ts b/src/approval/ApprovalController.test.ts index a9c3466c93..cd57eeb0e9 100644 --- a/src/approval/ApprovalController.test.ts +++ b/src/approval/ApprovalController.test.ts @@ -22,17 +22,27 @@ describe('approval controller', () => { }); it('adds correctly specified entry', () => { - expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE })).not.toThrow(); + expect(() => + approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }), + ).not.toThrow(); expect(approvalController.has({ id: 'foo' })).toStrictEqual(true); - expect(approvalController.has({ origin: 'bar.baz', type: TYPE })).toStrictEqual(true); + expect( + approvalController.has({ origin: 'bar.baz', type: TYPE }), + ).toStrictEqual(true); expect(approvalController.state[STORE_KEY]).toStrictEqual({ foo: { id: 'foo', origin: 'bar.baz', time: 1, type: TYPE }, }); }); it('adds id if non provided', () => { - expect(() => approvalController.add({ id: undefined, origin: 'bar.baz', type: TYPE })).not.toThrow(); + expect(() => + approvalController.add({ + id: undefined, + origin: 'bar.baz', + type: TYPE, + }), + ).not.toThrow(); const id = Object.keys(approvalController.state[STORE_KEY])[0]; expect(id && typeof id === 'string').toBeTruthy(); @@ -51,16 +61,25 @@ describe('approval controller', () => { expect(approvalController.has({ id: 'foo' })).toStrictEqual(true); expect(approvalController.has({ origin: 'bar.baz' })).toStrictEqual(true); expect(approvalController.has({ type: 'myType' })).toStrictEqual(true); - expect(approvalController.state[STORE_KEY].foo.requestData).toStrictEqual({ foo: 'bar' }); + expect( + approvalController.state[STORE_KEY].foo.requestData, + ).toStrictEqual({ foo: 'bar' }); }); it('adds multiple entries for same origin with different types and ids', () => { const ORIGIN = 'bar.baz'; - expect(() => approvalController.add({ id: 'foo1', origin: ORIGIN, type: 'myType1' })).not.toThrow(); - expect(() => approvalController.add({ id: 'foo2', origin: ORIGIN, type: 'myType2' })).not.toThrow(); + expect(() => + approvalController.add({ id: 'foo1', origin: ORIGIN, type: 'myType1' }), + ).not.toThrow(); + expect(() => + approvalController.add({ id: 'foo2', origin: ORIGIN, type: 'myType2' }), + ).not.toThrow(); - expect(approvalController.has({ id: 'foo1' }) && approvalController.has({ id: 'foo2' })).toStrictEqual(true); + expect( + approvalController.has({ id: 'foo1' }) && + approvalController.has({ id: 'foo2' }), + ).toStrictEqual(true); expect( approvalController.has({ origin: ORIGIN }) && approvalController.has({ origin: ORIGIN, type: 'myType1' }) && @@ -69,19 +88,31 @@ describe('approval controller', () => { }); it('throws on id collision', () => { - expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE })).not.toThrow(); + expect(() => + approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }), + ).not.toThrow(); - expect(() => approvalController.add({ id: 'foo', origin: 'fizz.buzz', type: TYPE })).toThrow( - getIdCollisionError('foo'), - ); + expect(() => + approvalController.add({ id: 'foo', origin: 'fizz.buzz', type: TYPE }), + ).toThrow(getIdCollisionError('foo')); }); it('throws on origin and type collision', () => { - expect(() => approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' })).not.toThrow(); + expect(() => + approvalController.add({ + id: 'foo', + origin: 'bar.baz', + type: 'myType', + }), + ).not.toThrow(); - expect(() => approvalController.add({ id: 'foo1', origin: 'bar.baz', type: 'myType' })).toThrow( - getOriginTypeCollisionError('bar.baz', 'myType'), - ); + expect(() => + approvalController.add({ + id: 'foo1', + origin: 'bar.baz', + type: 'myType', + }), + ).toThrow(getOriginTypeCollisionError('bar.baz', 'myType')); }); }); @@ -109,7 +140,12 @@ describe('approval controller', () => { it('gets entry', () => { const approvalController = new ApprovalController({ ...defaultConfig }); approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); - expect(approvalController.get('foo')).toStrictEqual({ id: 'foo', origin: 'bar.baz', type: 'myType', time: 1 }); + expect(approvalController.get('foo')).toStrictEqual({ + id: 'foo', + origin: 'bar.baz', + type: 'myType', + time: 1, + }); }); }); @@ -119,7 +155,8 @@ describe('approval controller', () => { beforeEach(() => { approvalController = new ApprovalController({ ...defaultConfig }); - addWithCatch = (args: any) => approvalController.add(args).catch(() => undefined); + addWithCatch = (args: any) => + approvalController.add(args).catch(() => undefined); }); it('gets the count when specifying origin and type', () => { @@ -127,17 +164,53 @@ describe('approval controller', () => { addWithCatch({ id: '2', origin: 'origin1', type: 'type1' }); addWithCatch({ id: '3', origin: 'origin2', type: 'type1' }); - expect(approvalController.getApprovalCount({ origin: 'origin1', type: TYPE })).toStrictEqual(1); - expect(approvalController.getApprovalCount({ origin: 'origin1', type: 'type1' })).toStrictEqual(1); - expect(approvalController.getApprovalCount({ origin: 'origin1', type: 'type2' })).toStrictEqual(0); + expect( + approvalController.getApprovalCount({ origin: 'origin1', type: TYPE }), + ).toStrictEqual(1); + expect( + approvalController.getApprovalCount({ + origin: 'origin1', + type: 'type1', + }), + ).toStrictEqual(1); + expect( + approvalController.getApprovalCount({ + origin: 'origin1', + type: 'type2', + }), + ).toStrictEqual(0); - expect(approvalController.getApprovalCount({ origin: 'origin2', type: TYPE })).toStrictEqual(0); - expect(approvalController.getApprovalCount({ origin: 'origin2', type: 'type1' })).toStrictEqual(1); - expect(approvalController.getApprovalCount({ origin: 'origin2', type: 'type2' })).toStrictEqual(0); + expect( + approvalController.getApprovalCount({ origin: 'origin2', type: TYPE }), + ).toStrictEqual(0); + expect( + approvalController.getApprovalCount({ + origin: 'origin2', + type: 'type1', + }), + ).toStrictEqual(1); + expect( + approvalController.getApprovalCount({ + origin: 'origin2', + type: 'type2', + }), + ).toStrictEqual(0); - expect(approvalController.getApprovalCount({ origin: 'origin3', type: TYPE })).toStrictEqual(0); - expect(approvalController.getApprovalCount({ origin: 'origin3', type: 'type1' })).toStrictEqual(0); - expect(approvalController.getApprovalCount({ origin: 'origin3', type: 'type2' })).toStrictEqual(0); + expect( + approvalController.getApprovalCount({ origin: 'origin3', type: TYPE }), + ).toStrictEqual(0); + expect( + approvalController.getApprovalCount({ + origin: 'origin3', + type: 'type1', + }), + ).toStrictEqual(0); + expect( + approvalController.getApprovalCount({ + origin: 'origin3', + type: 'type2', + }), + ).toStrictEqual(0); }); it('gets the count when specifying origin only', () => { @@ -145,11 +218,17 @@ describe('approval controller', () => { addWithCatch({ id: '2', origin: 'origin1', type: 'type1' }); addWithCatch({ id: '3', origin: 'origin2', type: 'type1' }); - expect(approvalController.getApprovalCount({ origin: 'origin1' })).toStrictEqual(2); + expect( + approvalController.getApprovalCount({ origin: 'origin1' }), + ).toStrictEqual(2); - expect(approvalController.getApprovalCount({ origin: 'origin2' })).toStrictEqual(1); + expect( + approvalController.getApprovalCount({ origin: 'origin2' }), + ).toStrictEqual(1); - expect(approvalController.getApprovalCount({ origin: 'origin3' })).toStrictEqual(0); + expect( + approvalController.getApprovalCount({ origin: 'origin3' }), + ).toStrictEqual(0); }); it('gets the count when specifying type only', () => { @@ -157,11 +236,17 @@ describe('approval controller', () => { addWithCatch({ id: '3', origin: 'origin2', type: 'type1' }); addWithCatch({ id: '4', origin: 'origin2', type: 'type2' }); - expect(approvalController.getApprovalCount({ type: 'type1' })).toStrictEqual(2); + expect( + approvalController.getApprovalCount({ type: 'type1' }), + ).toStrictEqual(2); - expect(approvalController.getApprovalCount({ type: 'type2' })).toStrictEqual(1); + expect( + approvalController.getApprovalCount({ type: 'type2' }), + ).toStrictEqual(1); - expect(approvalController.getApprovalCount({ type: 'type3' })).toStrictEqual(0); + expect( + approvalController.getApprovalCount({ type: 'type3' }), + ).toStrictEqual(0); }); }); @@ -170,7 +255,8 @@ describe('approval controller', () => { const approvalController = new ApprovalController({ ...defaultConfig }); expect(approvalController.getTotalApprovalCount()).toStrictEqual(0); - const addWithCatch = (args: any) => approvalController.add(args).catch(() => undefined); + const addWithCatch = (args: any) => + approvalController.add(args).catch(() => undefined); addWithCatch({ id: '1', origin: 'origin1', type: 'type0' }); expect(approvalController.getTotalApprovalCount()).toStrictEqual(1); @@ -211,7 +297,9 @@ describe('approval controller', () => { it('returns true for existing entry by origin and type', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); - expect(approvalController.has({ origin: 'bar.baz', type: 'myType' })).toStrictEqual(true); + expect( + approvalController.has({ origin: 'bar.baz', type: 'myType' }), + ).toStrictEqual(true); }); it('returns true for existing type', () => { @@ -229,19 +317,25 @@ describe('approval controller', () => { it('returns false for non-existing entry by origin', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); - expect(approvalController.has({ origin: 'fizz.buzz' })).toStrictEqual(false); + expect(approvalController.has({ origin: 'fizz.buzz' })).toStrictEqual( + false, + ); }); it('returns false for non-existing entry by existing origin and non-existing type', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); - expect(approvalController.has({ origin: 'bar.baz', type: 'myType' })).toStrictEqual(false); + expect( + approvalController.has({ origin: 'bar.baz', type: 'myType' }), + ).toStrictEqual(false); }); it('returns false for non-existing entry by non-existing origin and existing type', () => { approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); - expect(approvalController.has({ origin: 'fizz.buzz', type: 'myType' })).toStrictEqual(false); + expect( + approvalController.has({ origin: 'fizz.buzz', type: 'myType' }), + ).toStrictEqual(false); }); it('returns false for non-existing entry by type', () => { @@ -266,7 +360,11 @@ describe('approval controller', () => { it('resolves approval promise', async () => { numDeletions = 1; - const approvalPromise = approvalController.add({ id: 'foo', origin: 'bar.baz', type: 'myType' }); + const approvalPromise = approvalController.add({ + id: 'foo', + origin: 'bar.baz', + type: 'myType', + }); approvalController.resolve('foo', 'success'); const result = await approvalPromise; @@ -277,8 +375,16 @@ describe('approval controller', () => { it('resolves multiple approval promises out of order', async () => { numDeletions = 2; - const approvalPromise1 = approvalController.add({ id: 'foo1', origin: 'bar.baz', type: 'myType1' }); - const approvalPromise2 = approvalController.add({ id: 'foo2', origin: 'bar.baz', type: 'myType2' }); + const approvalPromise1 = approvalController.add({ + id: 'foo1', + origin: 'bar.baz', + type: 'myType1', + }); + const approvalPromise2 = approvalController.add({ + id: 'foo2', + origin: 'bar.baz', + type: 'myType2', + }); approvalController.resolve('foo2', 'success2'); @@ -293,7 +399,9 @@ describe('approval controller', () => { }); it('throws on unknown id', () => { - expect(() => approvalController.resolve('foo')).toThrow(getIdNotFoundError('foo')); + expect(() => approvalController.resolve('foo')).toThrow( + getIdNotFoundError('foo'), + ); expect(deleteSpy.callCount).toStrictEqual(numDeletions); }); }); @@ -312,7 +420,11 @@ describe('approval controller', () => { it('rejects approval promise', async () => { numDeletions = 1; - const approvalPromise = approvalController.add({ id: 'foo', origin: 'bar.baz', type: TYPE }); + const approvalPromise = approvalController.add({ + id: 'foo', + origin: 'bar.baz', + type: TYPE, + }); approvalController.reject('foo', new Error('failure')); await expect(approvalPromise).rejects.toThrow('failure'); expect(deleteSpy.callCount).toStrictEqual(numDeletions); @@ -321,8 +433,16 @@ describe('approval controller', () => { it('rejects multiple approval promises out of order', async () => { numDeletions = 2; - const rejectionPromise1 = approvalController.add({ id: 'foo1', origin: 'bar.baz', type: TYPE }); - const rejectionPromise2 = approvalController.add({ id: 'foo2', origin: 'bar.baz', type: 'myType2' }); + const rejectionPromise1 = approvalController.add({ + id: 'foo1', + origin: 'bar.baz', + type: TYPE, + }); + const rejectionPromise2 = approvalController.add({ + id: 'foo2', + origin: 'bar.baz', + type: 'myType2', + }); approvalController.reject('foo2', new Error('failure2')); approvalController.reject('foo1', new Error('failure1')); @@ -332,7 +452,9 @@ describe('approval controller', () => { }); it('throws on unknown id', () => { - expect(() => approvalController.reject('foo', new Error('bar'))).toThrow(getIdNotFoundError('foo')); + expect(() => approvalController.reject('foo', new Error('bar'))).toThrow( + getIdNotFoundError('foo'), + ); expect(deleteSpy.callCount).toStrictEqual(numDeletions); }); }); @@ -341,10 +463,26 @@ describe('approval controller', () => { it('resolves and rejects multiple approval promises out of order', async () => { const approvalController = new ApprovalController({ ...defaultConfig }); - const promise1 = approvalController.add({ id: 'foo1', origin: 'bar.baz', type: TYPE }); - const promise2 = approvalController.add({ id: 'foo2', origin: 'bar.baz', type: 'myType2' }); - const promise3 = approvalController.add({ id: 'foo3', origin: 'fizz.buzz', type: TYPE }); - const promise4 = approvalController.add({ id: 'foo4', origin: 'bar.baz', type: 'myType4' }); + const promise1 = approvalController.add({ + id: 'foo1', + origin: 'bar.baz', + type: TYPE, + }); + const promise2 = approvalController.add({ + id: 'foo2', + origin: 'bar.baz', + type: 'myType2', + }); + const promise3 = approvalController.add({ + id: 'foo3', + origin: 'fizz.buzz', + type: TYPE, + }); + const promise4 = approvalController.add({ + id: 'foo4', + origin: 'bar.baz', + type: 'myType4', + }); approvalController.resolve('foo2', 'success2'); @@ -357,7 +495,9 @@ describe('approval controller', () => { approvalController.reject('foo3', new Error('failure3')); await expect(promise3).rejects.toThrow('failure3'); - expect(approvalController.has({ origin: 'fizz.buzz' })).toStrictEqual(false); + expect(approvalController.has({ origin: 'fizz.buzz' })).toStrictEqual( + false, + ); expect(approvalController.has({ origin: 'bar.baz' })).toStrictEqual(true); approvalController.resolve('foo1', 'success1'); @@ -365,7 +505,9 @@ describe('approval controller', () => { result = await promise1; expect(result).toStrictEqual('success1'); - expect(approvalController.has({ origin: 'bar.baz' })).toStrictEqual(false); + expect(approvalController.has({ origin: 'bar.baz' })).toStrictEqual( + false, + ); }); }); @@ -383,8 +525,12 @@ describe('approval controller', () => { it('deletes existing entries', async () => { const rejectSpy = sinon.spy(approvalController, 'reject'); - approvalController.add({ id: 'foo2', origin: 'bar.baz', type: 'myType' }).catch((_error) => undefined); - approvalController.add({ id: 'foo3', origin: 'fizz.buzz', type: 'myType' }).catch((_error) => undefined); + approvalController + .add({ id: 'foo2', origin: 'bar.baz', type: 'myType' }) + .catch((_error) => undefined); + approvalController + .add({ id: 'foo3', origin: 'fizz.buzz', type: 'myType' }) + .catch((_error) => undefined); approvalController.clear(); @@ -397,7 +543,10 @@ describe('approval controller', () => { // helpers function getIdCollisionError(id: string) { - return getError(`Approval with id '${id}' already exists.`, errorCodes.rpc.internal); + return getError( + `Approval with id '${id}' already exists.`, + errorCodes.rpc.internal, + ); } function getOriginTypeCollisionError(origin: string, type = TYPE) { diff --git a/src/approval/ApprovalController.ts b/src/approval/ApprovalController.ts index fd25423648..22b724bcbb 100644 --- a/src/approval/ApprovalController.ts +++ b/src/approval/ApprovalController.ts @@ -54,7 +54,10 @@ export interface ApprovalState extends BaseState { const getAlreadyPendingMessage = (origin: string, type: string) => `Request of type '${type}' already pending for origin ${origin}. Please wait.`; -const defaultState: ApprovalState = { [APPROVALS_STORE_KEY]: {}, [APPROVAL_COUNT_STORE_KEY]: 0 }; +const defaultState: ApprovalState = { + [APPROVALS_STORE_KEY]: {}, + [APPROVAL_COUNT_STORE_KEY]: 0, +}; /** * Controller for managing requests that require user approval. @@ -65,7 +68,10 @@ const defaultState: ApprovalState = { [APPROVALS_STORE_KEY]: {}, [APPROVAL_COUNT * Adding a request returns a promise that resolves or rejects when the request * is approved or denied, respectively. */ -export class ApprovalController extends BaseController { +export class ApprovalController extends BaseController< + ApprovalConfig, + ApprovalState +> { private _approvals: Map; private _origins: Map>; @@ -113,7 +119,12 @@ export class ApprovalController extends BaseController { - const promise = this._add(opts.origin, opts.type, opts.id, opts.requestData); + const promise = this._add( + opts.origin, + opts.type, + opts.id, + opts.requestData, + ); this._showApprovalRequest(); return promise; } @@ -134,7 +145,12 @@ export class ApprovalController extends BaseController { + add(opts: { + id?: string; + origin: string; + type: string; + requestData?: RequestData; + }): Promise { return this._add(opts.origin, opts.type, opts.id, opts.requestData); } @@ -273,7 +289,9 @@ export class ApprovalController extends BaseController { + private _add( + origin: string, + type: string, + id: string = nanoid(), + requestData?: RequestData, + ): Promise { this._validateAddParams(id, origin, type, requestData); if (this._origins.get(origin)?.has(type)) { - throw ethErrors.rpc.resourceUnavailable(getAlreadyPendingMessage(origin, type)); + throw ethErrors.rpc.resourceUnavailable( + getAlreadyPendingMessage(origin, type), + ); } // add pending approval @@ -314,7 +339,12 @@ export class ApprovalController extends BaseController { it('should set default state', () => { @@ -22,7 +24,9 @@ describe('AccountTrackerController', () => { onPreferencesStateChange: stub(), getIdentities: () => ({}), }); - expect(() => console.log(controller.provider)).toThrow('Property only used for setting'); + expect(() => console.log(controller.provider)).toThrow( + 'Property only used for setting', + ); }); it('should get real balance', async () => { @@ -57,7 +61,9 @@ describe('AccountTrackerController', () => { }, ); controller.refresh(); - expect(controller.state.accounts).toStrictEqual({ baz: { balance: '0x0' } }); + expect(controller.state.accounts).toStrictEqual({ + baz: { balance: '0x0' }, + }); }); it('should subscribe to new sibling preference controllers', async () => { @@ -81,7 +87,8 @@ describe('AccountTrackerController', () => { const poll = spy(AccountTrackerController.prototype, 'poll'); const controller = new AccountTrackerController( { - onPreferencesStateChange: (listener) => preferences.subscribe(listener), + onPreferencesStateChange: (listener) => + preferences.subscribe(listener), getIdentities: () => ({}), }, { provider, interval: 100 }, diff --git a/src/assets/AccountTrackerController.ts b/src/assets/AccountTrackerController.ts index e342035144..f46ed2ee69 100644 --- a/src/assets/AccountTrackerController.ts +++ b/src/assets/AccountTrackerController.ts @@ -41,7 +41,10 @@ export interface AccountTrackerState extends BaseState { /** * Controller that tracks information for all accounts in the current keychain */ -export class AccountTrackerController extends BaseController { +export class AccountTrackerController extends BaseController< + AccountTrackerConfig, + AccountTrackerState +> { private ethQuery: any; private mutex = new Mutex(); @@ -52,8 +55,12 @@ export class AccountTrackerController extends BaseController existing.indexOf(address) === -1); - const oldAddresses = existing.filter((address) => addresses.indexOf(address) === -1); + const newAddresses = addresses.filter( + (address) => existing.indexOf(address) === -1, + ); + const oldAddresses = existing.filter( + (address) => addresses.indexOf(address) === -1, + ); newAddresses.forEach((address) => { accounts[address] = { balance: '0x0' }; }); @@ -84,7 +91,9 @@ export class AccountTrackerController extends BaseController void) => void; + onPreferencesStateChange: ( + listener: (preferencesState: PreferencesState) => void, + ) => void; getIdentities: () => PreferencesState['identities']; }, config?: Partial, diff --git a/src/assets/AssetsContractController.test.ts b/src/assets/AssetsContractController.test.ts index 1e63ee3e36..b6ce1928df 100644 --- a/src/assets/AssetsContractController.test.ts +++ b/src/assets/AssetsContractController.test.ts @@ -1,7 +1,9 @@ import HttpProvider from 'ethjs-provider-http'; import { AssetsContractController } from './AssetsContractController'; -const MAINNET_PROVIDER = new HttpProvider('https://mainnet.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035'); +const MAINNET_PROVIDER = new HttpProvider( + 'https://mainnet.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035', +); const GODSADDRESS = '0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab'; const CKADDRESS = '0x06012c8cf97BEaD5deAe237070F9587f8E7A266d'; const SAI_ADDRESS = '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359'; @@ -20,21 +22,33 @@ describe('AssetsContractController', () => { }); it('should throw when provider property is accessed', () => { - expect(() => console.log(assetsContract.provider)).toThrow('Property only used for setting'); + expect(() => console.log(assetsContract.provider)).toThrow( + 'Property only used for setting', + ); }); it('should determine if contract supports interface correctly', async () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); - const CKSupportsEnumerable = await assetsContract.contractSupportsEnumerableInterface(CKADDRESS); - const GODSSupportsEnumerable = await assetsContract.contractSupportsEnumerableInterface(GODSADDRESS); + const CKSupportsEnumerable = await assetsContract.contractSupportsEnumerableInterface( + CKADDRESS, + ); + const GODSSupportsEnumerable = await assetsContract.contractSupportsEnumerableInterface( + GODSADDRESS, + ); expect(CKSupportsEnumerable).toBe(false); expect(GODSSupportsEnumerable).toBe(true); }); it('should get balance of contract correctly', async () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); - const CKBalance = await assetsContract.getBalanceOf(CKADDRESS, '0xb1690c08e213a35ed9bab7b318de14420fb57d8c'); - const CKNoBalance = await assetsContract.getBalanceOf(CKADDRESS, '0xb1690c08e213a35ed9bab7b318de14420fb57d81'); + const CKBalance = await assetsContract.getBalanceOf( + CKADDRESS, + '0xb1690c08e213a35ed9bab7b318de14420fb57d8c', + ); + const CKNoBalance = await assetsContract.getBalanceOf( + CKADDRESS, + '0xb1690c08e213a35ed9bab7b318de14420fb57d81', + ); expect(CKBalance.toNumber()).not.toStrictEqual(0); expect(CKNoBalance.toNumber()).toStrictEqual(0); }); @@ -57,7 +71,10 @@ describe('AssetsContractController', () => { it('should return empty string as URI when address given is not an NFT', async () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); - const tokenId = await assetsContract.getCollectibleTokenURI('0x0000000000000000000000000000000000000000', 0); + const tokenId = await assetsContract.getCollectibleTokenURI( + '0x0000000000000000000000000000000000000000', + 0, + ); expect(tokenId).toStrictEqual(''); }); @@ -86,7 +103,9 @@ describe('AssetsContractController', () => { it('should get balances in a single call', async () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); - const balances = await assetsContract.getBalancesInSingleCall(SAI_ADDRESS, [SAI_ADDRESS]); + const balances = await assetsContract.getBalancesInSingleCall(SAI_ADDRESS, [ + SAI_ADDRESS, + ]); expect(balances[SAI_ADDRESS]).not.toStrictEqual(0); }); }); diff --git a/src/assets/AssetsContractController.ts b/src/assets/AssetsContractController.ts index 6414de09ac..2eaeed8e56 100644 --- a/src/assets/AssetsContractController.ts +++ b/src/assets/AssetsContractController.ts @@ -7,7 +7,8 @@ import BaseController, { BaseConfig, BaseState } from '../BaseController'; const ERC721METADATA_INTERFACE_ID = '0x5b5e139f'; const ERC721ENUMERABLE_INTERFACE_ID = '0x780e9d63'; -const SINGLE_CALL_BALANCES_ADDRESS = '0xb1f8e55c7f64d203c1400b9d8555d050f94adf39'; +const SINGLE_CALL_BALANCES_ADDRESS = + '0xb1f8e55c7f64d203c1400b9d8555d050f94adf39'; /** * @type AssetsContractConfig @@ -34,7 +35,10 @@ export interface BalanceMap { /** * Controller that interacts with contracts on mainnet through web3 */ -export class AssetsContractController extends BaseController { +export class AssetsContractController extends BaseController< + AssetsContractConfig, + BaseState +> { private web3: any; /** @@ -45,17 +49,23 @@ export class AssetsContractController extends BaseController { + private async contractSupportsInterface( + address: string, + interfaceId: string, + ): Promise { const contract = this.web3.eth.contract(abiERC721).at(address); return new Promise((resolve, reject) => { - contract.supportsInterface(interfaceId, (error: Error, result: boolean) => { - /* istanbul ignore if */ - if (error) { - reject(error); - return; - } - resolve(result); - }); + contract.supportsInterface( + interfaceId, + (error: Error, result: boolean) => { + /* istanbul ignore if */ + if (error) { + reject(error); + return; + } + resolve(result); + }, + ); }); } @@ -70,7 +80,10 @@ export class AssetsContractController extends BaseController, state?: Partial) { + constructor( + config?: Partial, + state?: Partial, + ) { super(config, state); this.defaultConfig = { provider: undefined, @@ -110,7 +123,10 @@ export class AssetsContractController extends BaseController { - return this.contractSupportsInterface(address, ERC721ENUMERABLE_INTERFACE_ID); + return this.contractSupportsInterface( + address, + ERC721ENUMERABLE_INTERFACE_ID, + ); } /** @@ -142,17 +158,25 @@ export class AssetsContractController extends BaseController { + getCollectibleTokenId( + address: string, + selectedAddress: string, + index: number, + ): Promise { const contract = this.web3.eth.contract(abiERC721).at(address); return new Promise((resolve, reject) => { - contract.tokenOfOwnerByIndex(selectedAddress, index, (error: Error, result: BN) => { - /* istanbul ignore if */ - if (error) { - reject(error); - return; - } - resolve(result.toNumber()); - }); + contract.tokenOfOwnerByIndex( + selectedAddress, + index, + (error: Error, result: BN) => { + /* istanbul ignore if */ + if (error) { + reject(error); + return; + } + resolve(result.toNumber()); + }, + ); }); } @@ -163,8 +187,13 @@ export class AssetsContractController extends BaseController { - const supportsMetadata = await this.contractSupportsMetadataInterface(address); + async getCollectibleTokenURI( + address: string, + tokenId: number, + ): Promise { + const supportsMetadata = await this.contractSupportsMetadataInterface( + address, + ); if (!supportsMetadata) { return ''; } @@ -267,28 +296,37 @@ export class AssetsContractController extends BaseController((resolve, reject) => { - contract.balances([selectedAddress], tokensToDetect, (error: Error, result: BN[]) => { - /* istanbul ignore if */ - if (error) { - reject(error); - return; - } - const nonZeroBalances: BalanceMap = {}; - /* istanbul ignore else */ - if (result.length > 0) { - tokensToDetect.forEach((tokenAddress, index) => { - const balance: BN = result[index]; - /* istanbul ignore else */ - if (!balance.isZero()) { - nonZeroBalances[tokenAddress] = balance; - } - }); - } - resolve(nonZeroBalances); - }); + contract.balances( + [selectedAddress], + tokensToDetect, + (error: Error, result: BN[]) => { + /* istanbul ignore if */ + if (error) { + reject(error); + return; + } + const nonZeroBalances: BalanceMap = {}; + /* istanbul ignore else */ + if (result.length > 0) { + tokensToDetect.forEach((tokenAddress, index) => { + const balance: BN = result[index]; + /* istanbul ignore else */ + if (!balance.isZero()) { + nonZeroBalances[tokenAddress] = balance; + } + }); + } + resolve(nonZeroBalances); + }, + ); }); } } diff --git a/src/assets/AssetsController.test.ts b/src/assets/AssetsController.test.ts index 301bcbe1d9..cbe0c8977f 100644 --- a/src/assets/AssetsController.test.ts +++ b/src/assets/AssetsController.test.ts @@ -2,12 +2,17 @@ import { createSandbox } from 'sinon'; import nock from 'nock'; import HttpProvider from 'ethjs-provider-http'; import PreferencesController from '../user/PreferencesController'; -import { NetworkController, NetworksChainId } from '../network/NetworkController'; +import { + NetworkController, + NetworksChainId, +} from '../network/NetworkController'; import { AssetsContractController } from './AssetsContractController'; import AssetsController from './AssetsController'; const KUDOSADDRESS = '0x2aea4add166ebf38b63d09a75de1a7b94aa24163'; -const MAINNET_PROVIDER = new HttpProvider('https://mainnet.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035'); +const MAINNET_PROVIDER = new HttpProvider( + 'https://mainnet.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035', +); const OPEN_SEA_HOST = 'https://api.opensea.io'; const OPEN_SEA_PATH = '/api/v1'; @@ -27,7 +32,9 @@ describe('AssetsController', () => { onNetworkStateChange: (listener) => network.subscribe(listener), getAssetName: assetsContract.getAssetName.bind(assetsContract), getAssetSymbol: assetsContract.getAssetSymbol.bind(assetsContract), - getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind(assetsContract), + getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind( + assetsContract, + ), }); nock(OPEN_SEA_HOST) @@ -53,17 +60,25 @@ describe('AssetsController', () => { image_original_url: 'url', name: 'Name', }) - .get(`${OPEN_SEA_PATH}/asset/0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163/1203`) + .get( + `${OPEN_SEA_PATH}/asset/0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163/1203`, + ) .reply(200, { description: 'Kudos Description', image_original_url: 'Kudos url', name: 'Kudos Name', }) - .get(`${OPEN_SEA_PATH}/asset/0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab/798958393`) + .get( + `${OPEN_SEA_PATH}/asset/0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab/798958393`, + ) .replyWithError(new TypeError('Failed to fetch')) - .get(`${OPEN_SEA_PATH}/asset_contract/0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab`) + .get( + `${OPEN_SEA_PATH}/asset_contract/0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab`, + ) .replyWithError(new TypeError('Failed to fetch')) - .get(`${OPEN_SEA_PATH}/asset_contract/0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163`) + .get( + `${OPEN_SEA_PATH}/asset_contract/0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163`, + ) .reply(200, { description: 'Kudos Description', image_url: 'Kudos url', @@ -72,10 +87,12 @@ describe('AssetsController', () => { total_supply: 10, }); - nock('https://ipfs.gitcoin.co:443').get('/api/v0/cat/QmPmt6EAaioN78ECnW5oCL8v2YvVSpoBjLCjrXhhsAvoov').reply(200, { - image: 'Kudos Image', - name: 'Kudos Name', - }); + nock('https://ipfs.gitcoin.co:443') + .get('/api/v0/cat/QmPmt6EAaioN78ECnW5oCL8v2YvVSpoBjLCjrXhhsAvoov') + .reply(200, { + image: 'Kudos Image', + name: 'Kudos Name', + }); }); afterEach(() => { @@ -169,11 +186,26 @@ describe('AssetsController', () => { it('should add token by provider type', async () => { const firstNetworkType = 'rinkeby'; const secondNetworkType = 'ropsten'; - network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); + network.update({ + provider: { + type: firstNetworkType, + chainId: NetworksChainId[firstNetworkType], + }, + }); await assetsController.addToken('foo', 'bar', 2); - network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType] } }); + network.update({ + provider: { + type: secondNetworkType, + chainId: NetworksChainId[secondNetworkType], + }, + }); expect(assetsController.state.tokens).toHaveLength(0); - network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); + network.update({ + provider: { + type: firstNetworkType, + chainId: NetworksChainId[firstNetworkType], + }, + }); expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xfoO', decimals: 2, @@ -209,13 +241,28 @@ describe('AssetsController', () => { it('should remove token by provider type', async () => { const firstNetworkType = 'rinkeby'; const secondNetworkType = 'ropsten'; - network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); + network.update({ + provider: { + type: firstNetworkType, + chainId: NetworksChainId[firstNetworkType], + }, + }); await assetsController.addToken('fou', 'baz', 2); - network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType] } }); + network.update({ + provider: { + type: secondNetworkType, + chainId: NetworksChainId[secondNetworkType], + }, + }); await assetsController.addToken('foo', 'bar', 2); assetsController.removeToken('0xfoO'); expect(assetsController.state.tokens).toHaveLength(0); - network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); + network.update({ + provider: { + type: firstNetworkType, + chainId: NetworksChainId[firstNetworkType], + }, + }); expect(assetsController.state.tokens[0]).toStrictEqual({ address: '0xFOu', decimals: 2, @@ -225,7 +272,11 @@ describe('AssetsController', () => { }); it('should add collectible and collectible contract', async () => { - await assetsController.addCollectible('foo', 1, { name: 'name', image: 'image', description: 'description' }); + await assetsController.addCollectible('foo', 1, { + name: 'name', + image: 'image', + description: 'description', + }); expect(assetsController.state.collectibles[0]).toStrictEqual({ address: '0xfoO', description: 'description', @@ -244,15 +295,31 @@ describe('AssetsController', () => { }); it('should not duplicate collectible nor collectible contract if already added', async () => { - await assetsController.addCollectible('foo', 1, { name: 'name', image: 'image', description: 'description' }); - await assetsController.addCollectible('foo', 1, { name: 'name', image: 'image', description: 'description' }); + await assetsController.addCollectible('foo', 1, { + name: 'name', + image: 'image', + description: 'description', + }); + await assetsController.addCollectible('foo', 1, { + name: 'name', + image: 'image', + description: 'description', + }); expect(assetsController.state.collectibles).toHaveLength(1); expect(assetsController.state.collectibleContracts).toHaveLength(1); }); it('should not add collectible contract if collectible contract already exists', async () => { - await assetsController.addCollectible('foo', 1, { name: 'name', image: 'image', description: 'description' }); - await assetsController.addCollectible('foo', 2, { name: 'name', image: 'image', description: 'description' }); + await assetsController.addCollectible('foo', 1, { + name: 'name', + image: 'image', + description: 'description', + }); + await assetsController.addCollectible('foo', 2, { + name: 'name', + image: 'image', + description: 'description', + }); expect(assetsController.state.collectibles).toHaveLength(2); expect(assetsController.state.collectibleContracts).toHaveLength(1); }); @@ -270,8 +337,12 @@ describe('AssetsController', () => { it('should add collectible and get collectible contract information from contract', async () => { assetsContract.configure({ provider: MAINNET_PROVIDER }); - sandbox.stub(assetsController, 'getCollectibleContractInformationFromApi' as any).returns(undefined); - sandbox.stub(assetsController, 'getCollectibleInformationFromApi' as any).returns(undefined); + sandbox + .stub(assetsController, 'getCollectibleContractInformationFromApi' as any) + .returns(undefined); + sandbox + .stub(assetsController, 'getCollectibleInformationFromApi' as any) + .returns(undefined); await assetsController.addCollectible(KUDOSADDRESS, 1203); expect(assetsController.state.collectibles[0]).toStrictEqual({ address: '0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163', @@ -316,11 +387,26 @@ describe('AssetsController', () => { sandbox .stub(assetsController, 'getCollectibleInformation' as any) .returns({ name: 'name', image: 'url', description: 'description' }); - network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); + network.update({ + provider: { + type: firstNetworkType, + chainId: NetworksChainId[firstNetworkType], + }, + }); await assetsController.addCollectible('foo', 1234); - network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType] } }); + network.update({ + provider: { + type: secondNetworkType, + chainId: NetworksChainId[secondNetworkType], + }, + }); expect(assetsController.state.collectibles).toHaveLength(0); - network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); + network.update({ + provider: { + type: firstNetworkType, + chainId: NetworksChainId[firstNetworkType], + }, + }); expect(assetsController.state.collectibles[0]).toStrictEqual({ address: '0xfoO', description: 'description', @@ -331,10 +417,20 @@ describe('AssetsController', () => { }); it('should not add collectibles with no contract information when auto detecting', async () => { - await assetsController.addCollectible('0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab', 123, undefined, true); + await assetsController.addCollectible( + '0x6EbeAf8e8E946F0716E6533A6f2cefc83f60e8Ab', + 123, + undefined, + true, + ); expect(assetsController.state.collectibles).toStrictEqual([]); expect(assetsController.state.collectibleContracts).toStrictEqual([]); - await assetsController.addCollectible('0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163', 1203, undefined, true); + await assetsController.addCollectible( + '0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163', + 1203, + undefined, + true, + ); expect(assetsController.state.collectibles).toStrictEqual([ { address: '0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163', @@ -357,15 +453,27 @@ describe('AssetsController', () => { }); it('should remove collectible and collectible contract', async () => { - await assetsController.addCollectible('foo', 1, { name: 'name', image: 'image', description: 'description' }); + await assetsController.addCollectible('foo', 1, { + name: 'name', + image: 'image', + description: 'description', + }); assetsController.removeCollectible('0xfoO', 1); expect(assetsController.state.collectibles).toHaveLength(0); expect(assetsController.state.collectibleContracts).toHaveLength(0); }); it('should not remove collectible contract if collectible still exists', async () => { - await assetsController.addCollectible('foo', 1, { name: 'name', image: 'image', description: 'description' }); - await assetsController.addCollectible('foo', 2, { name: 'name', image: 'image', description: 'description' }); + await assetsController.addCollectible('foo', 1, { + name: 'name', + image: 'image', + description: 'description', + }); + await assetsController.addCollectible('foo', 2, { + name: 'name', + image: 'image', + description: 'description', + }); assetsController.removeCollectible('0xfoO', 1); expect(assetsController.state.collectibles).toHaveLength(1); expect(assetsController.state.collectibleContracts).toHaveLength(1); @@ -399,14 +507,29 @@ describe('AssetsController', () => { .returns({ name: 'name', image: 'url', description: 'description' }); const firstNetworkType = 'rinkeby'; const secondNetworkType = 'ropsten'; - network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); + network.update({ + provider: { + type: firstNetworkType, + chainId: NetworksChainId[firstNetworkType], + }, + }); await assetsController.addCollectible('fou', 4321); - network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType] } }); + network.update({ + provider: { + type: secondNetworkType, + chainId: NetworksChainId[secondNetworkType], + }, + }); await assetsController.addCollectible('foo', 1234); assetsController.removeToken('0xfoO'); assetsController.removeCollectible('0xfoO', 1234); expect(assetsController.state.collectibles).toHaveLength(0); - network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); + network.update({ + provider: { + type: firstNetworkType, + chainId: NetworksChainId[firstNetworkType], + }, + }); expect(assetsController.state.collectibles[0]).toStrictEqual({ address: '0xFOu', description: 'description', @@ -420,9 +543,11 @@ describe('AssetsController', () => { const networkType = 'rinkeby'; const address = '0x123'; preferences.update({ selectedAddress: address }); - expect(preferences.state.selectedAddress).toEqual(address); - network.update({ provider: { type: networkType, chainId: NetworksChainId[networkType] } }); - expect(network.state.provider.type).toEqual(networkType); + expect(preferences.state.selectedAddress).toStrictEqual(address); + network.update({ + provider: { type: networkType, chainId: NetworksChainId[networkType] }, + }); + expect(network.state.provider.type).toStrictEqual(networkType); }); it('should add a valid suggested asset via watchAsset', async () => { @@ -434,7 +559,9 @@ describe('AssetsController', () => { }, 'ERC20', ); - expect(assetsController.state.suggestedAssets[0].asset.address).toBe('0xe9f786dfdd9ae4d57e830acb52296837765f0e5b'); + expect(assetsController.state.suggestedAssets[0].asset.address).toBe( + '0xe9f786dfdd9ae4d57e830acb52296837765f0e5b', + ); expect(assetsController.state.suggestedAssets[0].status).toBe('pending'); }); @@ -498,10 +625,14 @@ describe('AssetsController', () => { 'ERC20', ); const { suggestedAssets } = assetsController.state; - const index = suggestedAssets.findIndex(({ id }) => suggestedAssetMeta.id === id); + const index = suggestedAssets.findIndex( + ({ id }) => suggestedAssetMeta.id === id, + ); const newSuggestedAssetMeta = suggestedAssets[index]; suggestedAssetMeta.type = 'ERC721'; - assetsController.update({ suggestedAssets: [...suggestedAssets, newSuggestedAssetMeta] }); + assetsController.update({ + suggestedAssets: [...suggestedAssets, newSuggestedAssetMeta], + }); await assetsController.acceptWatchAsset(suggestedAssetMeta.id); await expect(result).rejects.toThrow('Asset of type ERC721 not supported'); }); @@ -521,8 +652,16 @@ describe('AssetsController', () => { }); it('should not add duplicate collectibles to the ignoredCollectibles list', async () => { - await assetsController.addCollectible('foo', 1, { name: 'name', image: 'image', description: 'description' }); - await assetsController.addCollectible('foo', 2, { name: 'name', image: 'image', description: 'description' }); + await assetsController.addCollectible('foo', 1, { + name: 'name', + image: 'image', + description: 'description', + }); + await assetsController.addCollectible('foo', 2, { + name: 'name', + image: 'image', + description: 'description', + }); expect(assetsController.state.collectibles).toHaveLength(2); expect(assetsController.state.ignoredCollectibles).toHaveLength(0); @@ -531,7 +670,11 @@ describe('AssetsController', () => { expect(assetsController.state.collectibles).toHaveLength(1); expect(assetsController.state.ignoredCollectibles).toHaveLength(1); - await assetsController.addCollectible('foo', 1, { name: 'name', image: 'image', description: 'description' }); + await assetsController.addCollectible('foo', 1, { + name: 'name', + image: 'image', + description: 'description', + }); expect(assetsController.state.collectibles).toHaveLength(2); expect(assetsController.state.ignoredCollectibles).toHaveLength(1); @@ -551,7 +694,11 @@ describe('AssetsController', () => { }); it('should be able to clear the ignoredCollectibles list', async () => { - await assetsController.addCollectible('0xfoO', 1, { name: 'name', image: 'image', description: 'description' }); + await assetsController.addCollectible('0xfoO', 1, { + name: 'name', + image: 'image', + description: 'description', + }); expect(assetsController.state.collectibles).toHaveLength(1); expect(assetsController.state.ignoredCollectibles).toHaveLength(0); diff --git a/src/assets/AssetsController.ts b/src/assets/AssetsController.ts index 834a122cb2..ff7e47a9f3 100644 --- a/src/assets/AssetsController.ts +++ b/src/assets/AssetsController.ts @@ -136,9 +136,15 @@ type SuggestedAssetMetaBase = { * @property asset - Asset suggested object */ export type SuggestedAssetMeta = - | (SuggestedAssetMetaBase & { status: SuggestedAssetStatus.failed; error: Error }) | (SuggestedAssetMetaBase & { - status: SuggestedAssetStatus.accepted | SuggestedAssetStatus.rejected | SuggestedAssetStatus.pending; + status: SuggestedAssetStatus.failed; + error: Error; + }) + | (SuggestedAssetMetaBase & { + status: + | SuggestedAssetStatus.accepted + | SuggestedAssetStatus.rejected + | SuggestedAssetStatus.pending; }); /** @@ -158,7 +164,9 @@ export type SuggestedAssetMeta = */ export interface AssetsState extends BaseState { allTokens: { [key: string]: { [key: string]: Token[] } }; - allCollectibleContracts: { [key: string]: { [key: string]: CollectibleContract[] } }; + allCollectibleContracts: { + [key: string]: { [key: string]: CollectibleContract[] }; + }; allCollectibles: { [key: string]: { [key: string]: Collectible[] } }; collectibleContracts: CollectibleContract[]; collectibles: Collectible[]; @@ -171,7 +179,10 @@ export interface AssetsState extends BaseState { /** * Controller that stores assets and exposes convenience methods */ -export class AssetsController extends BaseController { +export class AssetsController extends BaseController< + AssetsConfig, + AssetsState +> { private mutex = new Mutex(); private getCollectibleApi(contractAddress: string, tokenId: number) { @@ -182,13 +193,19 @@ export class AssetsController extends BaseController return `https://api.opensea.io/api/v1/asset_contract/${contractAddress}`; } - private failSuggestedAsset(suggestedAssetMeta: SuggestedAssetMeta, error: Error) { + private failSuggestedAsset( + suggestedAssetMeta: SuggestedAssetMeta, + error: Error, + ) { const failedSuggestedAssetMeta = { ...suggestedAssetMeta, status: SuggestedAssetStatus.failed, error, }; - this.hub.emit(`${suggestedAssetMeta.id}:finished`, failedSuggestedAssetMeta); + this.hub.emit( + `${suggestedAssetMeta.id}:finished`, + failedSuggestedAssetMeta, + ); } /** @@ -206,7 +223,9 @@ export class AssetsController extends BaseController let collectibleInformation: ApiCollectibleResponse; /* istanbul ignore if */ if (this.openSeaApiKey) { - collectibleInformation = await handleFetch(tokenURI, { headers: { 'X-API-KEY': this.openSeaApiKey } }); + collectibleInformation = await handleFetch(tokenURI, { + headers: { 'X-API-KEY': this.openSeaApiKey }, + }); } else { collectibleInformation = await handleFetch(tokenURI); } @@ -225,7 +244,10 @@ export class AssetsController extends BaseController contractAddress: string, tokenId: number, ): Promise { - const tokenURI = await this.getCollectibleTokenURI(contractAddress, tokenId); + const tokenURI = await this.getCollectibleTokenURI( + contractAddress, + tokenId, + ); const object = await handleFetch(tokenURI); const image = Object.prototype.hasOwnProperty.call(object, 'image') ? 'image' @@ -240,18 +262,27 @@ export class AssetsController extends BaseController * @param tokenId - The collectible identifier * @returns - Promise resolving to the current collectible name and image */ - private async getCollectibleInformation(contractAddress: string, tokenId: number): Promise { + private async getCollectibleInformation( + contractAddress: string, + tokenId: number, + ): Promise { let information; // First try with OpenSea information = await safelyExecute(async () => { - return await this.getCollectibleInformationFromApi(contractAddress, tokenId); + return await this.getCollectibleInformationFromApi( + contractAddress, + tokenId, + ); }); if (information) { return information; } // Then following ERC721 standard information = await safelyExecute(async () => { - return await this.getCollectibleInformationFromTokenURI(contractAddress, tokenId); + return await this.getCollectibleInformationFromTokenURI( + contractAddress, + tokenId, + ); }); /* istanbul ignore next */ if (information) { @@ -274,11 +305,19 @@ export class AssetsController extends BaseController let collectibleContractObject; /* istanbul ignore if */ if (this.openSeaApiKey) { - collectibleContractObject = await handleFetch(api, { headers: { 'X-API-KEY': this.openSeaApiKey } }); + collectibleContractObject = await handleFetch(api, { + headers: { 'X-API-KEY': this.openSeaApiKey }, + }); } else { collectibleContractObject = await handleFetch(api); } - const { name, symbol, image_url, description, total_supply } = collectibleContractObject; + const { + name, + symbol, + image_url, + description, + total_supply, + } = collectibleContractObject; return { name, symbol, image_url, description, total_supply }; } @@ -302,18 +341,24 @@ export class AssetsController extends BaseController * @param contractAddress - Hex address of the collectible contract * @returns - Promise resolving to the collectible contract name, image and description */ - private async getCollectibleContractInformation(contractAddress: string): Promise { + private async getCollectibleContractInformation( + contractAddress: string, + ): Promise { let information; // First try with OpenSea information = await safelyExecute(async () => { - return await this.getCollectibleContractInformationFromApi(contractAddress); + return await this.getCollectibleContractInformationFromApi( + contractAddress, + ); }); if (information) { return information; } // Then following ERC721 standard information = await safelyExecute(async () => { - return await this.getCollectibleContractInformationFromContract(contractAddress); + return await this.getCollectibleContractInformationFromContract( + contractAddress, + ); }); if (information) { return information; @@ -341,18 +386,35 @@ export class AssetsController extends BaseController const { allCollectibles, collectibles } = this.state; const { chainId, selectedAddress } = this.config; const existingEntry = collectibles.find( - (collectible) => collectible.address === address && collectible.tokenId === tokenId, + (collectible) => + collectible.address === address && collectible.tokenId === tokenId, ); if (existingEntry) { return collectibles; } - const { name, image, description } = opts || (await this.getCollectibleInformation(address, tokenId)); - const newEntry: Collectible = { address, tokenId, name, image, description }; + const { name, image, description } = + opts || (await this.getCollectibleInformation(address, tokenId)); + const newEntry: Collectible = { + address, + tokenId, + name, + image, + description, + }; const newCollectibles = [...collectibles, newEntry]; const addressCollectibles = allCollectibles[selectedAddress]; - const newAddressCollectibles = { ...addressCollectibles, ...{ [chainId]: newCollectibles } }; - const newAllCollectibles = { ...allCollectibles, ...{ [selectedAddress]: newAddressCollectibles } }; - this.update({ allCollectibles: newAllCollectibles, collectibles: newCollectibles }); + const newAddressCollectibles = { + ...addressCollectibles, + ...{ [chainId]: newCollectibles }, + }; + const newAllCollectibles = { + ...allCollectibles, + ...{ [selectedAddress]: newAddressCollectibles }, + }; + this.update({ + allCollectibles: newAllCollectibles, + collectibles: newCollectibles, + }); return newCollectibles; } finally { releaseLock(); @@ -366,21 +428,37 @@ export class AssetsController extends BaseController * @param detection? - Whether the collectible is manually added or auto-detected * @returns - Promise resolving to the current collectible contracts list */ - private async addCollectibleContract(address: string, detection?: boolean): Promise { + private async addCollectibleContract( + address: string, + detection?: boolean, + ): Promise { const releaseLock = await this.mutex.acquire(); try { address = toChecksumAddress(address); const { allCollectibleContracts, collectibleContracts } = this.state; const { chainId, selectedAddress } = this.config; - const existingEntry = collectibleContracts.find((collectibleContract) => collectibleContract.address === address); + const existingEntry = collectibleContracts.find( + (collectibleContract) => collectibleContract.address === address, + ); if (existingEntry) { return collectibleContracts; } - const contractInformation = await this.getCollectibleContractInformation(address); - const { name, symbol, image_url, description, total_supply } = contractInformation; + const contractInformation = await this.getCollectibleContractInformation( + address, + ); + const { + name, + symbol, + image_url, + description, + total_supply, + } = contractInformation; // If being auto-detected opensea information is expected // Oherwise at least name and symbol from contract is needed - if ((detection && !image_url) || Object.keys(contractInformation).length === 0) { + if ( + (detection && !image_url) || + Object.keys(contractInformation).length === 0 + ) { return collectibleContracts; } const newEntry: CollectibleContract = { @@ -392,7 +470,8 @@ export class AssetsController extends BaseController totalSupply: total_supply, }; const newCollectibleContracts = [...collectibleContracts, newEntry]; - const addressCollectibleContracts = allCollectibleContracts[selectedAddress]; + const addressCollectibleContracts = + allCollectibleContracts[selectedAddress]; const newAddressCollectibleContracts = { ...addressCollectibleContracts, ...{ [chainId]: newCollectibleContracts }, @@ -417,22 +496,33 @@ export class AssetsController extends BaseController * @param address - Hex address of the collectible contract * @param tokenId - Token identifier of the collectible */ - private removeAndIgnoreIndividualCollectible(address: string, tokenId: number) { + private removeAndIgnoreIndividualCollectible( + address: string, + tokenId: number, + ) { address = toChecksumAddress(address); const { allCollectibles, collectibles, ignoredCollectibles } = this.state; const { chainId, selectedAddress } = this.config; const newIgnoredCollectibles = [...ignoredCollectibles]; const newCollectibles = collectibles.filter((collectible) => { if (collectible.address === address && collectible.tokenId === tokenId) { - const alreadyIgnored = newIgnoredCollectibles.find((c) => c.address === address && c.tokenId === tokenId); + const alreadyIgnored = newIgnoredCollectibles.find( + (c) => c.address === address && c.tokenId === tokenId, + ); !alreadyIgnored && newIgnoredCollectibles.push(collectible); return false; } return true; }); const addressCollectibles = allCollectibles[selectedAddress]; - const newAddressCollectibles = { ...addressCollectibles, ...{ [chainId]: newCollectibles } }; - const newAllCollectibles = { ...allCollectibles, ...{ [selectedAddress]: newAddressCollectibles } }; + const newAddressCollectibles = { + ...addressCollectibles, + ...{ [chainId]: newCollectibles }, + }; + const newAllCollectibles = { + ...allCollectibles, + ...{ [selectedAddress]: newAddressCollectibles }, + }; this.update({ allCollectibles: newAllCollectibles, collectibles: newCollectibles, @@ -451,12 +541,22 @@ export class AssetsController extends BaseController const { allCollectibles, collectibles } = this.state; const { chainId, selectedAddress } = this.config; const newCollectibles = collectibles.filter( - (collectible) => !(collectible.address === address && collectible.tokenId === tokenId), + (collectible) => + !(collectible.address === address && collectible.tokenId === tokenId), ); const addressCollectibles = allCollectibles[selectedAddress]; - const newAddressCollectibles = { ...addressCollectibles, ...{ [chainId]: newCollectibles } }; - const newAllCollectibles = { ...allCollectibles, ...{ [selectedAddress]: newAddressCollectibles } }; - this.update({ allCollectibles: newAllCollectibles, collectibles: newCollectibles }); + const newAddressCollectibles = { + ...addressCollectibles, + ...{ [chainId]: newCollectibles }, + }; + const newAllCollectibles = { + ...allCollectibles, + ...{ [selectedAddress]: newAddressCollectibles }, + }; + this.update({ + allCollectibles: newAllCollectibles, + collectibles: newCollectibles, + }); } /** @@ -472,7 +572,8 @@ export class AssetsController extends BaseController const newCollectibleContracts = collectibleContracts.filter( (collectibleContract) => !(collectibleContract.address === address), ); - const addressCollectibleContracts = allCollectibleContracts[selectedAddress]; + const addressCollectibleContracts = + allCollectibleContracts[selectedAddress]; const newAddressCollectibleContracts = { ...addressCollectibleContracts, ...{ [chainId]: newCollectibleContracts }, @@ -529,8 +630,12 @@ export class AssetsController extends BaseController getAssetSymbol, getCollectibleTokenURI, }: { - onPreferencesStateChange: (listener: (preferencesState: PreferencesState) => void) => void; - onNetworkStateChange: (listener: (networkState: NetworkState) => void) => void; + onPreferencesStateChange: ( + listener: (preferencesState: PreferencesState) => void, + ) => void; + onNetworkStateChange: ( + listener: (networkState: NetworkState) => void, + ) => void; getAssetName: AssetsContractController['getAssetName']; getAssetSymbol: AssetsContractController['getAssetSymbol']; getCollectibleTokenURI: AssetsContractController['getCollectibleTokenURI']; @@ -560,22 +665,32 @@ export class AssetsController extends BaseController this.getAssetSymbol = getAssetSymbol; this.getCollectibleTokenURI = getCollectibleTokenURI; onPreferencesStateChange(({ selectedAddress }) => { - const { allCollectibleContracts, allCollectibles, allTokens } = this.state; + const { + allCollectibleContracts, + allCollectibles, + allTokens, + } = this.state; const { chainId } = this.config; this.configure({ selectedAddress }); this.update({ - collectibleContracts: allCollectibleContracts[selectedAddress]?.[chainId] || [], + collectibleContracts: + allCollectibleContracts[selectedAddress]?.[chainId] || [], collectibles: allCollectibles[selectedAddress]?.[chainId] || [], tokens: allTokens[selectedAddress]?.[chainId] || [], }); }); onNetworkStateChange(({ provider }) => { - const { allCollectibleContracts, allCollectibles, allTokens } = this.state; + const { + allCollectibleContracts, + allCollectibles, + allTokens, + } = this.state; const { selectedAddress } = this.config; const { chainId } = provider; this.configure({ chainId }); this.update({ - collectibleContracts: allCollectibleContracts[selectedAddress]?.[chainId] || [], + collectibleContracts: + allCollectibleContracts[selectedAddress]?.[chainId] || [], collectibles: allCollectibles[selectedAddress]?.[chainId] || [], tokens: allTokens[selectedAddress]?.[chainId] || [], }); @@ -600,7 +715,12 @@ export class AssetsController extends BaseController * @param image - Image of the token * @returns - Current token list */ - async addToken(address: string, symbol: string, decimals: number, image?: string): Promise { + async addToken( + address: string, + symbol: string, + decimals: number, + image?: string, + ): Promise { const releaseLock = await this.mutex.acquire(); try { address = toChecksumAddress(address); @@ -616,7 +736,10 @@ export class AssetsController extends BaseController } const addressTokens = allTokens[selectedAddress]; const newAddressTokens = { ...addressTokens, ...{ [chainId]: tokens } }; - const newAllTokens = { ...allTokens, ...{ [selectedAddress]: newAddressTokens } }; + const newAllTokens = { + ...allTokens, + ...{ [selectedAddress]: newAddressTokens }, + }; const newTokens = [...tokens]; this.update({ allTokens: newAllTokens, tokens: newTokens }); return newTokens; @@ -641,8 +764,15 @@ export class AssetsController extends BaseController const { address, symbol, decimals, image } = tokenToAdd; const checksumAddress = toChecksumAddress(address); - const newEntry: Token = { address: checksumAddress, symbol, decimals, image }; - const previousEntry = tokens.find((token) => token.address === checksumAddress); + const newEntry: Token = { + address: checksumAddress, + symbol, + decimals, + image, + }; + const previousEntry = tokens.find( + (token) => token.address === checksumAddress, + ); if (previousEntry) { const previousIndex = tokens.indexOf(previousEntry); tokens[previousIndex] = newEntry; @@ -653,7 +783,10 @@ export class AssetsController extends BaseController const addressTokens = allTokens[selectedAddress]; const newAddressTokens = { ...addressTokens, ...{ [chainId]: tokens } }; - const newAllTokens = { ...allTokens, ...{ [selectedAddress]: newAddressTokens } }; + const newAllTokens = { + ...allTokens, + ...{ [selectedAddress]: newAddressTokens }, + }; const newTokens = [...tokens]; this.update({ allTokens: newAllTokens, tokens: newTokens }); return newTokens; @@ -692,19 +825,22 @@ export class AssetsController extends BaseController } const result: Promise = new Promise((resolve, reject) => { - this.hub.once(`${suggestedAssetMeta.id}:finished`, (meta: SuggestedAssetMeta) => { - switch (meta.status) { - case SuggestedAssetStatus.accepted: - return resolve(meta.asset.address); - case SuggestedAssetStatus.rejected: - return reject(new Error('User rejected to watch the asset.')); - case SuggestedAssetStatus.failed: - return reject(new Error(meta.error.message)); - /* istanbul ignore next */ - default: - return reject(new Error(`Unknown status: ${meta.status}`)); - } - }); + this.hub.once( + `${suggestedAssetMeta.id}:finished`, + (meta: SuggestedAssetMeta) => { + switch (meta.status) { + case SuggestedAssetStatus.accepted: + return resolve(meta.asset.address); + case SuggestedAssetStatus.rejected: + return reject(new Error('User rejected to watch the asset.')); + case SuggestedAssetStatus.failed: + return reject(new Error(meta.error.message)); + /* istanbul ignore next */ + default: + return reject(new Error(`Unknown status: ${meta.status}`)); + } + }, + ); }); const { suggestedAssets } = this.state; suggestedAssets.push(suggestedAssetMeta); @@ -723,7 +859,9 @@ export class AssetsController extends BaseController */ async acceptWatchAsset(suggestedAssetID: string): Promise { const { suggestedAssets } = this.state; - const index = suggestedAssets.findIndex(({ id }) => suggestedAssetID === id); + const index = suggestedAssets.findIndex( + ({ id }) => suggestedAssetID === id, + ); const suggestedAssetMeta = suggestedAssets[index]; try { switch (suggestedAssetMeta.type) { @@ -731,15 +869,22 @@ export class AssetsController extends BaseController const { address, symbol, decimals, image } = suggestedAssetMeta.asset; await this.addToken(address, symbol, decimals, image); suggestedAssetMeta.status = SuggestedAssetStatus.accepted; - this.hub.emit(`${suggestedAssetMeta.id}:finished`, suggestedAssetMeta); + this.hub.emit( + `${suggestedAssetMeta.id}:finished`, + suggestedAssetMeta, + ); break; default: - throw new Error(`Asset of type ${suggestedAssetMeta.type} not supported`); + throw new Error( + `Asset of type ${suggestedAssetMeta.type} not supported`, + ); } } catch (error) { this.failSuggestedAsset(suggestedAssetMeta, error); } - const newSuggestedAssets = suggestedAssets.filter(({ id }) => id !== suggestedAssetID); + const newSuggestedAssets = suggestedAssets.filter( + ({ id }) => id !== suggestedAssetID, + ); this.update({ suggestedAssets: [...newSuggestedAssets] }); } @@ -751,14 +896,18 @@ export class AssetsController extends BaseController */ rejectWatchAsset(suggestedAssetID: string) { const { suggestedAssets } = this.state; - const index = suggestedAssets.findIndex(({ id }) => suggestedAssetID === id); + const index = suggestedAssets.findIndex( + ({ id }) => suggestedAssetID === id, + ); const suggestedAssetMeta = suggestedAssets[index]; if (!suggestedAssetMeta) { return; } suggestedAssetMeta.status = SuggestedAssetStatus.rejected; this.hub.emit(`${suggestedAssetMeta.id}:finished`, suggestedAssetMeta); - const newSuggestedAssets = suggestedAssets.filter(({ id }) => id !== suggestedAssetID); + const newSuggestedAssets = suggestedAssets.filter( + ({ id }) => id !== suggestedAssetID, + ); this.update({ suggestedAssets: [...newSuggestedAssets] }); } @@ -771,11 +920,21 @@ export class AssetsController extends BaseController * @param detection? - Whether the collectible is manually added or autodetected * @returns - Promise resolving to the current collectible list */ - async addCollectible(address: string, tokenId: number, opts?: CollectibleInformation, detection?: boolean) { + async addCollectible( + address: string, + tokenId: number, + opts?: CollectibleInformation, + detection?: boolean, + ) { address = toChecksumAddress(address); - const newCollectibleContracts = await this.addCollectibleContract(address, detection); + const newCollectibleContracts = await this.addCollectibleContract( + address, + detection, + ); // If collectible contract was not added, do not add individual collectible - const collectibleContract = newCollectibleContracts.find((contract) => contract.address === address); + const collectibleContract = newCollectibleContracts.find( + (contract) => contract.address === address, + ); // If collectible contract information, add individual collectible if (collectibleContract) { await this.addIndividualCollectible(address, tokenId, opts); @@ -794,7 +953,9 @@ export class AssetsController extends BaseController const newIgnoredTokens = [...ignoredTokens]; const newTokens = tokens.filter((token) => { if (token.address === address) { - const alreadyIgnored = newIgnoredTokens.find((t) => t.address === address); + const alreadyIgnored = newIgnoredTokens.find( + (t) => t.address === address, + ); !alreadyIgnored && newIgnoredTokens.push(token); return false; } @@ -802,8 +963,15 @@ export class AssetsController extends BaseController }); const addressTokens = allTokens[selectedAddress]; const newAddressTokens = { ...addressTokens, ...{ [chainId]: newTokens } }; - const newAllTokens = { ...allTokens, ...{ [selectedAddress]: newAddressTokens } }; - this.update({ allTokens: newAllTokens, tokens: newTokens, ignoredTokens: newIgnoredTokens }); + const newAllTokens = { + ...allTokens, + ...{ [selectedAddress]: newAddressTokens }, + }; + this.update({ + allTokens: newAllTokens, + tokens: newTokens, + ignoredTokens: newIgnoredTokens, + }); } /** @@ -818,7 +986,10 @@ export class AssetsController extends BaseController const newTokens = tokens.filter((token) => token.address !== address); const addressTokens = allTokens[selectedAddress]; const newAddressTokens = { ...addressTokens, ...{ [chainId]: newTokens } }; - const newAllTokens = { ...allTokens, ...{ [selectedAddress]: newAddressTokens } }; + const newAllTokens = { + ...allTokens, + ...{ [selectedAddress]: newAddressTokens }, + }; this.update({ allTokens: newAllTokens, tokens: newTokens }); } @@ -832,7 +1003,9 @@ export class AssetsController extends BaseController address = toChecksumAddress(address); this.removeIndividualCollectible(address, tokenId); const { collectibles } = this.state; - const remainingCollectible = collectibles.find((collectible) => collectible.address === address); + const remainingCollectible = collectibles.find( + (collectible) => collectible.address === address, + ); if (!remainingCollectible) { this.removeCollectibleContract(address); } @@ -848,7 +1021,9 @@ export class AssetsController extends BaseController address = toChecksumAddress(address); this.removeAndIgnoreIndividualCollectible(address, tokenId); const { collectibles } = this.state; - const remainingCollectible = collectibles.find((collectible) => collectible.address === address); + const remainingCollectible = collectibles.find( + (collectible) => collectible.address === address, + ); if (!remainingCollectible) { this.removeCollectibleContract(address); } diff --git a/src/assets/AssetsDetectionController.test.ts b/src/assets/AssetsDetectionController.test.ts index 08bf85a744..3fa41ee692 100644 --- a/src/assets/AssetsDetectionController.test.ts +++ b/src/assets/AssetsDetectionController.test.ts @@ -1,7 +1,10 @@ import { createSandbox, SinonStub, stub } from 'sinon'; import nock from 'nock'; import { BN } from 'ethereumjs-util'; -import { NetworkController, NetworksChainId } from '../network/NetworkController'; +import { + NetworkController, + NetworksChainId, +} from '../network/NetworkController'; import { PreferencesController } from '../user/PreferencesController'; import { AssetsController } from './AssetsController'; import { AssetsContractController } from './AssetsContractController'; @@ -20,7 +23,9 @@ describe('AssetsDetectionController', () => { let network: NetworkController; let assets: AssetsController; let assetsContract: AssetsContractController; - let getBalancesInSingleCall: SinonStub<[AssetsContractController['getBalancesInSingleCall']]>; + let getBalancesInSingleCall: SinonStub< + [AssetsContractController['getBalancesInSingleCall']] + >; const sandbox = createSandbox(); beforeEach(() => { @@ -32,7 +37,9 @@ describe('AssetsDetectionController', () => { onNetworkStateChange: (listener) => network.subscribe(listener), getAssetName: assetsContract.getAssetName.bind(assetsContract), getAssetSymbol: assetsContract.getAssetSymbol.bind(assetsContract), - getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind(assetsContract), + getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind( + assetsContract, + ), }); getBalancesInSingleCall = sandbox.stub(); assetsDetection = new AssetsDetectionController({ @@ -64,7 +71,9 @@ describe('AssetsDetectionController', () => { .persist(); nock(OPEN_SEA_HOST) - .get(`${OPEN_SEA_PATH}/asset_contract/0x1d963688FE2209A98dB35C67A041524822Cf04ff`) + .get( + `${OPEN_SEA_PATH}/asset_contract/0x1d963688FE2209A98dB35C67A041524822Cf04ff`, + ) .reply(200, { description: 'Description', image_url: 'url', @@ -72,7 +81,9 @@ describe('AssetsDetectionController', () => { symbol: 'FOO', total_supply: 0, }) - .get(`${OPEN_SEA_PATH}/asset_contract/0x1D963688FE2209A98db35c67A041524822cf04Hh`) + .get( + `${OPEN_SEA_PATH}/asset_contract/0x1D963688FE2209A98db35c67A041524822cf04Hh`, + ) .reply(200, { description: 'Description HH', image_url: 'url HH', @@ -80,9 +91,13 @@ describe('AssetsDetectionController', () => { symbol: 'HH', total_supply: 10, }) - .get(`${OPEN_SEA_PATH}/asset_contract/0x1d963688FE2209A98db35c67A041524822CF04gg`) + .get( + `${OPEN_SEA_PATH}/asset_contract/0x1d963688FE2209A98db35c67A041524822CF04gg`, + ) .replyWithError(new TypeError('Failed to fetch')) - .get(`${OPEN_SEA_PATH}/asset_contract/0x1D963688fe2209a98dB35c67a041524822Cf04ii`) + .get( + `${OPEN_SEA_PATH}/asset_contract/0x1D963688fe2209a98dB35c67a041524822Cf04ii`, + ) .replyWithError(new TypeError('Failed to fetch')) .get(`${OPEN_SEA_PATH}/assets?owner=0x1&limit=300`) .reply(200, { @@ -134,15 +149,24 @@ describe('AssetsDetectionController', () => { it('should poll and detect assets on interval while on mainnet', async () => { await new Promise((resolve) => { - const mockTokens = stub(AssetsDetectionController.prototype, 'detectTokens'); - const mockCollectibles = stub(AssetsDetectionController.prototype, 'detectCollectibles'); + const mockTokens = stub( + AssetsDetectionController.prototype, + 'detectTokens', + ); + const mockCollectibles = stub( + AssetsDetectionController.prototype, + 'detectCollectibles', + ); new AssetsDetectionController( { onAssetsStateChange: (listener) => assets.subscribe(listener), - onPreferencesStateChange: (listener) => preferences.subscribe(listener), + onPreferencesStateChange: (listener) => + preferences.subscribe(listener), onNetworkStateChange: (listener) => network.subscribe(listener), getOpenSeaApiKey: () => assets.openSeaApiKey, - getBalancesInSingleCall: assetsContract.getBalancesInSingleCall.bind(assetsContract), + getBalancesInSingleCall: assetsContract.getBalancesInSingleCall.bind( + assetsContract, + ), addTokens: assets.addTokens.bind(assets), addCollectible: assets.addCollectible.bind(assets), getAssetsState: () => assets.state, @@ -170,15 +194,24 @@ describe('AssetsDetectionController', () => { it('should not autodetect while not on mainnet', async () => { await new Promise((resolve) => { - const mockTokens = stub(AssetsDetectionController.prototype, 'detectTokens'); - const mockCollectibles = stub(AssetsDetectionController.prototype, 'detectCollectibles'); + const mockTokens = stub( + AssetsDetectionController.prototype, + 'detectTokens', + ); + const mockCollectibles = stub( + AssetsDetectionController.prototype, + 'detectCollectibles', + ); new AssetsDetectionController( { onAssetsStateChange: (listener) => assets.subscribe(listener), - onPreferencesStateChange: (listener) => preferences.subscribe(listener), + onPreferencesStateChange: (listener) => + preferences.subscribe(listener), onNetworkStateChange: (listener) => network.subscribe(listener), getOpenSeaApiKey: () => assets.openSeaApiKey, - getBalancesInSingleCall: assetsContract.getBalancesInSingleCall.bind(assetsContract), + getBalancesInSingleCall: assetsContract.getBalancesInSingleCall.bind( + assetsContract, + ), addTokens: assets.addTokens.bind(assets), addCollectible: assets.addCollectible.bind(assets), getAssetsState: () => assets.state, @@ -209,11 +242,15 @@ describe('AssetsDetectionController', () => { it('should detect, add collectibles and do nor remove not detected collectibles correctly', async () => { assetsDetection.configure({ networkType: MAINNET, selectedAddress: '0x1' }); - await assets.addCollectible('0x1D963688FE2209A98db35c67A041524822cf04Hh', 2573, { - description: 'Description 2573', - image: 'image/2573.png', - name: 'ID 2573', - }); + await assets.addCollectible( + '0x1D963688FE2209A98db35c67A041524822cf04Hh', + 2573, + { + description: 'Description 2573', + image: 'image/2573.png', + name: 'ID 2573', + }, + ); await assetsDetection.detectCollectibles(); expect(assets.state.collectibles).toStrictEqual([ { @@ -238,7 +275,10 @@ describe('AssetsDetectionController', () => { await assetsDetection.detectCollectibles(); expect(assets.state.collectibles).toHaveLength(1); expect(assets.state.ignoredCollectibles).toHaveLength(0); - assets.removeAndIgnoreCollectible('0x1d963688fe2209a98db35c67a041524822cf04ff', 2577); + assets.removeAndIgnoreCollectible( + '0x1d963688fe2209a98db35c67a041524822cf04ff', + 2577, + ); await assetsDetection.detectCollectibles(); expect(assets.state.collectibles).toHaveLength(0); expect(assets.state.ignoredCollectibles).toHaveLength(1); @@ -300,11 +340,15 @@ describe('AssetsDetectionController', () => { await assetsDetection.detectCollectibles(); // First fetch to API, only gets information from contract ending in HH expect(assets.state.collectibles).toStrictEqual([collectibleHH2574]); - expect(assets.state.collectibleContracts).toStrictEqual([collectibleContractHH]); + expect(assets.state.collectibleContracts).toStrictEqual([ + collectibleContractHH, + ]); // During next call of assets detection, API succeds returning contract ending in gg information nock(OPEN_SEA_HOST) - .get(`${OPEN_SEA_PATH}/asset_contract/0x1d963688FE2209A98db35c67A041524822CF04gg`) + .get( + `${OPEN_SEA_PATH}/asset_contract/0x1d963688FE2209A98db35c67A041524822CF04gg`, + ) .reply(200, { description: 'Description GG', image_url: 'url GG', @@ -312,7 +356,9 @@ describe('AssetsDetectionController', () => { symbol: 'GG', total_supply: 10, }) - .get(`${OPEN_SEA_PATH}/asset_contract/0x1D963688fe2209a98dB35c67a041524822Cf04ii`) + .get( + `${OPEN_SEA_PATH}/asset_contract/0x1D963688fe2209a98dB35c67a041524822Cf04ii`, + ) .reply(200, { description: 'Description II', image_url: 'url II', @@ -360,12 +406,18 @@ describe('AssetsDetectionController', () => { collectibleContractII, collectibleContractGG, ]); - expect(assets.state.collectibles).toStrictEqual([collectibleHH2574, collectibleII2577, collectibleGG2574]); + expect(assets.state.collectibles).toStrictEqual([ + collectibleHH2574, + collectibleII2577, + collectibleGG2574, + ]); }); it('should detect tokens correctly', async () => { assetsDetection.configure({ networkType: MAINNET, selectedAddress: '0x1' }); - getBalancesInSingleCall.resolves({ '0x6810e776880C02933D47DB1b9fc05908e5386b96': new BN(1) }); + getBalancesInSingleCall.resolves({ + '0x6810e776880C02933D47DB1b9fc05908e5386b96': new BN(1), + }); await assetsDetection.detectTokens(); expect(assets.state.tokens).toStrictEqual([ { @@ -379,7 +431,9 @@ describe('AssetsDetectionController', () => { it('should not autodetect tokens that exist in the ignoreList', async () => { assetsDetection.configure({ networkType: MAINNET, selectedAddress: '0x1' }); - getBalancesInSingleCall.resolves({ '0x6810e776880C02933D47DB1b9fc05908e5386b96': new BN(1) }); + getBalancesInSingleCall.resolves({ + '0x6810e776880C02933D47DB1b9fc05908e5386b96': new BN(1), + }); await assetsDetection.detectTokens(); assets.removeAndIgnoreToken('0x6810e776880C02933D47DB1b9fc05908e5386b96'); @@ -389,7 +443,9 @@ describe('AssetsDetectionController', () => { it('should not detect tokens if there is no selectedAddress set', async () => { assetsDetection.configure({ networkType: MAINNET }); - getBalancesInSingleCall.resolves({ '0x6810e776880C02933D47DB1b9fc05908e5386b96': new BN(1) }); + getBalancesInSingleCall.resolves({ + '0x6810e776880C02933D47DB1b9fc05908e5386b96': new BN(1), + }); await assetsDetection.detectTokens(); expect(assets.state.tokens).toStrictEqual([]); }); @@ -402,14 +458,24 @@ describe('AssetsDetectionController', () => { const detectAssets = sandbox.stub(assetsDetection, 'detectAssets'); preferences.update({ selectedAddress: secondAddress }); preferences.update({ selectedAddress: secondAddress }); - expect(preferences.state.selectedAddress).toEqual(secondAddress); + expect(preferences.state.selectedAddress).toStrictEqual(secondAddress); expect(detectAssets.calledTwice).toBe(false); preferences.update({ selectedAddress: firstAddress }); - expect(preferences.state.selectedAddress).toEqual(firstAddress); - network.update({ provider: { type: secondNetworkType, chainId: NetworksChainId[secondNetworkType] } }); - expect(network.state.provider.type).toEqual(secondNetworkType); - network.update({ provider: { type: firstNetworkType, chainId: NetworksChainId[firstNetworkType] } }); - expect(network.state.provider.type).toEqual(firstNetworkType); + expect(preferences.state.selectedAddress).toStrictEqual(firstAddress); + network.update({ + provider: { + type: secondNetworkType, + chainId: NetworksChainId[secondNetworkType], + }, + }); + expect(network.state.provider.type).toStrictEqual(secondNetworkType); + network.update({ + provider: { + type: firstNetworkType, + chainId: NetworksChainId[firstNetworkType], + }, + }); + expect(network.state.provider.type).toStrictEqual(firstNetworkType); assets.update({ tokens: TOKENS }); expect(assetsDetection.config.tokens).toStrictEqual(TOKENS); }); diff --git a/src/assets/AssetsDetectionController.ts b/src/assets/AssetsDetectionController.ts index 5e492620a1..7d5eb6554f 100644 --- a/src/assets/AssetsDetectionController.ts +++ b/src/assets/AssetsDetectionController.ts @@ -50,7 +50,10 @@ export interface AssetsDetectionConfig extends BaseConfig { /** * Controller that passively polls on a set interval for assets auto detection */ -export class AssetsDetectionController extends BaseController { +export class AssetsDetectionController extends BaseController< + AssetsDetectionConfig, + BaseState +> { private handle?: NodeJS.Timer; private getOwnerCollectiblesApi(address: string) { @@ -65,7 +68,11 @@ export class AssetsDetectionController extends BaseController void) => void; - onPreferencesStateChange: (listener: (preferencesState: PreferencesState) => void) => void; - onNetworkStateChange: (listener: (networkState: NetworkState) => void) => void; + onAssetsStateChange: ( + listener: (assetsState: AssetsState) => void, + ) => void; + onPreferencesStateChange: ( + listener: (preferencesState: PreferencesState) => void, + ) => void; + onNetworkStateChange: ( + listener: (networkState: NetworkState) => void, + ) => void; getOpenSeaApiKey: () => string | undefined; getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall']; addTokens: AssetsController['addTokens']; @@ -206,7 +219,9 @@ export class AssetsDetectionController extends BaseController token.address); + const tokensAddresses = this.config.tokens.filter( + /* istanbul ignore next*/ (token) => token.address, + ); const tokensToDetect: string[] = []; for (const address in contractMap) { const contract = contractMap[address]; @@ -221,14 +236,19 @@ export class AssetsDetectionController extends BaseController { - const balances = await this.getBalancesInSingleCall(selectedAddress, tokensToDetect); + const balances = await this.getBalancesInSingleCall( + selectedAddress, + tokensToDetect, + ); const tokensToAdd = []; for (const tokenAddress in balances) { let ignored; /* istanbul ignore else */ const { ignoredTokens } = this.getAssetsState(); if (ignoredTokens.length) { - ignored = ignoredTokens.find((token) => token.address === toChecksumAddress(tokenAddress)); + ignored = ignoredTokens.find( + (token) => token.address === toChecksumAddress(tokenAddress), + ); } if (!ignored) { tokensToAdd.push({ @@ -260,38 +280,43 @@ export class AssetsDetectionController extends BaseController { const apiCollectibles = await this.getOwnerCollectibles(); - const addCollectiblesPromises = apiCollectibles.map(async (collectible: ApiCollectibleResponse) => { - const { - token_id, - image_original_url, - name, - description, - asset_contract: { address }, - } = collectible; + const addCollectiblesPromises = apiCollectibles.map( + async (collectible: ApiCollectibleResponse) => { + const { + token_id, + image_original_url, + name, + description, + asset_contract: { address }, + } = collectible; - let ignored; - /* istanbul ignore else */ - const { ignoredCollectibles } = this.getAssetsState(); - if (ignoredCollectibles.length) { - ignored = ignoredCollectibles.find((c) => { - /* istanbul ignore next */ - return c.address === toChecksumAddress(address) && c.tokenId === Number(token_id); - }); - } - /* istanbul ignore else */ - if (!ignored) { - await this.addCollectible( - address, - Number(token_id), - { - description, - image: image_original_url, - name, - }, - true, - ); - } - }); + let ignored; + /* istanbul ignore else */ + const { ignoredCollectibles } = this.getAssetsState(); + if (ignoredCollectibles.length) { + ignored = ignoredCollectibles.find((c) => { + /* istanbul ignore next */ + return ( + c.address === toChecksumAddress(address) && + c.tokenId === Number(token_id) + ); + }); + } + /* istanbul ignore else */ + if (!ignored) { + await this.addCollectible( + address, + Number(token_id), + { + description, + image: image_original_url, + name, + }, + true, + ); + } + }, + ); await Promise.all(addCollectiblesPromises); }); } diff --git a/src/assets/CurrencyRateController.test.ts b/src/assets/CurrencyRateController.test.ts index ea39ac8070..f068046a6b 100644 --- a/src/assets/CurrencyRateController.test.ts +++ b/src/assets/CurrencyRateController.test.ts @@ -5,7 +5,11 @@ import CurrencyRateController from './CurrencyRateController'; describe('CurrencyRateController', () => { it('should set default state', () => { const fetchExchangeRateStub = stub(); - const controller = new CurrencyRateController({}, {}, fetchExchangeRateStub); + const controller = new CurrencyRateController( + {}, + {}, + fetchExchangeRateStub, + ); expect(controller.state).toStrictEqual({ conversionDate: 0, conversionRate: 0, @@ -19,7 +23,11 @@ describe('CurrencyRateController', () => { it('should initialize with the default config', () => { const fetchExchangeRateStub = stub(); - const controller = new CurrencyRateController({}, {}, fetchExchangeRateStub); + const controller = new CurrencyRateController( + {}, + {}, + fetchExchangeRateStub, + ); expect(controller.config).toStrictEqual({ currentCurrency: 'usd', disabled: false, @@ -34,7 +42,11 @@ describe('CurrencyRateController', () => { it('should initialize with the currency in state', () => { const fetchExchangeRateStub = stub(); const existingState = { currentCurrency: 'rep' }; - const controller = new CurrencyRateController({}, existingState, fetchExchangeRateStub); + const controller = new CurrencyRateController( + {}, + existingState, + fetchExchangeRateStub, + ); expect(controller.config).toStrictEqual({ currentCurrency: 'rep', disabled: false, @@ -48,19 +60,35 @@ describe('CurrencyRateController', () => { it('should throw when currentCurrency property is accessed', () => { const fetchExchangeRateStub = stub(); - const controller = new CurrencyRateController({}, {}, fetchExchangeRateStub); - expect(() => console.log(controller.currentCurrency)).toThrow('Property only used for setting'); + const controller = new CurrencyRateController( + {}, + {}, + fetchExchangeRateStub, + ); + expect(() => console.log(controller.currentCurrency)).toThrow( + 'Property only used for setting', + ); }); it('should throw when nativeCurrency property is accessed', () => { const fetchExchangeRateStub = stub(); - const controller = new CurrencyRateController({}, {}, fetchExchangeRateStub); - expect(() => console.log(controller.nativeCurrency)).toThrow('Property only used for setting'); + const controller = new CurrencyRateController( + {}, + {}, + fetchExchangeRateStub, + ); + expect(() => console.log(controller.nativeCurrency)).toThrow( + 'Property only used for setting', + ); }); it('should poll and update rate in the right interval', async () => { const fetchExchangeRateStub = stub(); - const controller = new CurrencyRateController({ interval: 100 }, {}, fetchExchangeRateStub); + const controller = new CurrencyRateController( + { interval: 100 }, + {}, + fetchExchangeRateStub, + ); await new Promise((resolve) => setTimeout(() => resolve(), 1)); expect(fetchExchangeRateStub.called).toBe(true); @@ -73,7 +101,11 @@ describe('CurrencyRateController', () => { it('should not update rates if disabled', async () => { const fetchExchangeRateStub = stub().resolves({}); - const controller = new CurrencyRateController({ interval: 10 }, {}, fetchExchangeRateStub); + const controller = new CurrencyRateController( + { interval: 10 }, + {}, + fetchExchangeRateStub, + ); controller.disabled = true; await controller.updateExchangeRate(); @@ -83,7 +115,11 @@ describe('CurrencyRateController', () => { it('should clear previous interval', async () => { const fetchExchangeRateStub = stub(); const mock = stub(global, 'clearTimeout'); - const controller = new CurrencyRateController({ interval: 1337 }, {}, fetchExchangeRateStub); + const controller = new CurrencyRateController( + { interval: 1337 }, + {}, + fetchExchangeRateStub, + ); await new Promise((resolve) => { setTimeout(() => { controller.poll(1338); @@ -98,7 +134,11 @@ describe('CurrencyRateController', () => { it('should update currency', async () => { const fetchExchangeRateStub = stub().resolves({ conversionRate: 10 }); - const controller = new CurrencyRateController({ interval: 10 }, {}, fetchExchangeRateStub); + const controller = new CurrencyRateController( + { interval: 10 }, + {}, + fetchExchangeRateStub, + ); expect(controller.state.conversionRate).toStrictEqual(0); await controller.updateExchangeRate(); expect(controller.state.conversionRate).toStrictEqual(10); @@ -116,6 +156,8 @@ describe('CurrencyRateController', () => { await controller.updateExchangeRate(); - expect(fetchExchangeRateStub.alwaysCalledWithExactly('xyz', 'ETH', true)).toBe(true); + expect( + fetchExchangeRateStub.alwaysCalledWithExactly('xyz', 'ETH', true), + ).toBe(true); }); }); diff --git a/src/assets/CurrencyRateController.ts b/src/assets/CurrencyRateController.ts index 78e04aba31..e8ec7666a4 100644 --- a/src/assets/CurrencyRateController.ts +++ b/src/assets/CurrencyRateController.ts @@ -43,7 +43,10 @@ export interface CurrencyRateState extends BaseState { * Controller that passively polls on a set interval for an exchange rate from the current base * asset to the current currency */ -export class CurrencyRateController extends BaseController { +export class CurrencyRateController extends BaseController< + CurrencyRateConfig, + CurrencyRateState +> { /* Optional config to include conversion to usd in all price url fetches and on state */ includeUSDRate?: boolean; @@ -155,7 +158,11 @@ export class CurrencyRateController extends BaseController { const sandbox = createSandbox(); - const getToken = (tokenBalances: TokenBalancesController, address: string) => { + const getToken = ( + tokenBalances: TokenBalancesController, + address: string, + ) => { const { tokens } = tokenBalances.config; return tokens.find((token) => token.address === address); }; @@ -32,7 +38,7 @@ describe('TokenBalancesController', () => { getSelectedAddress: () => '0x1234', getBalanceOf: stub(), }); - expect(tokenBalances.state).toEqual({ contractBalances: {} }); + expect(tokenBalances.state).toStrictEqual({ contractBalances: {} }); }); it('should set default config', () => { @@ -41,7 +47,7 @@ describe('TokenBalancesController', () => { getSelectedAddress: () => '0x1234', getBalanceOf: stub(), }); - expect(tokenBalances.config).toEqual({ + expect(tokenBalances.config).toStrictEqual({ interval: 180000, tokens: [], }); @@ -114,7 +120,9 @@ describe('TokenBalancesController', () => { onNetworkStateChange: (listener) => network.subscribe(listener), getAssetName: assetsContract.getAssetName.bind(assetsContract), getAssetSymbol: assetsContract.getAssetSymbol.bind(assetsContract), - getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind(assetsContract), + getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind( + assetsContract, + ), }); const address = '0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0'; const tokenBalances = new TokenBalancesController( @@ -125,18 +133,24 @@ describe('TokenBalancesController', () => { }, { interval: 1337, tokens: [{ address, decimals: 18, symbol: 'EOS' }] }, ); - expect(tokenBalances.state.contractBalances).toEqual({}); + expect(tokenBalances.state.contractBalances).toStrictEqual({}); assetsContract.configure({ provider: MAINNET_PROVIDER }); await tokenBalances.updateBalances(); const mytoken = getToken(tokenBalances, address); expect(mytoken?.balanceError).toBeNull(); - expect(Object.keys(tokenBalances.state.contractBalances)).toContain(address); - expect(tokenBalances.state.contractBalances[address].toNumber()).toBeGreaterThan(0); + expect(Object.keys(tokenBalances.state.contractBalances)).toContain( + address, + ); + expect( + tokenBalances.state.contractBalances[address].toNumber(), + ).toBeGreaterThan(0); }); it('should handle `getBalanceOf` error case', async () => { - const assetsContract = new AssetsContractController({ provider: MAINNET_PROVIDER }); + const assetsContract = new AssetsContractController({ + provider: MAINNET_PROVIDER, + }); const network = new NetworkController(); const preferences = new PreferencesController(); const assets = new AssetsController({ @@ -144,11 +158,15 @@ describe('TokenBalancesController', () => { onNetworkStateChange: (listener) => network.subscribe(listener), getAssetName: assetsContract.getAssetName.bind(assetsContract), getAssetSymbol: assetsContract.getAssetSymbol.bind(assetsContract), - getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind(assetsContract), + getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind( + assetsContract, + ), }); const errorMsg = 'Failed to get balance'; const address = '0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0'; - const getBalanceOfStub = stub().returns(Promise.reject(new Error(errorMsg))); + const getBalanceOfStub = stub().returns( + Promise.reject(new Error(errorMsg)), + ); const tokenBalances = new TokenBalancesController( { onAssetsStateChange: (listener) => assets.subscribe(listener), @@ -158,18 +176,24 @@ describe('TokenBalancesController', () => { { interval: 1337, tokens: [{ address, decimals: 18, symbol: 'EOS' }] }, ); - expect(tokenBalances.state.contractBalances).toEqual({}); + expect(tokenBalances.state.contractBalances).toStrictEqual({}); await tokenBalances.updateBalances(); const mytoken = getToken(tokenBalances, address); expect(mytoken?.balanceError).toBeInstanceOf(Error); expect(mytoken?.balanceError?.message).toBe(errorMsg); - expect(tokenBalances.state.contractBalances[address].toNumber()).toStrictEqual(0); + expect( + tokenBalances.state.contractBalances[address].toNumber(), + ).toStrictEqual(0); getBalanceOfStub.returns(new BN(1)); await tokenBalances.updateBalances(); expect(mytoken?.balanceError).toBeNull(); - expect(Object.keys(tokenBalances.state.contractBalances)).toContain(address); - expect(tokenBalances.state.contractBalances[address].toNumber()).toBeGreaterThan(0); + expect(Object.keys(tokenBalances.state.contractBalances)).toContain( + address, + ); + expect( + tokenBalances.state.contractBalances[address].toNumber(), + ).toBeGreaterThan(0); }); it('should subscribe to new sibling assets controllers', async () => { @@ -181,7 +205,9 @@ describe('TokenBalancesController', () => { onNetworkStateChange: (listener) => network.subscribe(listener), getAssetName: assetsContract.getAssetName.bind(assetsContract), getAssetSymbol: assetsContract.getAssetSymbol.bind(assetsContract), - getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind(assetsContract), + getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind( + assetsContract, + ), }); const tokenBalances = new TokenBalancesController( { diff --git a/src/assets/TokenBalancesController.ts b/src/assets/TokenBalancesController.ts index 38813a7aad..3bde78ca9a 100644 --- a/src/assets/TokenBalancesController.ts +++ b/src/assets/TokenBalancesController.ts @@ -37,7 +37,10 @@ export interface TokenBalancesState extends BaseState { * Controller that passively polls on a set interval token balances * for tokens stored in the AssetsController */ -export class TokenBalancesController extends BaseController { +export class TokenBalancesController extends BaseController< + TokenBalancesConfig, + TokenBalancesState +> { private handle?: NodeJS.Timer; /** @@ -65,7 +68,9 @@ export class TokenBalancesController extends BaseController void) => void; + onAssetsStateChange: ( + listener: (tokenState: AssetsState) => void, + ) => void; getSelectedAddress: () => PreferencesState['selectedAddress']; getBalanceOf: AssetsContractController['getBalanceOf']; }, @@ -116,7 +121,10 @@ export class TokenBalancesController extends BaseController { beforeEach(() => { nock(COINGECKO_HOST) - .get(`${COINGECKO_PATH}?contract_addresses=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359,0xfoO&vs_currencies=eth`) - .reply(200, { '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359': { eth: 0.00561045 } }) + .get( + `${COINGECKO_PATH}?contract_addresses=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359,0xfoO&vs_currencies=eth`, + ) + .reply(200, { + '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359': { eth: 0.00561045 }, + }) .get(`${COINGECKO_PATH}?contract_addresses=0xfoO&vs_currencies=eth`) .reply(200, {}) .get(`${COINGECKO_PATH}?contract_addresses=bar&vs_currencies=eth`) @@ -34,13 +38,19 @@ describe('TokenRatesController', () => { }); it('should set default state', () => { - const controller = new TokenRatesController({ onAssetsStateChange: stub(), onCurrencyRateStateChange: stub() }); - expect(controller.state).toEqual({ contractExchangeRates: {} }); + const controller = new TokenRatesController({ + onAssetsStateChange: stub(), + onCurrencyRateStateChange: stub(), + }); + expect(controller.state).toStrictEqual({ contractExchangeRates: {} }); }); it('should initialize with the default config', () => { - const controller = new TokenRatesController({ onAssetsStateChange: stub(), onCurrencyRateStateChange: stub() }); - expect(controller.config).toEqual({ + const controller = new TokenRatesController({ + onAssetsStateChange: stub(), + onCurrencyRateStateChange: stub(), + }); + expect(controller.config).toStrictEqual({ disabled: false, interval: 180000, nativeCurrency: 'eth', @@ -49,8 +59,13 @@ describe('TokenRatesController', () => { }); it('should throw when tokens property is accessed', () => { - const controller = new TokenRatesController({ onAssetsStateChange: stub(), onCurrencyRateStateChange: stub() }); - expect(() => console.log(controller.tokens)).toThrow('Property only used for setting'); + const controller = new TokenRatesController({ + onAssetsStateChange: stub(), + onCurrencyRateStateChange: stub(), + }); + expect(() => console.log(controller.tokens)).toThrow( + 'Property only used for setting', + ); }); it('should poll and update rate in the right interval', async () => { @@ -111,13 +126,16 @@ describe('TokenRatesController', () => { onNetworkStateChange: (listener) => network.subscribe(listener), getAssetName: assetsContract.getAssetName.bind(assetsContract), getAssetSymbol: assetsContract.getAssetSymbol.bind(assetsContract), - getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind(assetsContract), + getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind( + assetsContract, + ), }); const currencyRate = new CurrencyRateController(); const controller = new TokenRatesController( { onAssetsStateChange: (listener) => assets.subscribe(listener), - onCurrencyRateStateChange: (listener) => currencyRate.subscribe(listener), + onCurrencyRateStateChange: (listener) => + currencyRate.subscribe(listener), }, { interval: 10 }, ); @@ -129,9 +147,13 @@ describe('TokenRatesController', () => { { address: address2, decimals: 0, symbol: '' }, ]; await controller.updateExchangeRates(); - expect(Object.keys(controller.state.contractExchangeRates)).toContain(address); + expect(Object.keys(controller.state.contractExchangeRates)).toContain( + address, + ); expect(controller.state.contractExchangeRates[address]).toBeGreaterThan(0); - expect(Object.keys(controller.state.contractExchangeRates)).toContain(address2); + expect(Object.keys(controller.state.contractExchangeRates)).toContain( + address2, + ); expect(controller.state.contractExchangeRates[address2]).toStrictEqual(0); }); @@ -140,7 +162,10 @@ describe('TokenRatesController', () => { { onAssetsStateChange: stub(), onCurrencyRateStateChange: stub() }, { interval: 10 }, ); - stub(controller, 'fetchExchangeRate').throws({ error: 'Not Found', message: 'Not Found' }); + stub(controller, 'fetchExchangeRate').throws({ + error: 'Not Found', + message: 'Not Found', + }); expect(controller.state.contractExchangeRates).toStrictEqual({}); controller.tokens = [{ address: 'bar', decimals: 0, symbol: '' }]; const mock = stub(controller, 'updateExchangeRates'); @@ -157,13 +182,16 @@ describe('TokenRatesController', () => { onNetworkStateChange: (listener) => network.subscribe(listener), getAssetName: assetsContract.getAssetName.bind(assetsContract), getAssetSymbol: assetsContract.getAssetSymbol.bind(assetsContract), - getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind(assetsContract), + getCollectibleTokenURI: assetsContract.getCollectibleTokenURI.bind( + assetsContract, + ), }); const currencyRate = new CurrencyRateController(); const controller = new TokenRatesController( { onAssetsStateChange: (listener) => assets.subscribe(listener), - onCurrencyRateStateChange: (listener) => currencyRate.subscribe(listener), + onCurrencyRateStateChange: (listener) => + currencyRate.subscribe(listener), }, { interval: 10 }, ); diff --git a/src/assets/TokenRatesController.ts b/src/assets/TokenRatesController.ts index b9e0ef47db..06be7af4c2 100644 --- a/src/assets/TokenRatesController.ts +++ b/src/assets/TokenRatesController.ts @@ -64,7 +64,10 @@ export interface TokenRatesState extends BaseState { * Controller that passively polls on a set interval for token-to-fiat exchange rates * for tokens stored in the AssetsController */ -export class TokenRatesController extends BaseController { +export class TokenRatesController extends BaseController< + TokenRatesConfig, + TokenRatesState +> { private handle?: NodeJS.Timer; private tokenList: Token[] = []; @@ -92,8 +95,12 @@ export class TokenRatesController extends BaseController void) => void; - onCurrencyRateStateChange: (listener: (currencyRateState: CurrencyRateState) => void) => void; + onAssetsStateChange: ( + listener: (assetState: AssetsState) => void, + ) => void; + onCurrencyRateStateChange: ( + listener: (currencyRateState: CurrencyRateState) => void, + ) => void; }, config?: Partial, state?: Partial, @@ -174,7 +181,9 @@ export class TokenRatesController extends BaseController { const address = toChecksumAddress(token.address); const price = prices[token.address.toLowerCase()]; - newContractExchangeRates[address] = price ? price[nativeCurrency.toLowerCase()] : 0; + newContractExchangeRates[address] = price + ? price[nativeCurrency.toLowerCase()] + : 0; }); this.update({ contractExchangeRates: newContractExchangeRates }); } diff --git a/src/keyring/KeyringController.test.ts b/src/keyring/KeyringController.test.ts index 3def52a3b9..5fa548f7b3 100644 --- a/src/keyring/KeyringController.test.ts +++ b/src/keyring/KeyringController.test.ts @@ -22,14 +22,20 @@ const input = '"cipherparams":{"iv":"eba107752a238d2dd26e543860dccec4"},"cipher":"aes-128-ctr","kdf":"scrypt",' + '"kdfparams":{"dklen":32,"salt":"2a8894ff056db4cc1851e45390996dd26b075e5ceaf72c13ca4c202f94ca468a",' + '"n":131072,"r":8,"p":1},"mac":"8bd084028ecb331275a76583d41fe0e1212825a6d155e904d1baf448d33e7150"}}'; -const seedWords = 'puzzle seed penalty soldier say clay field arctic metal hen cage runway'; -const privateKey = '1e4e6a4c0c077f4ae8ddfbf372918e61dd0fb4a4cfa592cb16e7546d505e68fc'; +const seedWords = + 'puzzle seed penalty soldier say clay field arctic metal hen cage runway'; +const privateKey = + '1e4e6a4c0c077f4ae8ddfbf372918e61dd0fb4a4cfa592cb16e7546d505e68fc'; const password = 'password123'; describe('KeyringController', () => { let keyringController: KeyringController; let preferences: PreferencesController; - let initialState: { isUnlocked: boolean; keyringTypes: string[]; keyrings: Keyring[] }; + let initialState: { + isUnlocked: boolean; + keyringTypes: string[]; + keyrings: Keyring[]; + }; const baseConfig: Partial = { encryptor: new MockEncryptor() }; beforeEach(async () => { preferences = new PreferencesController(); @@ -55,32 +61,43 @@ describe('KeyringController', () => { }); it('should add new account', async () => { - const initialIdentitiesLength = Object.keys(preferences.state.identities).length; + const initialIdentitiesLength = Object.keys(preferences.state.identities) + .length; const currentKeyringMemState = await keyringController.addNewAccount(); expect(initialState.keyrings).toHaveLength(1); - expect(initialState.keyrings[0].accounts).not.toBe(currentKeyringMemState.keyrings); + expect(initialState.keyrings[0].accounts).not.toBe( + currentKeyringMemState.keyrings, + ); expect(currentKeyringMemState.keyrings[0].accounts).toHaveLength(2); const identitiesLength = Object.keys(preferences.state.identities).length; expect(identitiesLength).toBeGreaterThan(initialIdentitiesLength); }); it('should add new account without updating', async () => { - const initialIdentitiesLength = Object.keys(preferences.state.identities).length; + const initialIdentitiesLength = Object.keys(preferences.state.identities) + .length; const currentKeyringMemState = await keyringController.addNewAccountWithoutUpdate(); expect(initialState.keyrings).toHaveLength(1); - expect(initialState.keyrings[0].accounts).not.toBe(currentKeyringMemState.keyrings); + expect(initialState.keyrings[0].accounts).not.toBe( + currentKeyringMemState.keyrings, + ); expect(currentKeyringMemState.keyrings[0].accounts).toHaveLength(2); const identitiesLength = Object.keys(preferences.state.identities).length; expect(identitiesLength).toStrictEqual(initialIdentitiesLength); }); it('should create new vault and keychain', async () => { - const currentState = await keyringController.createNewVaultAndKeychain(password); + const currentState = await keyringController.createNewVaultAndKeychain( + password, + ); expect(initialState).not.toBe(currentState); }); it('should create new vault and restore', async () => { - const currentState = await keyringController.createNewVaultAndRestore(password, seedWords); + const currentState = await keyringController.createNewVaultAndRestore( + password, + seedWords, + ); expect(initialState).not.toBe(currentState); }); @@ -92,14 +109,21 @@ describe('KeyringController', () => { it('should export seed phrase', () => { const seed = keyringController.exportSeedPhrase(password); expect(seed).not.toBe(''); - expect(() => keyringController.exportSeedPhrase('')).toThrow('Invalid password'); + expect(() => keyringController.exportSeedPhrase('')).toThrow( + 'Invalid password', + ); }); it('should export account', async () => { const account = initialState.keyrings[0].accounts[0]; - const newPrivateKey = await keyringController.exportAccount(password, account); + const newPrivateKey = await keyringController.exportAccount( + password, + account, + ); expect(newPrivateKey).not.toBe(''); - expect(() => keyringController.exportAccount('', account)).toThrow('Invalid password'); + expect(() => keyringController.exportAccount('', account)).toThrow( + 'Invalid password', + ); }); it('should get accounts', async () => { @@ -111,13 +135,19 @@ describe('KeyringController', () => { it('should import account with strategy privateKey', async () => { let error1; try { - await keyringController.importAccountWithStrategy(AccountImportStrategy.privateKey, []); + await keyringController.importAccountWithStrategy( + AccountImportStrategy.privateKey, + [], + ); } catch (e) { error1 = e; } let error2; try { - await keyringController.importAccountWithStrategy(AccountImportStrategy.privateKey, ['123']); + await keyringController.importAccountWithStrategy( + AccountImportStrategy.privateKey, + ['123'], + ); } catch (e) { error2 = e; } @@ -125,24 +155,39 @@ describe('KeyringController', () => { expect(error2.message).toBe('Cannot import invalid private key.'); const address = '0x51253087e6f8358b5f10c0a94315d69db3357859'; const newKeyring = { accounts: [address], type: 'Simple Key Pair' }; - const obj = await keyringController.importAccountWithStrategy(AccountImportStrategy.privateKey, [privateKey]); - const modifiedState = { ...initialState, keyrings: [initialState.keyrings[0], newKeyring] }; + const obj = await keyringController.importAccountWithStrategy( + AccountImportStrategy.privateKey, + [privateKey], + ); + const modifiedState = { + ...initialState, + keyrings: [initialState.keyrings[0], newKeyring], + }; expect(obj).toStrictEqual(modifiedState); }); it('should import account with strategy json', async () => { const somePassword = 'holachao123'; const address = '0xb97c80fab7a3793bbe746864db80d236f1345ea7'; - const obj = await keyringController.importAccountWithStrategy(AccountImportStrategy.json, [input, somePassword]); + const obj = await keyringController.importAccountWithStrategy( + AccountImportStrategy.json, + [input, somePassword], + ); const newKeyring = { accounts: [address], type: 'Simple Key Pair' }; - const modifiedState = { ...initialState, keyrings: [initialState.keyrings[0], newKeyring] }; + const modifiedState = { + ...initialState, + keyrings: [initialState.keyrings[0], newKeyring], + }; expect(obj).toStrictEqual(modifiedState); }); it('should throw when passed an unrecognized strategy', async () => { const somePassword = 'holachao123'; await expect( - keyringController.importAccountWithStrategy('junk' as AccountImportStrategy, [input, somePassword]), + keyringController.importAccountWithStrategy( + 'junk' as AccountImportStrategy, + [input, somePassword], + ), ).rejects.toThrow("Unexpected import strategy: 'junk'"); }); @@ -150,30 +195,47 @@ describe('KeyringController', () => { const somePassword = 'holachao12'; let error; try { - await keyringController.importAccountWithStrategy(AccountImportStrategy.json, [input, somePassword]); + await keyringController.importAccountWithStrategy( + AccountImportStrategy.json, + [input, somePassword], + ); } catch (e) { error = e; } - expect(error.message).toBe('Key derivation failed - possibly wrong passphrase'); + expect(error.message).toBe( + 'Key derivation failed - possibly wrong passphrase', + ); }); it('should remove account', async () => { - await keyringController.importAccountWithStrategy(AccountImportStrategy.privateKey, [privateKey]); - const finalState = await keyringController.removeAccount('0x51253087e6f8358b5f10c0a94315d69db3357859'); + await keyringController.importAccountWithStrategy( + AccountImportStrategy.privateKey, + [privateKey], + ); + const finalState = await keyringController.removeAccount( + '0x51253087e6f8358b5f10c0a94315d69db3357859', + ); expect(finalState).toStrictEqual(initialState); }); it('should sign message', async () => { - const data = '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0'; + const data = + '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0'; const account = initialState.keyrings[0].accounts[0]; - const signature = await keyringController.signMessage({ data, from: account }); + const signature = await keyringController.signMessage({ + data, + from: account, + }); expect(signature).not.toBe(''); }); it('should sign personal message', async () => { const data = ethUtil.bufferToHex(Buffer.from('Hello from test', 'utf8')); const account = initialState.keyrings[0].accounts[0]; - const signature = await keyringController.signPersonalMessage({ data, from: account }); + const signature = await keyringController.signPersonalMessage({ + data, + from: account, + }); const recovered = recoverPersonalSignature({ data, sig: signature }); expect(account).toBe(recovered); }); @@ -193,8 +255,13 @@ describe('KeyringController', () => { ]; const account = initialState.keyrings[0].accounts[0]; await expect( - keyringController.signTypedMessage({ data: typedMsgParams, from: account }, 'junk' as SignTypedDataVersion), - ).rejects.toThrow("Keyring Controller signTypedMessage: Error: Unexpected signTypedMessage version: 'junk'"); + keyringController.signTypedMessage( + { data: typedMsgParams, from: account }, + 'junk' as SignTypedDataVersion, + ), + ).rejects.toThrow( + "Keyring Controller signTypedMessage: Error: Unexpected signTypedMessage version: 'junk'", + ); }); it('should sign typed message V1', async () => { @@ -215,7 +282,10 @@ describe('KeyringController', () => { { data: typedMsgParams, from: account }, SignTypedDataVersion.V1, ); - const recovered = recoverTypedSignatureLegacy({ data: typedMsgParams, sig: signature as string }); + const recovered = recoverTypedSignatureLegacy({ + data: typedMsgParams, + sig: signature as string, + }); expect(account).toBe(recovered); }); @@ -229,8 +299,14 @@ describe('KeyringController', () => { }, message: { contents: 'Hello, Bob!', - from: { name: 'Cow', wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826' }, - to: { name: 'Bob', wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB' }, + from: { + name: 'Cow', + wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', + }, + to: { + name: 'Bob', + wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', + }, }, primaryType: 'Mail', types: { @@ -256,7 +332,10 @@ describe('KeyringController', () => { { data: JSON.stringify(msgParams), from: account }, SignTypedDataVersion.V3, ); - const recovered = recoverTypedSignature({ data: msgParams as any, sig: signature as string }); + const recovered = recoverTypedSignature({ + data: msgParams as any, + sig: signature as string, + }); expect(account).toBe(recovered); }); @@ -272,7 +351,10 @@ describe('KeyringController', () => { contents: 'Hello, Bob!', from: { name: 'Cow', - wallets: ['0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', '0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF'], + wallets: [ + '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', + '0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF', + ], }, to: [ { @@ -314,7 +396,10 @@ describe('KeyringController', () => { { data: JSON.stringify(msgParams), from: account }, SignTypedDataVersion.V4, ); - const recovered = recoverTypedSignature_v4({ data: msgParams as any, sig: signature as string }); + const recovered = recoverTypedSignature_v4({ + data: msgParams as any, + sig: signature as string, + }); expect(account).toBe(recovered); }); @@ -323,13 +408,19 @@ describe('KeyringController', () => { const account = initialState.keyrings[0].accounts[0]; let error1; try { - await keyringController.signTypedMessage({ data: msgParams, from: account }, SignTypedDataVersion.V1); + await keyringController.signTypedMessage( + { data: msgParams, from: account }, + SignTypedDataVersion.V1, + ); } catch (e) { error1 = e; } let error2; try { - await keyringController.signTypedMessage({ data: msgParams, from: account }, SignTypedDataVersion.V3); + await keyringController.signTypedMessage( + { data: msgParams, from: account }, + SignTypedDataVersion.V3, + ); } catch (e) { error2 = e; } @@ -349,7 +440,10 @@ describe('KeyringController', () => { value: '0x5208', }; const ethTransaction = new Transaction({ ...transaction }); - const signature = await keyringController.signTransaction(ethTransaction, account); + const signature = await keyringController.signTransaction( + ethTransaction, + account, + ); expect(signature).not.toBe(''); }); @@ -361,10 +455,15 @@ describe('KeyringController', () => { it('should subscribe and unsubscribe', async () => { const listener = stub(); keyringController.subscribe(listener); - await keyringController.importAccountWithStrategy(AccountImportStrategy.privateKey, [privateKey]); + await keyringController.importAccountWithStrategy( + AccountImportStrategy.privateKey, + [privateKey], + ); expect(listener.called).toBe(true); keyringController.unsubscribe(listener); - await keyringController.removeAccount('0x51253087e6f8358b5f10c0a94315d69db3357859'); + await keyringController.removeAccount( + '0x51253087e6f8358b5f10c0a94315d69db3357859', + ); expect(listener.calledTwice).toBe(false); }); diff --git a/src/keyring/KeyringController.ts b/src/keyring/KeyringController.ts index b4378ef942..29ebcd8a80 100644 --- a/src/keyring/KeyringController.ts +++ b/src/keyring/KeyringController.ts @@ -1,10 +1,19 @@ import * as ethUtil from 'ethereumjs-util'; import { stripHexPrefix } from 'ethjs-util'; -import { normalize as normalizeAddress, signTypedData, signTypedData_v4, signTypedDataLegacy } from 'eth-sig-util'; +import { + normalize as normalizeAddress, + signTypedData, + signTypedData_v4, + signTypedDataLegacy, +} from 'eth-sig-util'; import Wallet, { thirdparty as importers } from 'ethereumjs-wallet'; import Keyring from 'eth-keyring-controller'; import { Mutex } from 'async-mutex'; -import BaseController, { BaseConfig, BaseState, Listener } from '../BaseController'; +import BaseController, { + BaseConfig, + BaseState, + Listener, +} from '../BaseController'; import PreferencesController from '../user/PreferencesController'; import { PersonalMessageParams } from '../message-manager/PersonalMessageManager'; import { TypedMessageParams } from '../message-manager/TypedMessageManager'; @@ -109,7 +118,10 @@ export enum SignTypedDataVersion { /** * Controller responsible for establishing and managing user identity */ -export class KeyringController extends BaseController { +export class KeyringController extends BaseController< + KeyringConfig, + KeyringState +> { private mutex = new Mutex(); /** @@ -152,7 +164,9 @@ export class KeyringController extends BaseController, ) { super(config, state); - privates.set(this, { keyring: new Keyring(Object.assign({ initState: state }, config)) }); + privates.set(this, { + keyring: new Keyring(Object.assign({ initState: state }, config)), + }); this.defaultState = { ...privates.get(this).keyring.store.getState(), keyrings: [], @@ -171,7 +185,9 @@ export class KeyringController extends BaseController { - const primaryKeyring = privates.get(this).keyring.getKeyringsByType('HD Key Tree')[0]; + const primaryKeyring = privates + .get(this) + .keyring.getKeyringsByType('HD Key Tree')[0]; /* istanbul ignore if */ if (!primaryKeyring) { throw new Error('No HD keyring found'); @@ -197,7 +213,9 @@ export class KeyringController extends BaseController { - const primaryKeyring = privates.get(this).keyring.getKeyringsByType('HD Key Tree')[0]; + const primaryKeyring = privates + .get(this) + .keyring.getKeyringsByType('HD Key Tree')[0]; /* istanbul ignore if */ if (!primaryKeyring) { throw new Error('No HD keyring found'); @@ -219,7 +237,9 @@ export class KeyringController extends BaseController { + async importAccountWithStrategy( + strategy: AccountImportStrategy, + args: any[], + ): Promise { let privateKey; switch (strategy) { case 'privateKey': @@ -326,7 +351,9 @@ export class KeyringController extends BaseController { - const primaryKeyring = privates.get(this).keyring.getKeyringsByType(KeyringTypes.hd)[0]; + const primaryKeyring = privates + .get(this) + .keyring.getKeyringsByType(KeyringTypes.hd)[0]; /* istanbul ignore if */ if (!primaryKeyring) { throw new Error('No HD keyring found.'); @@ -489,8 +527,13 @@ export class KeyringController extends BaseController => { const keyringAccounts = await keyring.getAccounts(); const accounts = Array.isArray(keyringAccounts) - ? keyringAccounts.map((address) => ethUtil.toChecksumAddress(address)) + ? keyringAccounts.map((address) => + ethUtil.toChecksumAddress(address), + ) : /* istanbul ignore next */ []; return { accounts, diff --git a/src/message-manager/AbstractMessageManager.test.ts b/src/message-manager/AbstractMessageManager.test.ts index b8e4f2a200..2fe9d7fda9 100644 --- a/src/message-manager/AbstractMessageManager.test.ts +++ b/src/message-manager/AbstractMessageManager.test.ts @@ -1,8 +1,18 @@ -import { TypedMessage, TypedMessageParams, TypedMessageParamsMetamask } from './TypedMessageManager'; +import { + TypedMessage, + TypedMessageParams, + TypedMessageParamsMetamask, +} from './TypedMessageManager'; import AbstractMessageManager from './AbstractMessageManager'; -class AbstractTestManager extends AbstractMessageManager { - prepMessageForSigning(messageParams: TypedMessageParamsMetamask): Promise { +class AbstractTestManager extends AbstractMessageManager< + TypedMessage, + TypedMessageParams, + TypedMessageParamsMetamask +> { + prepMessageForSigning( + messageParams: TypedMessageParamsMetamask, + ): Promise { delete messageParams.metamaskId; delete messageParams.version; return Promise.resolve(messageParams); @@ -34,7 +44,10 @@ const messageData = typedMessage; describe('AbstractTestManager', () => { it('should set default state', () => { const controller = new AbstractTestManager(); - expect(controller.state).toStrictEqual({ unapprovedMessages: {}, unapprovedMessagesCount: 0 }); + expect(controller.state).toStrictEqual({ + unapprovedMessages: {}, + unapprovedMessagesCount: 0, + }); }); it('should set default config', () => { @@ -167,7 +180,11 @@ describe('AbstractTestManager', () => { time: messageTime, type: messageType, }); - const messageParams = await controller.approveMessage({ ...firstMessage, metamaskId: messageId, version }); + const messageParams = await controller.approveMessage({ + ...firstMessage, + metamaskId: messageId, + version, + }); const message = controller.getMessage(messageId); expect(messageParams).toStrictEqual(firstMessage); expect(message).not.toBeUndefined(); diff --git a/src/message-manager/AbstractMessageManager.ts b/src/message-manager/AbstractMessageManager.ts index 5c8b0a6759..d28f66ec14 100644 --- a/src/message-manager/AbstractMessageManager.ts +++ b/src/message-manager/AbstractMessageManager.ts @@ -65,7 +65,8 @@ export interface AbstractMessageParamsMetamask extends AbstractMessageParams { * @property unapprovedMessages - A collection of all Messages in the 'unapproved' state * @property unapprovedMessagesCount - The count of all Messages in this.unapprovedMessages */ -export interface MessageManagerState extends BaseState { +export interface MessageManagerState + extends BaseState { unapprovedMessages: { [key: string]: M }; unapprovedMessagesCount: number; } @@ -141,7 +142,10 @@ export abstract class AbstractMessageManager< * @param config - Initial options used to configure this controller * @param state - Initial state to set on this controller */ - constructor(config?: Partial, state?: Partial>) { + constructor( + config?: Partial, + state?: Partial>, + ) { super(config, state); this.defaultState = { unapprovedMessages: {}, diff --git a/src/message-manager/MessageManager.test.ts b/src/message-manager/MessageManager.test.ts index db969b25ae..5a495de1de 100644 --- a/src/message-manager/MessageManager.test.ts +++ b/src/message-manager/MessageManager.test.ts @@ -3,7 +3,10 @@ import MessageManager from './MessageManager'; describe('PersonalMessageManager', () => { it('should set default state', () => { const controller = new MessageManager(); - expect(controller.state).toStrictEqual({ unapprovedMessages: {}, unapprovedMessagesCount: 0 }); + expect(controller.state).toStrictEqual({ + unapprovedMessages: {}, + unapprovedMessagesCount: 0, + }); }); it('should set default config', () => { @@ -102,7 +105,10 @@ describe('PersonalMessageManager', () => { from: '0xfoO', }; const originalRequest = { origin: 'origin' }; - const messageId = controller.addUnapprovedMessage(messageParams, originalRequest); + const messageId = controller.addUnapprovedMessage( + messageParams, + originalRequest, + ); expect(messageId).not.toBeUndefined(); const message = controller.getMessage(messageId); if (message) { @@ -155,7 +161,10 @@ describe('PersonalMessageManager', () => { const controller = new MessageManager(); const firstMessage = { from: 'foo', data: '0x123' }; const messageId = controller.addUnapprovedMessage(firstMessage); - const messageParams = await controller.approveMessage({ ...firstMessage, metamaskId: messageId }); + const messageParams = await controller.approveMessage({ + ...firstMessage, + metamaskId: messageId, + }); const message = controller.getMessage(messageId); expect(messageParams).toStrictEqual(firstMessage); expect(message).not.toBeUndefined(); diff --git a/src/message-manager/MessageManager.ts b/src/message-manager/MessageManager.ts index 76553a6d77..8b4bce49b3 100644 --- a/src/message-manager/MessageManager.ts +++ b/src/message-manager/MessageManager.ts @@ -54,7 +54,11 @@ export interface MessageParamsMetamask extends AbstractMessageParamsMetamask { /** * Controller in charge of managing - storing, adding, removing, updating - Messages. */ -export class MessageManager extends AbstractMessageManager { +export class MessageManager extends AbstractMessageManager< + Message, + MessageParams, + MessageParamsMetamask +> { /** * Name of this controller used during composition */ @@ -68,7 +72,10 @@ export class MessageManager extends AbstractMessageManager { + addUnapprovedMessageAsync( + messageParams: MessageParams, + req?: OriginalRequest, + ): Promise { return new Promise((resolve, reject) => { validateSignMessageData(messageParams); const messageId = this.addUnapprovedMessage(messageParams, req); @@ -77,9 +84,19 @@ export class MessageManager extends AbstractMessageManager { + prepMessageForSigning( + messageParams: MessageParamsMetamask, + ): Promise { delete messageParams.metamaskId; return Promise.resolve(messageParams); } diff --git a/src/message-manager/PersonalMessageManager.test.ts b/src/message-manager/PersonalMessageManager.test.ts index 34ecdf4a4b..13f22a6e17 100644 --- a/src/message-manager/PersonalMessageManager.test.ts +++ b/src/message-manager/PersonalMessageManager.test.ts @@ -3,7 +3,10 @@ import PersonalMessageManager from './PersonalMessageManager'; describe('PersonalMessageManager', () => { it('should set default state', () => { const controller = new PersonalMessageManager(); - expect(controller.state).toStrictEqual({ unapprovedMessages: {}, unapprovedMessagesCount: 0 }); + expect(controller.state).toStrictEqual({ + unapprovedMessages: {}, + unapprovedMessagesCount: 0, + }); }); it('should set default config', () => { @@ -102,7 +105,10 @@ describe('PersonalMessageManager', () => { from: '0xfoO', }; const originalRequest = { origin: 'origin' }; - const messageId = controller.addUnapprovedMessage(messageParams, originalRequest); + const messageId = controller.addUnapprovedMessage( + messageParams, + originalRequest, + ); expect(messageId).not.toBeUndefined(); const message = controller.getMessage(messageId); if (message) { @@ -155,7 +161,10 @@ describe('PersonalMessageManager', () => { const controller = new PersonalMessageManager(); const firstMessage = { from: 'foo', data: '0x123' }; const messageId = controller.addUnapprovedMessage(firstMessage); - const messageParams = await controller.approveMessage({ ...firstMessage, metamaskId: messageId }); + const messageParams = await controller.approveMessage({ + ...firstMessage, + metamaskId: messageId, + }); const message = controller.getMessage(messageId); expect(messageParams).toStrictEqual(firstMessage); expect(message).not.toBeUndefined(); diff --git a/src/message-manager/PersonalMessageManager.ts b/src/message-manager/PersonalMessageManager.ts index cfb36f2722..e672c1617d 100644 --- a/src/message-manager/PersonalMessageManager.ts +++ b/src/message-manager/PersonalMessageManager.ts @@ -47,7 +47,8 @@ export interface PersonalMessageParams extends AbstractMessageParams { * @property from - Address to sign this message from * @property origin? - Added for request origin identification */ -export interface PersonalMessageParamsMetamask extends AbstractMessageParamsMetamask { +export interface PersonalMessageParamsMetamask + extends AbstractMessageParamsMetamask { data: string; } @@ -72,7 +73,10 @@ export class PersonalMessageManager extends AbstractMessageManager< * @param req? - The original request object possibly containing the origin * @returns - Promise resolving to the raw data of the signature request */ - addUnapprovedMessageAsync(messageParams: PersonalMessageParams, req?: OriginalRequest): Promise { + addUnapprovedMessageAsync( + messageParams: PersonalMessageParams, + req?: OriginalRequest, + ): Promise { return new Promise((resolve, reject) => { validateSignMessageData(messageParams); const messageId = this.addUnapprovedMessage(messageParams, req); @@ -81,10 +85,18 @@ export class PersonalMessageManager extends AbstractMessageManager< case 'signed': return resolve(data.rawSig as string); case 'rejected': - return reject(new Error('MetaMask Personal Message Signature: User denied message signature.')); + return reject( + new Error( + 'MetaMask Personal Message Signature: User denied message signature.', + ), + ); default: return reject( - new Error(`MetaMask Personal Message Signature: Unknown problem: ${JSON.stringify(messageParams)}`), + new Error( + `MetaMask Personal Message Signature: Unknown problem: ${JSON.stringify( + messageParams, + )}`, + ), ); } }); @@ -101,7 +113,10 @@ export class PersonalMessageManager extends AbstractMessageManager< * @param req? - The original request object possibly containing the origin * @returns - The id of the newly created message */ - addUnapprovedMessage(messageParams: PersonalMessageParams, req?: OriginalRequest) { + addUnapprovedMessage( + messageParams: PersonalMessageParams, + req?: OriginalRequest, + ) { if (req) { messageParams.origin = req.origin; } @@ -115,7 +130,10 @@ export class PersonalMessageManager extends AbstractMessageManager< type: 'personal_sign', }; this.addMessage(messageData); - this.hub.emit(`unapprovedMessage`, { ...messageParams, ...{ metamaskId: messageId } }); + this.hub.emit(`unapprovedMessage`, { + ...messageParams, + ...{ metamaskId: messageId }, + }); return messageId; } @@ -126,7 +144,9 @@ export class PersonalMessageManager extends AbstractMessageManager< * @param messageParams - The messageParams to modify * @returns - Promise resolving to the messageParams with the metamaskId property removed */ - prepMessageForSigning(messageParams: PersonalMessageParamsMetamask): Promise { + prepMessageForSigning( + messageParams: PersonalMessageParamsMetamask, + ): Promise { delete messageParams.metamaskId; return Promise.resolve(messageParams); } diff --git a/src/message-manager/TypedMessageManager.test.ts b/src/message-manager/TypedMessageManager.test.ts index 03d68ee2eb..1807080555 100644 --- a/src/message-manager/TypedMessageManager.test.ts +++ b/src/message-manager/TypedMessageManager.test.ts @@ -15,7 +15,10 @@ const typedMessage = [ describe('TypedMessageManager', () => { it('should set default state', () => { const controller = new TypedMessageManager(); - expect(controller.state).toStrictEqual({ unapprovedMessages: {}, unapprovedMessagesCount: 0 }); + expect(controller.state).toStrictEqual({ + unapprovedMessages: {}, + unapprovedMessagesCount: 0, + }); }); it('should set default config', () => { @@ -118,7 +121,9 @@ describe('TypedMessageManager', () => { expect(unapprovedMessages[keys[0]].status).toBe('errored'); }); controller.setMessageStatusErrored(keys[0], 'error message'); - await expect(result).rejects.toThrow('MetaMask Typed Message Signature: error message'); + await expect(result).rejects.toThrow( + 'MetaMask Typed Message Signature: error message', + ); }); it('should throw when unapproved finishes', async () => { @@ -150,7 +155,11 @@ describe('TypedMessageManager', () => { from: '0xfoO', }; const originalRequest = { origin: 'origin' }; - const messageId = controller.addUnapprovedMessage(messageParams, version, originalRequest); + const messageId = controller.addUnapprovedMessage( + messageParams, + version, + originalRequest, + ); expect(messageId).not.toBeUndefined(); const message = controller.getMessage(messageId); if (message) { @@ -249,7 +258,11 @@ describe('TypedMessageManager', () => { const firstMessage = { from: '0xfoO', data: messageData }; const version = 'V1'; const messageId = controller.addUnapprovedMessage(firstMessage, version); - const messageParams = await controller.approveMessage({ ...firstMessage, metamaskId: messageId, version }); + const messageParams = await controller.approveMessage({ + ...firstMessage, + metamaskId: messageId, + version, + }); const message = controller.getMessage(messageId); expect(messageParams).toStrictEqual(firstMessage); expect(message).not.toBeUndefined(); diff --git a/src/message-manager/TypedMessageManager.ts b/src/message-manager/TypedMessageManager.ts index efcc5e4609..6f1afc2dad 100644 --- a/src/message-manager/TypedMessageManager.ts +++ b/src/message-manager/TypedMessageManager.ts @@ -1,5 +1,8 @@ import { v1 as random } from 'uuid'; -import { validateTypedSignMessageDataV3, validateTypedSignMessageDataV1 } from '../util'; +import { + validateTypedSignMessageDataV3, + validateTypedSignMessageDataV1, +} from '../util'; import AbstractMessageManager, { AbstractMessage, AbstractMessageParams, @@ -58,7 +61,8 @@ export interface TypedMessageParams extends AbstractMessageParams { * @property origin? - Added for request origin identification * @property version - Compatibility version EIP712 */ -export interface TypedMessageParamsMetamask extends AbstractMessageParamsMetamask { +export interface TypedMessageParamsMetamask + extends AbstractMessageParamsMetamask { data: Record[] | string; metamaskId?: string; error?: string; @@ -105,12 +109,22 @@ export class TypedMessageManager extends AbstractMessageManager< case 'signed': return resolve(data.rawSig as string); case 'rejected': - return reject(new Error('MetaMask Typed Message Signature: User denied message signature.')); + return reject( + new Error( + 'MetaMask Typed Message Signature: User denied message signature.', + ), + ); case 'errored': - return reject(new Error(`MetaMask Typed Message Signature: ${data.error}`)); + return reject( + new Error(`MetaMask Typed Message Signature: ${data.error}`), + ); default: return reject( - new Error(`MetaMask Typed Message Signature: Unknown problem: ${JSON.stringify(messageParams)}`), + new Error( + `MetaMask Typed Message Signature: Unknown problem: ${JSON.stringify( + messageParams, + )}`, + ), ); } }); @@ -128,9 +142,17 @@ export class TypedMessageManager extends AbstractMessageManager< * @param req? - The original request object possibly containing the origin * @returns - The id of the newly created TypedMessage */ - addUnapprovedMessage(messageParams: TypedMessageParams, version: string, req?: OriginalRequest) { + addUnapprovedMessage( + messageParams: TypedMessageParams, + version: string, + req?: OriginalRequest, + ) { const messageId = random(); - const messageParamsMetamask = { ...messageParams, metamaskId: messageId, version }; + const messageParamsMetamask = { + ...messageParams, + metamaskId: messageId, + version, + }; if (req) { messageParams.origin = req.origin; } @@ -170,7 +192,9 @@ export class TypedMessageManager extends AbstractMessageManager< * @param messageParams - The messageParams to modify * @returns - Promise resolving to the messageParams with the metamaskId and version properties removed */ - prepMessageForSigning(messageParams: TypedMessageParamsMetamask): Promise { + prepMessageForSigning( + messageParams: TypedMessageParamsMetamask, + ): Promise { delete messageParams.metamaskId; delete messageParams.version; return Promise.resolve(messageParams); diff --git a/src/network/NetworkController.test.ts b/src/network/NetworkController.test.ts index 3b29354556..b6a2bbf8a3 100644 --- a/src/network/NetworkController.test.ts +++ b/src/network/NetworkController.test.ts @@ -1,6 +1,10 @@ import { stub } from 'sinon'; import Web3ProviderEngine from 'web3-provider-engine'; -import NetworkController, { NetworksChainId, ProviderConfig, NetworkType } from './NetworkController'; +import NetworkController, { + NetworksChainId, + ProviderConfig, + NetworkType, +} from './NetworkController'; const RPC_TARGET = 'http://foo'; @@ -18,7 +22,9 @@ describe('NetworkController', () => { it('should throw when providerConfig property is accessed', () => { const controller = new NetworkController(); - expect(() => console.log(controller.providerConfig)).toThrow('Property only used for setting'); + expect(() => console.log(controller.providerConfig)).toThrow( + 'Property only used for setting', + ); }); it('should create a provider instance for default infura network', () => { @@ -115,14 +121,18 @@ describe('NetworkController', () => { it('should throw when setting an unrecognized provider type', () => { const controller = new NetworkController(); - expect(() => controller.setProviderType('junk' as NetworkType)).toThrow("Unrecognized network type: 'junk'"); + expect(() => controller.setProviderType('junk' as NetworkType)).toThrow( + "Unrecognized network type: 'junk'", + ); }); it('should verify the network on an error', async () => { const testConfig = { infuraProjectId: 'foo', }; - const controller = new NetworkController(testConfig, { network: 'loading' }); + const controller = new NetworkController(testConfig, { + network: 'loading', + }); controller.providerConfig = {} as ProviderConfig; controller.lookupNetwork = stub(); controller.provider.emit('error', {}); diff --git a/src/network/NetworkController.ts b/src/network/NetworkController.ts index e567dea9d1..08ab273220 100644 --- a/src/network/NetworkController.ts +++ b/src/network/NetworkController.ts @@ -8,7 +8,14 @@ import BaseController, { BaseConfig, BaseState } from '../BaseController'; /** * Human-readable network name */ -export type NetworkType = 'kovan' | 'localhost' | 'mainnet' | 'rinkeby' | 'goerli' | 'ropsten' | 'rpc'; +export type NetworkType = + | 'kovan' + | 'localhost' + | 'mainnet' + | 'rinkeby' + | 'goerli' + | 'ropsten' + | 'rpc'; export enum NetworksChainId { mainnet = '1', @@ -70,7 +77,10 @@ const LOCALHOST_RPC_URL = 'http://localhost:8545'; /** * Controller that creates and manages an Ethereum network provider */ -export class NetworkController extends BaseController { +export class NetworkController extends BaseController< + NetworkConfig, + NetworkState +> { private ethQuery: any; private internalProviderConfig: ProviderConfig = {} as ProviderConfig; @@ -96,7 +106,8 @@ export class NetworkController extends BaseController { - this.update({ network: error ? /* istanbul ignore next*/ 'loading' : network }); - releaseLock(); - }); + this.ethQuery.sendAsync( + { method: 'net_version' }, + (error: Error, network: string) => { + this.update({ + network: error ? /* istanbul ignore next*/ 'loading' : network, + }); + releaseLock(); + }, + ); } /** @@ -226,7 +250,12 @@ export class NetworkController extends BaseController { isShown: false, }, }; - expect(controller.state.notifications).toStrictEqual(expectedStateNotifications); + expect(controller.state.notifications).toStrictEqual( + expectedStateNotifications, + ); }); it('should add new notifcation to state', () => { diff --git a/src/notification/NotificationController.ts b/src/notification/NotificationController.ts index 116fe52cc0..7ea5eb2e2c 100644 --- a/src/notification/NotificationController.ts +++ b/src/notification/NotificationController.ts @@ -49,7 +49,10 @@ const defaultState = { /** * Controller for managing in-app announcement notifications. */ -export class NotificationController extends BaseController { +export class NotificationController extends BaseController< + NotificationConfig, + NotificationState +> { /** * Creates a NotificationController instance * @@ -73,14 +76,18 @@ export class NotificationController extends BaseController { - newNotifications[notification.id] = this.state.notifications[notification.id] - ? this.state.notifications[notification.id] - : { - ...notification, - isShown: false, - }; - }); + Object.values(allNotifications).forEach( + (notification: StateNotification) => { + newNotifications[notification.id] = this.state.notifications[ + notification.id + ] + ? this.state.notifications[notification.id] + : { + ...notification, + isShown: false, + }; + }, + ); this.update({ notifications: newNotifications }); } diff --git a/src/third-party/EnsController.test.ts b/src/third-party/EnsController.test.ts index 9015c90703..be991ff983 100644 --- a/src/third-party/EnsController.test.ts +++ b/src/third-party/EnsController.test.ts @@ -193,7 +193,9 @@ describe('EnsController', () => { const controller = new EnsController(); expect(() => { controller.set('1', name1, 'foo'); - }).toThrow('Invalid ENS entry: { chainId:1, ensName:foobarb.eth, address:foo}'); + }).toThrow( + 'Invalid ENS entry: { chainId:1, ensName:foobarb.eth, address:foo}', + ); expect(controller.state).toStrictEqual({ ensEntries: {} }); }); diff --git a/src/third-party/EnsController.ts b/src/third-party/EnsController.ts index 860211a2ef..bc519e2879 100644 --- a/src/third-party/EnsController.ts +++ b/src/third-party/EnsController.ts @@ -69,7 +69,11 @@ export class EnsController extends BaseController { */ delete(chainId: string, ensName: string): boolean { const normalizedEnsName = normalizeEnsName(ensName); - if (!normalizedEnsName || !this.state.ensEntries[chainId] || !this.state.ensEntries[chainId][normalizedEnsName]) { + if ( + !normalizedEnsName || + !this.state.ensEntries[chainId] || + !this.state.ensEntries[chainId][normalizedEnsName] + ) { return false; } @@ -120,7 +124,9 @@ export class EnsController extends BaseController { typeof ensName !== 'string' || (address && !isValidAddress(address)) ) { - throw new Error(`Invalid ENS entry: { chainId:${chainId}, ensName:${ensName}, address:${address}}`); + throw new Error( + `Invalid ENS entry: { chainId:${chainId}, ensName:${ensName}, address:${address}}`, + ); } const normalizedEnsName = normalizeEnsName(ensName); @@ -131,7 +137,10 @@ export class EnsController extends BaseController { const normalizedAddress = address ? toChecksumAddress(address) : null; const subState = this.state.ensEntries[chainId]; - if (subState?.[normalizedEnsName] && subState[normalizedEnsName].address === normalizedAddress) { + if ( + subState?.[normalizedEnsName] && + subState[normalizedEnsName].address === normalizedAddress + ) { return false; } diff --git a/src/third-party/PhishingController.test.ts b/src/third-party/PhishingController.test.ts index f3ce494291..9f1c60f6e7 100644 --- a/src/third-party/PhishingController.test.ts +++ b/src/third-party/PhishingController.test.ts @@ -110,6 +110,8 @@ describe('PhishingController', () => { const controller = new PhishingController(); - await expect(controller.updatePhishingLists()).rejects.toThrow(/Fetch failed with status '500'/u); + await expect(controller.updatePhishingLists()).rejects.toThrow( + /Fetch failed with status '500'/u, + ); }); }); diff --git a/src/third-party/PhishingController.ts b/src/third-party/PhishingController.ts index b6a222c510..adc2c3a222 100644 --- a/src/third-party/PhishingController.ts +++ b/src/third-party/PhishingController.ts @@ -50,8 +50,12 @@ export interface PhishingState extends BaseState { /** * Controller that passively polls on a set interval for approved and unapproved website origins */ -export class PhishingController extends BaseController { - private configUrl = 'https://cdn.jsdelivr.net/gh/MetaMask/eth-phishing-detect@master/src/config.json'; +export class PhishingController extends BaseController< + PhishingConfig, + PhishingState +> { + private configUrl = + 'https://cdn.jsdelivr.net/gh/MetaMask/eth-phishing-detect@master/src/config.json'; private detector: any; @@ -68,7 +72,10 @@ export class PhishingController extends BaseController, state?: Partial) { + constructor( + config?: Partial, + state?: Partial, + ) { super(config, state); this.defaultConfig = { interval: 60 * 60 * 1000 }; this.defaultState = { @@ -137,7 +144,9 @@ export class PhishingController extends BaseController { + private async queryConfig( + input: RequestInfo, + ): Promise { const response = await fetch(input, { cache: 'no-cache' }); switch (response.status) { @@ -149,7 +158,9 @@ export class PhishingController extends BaseController gasPrice: (callback: any) => { callback(undefined, '0x0'); }, - getBlockByNumber: (_blocknumber: any, _fetchTxs: boolean, callback: any) => { + getBlockByNumber: ( + _blocknumber: any, + _fetchTxs: boolean, + callback: any, + ) => { callback(undefined, { gasLimit: '0x0' }); }, getCode: (_to: any, callback: any) => { @@ -60,11 +72,21 @@ function mockFetchs(data: any) { } const MOCK_PRFERENCES = { state: { selectedAddress: 'foo' } }; -const PROVIDER = new HttpProvider('https://ropsten.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035'); -const MAINNET_PROVIDER = new HttpProvider('https://mainnet.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035'); +const PROVIDER = new HttpProvider( + 'https://ropsten.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035', +); +const MAINNET_PROVIDER = new HttpProvider( + 'https://mainnet.infura.io/v3/341eacb578dd44a1a049cbc5f6fd4035', +); const MOCK_NETWORK = { getProvider: () => PROVIDER, - state: { network: '3', provider: { type: 'ropsten' as NetworkType, chainId: NetworksChainId.ropsten } }, + state: { + network: '3', + provider: { + type: 'ropsten' as NetworkType, + chainId: NetworksChainId.ropsten, + }, + }, subscribe: () => undefined, }; const MOCK_NETWORK_WITHOUT_CHAIN_ID = { @@ -74,12 +96,20 @@ const MOCK_NETWORK_WITHOUT_CHAIN_ID = { }; const MOCK_MAINNET_NETWORK = { getProvider: () => MAINNET_PROVIDER, - state: { network: '1', provider: { type: 'mainnet' as NetworkType, chainId: NetworksChainId.mainnet } }, + state: { + network: '1', + provider: { + type: 'mainnet' as NetworkType, + chainId: NetworksChainId.mainnet, + }, + }, subscribe: () => undefined, }; -const TOKEN_TRANSACTION_HASH = '0x01d1cebeab9da8d887b36000c25fa175737e150f193ea37d5bb66347d834e999'; -const ETHER_TRANSACTION_HASH = '0xa9d17df83756011ea63e1f0ca50a6627df7cac9806809e36680fcf4e88cb9dae'; +const TOKEN_TRANSACTION_HASH = + '0x01d1cebeab9da8d887b36000c25fa175737e150f193ea37d5bb66347d834e999'; +const ETHER_TRANSACTION_HASH = + '0xa9d17df83756011ea63e1f0ca50a6627df7cac9806809e36680fcf4e88cb9dae'; const ETH_TRANSACTIONS = [ { @@ -165,7 +195,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1564091067', hash: TOKEN_TRANSACTION_HASH, nonce: '2329', - blockHash: '0x3c30a9be9aea7be13caad419444140c11839d72e70479ec7e9c6d8bd08c533bc', + blockHash: + '0x3c30a9be9aea7be13caad419444140c11839d72e70479ec7e9c6d8bd08c533bc', from: '0xdfa6edae2ec0cf1d4a60542422724a48195a5071', contractAddress: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', to: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', @@ -186,7 +217,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1564091247', hash: '0xdcd1c8bee545d3f76d80b20a23ad44276ba2e376681228eb4570cf3518491279', nonce: '2330', - blockHash: '0x16986dd66bedb20a5b846ec2b6c0ecaa62f1c4b51fac58c1326101fd9126dd82', + blockHash: + '0x16986dd66bedb20a5b846ec2b6c0ecaa62f1c4b51fac58c1326101fd9126dd82', from: '0xdfa6edae2ec0cf1d4a60542422724a48195a5071', contractAddress: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', to: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', @@ -207,7 +239,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1564111652', hash: '0x070369e6f560b0deca52e050ff1a961fa7b688bbec5cea08435921c9d9b0f52e', nonce: '2333', - blockHash: '0x0aff8b36881be99df6d176d7c64c2171672c0483684a10c112d2c90fefe30a0a', + blockHash: + '0x0aff8b36881be99df6d176d7c64c2171672c0483684a10c112d2c90fefe30a0a', from: '0xdfa6edae2ec0cf1d4a60542422724a48195a5071', contractAddress: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', to: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', @@ -228,7 +261,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1564126442', hash: '0x8ef20ec9597c8c2e945bcc76d2668e5d3bb088b081fe8c5b5af2e1cbd315a20f', nonce: '31', - blockHash: '0xb80d4d861ecb7a3cb14e591c0aaeb226842d0267772affa2acc1a590c7535647', + blockHash: + '0xb80d4d861ecb7a3cb14e591c0aaeb226842d0267772affa2acc1a590c7535647', from: '0x6c70e3563cef0c6835703bb2664c9f59a92353e4', contractAddress: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', to: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', @@ -249,7 +283,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1564168901', hash: '0xa0f2d7b558bb3cc28fa568f6feb8ed30eb28a01a674d7c0d4ae603fc691e6020', nonce: '2368', - blockHash: '0x62c515ea049842c968ca67499f47a32a11394364d319d9c9cc0a0211652a7294', + blockHash: + '0x62c515ea049842c968ca67499f47a32a11394364d319d9c9cc0a0211652a7294', from: '0xdfa6edae2ec0cf1d4a60542422724a48195a5071', contractAddress: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', to: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', @@ -270,7 +305,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1565339223', hash: '0x464df60fe00b6dd04c9e8ab341d02af9b10a619d2fcd60fd2971f10edf12118f', nonce: '206760', - blockHash: '0x98275388ef6708debe35ac7bf2e30143c9b1fd9e0e457ca03598fc1f4209e273', + blockHash: + '0x98275388ef6708debe35ac7bf2e30143c9b1fd9e0e457ca03598fc1f4209e273', from: '0x00cfbbaf7ddb3a1476767101c12a0162e241fbad', contractAddress: '0x4dc3643dbc642b72c158e7f3d2ff232df61cb6ce', to: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', @@ -291,7 +327,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1565815049', hash: '0xc0682327ad3efd56dfa33e8206b4e09efad4e419a6191076069d217e3ee2341f', nonce: '2506', - blockHash: '0xd0aa3c0e319fdfeb21b0192cf77b9760b8668060a5977a5f10f8413531083afa', + blockHash: + '0xd0aa3c0e319fdfeb21b0192cf77b9760b8668060a5977a5f10f8413531083afa', from: '0xdfa6edae2ec0cf1d4a60542422724a48195a5071', contractAddress: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', to: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', @@ -312,7 +349,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1565815221', hash: '0x989ea9f3ee576fa43957f44363e839adf1a4a397c3d8392a4f7cbbf7949fd0ae', nonce: '2', - blockHash: '0xb9cf1d29c665c052e3831b5754903e539c5b0b1d33b8bcab6cd2d450764d601f', + blockHash: + '0xb9cf1d29c665c052e3831b5754903e539c5b0b1d33b8bcab6cd2d450764d601f', from: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', contractAddress: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', to: '0x09cabec1ead1c0ba254b09efb3ee13841712be14', @@ -333,7 +371,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1570244087', hash: '0xc0016b89b3b525b30d73f242653b0d80ec3ebf285376dff5bb52cef3261498b2', nonce: '3', - blockHash: '0x1ceb2f8b83087f010773e2acf63d1526633c8a884bd1980f118a1bba576be69f', + blockHash: + '0x1ceb2f8b83087f010773e2acf63d1526633c8a884bd1980f118a1bba576be69f', from: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', contractAddress: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', to: '0xdfa6edae2ec0cf1d4a60542422724a48195a5071', @@ -354,7 +393,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1570244087', hash: '0xc0016b89b3b525b30d73f242653b0d80ec3ebf285376dff5bb52cef3261498b2', nonce: '3', - blockHash: '0x1ceb2f8b83087f010773e2acf63d1526633c8a884bd1980f118a1bba576be69f', + blockHash: + '0x1ceb2f8b83087f010773e2acf63d1526633c8a884bd1980f118a1bba576be69f', from: '0xdfa6edae2ec0cf1d4a60542422724a48195a5071', contractAddress: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', to: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', @@ -375,7 +415,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1570440625', hash: '0xd8397138bb93d56e50d01e92a9eae99ebd3ae28844acdaa4663976a5501116cf', nonce: '2837', - blockHash: '0xba45dd64e71e146066af9b6d2dd3bc5d72f4a3399148c155dced74c139fc3c51', + blockHash: + '0xba45dd64e71e146066af9b6d2dd3bc5d72f4a3399148c155dced74c139fc3c51', from: '0xdfa6edae2ec0cf1d4a60542422724a48195a5071', contractAddress: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359', to: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', @@ -396,7 +437,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1600310867', hash: '0xc8bd16b6b41b4c24849eb6869702e1489c808cb5b125b01f084e38fefcb5ea77', nonce: '4', - blockHash: '0x7fa16a022bcf1f69c2d7adf6bd7d3f058e808eec5c66aaa910dfa8016a5333d1', + blockHash: + '0x7fa16a022bcf1f69c2d7adf6bd7d3f058e808eec5c66aaa910dfa8016a5333d1', from: '0x090d4613473dee047c3f2706764f49e0821d256e', contractAddress: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', to: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', @@ -417,7 +459,8 @@ const TOKEN_TRANSACTIONS = [ timeStamp: '1600321973', hash: '0xa7162489faef826ee77862ed5210b01726524f09428f69842118dad394842d62', nonce: '6', - blockHash: '0xa74eb9d16f65f307dde4ce58c813c981b28f242edf1090ee2ac42caac9dccaca', + blockHash: + '0xa74eb9d16f65f307dde4ce58c813c981b28f242edf1090ee2ac42caac9dccaca', from: '0x6bf137f335ea1b8f193b8f6ea92561a60d23a207', contractAddress: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', to: '0x5e736f1f25992b2cad20ded179a52823d3d24b26', @@ -530,7 +573,10 @@ describe('TransactionController', () => { onNetworkStateChange: MOCK_NETWORK.subscribe, getProvider: MOCK_NETWORK.getProvider, }); - expect(controller.state).toEqual({ methodData: {}, transactions: [] }); + expect(controller.state).toStrictEqual({ + methodData: {}, + transactions: [], + }); }); it('should set default config', () => { @@ -539,15 +585,17 @@ describe('TransactionController', () => { onNetworkStateChange: MOCK_NETWORK.subscribe, getProvider: MOCK_NETWORK.getProvider, }); - expect(controller.config).toEqual({ + expect(controller.config).toStrictEqual({ interval: 5000, - provider: undefined, }); }); it('should poll and update transaction statuses in the right interval', async () => { await new Promise((resolve) => { - const mock = stub(TransactionController.prototype, 'queryTransactionStatuses'); + const mock = stub( + TransactionController.prototype, + 'queryTransactionStatuses', + ); new TransactionController( { getNetworkState: () => MOCK_NETWORK.state, @@ -611,7 +659,9 @@ describe('TransactionController', () => { onNetworkStateChange: MOCK_NETWORK.subscribe, getProvider: MOCK_NETWORK.getProvider, }); - await expect(controller.addTransaction({ from: 'foo' } as any)).rejects.toThrow('Invalid "from" address'); + await expect( + controller.addTransaction({ from: 'foo' } as any), + ).rejects.toThrow('Invalid "from" address'); }); it('should add a valid transaction', async () => { @@ -626,14 +676,22 @@ describe('TransactionController', () => { to: from, }); expect(controller.state.transactions[0].transaction.from).toBe(from); - expect(controller.state.transactions[0].networkID).toBe(MOCK_NETWORK.state.network); - expect(controller.state.transactions[0].chainId).toBe(MOCK_NETWORK.state.provider.chainId); - expect(controller.state.transactions[0].status).toBe(TransactionStatus.unapproved); + expect(controller.state.transactions[0].networkID).toBe( + MOCK_NETWORK.state.network, + ); + expect(controller.state.transactions[0].chainId).toBe( + MOCK_NETWORK.state.provider.chainId, + ); + expect(controller.state.transactions[0].status).toBe( + TransactionStatus.unapproved, + ); }); it('should add a valid transaction after a network switch', async () => { const getNetworkState = stub().returns(MOCK_NETWORK.state); - let networkStateChangeListener: ((state: NetworkState) => void) | null = null; + let networkStateChangeListener: + | ((state: NetworkState) => void) + | null = null; const onNetworkStateChange = (listener: (state: NetworkState) => void) => { networkStateChangeListener = listener; }; @@ -656,9 +714,15 @@ describe('TransactionController', () => { to: from, }); expect(controller.state.transactions[0].transaction.from).toBe(from); - expect(controller.state.transactions[0].networkID).toBe(MOCK_MAINNET_NETWORK.state.network); - expect(controller.state.transactions[0].chainId).toBe(MOCK_MAINNET_NETWORK.state.provider.chainId); - expect(controller.state.transactions[0].status).toBe(TransactionStatus.unapproved); + expect(controller.state.transactions[0].networkID).toBe( + MOCK_MAINNET_NETWORK.state.network, + ); + expect(controller.state.transactions[0].chainId).toBe( + MOCK_MAINNET_NETWORK.state.provider.chainId, + ); + expect(controller.state.transactions[0].status).toBe( + TransactionStatus.unapproved, + ); }); it('should cancel a transaction', async () => { @@ -674,11 +738,16 @@ describe('TransactionController', () => { }); controller.cancelTransaction('foo'); const transactionListener = new Promise(async (resolve) => { - controller.hub.once(`${controller.state.transactions[0].id}:finished`, () => { - expect(controller.state.transactions[0].transaction.from).toBe(from); - expect(controller.state.transactions[0].status).toBe(TransactionStatus.rejected); - resolve(''); - }); + controller.hub.once( + `${controller.state.transactions[0].id}:finished`, + () => { + expect(controller.state.transactions[0].transaction.from).toBe(from); + expect(controller.state.transactions[0].status).toBe( + TransactionStatus.rejected, + ); + resolve(''); + }, + ); }); controller.cancelTransaction(controller.state.transactions[0].id); await expect(result).rejects.toThrow('User rejected the transaction'); @@ -783,7 +852,8 @@ describe('TransactionController', () => { it('should fail if no chainId is defined', async () => { const controller = new TransactionController( { - getNetworkState: () => MOCK_NETWORK_WITHOUT_CHAIN_ID.state as NetworkState, + getNetworkState: () => + MOCK_NETWORK_WITHOUT_CHAIN_ID.state as NetworkState, onNetworkStateChange: MOCK_NETWORK_WITHOUT_CHAIN_ID.subscribe, getProvider: MOCK_NETWORK_WITHOUT_CHAIN_ID.getProvider, }, @@ -822,12 +892,15 @@ describe('TransactionController', () => { to: from, value: '0x0', }); - controller.hub.once(`${controller.state.transactions[0].id}:finished`, () => { - const { transaction, status } = controller.state.transactions[0]; - expect(transaction.from).toBe(from); - expect(status).toBe(TransactionStatus.submitted); - resolve(''); - }); + controller.hub.once( + `${controller.state.transactions[0].id}:finished`, + () => { + const { transaction, status } = controller.state.transactions[0]; + expect(transaction.from).toBe(from); + expect(status).toBe(TransactionStatus.submitted); + resolve(''); + }, + ); controller.approveTransaction(controller.state.transactions[0].id); }); }); @@ -854,10 +927,15 @@ describe('TransactionController', () => { } as any); controller.state.transactions.push({} as any); - controller.hub.once(`${controller.state.transactions[0].id}:confirmed`, () => { - expect(controller.state.transactions[0].status).toBe(TransactionStatus.confirmed); - resolve(''); - }); + controller.hub.once( + `${controller.state.transactions[0].id}:confirmed`, + () => { + expect(controller.state.transactions[0].status).toBe( + TransactionStatus.confirmed, + ); + resolve(''); + }, + ); controller.queryTransactionStatuses(); }); }); @@ -884,10 +962,15 @@ describe('TransactionController', () => { } as any); controller.state.transactions.push({} as any); - controller.hub.once(`${controller.state.transactions[0].id}:confirmed`, () => { - expect(controller.state.transactions[0].status).toBe(TransactionStatus.confirmed); - resolve(''); - }); + controller.hub.once( + `${controller.state.transactions[0].id}:confirmed`, + () => { + expect(controller.state.transactions[0].status).toBe( + TransactionStatus.confirmed, + ); + resolve(''); + }, + ); controller.queryTransactionStatuses(); }); }); @@ -994,7 +1077,9 @@ describe('TransactionController', () => { args: [{ type: 'uint256' }, { type: 'uint256' }], name: 'Eth To Token Swap Input', }); - expect(registry.registryMethod).toStrictEqual('ethToTokenSwapInput(uint256,uint256)'); + expect(registry.registryMethod).toStrictEqual( + 'ethToTokenSwapInput(uint256,uint256)', + ); }); it('should handle known method data', async () => { @@ -1049,9 +1134,9 @@ describe('TransactionController', () => { const to = '0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d'; await controller.addTransaction({ from, to }); controller.stopTransaction('nonexistent'); - await expect(controller.stopTransaction(controller.state.transactions[0].id)).rejects.toThrow( - 'No sign method defined', - ); + await expect( + controller.stopTransaction(controller.state.transactions[0].id), + ).rejects.toThrow('No sign method defined'); }); it('should speed up a transaction', async () => { @@ -1077,7 +1162,9 @@ describe('TransactionController', () => { await controller.speedUpTransaction(controller.state.transactions[0].id); expect(controller.state.transactions).toHaveLength(2); - expect(controller.state.transactions[1].transaction.gasPrice).toBe('0x5916a6d6'); + expect(controller.state.transactions[1].transaction.gasPrice).toBe( + '0x5916a6d6', + ); resolve(''); }); }); diff --git a/src/transaction/TransactionController.ts b/src/transaction/TransactionController.ts index 7f9bed4fd1..360f917b0a 100644 --- a/src/transaction/TransactionController.ts +++ b/src/transaction/TransactionController.ts @@ -7,7 +7,10 @@ import Transaction from 'ethereumjs-tx'; import { v1 as random } from 'uuid'; import { Mutex } from 'async-mutex'; import BaseController, { BaseConfig, BaseState } from '../BaseController'; -import type { NetworkState, NetworkController } from '../network/NetworkController'; +import type { + NetworkState, + NetworkController, +} from '../network/NetworkController'; import { BNToHex, fractionBN, @@ -131,7 +134,9 @@ type TransactionMetaBase = { * @property blockNumber - Number of the block where the transaction has been included */ export type TransactionMeta = - | ({ status: Exclude } & TransactionMetaBase) + | ({ + status: Exclude; + } & TransactionMetaBase) | ({ status: TransactionStatus.failed; error: Error } & TransactionMetaBase); /** @@ -232,7 +237,10 @@ export const SPEED_UP_RATE = 1.1; /** * Controller responsible for submitting and managing transactions */ -export class TransactionController extends BaseController { +export class TransactionController extends BaseController< + TransactionConfig, + TransactionState +> { private ethQuery: any; private registry: any; @@ -314,7 +322,17 @@ export class TransactionController extends BaseController { const time = parseInt(txMeta.timeStamp, 10) * 1000; - const { to, from, gas, gasPrice, hash, contractAddress, tokenDecimal, tokenSymbol, value } = txMeta; + const { + to, + from, + gas, + gasPrice, + hash, + contractAddress, + tokenDecimal, + tokenSymbol, + value, + } = txMeta; return { id: random({ msecs: time }), isTransfer: true, @@ -422,12 +440,16 @@ export class TransactionController extends BaseController fourBytePrefix === knownFourBytePrefix); + const knownMethod = Object.keys(methodData).find( + (knownFourBytePrefix) => fourBytePrefix === knownFourBytePrefix, + ); if (knownMethod) { return methodData[fourBytePrefix]; } const registry = await this.registryLookup(fourBytePrefix); - this.update({ methodData: { ...methodData, ...{ [fourBytePrefix]: registry } } }); + this.update({ + methodData: { ...methodData, ...{ [fourBytePrefix]: registry } }, + }); return registry; } finally { releaseLock(); @@ -444,7 +466,11 @@ export class TransactionController extends BaseController { + async addTransaction( + transaction: Transaction, + origin?: string, + deviceConfirmedOn?: WalletDevice, + ): Promise { const { provider, network } = this.getNetworkState(); const { transactions } = this.state; transaction = normalizeTransaction(transaction); @@ -471,21 +497,36 @@ export class TransactionController extends BaseController = new Promise((resolve, reject) => { - this.hub.once(`${transactionMeta.id}:finished`, (meta: TransactionMeta) => { - switch (meta.status) { - case TransactionStatus.submitted: - return resolve(meta.transactionHash as string); - case TransactionStatus.rejected: - return reject(ethErrors.provider.userRejectedRequest('User rejected the transaction')); - case TransactionStatus.cancelled: - return reject(ethErrors.rpc.internal('User cancelled the transaction')); - case TransactionStatus.failed: - return reject(ethErrors.rpc.internal(meta.error.message)); - /* istanbul ignore next */ - default: - return reject(ethErrors.rpc.internal(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(meta)}`)); - } - }); + this.hub.once( + `${transactionMeta.id}:finished`, + (meta: TransactionMeta) => { + switch (meta.status) { + case TransactionStatus.submitted: + return resolve(meta.transactionHash as string); + case TransactionStatus.rejected: + return reject( + ethErrors.provider.userRejectedRequest( + 'User rejected the transaction', + ), + ); + case TransactionStatus.cancelled: + return reject( + ethErrors.rpc.internal('User cancelled the transaction'), + ); + case TransactionStatus.failed: + return reject(ethErrors.rpc.internal(meta.error.message)); + /* istanbul ignore next */ + default: + return reject( + ethErrors.rpc.internal( + `MetaMask Tx Signature: Unknown problem: ${JSON.stringify( + meta, + )}`, + ), + ); + } + }, + ); }); transactions.push(transactionMeta); @@ -516,7 +557,10 @@ export class TransactionController extends BaseController id === transactionID); + const transactionMeta = this.state.transactions.find( + ({ id }) => id === transactionID, + ); if (!transactionMeta) { return; } transactionMeta.status = TransactionStatus.rejected; this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta); - const transactions = this.state.transactions.filter(({ id }) => id !== transactionID); + const transactions = this.state.transactions.filter( + ({ id }) => id !== transactionID, + ); this.update({ transactions: [...transactions] }); } @@ -573,7 +626,9 @@ export class TransactionController extends BaseController id === transactionID); + const transactionMeta = this.state.transactions.find( + ({ id }) => id === transactionID, + ); if (!transactionMeta) { return; } @@ -584,8 +639,15 @@ export class TransactionController extends BaseController id === transactionID); + const transactionMeta = this.state.transactions.find( + ({ id }) => id === transactionID, + ); /* istanbul ignore next */ if (!transactionMeta) { return; @@ -623,12 +687,24 @@ export class TransactionController extends BaseController transactionMeta.id === id); transactions[index] = transactionMeta; @@ -756,11 +853,15 @@ export class TransactionController extends BaseController { - // Using fallback to networkID only when there is no chainId present. Should be removed when networkID is completely removed. - const isCurrentNetwork = chainId === currentChainId || (!chainId && networkID === currentNetworkID); - return !isCurrentNetwork; - }); + const newTransactions = this.state.transactions.filter( + ({ networkID, chainId }) => { + // Using fallback to networkID only when there is no chainId present. Should be removed when networkID is completely removed. + const isCurrentNetwork = + chainId === currentChainId || + (!chainId && networkID === currentNetworkID); + return !isCurrentNetwork; + }, + ); this.update({ transactions: newTransactions }); } @@ -773,7 +874,10 @@ export class TransactionController extends BaseController { + async fetchAll( + address: string, + opt?: FetchAllOptions, + ): Promise { const { provider, network: currentNetworkID } = this.getNetworkState(); const { chainId: currentChainId, type: networkType } = provider; @@ -783,13 +887,18 @@ export class TransactionController extends BaseController - this.normalizeTx(tx, currentNetworkID, currentChainId), + const normalizedTxs = etherscanTxResponse.result.map( + (tx: EtherscanTransactionMeta) => + this.normalizeTx(tx, currentNetworkID, currentChainId), ); - const normalizedTokenTxs = etherscanTokenResponse.result.map((tx: EtherscanTransactionMeta) => - this.normalizeTokenTx(tx, currentNetworkID, currentChainId), + const normalizedTokenTxs = etherscanTokenResponse.result.map( + (tx: EtherscanTransactionMeta) => + this.normalizeTokenTx(tx, currentNetworkID, currentChainId), ); const remoteTxs = [...normalizedTxs, ...normalizedTokenTxs].filter((tx) => { @@ -807,13 +916,16 @@ export class TransactionController extends BaseController { it('should add a contact entry with chainId and memo', () => { const controller = new AddressBookController(); - controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo', '1', 'account 1'); + controller.set( + '0x32Be343B94f860124dC4fEe278FDCBD38C102D88', + 'foo', + '1', + 'account 1', + ); expect(controller.state).toStrictEqual({ addressBook: { 1: { @@ -44,8 +49,18 @@ describe('AddressBookController', () => { it('should add multiple contact entries with different chainIds', () => { const controller = new AddressBookController(); - controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo', '1', 'account 2'); - controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo', '2', 'account 2'); + controller.set( + '0x32Be343B94f860124dC4fEe278FDCBD38C102D88', + 'foo', + '1', + 'account 2', + ); + controller.set( + '0x32Be343B94f860124dC4fEe278FDCBD38C102D88', + 'foo', + '2', + 'account 2', + ); expect(controller.state).toStrictEqual({ addressBook: { @@ -154,7 +169,10 @@ describe('AddressBookController', () => { it('should correctly mark ens entries', () => { const controller = new AddressBookController(); - controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'metamask.eth'); + controller.set( + '0x32Be343B94f860124dC4fEe278FDCBD38C102D88', + 'metamask.eth', + ); expect(controller.state).toStrictEqual({ addressBook: { 1: { @@ -180,7 +198,9 @@ describe('AddressBookController', () => { it('should return true to indicate an address book entry has been added', () => { const controller = new AddressBookController(); - expect(controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo')).toBeTruthy(); + expect( + controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo'), + ).toBeTruthy(); }); it('should return false to indicate an address book entry has NOT been added', () => { @@ -191,7 +211,9 @@ describe('AddressBookController', () => { it('should return true to indicate an address book entry has been deleted', () => { const controller = new AddressBookController(); controller.set('0x32Be343B94f860124dC4fEe278FDCBD38C102D88', 'foo'); - expect(controller.delete('1', '0x32Be343B94f860124dC4fEe278FDCBD38C102D88')).toBeTruthy(); + expect( + controller.delete('1', '0x32Be343B94f860124dC4fEe278FDCBD38C102D88'), + ).toBeTruthy(); }); it('should return false to indicate an address book entry has NOT been deleted', () => { diff --git a/src/user/AddressBookController.ts b/src/user/AddressBookController.ts index b64b918908..21783f0c4e 100644 --- a/src/user/AddressBookController.ts +++ b/src/user/AddressBookController.ts @@ -50,7 +50,10 @@ export interface AddressBookState extends BaseState { /** * Controller that manages a list of recipient addresses associated with nicknames */ -export class AddressBookController extends BaseController { +export class AddressBookController extends BaseController< + BaseConfig, + AddressBookState +> { /** * Name of this controller used during composition */ @@ -85,7 +88,11 @@ export class AddressBookController extends BaseController { controller.addIdentities(['foo']); controller.addIdentities(['foo']); expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toStrictEqual('Account 1'); - expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual(Date.now()); + expect(controller.state.identities['0xfoO'].name).toStrictEqual( + 'Account 1', + ); + expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual( + Date.now(), + ); }); it('should remove identity', () => { @@ -47,14 +51,24 @@ describe('PreferencesController', () => { controller.addIdentities(['foo', 'bar']); controller.syncIdentities(['foo', 'bar']); expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toStrictEqual('Account 1'); - expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual(Date.now()); + expect(controller.state.identities['0xfoO'].name).toStrictEqual( + 'Account 1', + ); + expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual( + Date.now(), + ); expect(controller.state.identities['0xbar'].address).toStrictEqual('0xbar'); - expect(controller.state.identities['0xbar'].name).toStrictEqual('Account 2'); - expect(controller.state.identities['0xbar'].importTime).toBeLessThanOrEqual(Date.now()); + expect(controller.state.identities['0xbar'].name).toStrictEqual( + 'Account 2', + ); + expect(controller.state.identities['0xbar'].importTime).toBeLessThanOrEqual( + Date.now(), + ); controller.syncIdentities(['foo']); expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toStrictEqual('Account 1'); + expect(controller.state.identities['0xfoO'].name).toStrictEqual( + 'Account 1', + ); expect(controller.state.selectedAddress).toBe('0xfoO'); }); @@ -62,11 +76,19 @@ describe('PreferencesController', () => { const controller = new PreferencesController(); controller.updateIdentities(['foo', 'bar']); expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toStrictEqual('Account 1'); - expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual(Date.now()); + expect(controller.state.identities['0xfoO'].name).toStrictEqual( + 'Account 1', + ); + expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual( + Date.now(), + ); expect(controller.state.identities['0xbar'].address).toStrictEqual('0xbar'); - expect(controller.state.identities['0xbar'].name).toStrictEqual('Account 2'); - expect(controller.state.identities['0xbar'].importTime).toBeLessThanOrEqual(Date.now()); + expect(controller.state.identities['0xbar'].name).toStrictEqual( + 'Account 2', + ); + expect(controller.state.identities['0xbar'].importTime).toBeLessThanOrEqual( + Date.now(), + ); }); it('should not update existing identities', () => { @@ -76,10 +98,16 @@ describe('PreferencesController', () => { ); controller.updateIdentities(['foo', 'bar']); expect(controller.state.identities['0xfoO'].address).toStrictEqual('0xfoO'); - expect(controller.state.identities['0xfoO'].name).toStrictEqual('Account 1'); - expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual(Date.now()); + expect(controller.state.identities['0xfoO'].name).toStrictEqual( + 'Account 1', + ); + expect(controller.state.identities['0xfoO'].importTime).toBeLessThanOrEqual( + Date.now(), + ); expect(controller.state.identities['0xbar'].address).toStrictEqual('0xbar'); - expect(controller.state.identities['0xbar'].name).toStrictEqual('Custom name'); + expect(controller.state.identities['0xbar'].name).toStrictEqual( + 'Custom name', + ); expect(controller.state.identities['0xbar'].importTime).toBeUndefined(); }); @@ -147,8 +175,15 @@ describe('PreferencesController', () => { ticker: 'LOCAL', }; controller.addToFrequentRpcList('rpc_url', undefined, 'RPC', 'RPC'); - controller.addToFrequentRpcList('http://localhost:8545', undefined, 'LOCAL'); - expect(controller.state.frequentRpcList).toStrictEqual([rpcUrlNetwork, localhostNetwork]); + controller.addToFrequentRpcList( + 'http://localhost:8545', + undefined, + 'LOCAL', + ); + expect(controller.state.frequentRpcList).toStrictEqual([ + rpcUrlNetwork, + localhostNetwork, + ]); controller.addToFrequentRpcList('rpc_url'); expect(controller.state.frequentRpcList).toStrictEqual([ localhostNetwork, @@ -175,12 +210,16 @@ describe('PreferencesController', () => { it('should set IPFS gateway', () => { const controller = new PreferencesController(); controller.setIpfsGateway('https://ipfs.infura.io/ipfs/'); - expect(controller.state.ipfsGateway).toStrictEqual('https://ipfs.infura.io/ipfs/'); + expect(controller.state.ipfsGateway).toStrictEqual( + 'https://ipfs.infura.io/ipfs/', + ); }); it('should update selected address as checksummed', () => { const controller = new PreferencesController(); controller.setSelectedAddress('0x95d2bc047b0ddec1e4a178eeb64d59f5e735cd0a'); - expect(controller.state.selectedAddress).toStrictEqual('0x95D2bC047B0dDEc1E4A178EeB64d59F5E735cd0A'); + expect(controller.state.selectedAddress).toStrictEqual( + '0x95D2bC047B0dDEc1E4A178EeB64d59F5E735cd0A', + ); }); }); diff --git a/src/user/PreferencesController.ts b/src/user/PreferencesController.ts index 4d952770cf..df0b239e4d 100644 --- a/src/user/PreferencesController.ts +++ b/src/user/PreferencesController.ts @@ -51,7 +51,10 @@ export interface PreferencesState extends BaseState { /** * Controller that stores shared settings and exposes convenience methods */ -export class PreferencesController extends BaseController { +export class PreferencesController extends BaseController< + BaseConfig, + PreferencesState +> { /** * Name of this controller used during composition */ @@ -90,7 +93,11 @@ export class PreferencesController extends BaseController toChecksumAddress(address)); const oldIdentities = this.state.identities; - const identities = addresses.reduce((ids: { [address: string]: ContactEntry }, address, index) => { - ids[address] = oldIdentities[address] || { - address, - name: `Account ${index + 1}`, - importTime: Date.now(), - }; - return ids; - }, {}); + const identities = addresses.reduce( + (ids: { [address: string]: ContactEntry }, address, index) => { + ids[address] = oldIdentities[address] || { + address, + name: `Account ${index + 1}`, + importTime: Date.now(), + }; + return ids; + }, + {}, + ); let { selectedAddress } = this.state; if (!Object.keys(identities).includes(selectedAddress)) { selectedAddress = Object.keys(identities)[0]; @@ -208,7 +221,13 @@ export class PreferencesController extends BaseController { return rpcUrl === url; @@ -216,7 +235,13 @@ export class PreferencesController extends BaseController jest.fn().mockImplementation(() => { @@ -27,7 +29,11 @@ jest.mock('eth-query', () => } callback(undefined, '0x0'); }, - getBlockByNumber: (_blocknumber: any, _fetchTxs: boolean, callback: any) => { + getBlockByNumber: ( + _blocknumber: any, + _fetchTxs: boolean, + callback: any, + ) => { callback(undefined, { gasLimit: '0x0' }); }, getCode: (_to: any, callback: any) => { @@ -69,7 +75,9 @@ describe('util', () => { expect(util.getBuyURL('3')).toBe('https://faucet.metamask.io/'); expect(util.getBuyURL('4')).toBe('https://www.rinkeby.io/'); expect(util.getBuyURL('5')).toBe('https://goerli-faucet.slock.it/'); - expect(util.getBuyURL('42')).toBe('https://github.com/kovan-testnet/faucet'); + expect(util.getBuyURL('42')).toBe( + 'https://github.com/kovan-testnet/faucet', + ); expect(util.getBuyURL('unrecognized network ID')).toBeUndefined(); }); @@ -158,7 +166,12 @@ describe('util', () => { }); it('should return a correctly structured url with from block', () => { const fromBlock = 'xxxxxx'; - const url = util.getEtherscanApiUrl(networkType, address, action, fromBlock); + const url = util.getEtherscanApiUrl( + networkType, + address, + action, + fromBlock, + ); expect(url.indexOf(`&startBlock=${fromBlock}`)).toBeGreaterThan(0); }); it('should return a correctly structured url with testnet subdomain', () => { @@ -168,7 +181,13 @@ describe('util', () => { }); it('should return a correctly structured url with apiKey', () => { const apiKey = 'xxxxxx'; - const url = util.getEtherscanApiUrl(networkType, address, action, 'xxxxxx', apiKey); + const url = util.getEtherscanApiUrl( + networkType, + address, + action, + 'xxxxxx', + apiKey, + ); expect(url.indexOf(`&apikey=${apiKey}`)).toBeGreaterThan(0); }); }); @@ -253,7 +272,9 @@ describe('util', () => { to: '0x3244e191f1b4903970224322180f1fbbc415696b', value: 'one million dollar$', } as any), - ).toThrow('Invalid "value": one million dollar$ number must be a valid number.'); + ).toThrow( + 'Invalid "value": one million dollar$ number must be a valid number.', + ); expect(() => util.validateTransaction({ from: '0x3244e191f1b4903970224322180f1fbbc415696b', @@ -269,7 +290,9 @@ describe('util', () => { '879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0', ); const secondNormalized = util.normalizeMessageData('somedata'); - expect(firstNormalized).toStrictEqual('0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0'); + expect(firstNormalized).toStrictEqual( + '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0', + ); expect(secondNormalized).toStrictEqual('0x736f6d6564617461'); }); @@ -293,7 +316,9 @@ describe('util', () => { data: '0x879a05', from: '3244e191f1b4903970224322180f1fbbc415696b', } as any), - ).toThrow('Invalid "from" address: 3244e191f1b4903970224322180f1fbbc415696b must be a valid string.'); + ).toThrow( + 'Invalid "from" address: 3244e191f1b4903970224322180f1fbbc415696b must be a valid string.', + ); }); it('should throw if invalid type from address', () => { @@ -592,7 +617,9 @@ describe('util', () => { } catch (e) { error = e; } - expect(error.message).toBe(`Fetch failed with status '500' for request '${SOME_FAILING_API}'`); + expect(error.message).toBe( + `Fetch failed with status '500' for request '${SOME_FAILING_API}'`, + ); }); }); @@ -707,7 +734,9 @@ describe('util', () => { it('should query and reject if error', async () => { const ethQuery = new EthQuery(PROVIDER); mockFlags.gasPrice = 'Uh oh'; - await expect(util.query(ethQuery, 'gasPrice', [])).rejects.toThrow('Uh oh'); + await expect(util.query(ethQuery, 'gasPrice', [])).rejects.toThrow( + 'Uh oh', + ); }); }); }); diff --git a/src/util.ts b/src/util.ts index f146afb127..7e25b1d8fa 100644 --- a/src/util.ts +++ b/src/util.ts @@ -4,7 +4,10 @@ import { ethErrors } from 'eth-rpc-errors'; import ensNamehash from 'eth-ens-namehash'; import { TYPED_MESSAGE_SCHEMA, typedSignatureHash } from 'eth-sig-util'; import jsonschema from 'jsonschema'; -import { Transaction, FetchAllOptions } from './transaction/TransactionController'; +import { + Transaction, + FetchAllOptions, +} from './transaction/TransactionController'; import { MessageParams } from './message-manager/MessageManager'; import { PersonalMessageParams } from './message-manager/PersonalMessageManager'; import { TypedMessageParams } from './message-manager/TypedMessageManager'; @@ -41,7 +44,11 @@ export function BNToHex(inputBn: any) { * @param denominator - Denominator of the fraction multiplier * @returns - Product of the multiplication */ -export function fractionBN(targetBN: any, numerator: number | string, denominator: number | string) { +export function fractionBN( + targetBN: any, + numerator: number | string, + denominator: number | string, +) { const numBN = new BN(numerator); const denomBN = new BN(denominator); return targetBN.mul(numBN).div(denomBN); @@ -55,7 +62,11 @@ export function fractionBN(targetBN: any, numerator: number | string, denominato * @param amount - How much ETH is desired * @returns - URL to buy ETH based on network */ -export function getBuyURL(networkCode = '1', address?: string, amount = 5): string | undefined { +export function getBuyURL( + networkCode = '1', + address?: string, + amount = 5, +): string | undefined { switch (networkCode) { case '1': return `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH`; @@ -116,11 +127,23 @@ export async function handleTransactionFetch( opt?: FetchAllOptions, ): Promise<[{ [result: string]: [] }, { [result: string]: [] }]> { // transactions - const etherscanTxUrl = getEtherscanApiUrl(networkType, address, 'txlist', opt?.fromBlock, opt?.etherscanApiKey); + const etherscanTxUrl = getEtherscanApiUrl( + networkType, + address, + 'txlist', + opt?.fromBlock, + opt?.etherscanApiKey, + ); const etherscanTxResponsePromise = handleFetch(etherscanTxUrl); // tokens - const etherscanTokenUrl = getEtherscanApiUrl(networkType, address, 'tokentx', opt?.fromBlock, opt?.etherscanApiKey); + const etherscanTokenUrl = getEtherscanApiUrl( + networkType, + address, + 'tokentx', + opt?.fromBlock, + opt?.etherscanApiKey, + ); const etherscanTokenResponsePromise = handleFetch(etherscanTokenUrl); let [etherscanTxResponse, etherscanTokenResponse] = await Promise.all([ @@ -128,11 +151,17 @@ export async function handleTransactionFetch( etherscanTokenResponsePromise, ]); - if (etherscanTxResponse.status === '0' || etherscanTxResponse.result.length <= 0) { + if ( + etherscanTxResponse.status === '0' || + etherscanTxResponse.result.length <= 0 + ) { etherscanTxResponse = { result: [] }; } - if (etherscanTokenResponse.status === '0' || etherscanTokenResponse.result.length <= 0) { + if ( + etherscanTokenResponse.status === '0' || + etherscanTokenResponse.result.length <= 0 + ) { etherscanTokenResponse = { result: [] }; } @@ -193,7 +222,11 @@ export function normalizeTransaction(transaction: Transaction) { * @param retry - Function called if an error is caught * @returns - Promise resolving to the result of the async operation */ -export async function safelyExecute(operation: () => Promise, logError = false, retry?: (error: Error) => void) { +export async function safelyExecute( + operation: () => Promise, + logError = false, + retry?: (error: Error) => void, +) { try { return await operation(); } catch (error) { @@ -215,7 +248,11 @@ export async function safelyExecute(operation: () => Promise, logError = fa * @param timeout - Timeout to fail the operation * @returns - Promise resolving to the result of the async operation */ -export async function safelyExecuteWithTimeout(operation: () => Promise, logError = false, timeout = 500) { +export async function safelyExecuteWithTimeout( + operation: () => Promise, + logError = false, + timeout = 500, +) { try { return await Promise.race([ operation(), @@ -241,17 +278,27 @@ export async function safelyExecuteWithTimeout(operation: () => Promise, lo * @param transaction - Transaction object to validate */ export function validateTransaction(transaction: Transaction) { - if (!transaction.from || typeof transaction.from !== 'string' || !isValidAddress(transaction.from)) { - throw new Error(`Invalid "from" address: ${transaction.from} must be a valid string.`); + if ( + !transaction.from || + typeof transaction.from !== 'string' || + !isValidAddress(transaction.from) + ) { + throw new Error( + `Invalid "from" address: ${transaction.from} must be a valid string.`, + ); } if (transaction.to === '0x' || transaction.to === undefined) { if (transaction.data) { delete transaction.to; } else { - throw new Error(`Invalid "to" address: ${transaction.to} must be a valid string.`); + throw new Error( + `Invalid "to" address: ${transaction.to} must be a valid string.`, + ); } } else if (transaction.to !== undefined && !isValidAddress(transaction.to)) { - throw new Error(`Invalid "to" address: ${transaction.to} must be a valid string.`); + throw new Error( + `Invalid "to" address: ${transaction.to} must be a valid string.`, + ); } if (transaction.value !== undefined) { const value = transaction.value.toString(); @@ -259,13 +306,20 @@ export function validateTransaction(transaction: Transaction) { throw new Error(`Invalid "value": ${value} is not a positive number.`); } if (value.includes('.')) { - throw new Error(`Invalid "value": ${value} number must be denominated in wei.`); + throw new Error( + `Invalid "value": ${value} number must be denominated in wei.`, + ); } const intValue = parseInt(transaction.value, 10); const isValid = - Number.isFinite(intValue) && !Number.isNaN(intValue) && !isNaN(Number(value)) && Number.isSafeInteger(intValue); + Number.isFinite(intValue) && + !Number.isNaN(intValue) && + !isNaN(Number(value)) && + Number.isSafeInteger(intValue); if (!isValid) { - throw new Error(`Invalid "value": ${value} number must be a valid number.`); + throw new Error( + `Invalid "value": ${value} number must be a valid number.`, + ); } } } @@ -296,12 +350,22 @@ export function normalizeMessageData(data: string) { * * @param messageData - PersonalMessageParams object to validate */ -export function validateSignMessageData(messageData: PersonalMessageParams | MessageParams) { - if (!messageData.from || typeof messageData.from !== 'string' || !isValidAddress(messageData.from)) { - throw new Error(`Invalid "from" address: ${messageData.from} must be a valid string.`); +export function validateSignMessageData( + messageData: PersonalMessageParams | MessageParams, +) { + if ( + !messageData.from || + typeof messageData.from !== 'string' || + !isValidAddress(messageData.from) + ) { + throw new Error( + `Invalid "from" address: ${messageData.from} must be a valid string.`, + ); } if (!messageData.data || typeof messageData.data !== 'string') { - throw new Error(`Invalid message "data": ${messageData.data} must be a valid string.`); + throw new Error( + `Invalid message "data": ${messageData.data} must be a valid string.`, + ); } } @@ -312,12 +376,22 @@ export function validateSignMessageData(messageData: PersonalMessageParams | Mes * @param messageData - TypedMessageParams object to validate * @param activeChainId - Active chain id */ -export function validateTypedSignMessageDataV1(messageData: TypedMessageParams) { - if (!messageData.from || typeof messageData.from !== 'string' || !isValidAddress(messageData.from)) { - throw new Error(`Invalid "from" address: ${messageData.from} must be a valid string.`); +export function validateTypedSignMessageDataV1( + messageData: TypedMessageParams, +) { + if ( + !messageData.from || + typeof messageData.from !== 'string' || + !isValidAddress(messageData.from) + ) { + throw new Error( + `Invalid "from" address: ${messageData.from} must be a valid string.`, + ); } if (!messageData.data || !Array.isArray(messageData.data)) { - throw new Error(`Invalid message "data": ${messageData.data} must be a valid array.`); + throw new Error( + `Invalid message "data": ${messageData.data} must be a valid array.`, + ); } try { // typedSignatureHash will throw if the data is invalid. @@ -333,12 +407,22 @@ export function validateTypedSignMessageDataV1(messageData: TypedMessageParams) * * @param messageData - TypedMessageParams object to validate */ -export function validateTypedSignMessageDataV3(messageData: TypedMessageParams) { - if (!messageData.from || typeof messageData.from !== 'string' || !isValidAddress(messageData.from)) { - throw new Error(`Invalid "from" address: ${messageData.from} must be a valid string.`); +export function validateTypedSignMessageDataV3( + messageData: TypedMessageParams, +) { + if ( + !messageData.from || + typeof messageData.from !== 'string' || + !isValidAddress(messageData.from) + ) { + throw new Error( + `Invalid "from" address: ${messageData.from} must be a valid string.`, + ); } if (!messageData.data || typeof messageData.data !== 'string') { - throw new Error(`Invalid message "data": ${messageData.data} must be a valid array.`); + throw new Error( + `Invalid message "data": ${messageData.data} must be a valid array.`, + ); } let data; try { @@ -348,7 +432,9 @@ export function validateTypedSignMessageDataV3(messageData: TypedMessageParams) } const validation = jsonschema.validate(data, TYPED_MESSAGE_SCHEMA); if (validation.errors.length > 0) { - throw new Error('Data must conform to EIP-712 schema. See https://git.io/fNtcx.'); + throw new Error( + 'Data must conform to EIP-712 schema. See https://git.io/fNtcx.', + ); } } @@ -360,17 +446,23 @@ export function validateTypedSignMessageDataV3(messageData: TypedMessageParams) export function validateTokenToWatch(token: Token) { const { address, symbol, decimals } = token; if (!address || !symbol || typeof decimals === 'undefined') { - throw ethErrors.rpc.invalidParams(`Must specify address, symbol, and decimals.`); + throw ethErrors.rpc.invalidParams( + `Must specify address, symbol, and decimals.`, + ); } if (typeof symbol !== 'string') { throw ethErrors.rpc.invalidParams(`Invalid symbol: not a string.`); } if (symbol.length > 11) { - throw ethErrors.rpc.invalidParams(`Invalid symbol "${symbol}": longer than 11 characters.`); + throw ethErrors.rpc.invalidParams( + `Invalid symbol "${symbol}": longer than 11 characters.`, + ); } const numDecimals = parseInt((decimals as unknown) as string, 10); if (isNaN(numDecimals) || numDecimals > 36 || numDecimals < 0) { - throw ethErrors.rpc.invalidParams(`Invalid decimals "${decimals}": must be 0 <= 36.`); + throw ethErrors.rpc.invalidParams( + `Invalid decimals "${decimals}": must be 0 <= 36.`, + ); } if (!isValidAddress(address)) { throw ethErrors.rpc.invalidParams(`Invalid address "${address}".`); @@ -402,7 +494,9 @@ export function isSmartContractCode(code: string) { export async function successfulFetch(request: string, options?: RequestInit) { const response = await fetch(request, options); if (!response.ok) { - throw new Error(`Fetch failed with status '${response.status}' for request '${request}'`); + throw new Error( + `Fetch failed with status '${response.status}' for request '${request}'`, + ); } return response; } @@ -429,7 +523,11 @@ export async function handleFetch(request: string, options?: RequestInit) { * * @returns - Promise resolving the request */ -export async function timeoutFetch(url: string, options?: RequestInit, timeout = 500): Promise { +export async function timeoutFetch( + url: string, + options?: RequestInit, + timeout = 500, +): Promise { return Promise.race([ successfulFetch(url, options), new Promise((_, reject) => @@ -472,7 +570,11 @@ export function normalizeEnsName(ensName: string): string | null { * * @returns - Promise resolving the request */ -export function query(ethQuery: any, method: string, args: any[] = []): Promise { +export function query( + ethQuery: any, + method: string, + args: any[] = [], +): Promise { return new Promise((resolve, reject) => { ethQuery[method](...args, (error: Error, result: any) => { if (error) { From eaea482cf6958d75b4201594f768a77d200a00c9 Mon Sep 17 00:00:00 2001 From: Erik Marks <25517051+rekmarks@users.noreply.github.com> Date: Wed, 14 Apr 2021 08:22:48 -0700 Subject: [PATCH 09/10] Update .eslintrc.js Co-authored-by: Mark Stacey --- .eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 46373ae7e3..519e0eb3aa 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -41,7 +41,7 @@ module.exports = { extends: ['@metamask/eslint-config-typescript'], rules: { // `no-shadow` has incompatibilities with TypeScript - // TODO: Migrate this into @metamask/eslint-config? + // TODO: Migrate this into @metamask/eslint-config 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'error', From 6baa86dcdccca407695cf74c0e98053c4618ced9 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Wed, 14 Apr 2021 09:01:51 -0700 Subject: [PATCH 10/10] Add comment explaining disabled rules --- .eslintrc.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 519e0eb3aa..068035ce77 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,16 +15,18 @@ module.exports = { files: ['*.test.ts', '*.test.js'], extends: ['@metamask/eslint-config-jest'], rules: { + // TODO: Re-enable most of these rules 'jest/no-conditional-expect': 'off', 'jest/no-restricted-matchers': [ 'error', { resolves: 'Use `expect(await promise)` instead.', - // 'toBeFalsy': 'Avoid `toBeFalsy`', - // 'toBeTruthy': 'Avoid `toBeTruthy`', toMatchSnapshot: 'Use `toMatchInlineSnapshot()` instead', toThrowErrorMatchingSnapshot: 'Use `toThrowErrorMatchingInlineSnapshot()` instead', + // TODO: Re-enable these. Requires lots of manual fixes. + // 'toBeFalsy': 'Avoid `toBeFalsy`', + // 'toBeTruthy': 'Avoid `toBeTruthy`', }, ], 'jest/no-test-return-statement': 'off',