diff --git a/.depcheckrc.yml b/.depcheckrc.yml
index e8fe7a446ec..5a506bc7a51 100644
--- a/.depcheckrc.yml
+++ b/.depcheckrc.yml
@@ -4,8 +4,6 @@ ignores:
- 'webpack-cli'
- '@react-native-community/datetimepicker'
- '@react-native-community/slider'
- - 'patch-package'
- - '@lavamoat/allow-scripts'
# This is used on the patch for TokenRatesController of Assets controllers, for we to be able to use the last version of it
- cockatiel
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 536976ae751..ef0325cdc08 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -63,32 +63,3 @@ patches/react-native+0.*.patch @MetaMask/supply-chain
# Staking Team
app/components/UI/Stake @MetaMask/metamask-staking
-
-# Assets Team
-app/components/hooks/useIsOriginalNativeTokenSymbol @MetaMask/metamask-assets
-app/components/hooks/useTokenBalancesController @MetaMask/metamask-assets
-app/components/hooks/useTokenBalance.tsx @MetaMask/metamask-assets
-app/components/hooks/useSafeChains.ts @MetaMask/metamask-assets
-app/components/UI/AssetOverview @MetaMask/metamask-assets
-app/components/UI/Collectibles @MetaMask/metamask-assets
-app/components/UI/CollectibleContractElement @MetaMask/metamask-assets
-app/components/UI/CollectivelContractInformation @MetaMask/metamask-assets
-app/components/UI/CollectibleContractOverview @MetaMask/metamask-assets
-app/components/UI/CollectibleContracts @MetaMask/metamask-assets
-app/components/UI/CollectibleDetectionModal @MetaMask/metamask-assets
-app/components/UI/CollectibleMedia @MetaMask/metamask-assets
-app/components/UI/CollectibleModal @MetaMask/metamask-assets
-app/components/UI/CollectibleOverview @MetaMask/metamask-assets
-app/components/UI/ConfirmAddAsset @MetaMask/metamask-assets
-app/components/UI/Tokens @MetaMask/metamask-assets
-app/components/Views/AddAsset @MetaMask/metamask-assets
-app/components/Views/Asset @MetaMask/metamask-assets
-app/components/Views/AssetDetails @MetaMask/metamask-assets
-app/components/Views/AssetHideConfirmation @MetaMask/metamask-assets
-app/components/Views/AssetOptions @MetaMask/metamask-assets
-app/components/Views/Collectible @MetaMask/metamask-assets
-app/components/Views/CollectibleView @MetaMask/metamask-assets
-app/components/Views/DetectedTokens @MetaMask/metamask-assets
-app/components/Views/NFTAutoDetectionModal @MetaMask/metamask-assets
-app/components/Views/NftDetails @MetaMask/metamask-assets
-app/reducers/collectibles @MetaMask/metamask-assets
diff --git a/.github/scripts/bitrise/.yarnrc b/.github/scripts/bitrise/.yarnrc
deleted file mode 100644
index 5455c6c5d38..00000000000
--- a/.github/scripts/bitrise/.yarnrc
+++ /dev/null
@@ -1 +0,0 @@
-ignore-scripts true
diff --git a/.github/scripts/package.json b/.github/scripts/package.json
deleted file mode 100644
index e2bf8d85b07..00000000000
--- a/.github/scripts/package.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "name": "@metamask/mobile-ci-scripts",
- "private": true,
- "scripts": {
- "add-release-label-to-pr-and-linked-issues": "ts-node ./add-release-label-to-pr-and-linked-issues.ts",
- "fitness-functions": "ts-node ./fitness-functions/index.ts",
- "check-pr-has-required-labels": "ts-node ./check-pr-has-required-labels.ts",
- "close-release-bug-report-issue": "ts-node ./close-release-bug-report-issue.ts",
- "check-template-and-add-labels": "ts-node ./check-template-and-add-labels.ts",
- "run-bitrise-e2e-check": "ts-node ./bitrise/run-bitrise-e2e-check.ts"
- },
- "dependencies": {
- "@actions/core": "^1.10.1",
- "@actions/github": "^6.0.0",
- "axios": "^1.7.4",
- "simple-git": "^3.25.0"
- },
- "devDependencies": {
- "@lavamoat/allow-scripts": "^3.2.0",
- "@lavamoat/preinstall-always-fail": "^2.1.0",
- "@types/node": "^20.16.2",
- "ts-node": "^10.5.0",
- "typescript": "~5.4.5"
- },
- "packageManager": "yarn@1.22.22",
- "lavamoat": {
- "allowScripts": {
- "@lavamoat/preinstall-always-fail": false,
- "ts-node>@swc/core": false
- }
- }
-}
diff --git a/.github/scripts/tsconfig.json b/.github/scripts/tsconfig.json
deleted file mode 100644
index 4082f16a5d9..00000000000
--- a/.github/scripts/tsconfig.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "extends": "../../tsconfig.json"
-}
diff --git a/.github/scripts/yarn.lock b/.github/scripts/yarn.lock
deleted file mode 100644
index 0a66131a3c4..00000000000
--- a/.github/scripts/yarn.lock
+++ /dev/null
@@ -1,1318 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
-"@actions/core@^1.10.1":
- version "1.10.1"
- resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.10.1.tgz#61108e7ac40acae95ee36da074fa5850ca4ced8a"
- integrity sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==
- dependencies:
- "@actions/http-client" "^2.0.1"
- uuid "^8.3.2"
-
-"@actions/github@^6.0.0":
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/@actions/github/-/github-6.0.0.tgz#65883433f9d81521b782a64cc1fd45eef2191ea7"
- integrity sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g==
- dependencies:
- "@actions/http-client" "^2.2.0"
- "@octokit/core" "^5.0.1"
- "@octokit/plugin-paginate-rest" "^9.0.0"
- "@octokit/plugin-rest-endpoint-methods" "^10.0.0"
-
-"@actions/http-client@^2.0.1", "@actions/http-client@^2.2.0":
- version "2.2.3"
- resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.2.3.tgz#31fc0b25c0e665754ed39a9f19a8611fc6dab674"
- integrity sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==
- dependencies:
- tunnel "^0.0.6"
- undici "^5.25.4"
-
-"@cspotcode/source-map-support@^0.8.0":
- version "0.8.1"
- resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
- integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
- dependencies:
- "@jridgewell/trace-mapping" "0.3.9"
-
-"@fastify/busboy@^2.0.0":
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d"
- integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==
-
-"@isaacs/cliui@^8.0.2":
- version "8.0.2"
- resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
- integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==
- dependencies:
- string-width "^5.1.2"
- string-width-cjs "npm:string-width@^4.2.0"
- strip-ansi "^7.0.1"
- strip-ansi-cjs "npm:strip-ansi@^6.0.1"
- wrap-ansi "^8.1.0"
- wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
-
-"@jridgewell/resolve-uri@^3.0.3":
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
- integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
-
-"@jridgewell/sourcemap-codec@^1.4.10":
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
- integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
-
-"@jridgewell/trace-mapping@0.3.9":
- version "0.3.9"
- resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
- integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
- dependencies:
- "@jridgewell/resolve-uri" "^3.0.3"
- "@jridgewell/sourcemap-codec" "^1.4.10"
-
-"@kwsites/file-exists@^1.1.1":
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz#ad1efcac13e1987d8dbaf235ef3be5b0d96faa99"
- integrity sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==
- dependencies:
- debug "^4.1.1"
-
-"@kwsites/promise-deferred@^1.1.1":
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz#8ace5259254426ccef57f3175bc64ed7095ed919"
- integrity sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==
-
-"@lavamoat/aa@^4.3.0":
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/@lavamoat/aa/-/aa-4.3.0.tgz#eabc756f3838971e05321335035ae11f2b1aaf8a"
- integrity sha512-Kwf64RpLDvWCzHdG1IEItGf0JfMH9DyjBhIAj1NNJQzmKIaFE/DCc6INKJu7iVqp7aVdKaVB3aJlV4/lKOmJDg==
- dependencies:
- resolve "1.22.8"
-
-"@lavamoat/allow-scripts@^3.2.0":
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/@lavamoat/allow-scripts/-/allow-scripts-3.2.0.tgz#bbd0f0039cf28670f0911b71af2c7554e0400872"
- integrity sha512-UQmyNgOY4OLsHqv8YrRTvsf61NSp1rCO+NXAQNWxtG+qyrvPYqo5rfj3PdmwUOlwAHebPSVfc4Yz7+Eg+DRxkg==
- dependencies:
- "@lavamoat/aa" "^4.3.0"
- "@npmcli/run-script" "8.1.0"
- bin-links "4.0.4"
- npm-normalize-package-bin "3.0.1"
- yargs "17.7.2"
-
-"@lavamoat/preinstall-always-fail@^2.1.0":
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/@lavamoat/preinstall-always-fail/-/preinstall-always-fail-2.1.0.tgz#048d71940d83d424e2ef625a8b81c4dc6d64645c"
- integrity sha512-9xSsxxryPMyBWxfV+Ver7FZhvC1PhE1wDCsYFwsiqZzmeWH8e7Y7gWRFJqPw4M8WNbfeuPhKbLdCK3GsPz9gKg==
-
-"@npmcli/agent@^2.0.0":
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/@npmcli/agent/-/agent-2.2.2.tgz#967604918e62f620a648c7975461c9c9e74fc5d5"
- integrity sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==
- dependencies:
- agent-base "^7.1.0"
- http-proxy-agent "^7.0.0"
- https-proxy-agent "^7.0.1"
- lru-cache "^10.0.1"
- socks-proxy-agent "^8.0.3"
-
-"@npmcli/fs@^3.1.0":
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.1.tgz#59cdaa5adca95d135fc00f2bb53f5771575ce726"
- integrity sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==
- dependencies:
- semver "^7.3.5"
-
-"@npmcli/git@^5.0.0":
- version "5.0.8"
- resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-5.0.8.tgz#8ba3ff8724192d9ccb2735a2aa5380a992c5d3d1"
- integrity sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==
- dependencies:
- "@npmcli/promise-spawn" "^7.0.0"
- ini "^4.1.3"
- lru-cache "^10.0.1"
- npm-pick-manifest "^9.0.0"
- proc-log "^4.0.0"
- promise-inflight "^1.0.1"
- promise-retry "^2.0.1"
- semver "^7.3.5"
- which "^4.0.0"
-
-"@npmcli/node-gyp@^3.0.0":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz#101b2d0490ef1aa20ed460e4c0813f0db560545a"
- integrity sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==
-
-"@npmcli/package-json@^5.0.0":
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-5.2.0.tgz#a1429d3111c10044c7efbfb0fce9f2c501f4cfad"
- integrity sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==
- dependencies:
- "@npmcli/git" "^5.0.0"
- glob "^10.2.2"
- hosted-git-info "^7.0.0"
- json-parse-even-better-errors "^3.0.0"
- normalize-package-data "^6.0.0"
- proc-log "^4.0.0"
- semver "^7.5.3"
-
-"@npmcli/promise-spawn@^7.0.0":
- version "7.0.2"
- resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz#1d53d34ffeb5d151bfa8ec661bcccda8bbdfd532"
- integrity sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==
- dependencies:
- which "^4.0.0"
-
-"@npmcli/run-script@8.1.0":
- version "8.1.0"
- resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-8.1.0.tgz#a563e5e29b1ca4e648a6b1bbbfe7220b4bfe39fc"
- integrity sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==
- dependencies:
- "@npmcli/node-gyp" "^3.0.0"
- "@npmcli/package-json" "^5.0.0"
- "@npmcli/promise-spawn" "^7.0.0"
- node-gyp "^10.0.0"
- proc-log "^4.0.0"
- which "^4.0.0"
-
-"@octokit/auth-token@^4.0.0":
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-4.0.0.tgz#40d203ea827b9f17f42a29c6afb93b7745ef80c7"
- integrity sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==
-
-"@octokit/core@^5.0.1":
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/@octokit/core/-/core-5.2.0.tgz#ddbeaefc6b44a39834e1bb2e58a49a117672a7ea"
- integrity sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==
- dependencies:
- "@octokit/auth-token" "^4.0.0"
- "@octokit/graphql" "^7.1.0"
- "@octokit/request" "^8.3.1"
- "@octokit/request-error" "^5.1.0"
- "@octokit/types" "^13.0.0"
- before-after-hook "^2.2.0"
- universal-user-agent "^6.0.0"
-
-"@octokit/endpoint@^9.0.1":
- version "9.0.5"
- resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-9.0.5.tgz#e6c0ee684e307614c02fc6ac12274c50da465c44"
- integrity sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==
- dependencies:
- "@octokit/types" "^13.1.0"
- universal-user-agent "^6.0.0"
-
-"@octokit/graphql@^7.1.0":
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-7.1.0.tgz#9bc1c5de92f026648131f04101cab949eeffe4e0"
- integrity sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==
- dependencies:
- "@octokit/request" "^8.3.0"
- "@octokit/types" "^13.0.0"
- universal-user-agent "^6.0.0"
-
-"@octokit/openapi-types@^20.0.0":
- version "20.0.0"
- resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-20.0.0.tgz#9ec2daa0090eeb865ee147636e0c00f73790c6e5"
- integrity sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==
-
-"@octokit/openapi-types@^22.2.0":
- version "22.2.0"
- resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-22.2.0.tgz#75aa7dcd440821d99def6a60b5f014207ae4968e"
- integrity sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==
-
-"@octokit/plugin-paginate-rest@^9.0.0":
- version "9.2.1"
- resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz#2e2a2f0f52c9a4b1da1a3aa17dabe3c459b9e401"
- integrity sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw==
- dependencies:
- "@octokit/types" "^12.6.0"
-
-"@octokit/plugin-rest-endpoint-methods@^10.0.0":
- version "10.4.1"
- resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz#41ba478a558b9f554793075b2e20cd2ef973be17"
- integrity sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==
- dependencies:
- "@octokit/types" "^12.6.0"
-
-"@octokit/request-error@^5.1.0":
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-5.1.0.tgz#ee4138538d08c81a60be3f320cd71063064a3b30"
- integrity sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==
- dependencies:
- "@octokit/types" "^13.1.0"
- deprecation "^2.0.0"
- once "^1.4.0"
-
-"@octokit/request@^8.3.0", "@octokit/request@^8.3.1":
- version "8.4.0"
- resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.4.0.tgz#7f4b7b1daa3d1f48c0977ad8fffa2c18adef8974"
- integrity sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==
- dependencies:
- "@octokit/endpoint" "^9.0.1"
- "@octokit/request-error" "^5.1.0"
- "@octokit/types" "^13.1.0"
- universal-user-agent "^6.0.0"
-
-"@octokit/types@^12.6.0":
- version "12.6.0"
- resolved "https://registry.yarnpkg.com/@octokit/types/-/types-12.6.0.tgz#8100fb9eeedfe083aae66473bd97b15b62aedcb2"
- integrity sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==
- dependencies:
- "@octokit/openapi-types" "^20.0.0"
-
-"@octokit/types@^13.0.0", "@octokit/types@^13.1.0":
- version "13.5.0"
- resolved "https://registry.yarnpkg.com/@octokit/types/-/types-13.5.0.tgz#4796e56b7b267ebc7c921dcec262b3d5bfb18883"
- integrity sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==
- dependencies:
- "@octokit/openapi-types" "^22.2.0"
-
-"@pkgjs/parseargs@^0.11.0":
- version "0.11.0"
- resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
- integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
-
-"@tsconfig/node10@^1.0.7":
- version "1.0.11"
- resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2"
- integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==
-
-"@tsconfig/node12@^1.0.7":
- version "1.0.11"
- resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
- integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
-
-"@tsconfig/node14@^1.0.0":
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
- integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
-
-"@tsconfig/node16@^1.0.2":
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
- integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
-
-"@types/node@^20.16.2":
- version "20.16.2"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-20.16.2.tgz#9e388f503a5af306e8c63319334887390966a11e"
- integrity sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==
- dependencies:
- undici-types "~6.19.2"
-
-abbrev@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf"
- integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==
-
-acorn-walk@^8.1.1:
- version "8.3.3"
- resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e"
- integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==
- dependencies:
- acorn "^8.11.0"
-
-acorn@^8.11.0, acorn@^8.4.1:
- version "8.12.1"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
- integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
-
-agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1:
- version "7.1.1"
- resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317"
- integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==
- dependencies:
- debug "^4.3.4"
-
-aggregate-error@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
- integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
- dependencies:
- clean-stack "^2.0.0"
- indent-string "^4.0.0"
-
-ansi-regex@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
- integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
-
-ansi-regex@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
- integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
-
-ansi-styles@^4.0.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
- integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
- dependencies:
- color-convert "^2.0.1"
-
-ansi-styles@^6.1.0:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
- integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
-
-arg@^4.1.0:
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
- integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
-
-asynckit@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
- integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
-
-axios@^1.7.4:
- version "1.7.5"
- resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.5.tgz#21eed340eb5daf47d29b6e002424b3e88c8c54b1"
- integrity sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==
- dependencies:
- follow-redirects "^1.15.6"
- form-data "^4.0.0"
- proxy-from-env "^1.1.0"
-
-balanced-match@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
- integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
-
-before-after-hook@^2.2.0:
- version "2.2.3"
- resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c"
- integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==
-
-bin-links@4.0.4:
- version "4.0.4"
- resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-4.0.4.tgz#c3565832b8e287c85f109a02a17027d152a58a63"
- integrity sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==
- dependencies:
- cmd-shim "^6.0.0"
- npm-normalize-package-bin "^3.0.0"
- read-cmd-shim "^4.0.0"
- write-file-atomic "^5.0.0"
-
-brace-expansion@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
- integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
- dependencies:
- balanced-match "^1.0.0"
-
-cacache@^18.0.0:
- version "18.0.4"
- resolved "https://registry.yarnpkg.com/cacache/-/cacache-18.0.4.tgz#4601d7578dadb59c66044e157d02a3314682d6a5"
- integrity sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==
- dependencies:
- "@npmcli/fs" "^3.1.0"
- fs-minipass "^3.0.0"
- glob "^10.2.2"
- lru-cache "^10.0.1"
- minipass "^7.0.3"
- minipass-collect "^2.0.1"
- minipass-flush "^1.0.5"
- minipass-pipeline "^1.2.4"
- p-map "^4.0.0"
- ssri "^10.0.0"
- tar "^6.1.11"
- unique-filename "^3.0.0"
-
-chownr@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
- integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
-
-clean-stack@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
- integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
-
-cliui@^8.0.1:
- version "8.0.1"
- resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
- integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
- dependencies:
- string-width "^4.2.0"
- strip-ansi "^6.0.1"
- wrap-ansi "^7.0.0"
-
-cmd-shim@^6.0.0:
- version "6.0.3"
- resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.3.tgz#c491e9656594ba17ac83c4bd931590a9d6e26033"
- integrity sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==
-
-color-convert@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
- integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
- dependencies:
- color-name "~1.1.4"
-
-color-name@~1.1.4:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
- integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
-
-combined-stream@^1.0.8:
- version "1.0.8"
- resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
- integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
- dependencies:
- delayed-stream "~1.0.0"
-
-create-require@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
- integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
-
-cross-spawn@^7.0.0:
- version "7.0.3"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
- integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
- dependencies:
- path-key "^3.1.0"
- shebang-command "^2.0.0"
- which "^2.0.1"
-
-debug@4, debug@^4.1.1, debug@^4.3.4, debug@^4.3.5:
- version "4.3.6"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
- integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==
- dependencies:
- ms "2.1.2"
-
-delayed-stream@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
- integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
-
-deprecation@^2.0.0:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
- integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==
-
-diff@^4.0.1:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
- integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
-
-eastasianwidth@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
- integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
-
-emoji-regex@^8.0.0:
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
- integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
-
-emoji-regex@^9.2.2:
- version "9.2.2"
- resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
- integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
-
-encoding@^0.1.13:
- version "0.1.13"
- resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
- integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
- dependencies:
- iconv-lite "^0.6.2"
-
-env-paths@^2.2.0:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
- integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
-
-err-code@^2.0.2:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
- integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==
-
-escalade@^3.1.1:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
- integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
-
-exponential-backoff@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
- integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==
-
-follow-redirects@^1.15.6:
- version "1.15.6"
- resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
- integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
-
-foreground-child@^3.1.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77"
- integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==
- dependencies:
- cross-spawn "^7.0.0"
- signal-exit "^4.0.1"
-
-form-data@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
- integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.8"
- mime-types "^2.1.12"
-
-fs-minipass@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
- integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
- dependencies:
- minipass "^3.0.0"
-
-fs-minipass@^3.0.0:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54"
- integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==
- dependencies:
- minipass "^7.0.3"
-
-function-bind@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
- integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
-
-get-caller-file@^2.0.5:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
- integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
-
-glob@^10.2.2, glob@^10.3.10:
- version "10.4.5"
- resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956"
- integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
- dependencies:
- foreground-child "^3.1.0"
- jackspeak "^3.1.2"
- minimatch "^9.0.4"
- minipass "^7.1.2"
- package-json-from-dist "^1.0.0"
- path-scurry "^1.11.1"
-
-graceful-fs@^4.2.6:
- version "4.2.11"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
- integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
-
-hasown@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
- integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
- dependencies:
- function-bind "^1.1.2"
-
-hosted-git-info@^7.0.0:
- version "7.0.2"
- resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz#9b751acac097757667f30114607ef7b661ff4f17"
- integrity sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==
- dependencies:
- lru-cache "^10.0.1"
-
-http-cache-semantics@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
- integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
-
-http-proxy-agent@^7.0.0:
- version "7.0.2"
- resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e"
- integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==
- dependencies:
- agent-base "^7.1.0"
- debug "^4.3.4"
-
-https-proxy-agent@^7.0.1:
- version "7.0.5"
- resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2"
- integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==
- dependencies:
- agent-base "^7.0.2"
- debug "4"
-
-iconv-lite@^0.6.2:
- version "0.6.3"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
- integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
- dependencies:
- safer-buffer ">= 2.1.2 < 3.0.0"
-
-imurmurhash@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
- integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
-
-indent-string@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
- integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
-
-ini@^4.1.3:
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.3.tgz#4c359675a6071a46985eb39b14e4a2c0ec98a795"
- integrity sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==
-
-ip-address@^9.0.5:
- version "9.0.5"
- resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a"
- integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==
- dependencies:
- jsbn "1.1.0"
- sprintf-js "^1.1.3"
-
-is-core-module@^2.13.0:
- version "2.15.1"
- resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37"
- integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==
- dependencies:
- hasown "^2.0.2"
-
-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"
- integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
-
-is-lambda@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5"
- integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==
-
-isexe@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
- integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
-
-isexe@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d"
- integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==
-
-jackspeak@^3.1.2:
- version "3.4.3"
- resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a"
- integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==
- dependencies:
- "@isaacs/cliui" "^8.0.2"
- optionalDependencies:
- "@pkgjs/parseargs" "^0.11.0"
-
-jsbn@1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
- integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==
-
-json-parse-even-better-errors@^3.0.0:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz#b43d35e89c0f3be6b5fbbe9dc6c82467b30c28da"
- integrity sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==
-
-lru-cache@^10.0.1, lru-cache@^10.2.0:
- version "10.4.3"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
- integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
-
-make-error@^1.1.1:
- version "1.3.6"
- resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
- integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
-
-make-fetch-happen@^13.0.0:
- version "13.0.1"
- resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz#273ba2f78f45e1f3a6dca91cede87d9fa4821e36"
- integrity sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==
- dependencies:
- "@npmcli/agent" "^2.0.0"
- cacache "^18.0.0"
- http-cache-semantics "^4.1.1"
- is-lambda "^1.0.1"
- minipass "^7.0.2"
- minipass-fetch "^3.0.0"
- minipass-flush "^1.0.5"
- minipass-pipeline "^1.2.4"
- negotiator "^0.6.3"
- proc-log "^4.2.0"
- promise-retry "^2.0.1"
- ssri "^10.0.0"
-
-mime-db@1.52.0:
- version "1.52.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
- integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
-
-mime-types@^2.1.12:
- version "2.1.35"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
- integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
- dependencies:
- mime-db "1.52.0"
-
-minimatch@^9.0.4:
- version "9.0.5"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
- integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
- dependencies:
- brace-expansion "^2.0.1"
-
-minipass-collect@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-2.0.1.tgz#1621bc77e12258a12c60d34e2276ec5c20680863"
- integrity sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==
- dependencies:
- minipass "^7.0.3"
-
-minipass-fetch@^3.0.0:
- version "3.0.5"
- resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.5.tgz#f0f97e40580affc4a35cc4a1349f05ae36cb1e4c"
- integrity sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==
- dependencies:
- minipass "^7.0.3"
- minipass-sized "^1.0.3"
- minizlib "^2.1.2"
- optionalDependencies:
- encoding "^0.1.13"
-
-minipass-flush@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373"
- integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==
- dependencies:
- minipass "^3.0.0"
-
-minipass-pipeline@^1.2.4:
- version "1.2.4"
- resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c"
- integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==
- dependencies:
- minipass "^3.0.0"
-
-minipass-sized@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70"
- integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==
- dependencies:
- minipass "^3.0.0"
-
-minipass@^3.0.0:
- version "3.3.6"
- resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a"
- integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
- dependencies:
- yallist "^4.0.0"
-
-minipass@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d"
- integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==
-
-"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3, minipass@^7.1.2:
- version "7.1.2"
- resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707"
- integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
-
-minizlib@^2.1.1, minizlib@^2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
- integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
- dependencies:
- minipass "^3.0.0"
- yallist "^4.0.0"
-
-mkdirp@^1.0.3:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
- integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
-
-ms@2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
- integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-
-negotiator@^0.6.3:
- version "0.6.3"
- resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
- integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
-
-node-gyp@^10.0.0:
- version "10.2.0"
- resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-10.2.0.tgz#80101c4aa4f7ab225f13fcc8daaaac4eb1a8dd86"
- integrity sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==
- dependencies:
- env-paths "^2.2.0"
- exponential-backoff "^3.1.1"
- glob "^10.3.10"
- graceful-fs "^4.2.6"
- make-fetch-happen "^13.0.0"
- nopt "^7.0.0"
- proc-log "^4.1.0"
- semver "^7.3.5"
- tar "^6.2.1"
- which "^4.0.0"
-
-nopt@^7.0.0:
- version "7.2.1"
- resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7"
- integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==
- dependencies:
- abbrev "^2.0.0"
-
-normalize-package-data@^6.0.0:
- version "6.0.2"
- resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-6.0.2.tgz#a7bc22167fe24025412bcff0a9651eb768b03506"
- integrity sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==
- dependencies:
- hosted-git-info "^7.0.0"
- semver "^7.3.5"
- validate-npm-package-license "^3.0.4"
-
-npm-install-checks@^6.0.0:
- version "6.3.0"
- resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-6.3.0.tgz#046552d8920e801fa9f919cad569545d60e826fe"
- integrity sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==
- dependencies:
- semver "^7.1.1"
-
-npm-normalize-package-bin@3.0.1, npm-normalize-package-bin@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832"
- integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==
-
-npm-package-arg@^11.0.0:
- version "11.0.3"
- resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-11.0.3.tgz#dae0c21199a99feca39ee4bfb074df3adac87e2d"
- integrity sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==
- dependencies:
- hosted-git-info "^7.0.0"
- proc-log "^4.0.0"
- semver "^7.3.5"
- validate-npm-package-name "^5.0.0"
-
-npm-pick-manifest@^9.0.0:
- version "9.1.0"
- resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz#83562afde52b0b07cb6244361788d319ce7e8636"
- integrity sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==
- dependencies:
- npm-install-checks "^6.0.0"
- npm-normalize-package-bin "^3.0.0"
- npm-package-arg "^11.0.0"
- semver "^7.3.5"
-
-once@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
- integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
- dependencies:
- wrappy "1"
-
-p-map@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
- integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==
- dependencies:
- aggregate-error "^3.0.0"
-
-package-json-from-dist@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00"
- integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==
-
-path-key@^3.1.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
- integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
-
-path-parse@^1.0.7:
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
- integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
-
-path-scurry@^1.11.1:
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2"
- integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==
- dependencies:
- lru-cache "^10.2.0"
- minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
-
-proc-log@^4.0.0, proc-log@^4.1.0, proc-log@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-4.2.0.tgz#b6f461e4026e75fdfe228b265e9f7a00779d7034"
- integrity sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==
-
-promise-inflight@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
- integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==
-
-promise-retry@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22"
- integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==
- dependencies:
- err-code "^2.0.2"
- retry "^0.12.0"
-
-proxy-from-env@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
- integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
-
-read-cmd-shim@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz#640a08b473a49043e394ae0c7a34dd822c73b9bb"
- integrity sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==
-
-require-directory@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
- integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
-
-resolve@1.22.8:
- version "1.22.8"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
- integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
- dependencies:
- is-core-module "^2.13.0"
- path-parse "^1.0.7"
- supports-preserve-symlinks-flag "^1.0.0"
-
-retry@^0.12.0:
- version "0.12.0"
- resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
- integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==
-
-"safer-buffer@>= 2.1.2 < 3.0.0":
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
- integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
-
-semver@^7.1.1, semver@^7.3.5, semver@^7.5.3:
- version "7.6.3"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
- integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
-
-shebang-command@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
- integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
- dependencies:
- shebang-regex "^3.0.0"
-
-shebang-regex@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
- integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
-
-signal-exit@^4.0.1:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
- integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
-
-simple-git@^3.25.0:
- version "3.25.0"
- resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-3.25.0.tgz#3666e76d6831f0583dc380645945b97e0ac4aab6"
- integrity sha512-KIY5sBnzc4yEcJXW7Tdv4viEz8KyG+nU0hay+DWZasvdFOYKeUZ6Xc25LUHHjw0tinPT7O1eY6pzX7pRT1K8rw==
- dependencies:
- "@kwsites/file-exists" "^1.1.1"
- "@kwsites/promise-deferred" "^1.1.1"
- debug "^4.3.5"
-
-smart-buffer@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
- integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
-
-socks-proxy-agent@^8.0.3:
- version "8.0.4"
- resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz#9071dca17af95f483300316f4b063578fa0db08c"
- integrity sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==
- dependencies:
- agent-base "^7.1.1"
- debug "^4.3.4"
- socks "^2.8.3"
-
-socks@^2.8.3:
- version "2.8.3"
- resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5"
- integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==
- dependencies:
- ip-address "^9.0.5"
- smart-buffer "^4.2.0"
-
-spdx-correct@^3.0.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c"
- integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==
- dependencies:
- spdx-expression-parse "^3.0.0"
- spdx-license-ids "^3.0.0"
-
-spdx-exceptions@^2.1.0:
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66"
- integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==
-
-spdx-expression-parse@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
- integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
- dependencies:
- spdx-exceptions "^2.1.0"
- spdx-license-ids "^3.0.0"
-
-spdx-license-ids@^3.0.0:
- version "3.0.20"
- resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz#e44ed19ed318dd1e5888f93325cee800f0f51b89"
- integrity sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==
-
-sprintf-js@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a"
- integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==
-
-ssri@^10.0.0:
- version "10.0.6"
- resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.6.tgz#a8aade2de60ba2bce8688e3fa349bad05c7dc1e5"
- integrity sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==
- dependencies:
- minipass "^7.0.3"
-
-"string-width-cjs@npm:string-width@^4.2.0":
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
- integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.1"
-
-string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
- integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.1"
-
-string-width@^5.0.1, string-width@^5.1.2:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
- integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
- dependencies:
- eastasianwidth "^0.2.0"
- emoji-regex "^9.2.2"
- strip-ansi "^7.0.1"
-
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
- integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
- dependencies:
- ansi-regex "^5.0.1"
-
-strip-ansi@^6.0.0, strip-ansi@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
- integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
- dependencies:
- ansi-regex "^5.0.1"
-
-strip-ansi@^7.0.1:
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
- integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
- dependencies:
- ansi-regex "^6.0.1"
-
-supports-preserve-symlinks-flag@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
- integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
-
-tar@^6.1.11, tar@^6.2.1:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a"
- integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==
- dependencies:
- chownr "^2.0.0"
- fs-minipass "^2.0.0"
- minipass "^5.0.0"
- minizlib "^2.1.1"
- mkdirp "^1.0.3"
- yallist "^4.0.0"
-
-ts-node@^10.5.0:
- version "10.9.2"
- resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f"
- integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==
- dependencies:
- "@cspotcode/source-map-support" "^0.8.0"
- "@tsconfig/node10" "^1.0.7"
- "@tsconfig/node12" "^1.0.7"
- "@tsconfig/node14" "^1.0.0"
- "@tsconfig/node16" "^1.0.2"
- acorn "^8.4.1"
- acorn-walk "^8.1.1"
- arg "^4.1.0"
- create-require "^1.1.0"
- diff "^4.0.1"
- make-error "^1.1.1"
- v8-compile-cache-lib "^3.0.1"
- yn "3.1.1"
-
-tunnel@^0.0.6:
- version "0.0.6"
- resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
- integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
-
-typescript@~5.4.5:
- version "5.4.5"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611"
- integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==
-
-undici-types@~6.19.2:
- version "6.19.8"
- resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
- integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
-
-undici@^5.25.4:
- version "5.28.4"
- resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068"
- integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==
- dependencies:
- "@fastify/busboy" "^2.0.0"
-
-unique-filename@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea"
- integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==
- dependencies:
- unique-slug "^4.0.0"
-
-unique-slug@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3"
- integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==
- dependencies:
- imurmurhash "^0.1.4"
-
-universal-user-agent@^6.0.0:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.1.tgz#15f20f55da3c930c57bddbf1734c6654d5fd35aa"
- integrity sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==
-
-uuid@^8.3.2:
- version "8.3.2"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
- integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
-
-v8-compile-cache-lib@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
- integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
-
-validate-npm-package-license@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
- integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
- dependencies:
- spdx-correct "^3.0.0"
- spdx-expression-parse "^3.0.0"
-
-validate-npm-package-name@^5.0.0:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8"
- integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==
-
-which@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
- integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
- dependencies:
- isexe "^2.0.0"
-
-which@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a"
- integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==
- dependencies:
- isexe "^3.1.1"
-
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
- integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
- dependencies:
- ansi-styles "^4.0.0"
- string-width "^4.1.0"
- strip-ansi "^6.0.0"
-
-wrap-ansi@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
- integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
- dependencies:
- ansi-styles "^4.0.0"
- string-width "^4.1.0"
- strip-ansi "^6.0.0"
-
-wrap-ansi@^8.1.0:
- version "8.1.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
- integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
- dependencies:
- ansi-styles "^6.1.0"
- string-width "^5.0.1"
- strip-ansi "^7.0.1"
-
-wrappy@1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
- integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
-
-write-file-atomic@^5.0.0:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7"
- integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==
- dependencies:
- imurmurhash "^0.1.4"
- signal-exit "^4.0.1"
-
-y18n@^5.0.5:
- version "5.0.8"
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
- integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
-
-yallist@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
- integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
-
-yargs-parser@^21.1.1:
- version "21.1.1"
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
- integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
-
-yargs@17.7.2:
- version "17.7.2"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
- integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
- dependencies:
- cliui "^8.0.1"
- escalade "^3.1.1"
- get-caller-file "^2.0.5"
- require-directory "^2.1.1"
- string-width "^4.2.3"
- y18n "^5.0.5"
- yargs-parser "^21.1.1"
-
-yn@3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
- integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
diff --git a/.github/workflows/add-release-label.yml b/.github/workflows/add-release-label.yml
index a5a4daa7103..b56d691ae99 100644
--- a/.github/workflows/add-release-label.yml
+++ b/.github/workflows/add-release-label.yml
@@ -21,17 +21,16 @@ jobs:
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
+ cache: yarn
- name: Install dependencies
run: yarn --immutable
- working-directory: '.github/scripts'
- name: Get the next semver version
id: get-next-semver-version
env:
FORCE_NEXT_SEMVER_VERSION: ${{ vars.FORCE_NEXT_SEMVER_VERSION }}
run: ./scripts/get-next-semver-version.sh "$FORCE_NEXT_SEMVER_VERSION"
- working-directory: '.github/scripts'
- name: Add release label to PR and linked issues
id: add-release-label-to-pr-and-linked-issues
@@ -39,4 +38,3 @@ jobs:
RELEASE_LABEL_TOKEN: ${{ secrets.RELEASE_LABEL_TOKEN }}
NEXT_SEMVER_VERSION: ${{ env.NEXT_SEMVER_VERSION }}
run: yarn run add-release-label-to-pr-and-linked-issues
- working-directory: '.github/scripts'
diff --git a/.github/workflows/check-pr-labels.yml b/.github/workflows/check-pr-labels.yml
index e17447c2711..6e0bf98762a 100644
--- a/.github/workflows/check-pr-labels.yml
+++ b/.github/workflows/check-pr-labels.yml
@@ -26,14 +26,13 @@ jobs:
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
+ cache: yarn
- name: Install dependencies
run: yarn --immutable
- working-directory: '.github/scripts'
- name: Check PR has required labels
id: check-pr-has-required-labels
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: yarn run check-pr-has-required-labels
- working-directory: '.github/scripts'
diff --git a/.github/workflows/check-template-and-add-labels.yml b/.github/workflows/check-template-and-add-labels.yml
index a01f1b6d5b7..e5311b70d22 100644
--- a/.github/workflows/check-template-and-add-labels.yml
+++ b/.github/workflows/check-template-and-add-labels.yml
@@ -19,14 +19,13 @@ jobs:
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
+ cache: yarn
- name: Install dependencies
run: yarn --immutable
- working-directory: '.github/scripts'
- name: Check template and add labels
id: check-template-and-add-labels
env:
LABEL_TOKEN: ${{ secrets.LABEL_TOKEN }}
run: npm run check-template-and-add-labels
- working-directory: '.github/scripts'
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 64bd5bea831..f13a32ae47f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -5,22 +5,16 @@ on:
pull_request:
merge_group:
types: [checks_requested]
-
+
jobs:
- check-diff:
- runs-on: macos-latest
+ setup:
+ runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
cache: yarn
- - uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 #v1
- with:
- ruby-version: '3.1.5'
- bundler-cache: true
- env:
- BUNDLE_GEMFILE: ios/Gemfile
- name: Determine whether the current PR is a draft
id: set-is-draft
if: github.event_name == 'pull_request' && github.event.pull_request.number
@@ -33,7 +27,7 @@ jobs:
run: printf '%s\n\n%s' '@metamask:registry=https://npm.pkg.github.com' "//npm.pkg.github.com/:_authToken=${PACKAGE_READ_TOKEN}" > .npmrc
env:
PACKAGE_READ_TOKEN: ${{ secrets.PACKAGE_READ_TOKEN }}
- - run: yarn setup
+ - run: yarn setup --node
- name: Require clean working directory
shell: bash
run: |
@@ -45,6 +39,7 @@ jobs:
fi
dedupe:
runs-on: ubuntu-20.04
+ needs: setup
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
@@ -63,6 +58,7 @@ jobs:
fi
scripts:
runs-on: ubuntu-20.04
+ needs: setup
strategy:
matrix:
scripts:
@@ -90,6 +86,7 @@ jobs:
fi
unit-tests:
runs-on: ubuntu-20.04
+ needs: setup
strategy:
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
@@ -159,6 +156,7 @@ jobs:
js-bundle-size-check:
runs-on: ubuntu-20.04
+ needs: setup
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
@@ -222,10 +220,6 @@ jobs:
uses: actions/checkout@v3
- name: SonarCloud Quality Gate Status
id: sonar-status
- env:
- REPO: ${{ github.repository }}
- ISSUE_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Skip step if event is a PR
if [[ "${{ github.event_name }}" != "pull_request" ]]; then
@@ -233,40 +227,31 @@ jobs:
exit 0
fi
- # Bypass step if skip-sonar-cloud label is found
- LABEL=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
- "https://api.github.com/repos/$REPO/issues/$ISSUE_NUMBER/labels" | \
- jq -r '.[] | select(.name=="skip-sonar-cloud") | .name')
+ sleep 30
- if [[ "$LABEL" == "skip-sonar-cloud" ]]; then
- echo "skip-sonar-cloud label found. Skipping SonarCloud Quality Gate check."
- else
- sleep 30
-
- PROJECT_KEY="metamask-mobile"
- PR_NUMBER="${{ github.event.pull_request.number }}"
- SONAR_TOKEN="${{ secrets.SONAR_TOKEN }}"
+ PROJECT_KEY="metamask-mobile"
+ PR_NUMBER="${{ github.event.pull_request.number }}"
+ SONAR_TOKEN="${{ secrets.SONAR_TOKEN }}"
- if [ -z "$PR_NUMBER" ]; then
- echo "No pull request number found. Failing the check."
- exit 1
- fi
+ if [ -z "$PR_NUMBER" ]; then
+ echo "No pull request number found. Failing the check."
+ exit 1
+ fi
- RESPONSE=$(curl -s -u "$SONAR_TOKEN:" \
- "https://sonarcloud.io/api/qualitygates/project_status?projectKey=$PROJECT_KEY&pullRequest=$PR_NUMBER")
- echo "SonarCloud API Response: $RESPONSE"
+ RESPONSE=$(curl -s -u "$SONAR_TOKEN:" \
+ "https://sonarcloud.io/api/qualitygates/project_status?projectKey=$PROJECT_KEY&pullRequest=$PR_NUMBER")
+ echo "SonarCloud API Response: $RESPONSE"
- STATUS=$(echo "$RESPONSE" | jq -r '.projectStatus.status')
+ STATUS=$(echo "$RESPONSE" | jq -r '.projectStatus.status')
- if [[ "$STATUS" == "ERROR" ]]; then
- echo "Quality Gate failed."
- exit 1
- elif [[ "$STATUS" == "OK" ]]; then
- echo "Quality Gate passed."
- else
- echo "Could not determine Quality Gate status."
- exit 1
- fi
+ if [[ "$STATUS" == "ERROR" ]]; then
+ echo "Quality Gate failed."
+ exit 1
+ elif [[ "$STATUS" == "OK" ]]; then
+ echo "Quality Gate passed."
+ else
+ echo "Could not determine Quality Gate status."
+ exit 1
fi
check-workflows:
name: Check workflows
@@ -285,11 +270,12 @@ jobs:
runs-on: ubuntu-20.04
needs:
[
- check-diff,
+ setup,
dedupe,
scripts,
unit-tests,
check-workflows,
+ sonar-cloud,
js-bundle-size-check,
sonar-cloud-quality-gate-status,
]
diff --git a/.github/workflows/close-bug-report.yml b/.github/workflows/close-bug-report.yml
index a80004cae14..981824ed300 100644
--- a/.github/workflows/close-bug-report.yml
+++ b/.github/workflows/close-bug-report.yml
@@ -21,10 +21,10 @@ jobs:
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
+ cache: yarn
- name: Install dependencies
run: yarn --immutable
- working-directory: '.github/scripts'
- name: Close release bug report issue
id: close-release-bug-report-issue
@@ -32,4 +32,3 @@ jobs:
BUG_REPORT_REPO: mobile-planning
BUG_REPORT_TOKEN: ${{ secrets.BUG_REPORT_TOKEN }}
run: yarn run close-release-bug-report-issue
- working-directory: '.github/scripts'
diff --git a/.github/workflows/crowdin_action.yml b/.github/workflows/crowdin_action.yml
index eba1ac69154..2582db7e5a7 100644
--- a/.github/workflows/crowdin_action.yml
+++ b/.github/workflows/crowdin_action.yml
@@ -10,6 +10,8 @@ on:
- main
schedule:
- cron: "0 */12 * * *"
+ merge_group:
+ types: [checks_requested]
jobs:
synchronize-with-crowdin:
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index a95001cb1c6..f577c5ba755 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -4,20 +4,21 @@ on:
branches: main
pull_request:
+
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
- uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3
+ uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3
+ uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3
- uses: actions/checkout@v3
- name: Build and load
- uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
+ uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: .
file: scripts/docker/Dockerfile
diff --git a/.github/workflows/fitness-functions.yml b/.github/workflows/fitness-functions.yml
index 6e621537271..454b9ce8bc4 100644
--- a/.github/workflows/fitness-functions.yml
+++ b/.github/workflows/fitness-functions.yml
@@ -18,10 +18,10 @@ jobs:
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
+ cache: yarn
- name: Install dependencies
run: yarn --immutable
- working-directory: '.github/scripts'
- name: Run fitness functions
env:
@@ -33,4 +33,3 @@ jobs:
# then saved to a file called "diff".
git diff "$(git merge-base "origin/$BASE_REF" HEAD)" HEAD -- . > ./diff
yarn run fitness-functions -- "ci" "./diff"
- working-directory: '.github/scripts'
diff --git a/.github/workflows/run-bitrise-e2e-check.yml b/.github/workflows/run-bitrise-e2e-check.yml
index ac1016e9d86..23274a35f5e 100644
--- a/.github/workflows/run-bitrise-e2e-check.yml
+++ b/.github/workflows/run-bitrise-e2e-check.yml
@@ -43,10 +43,10 @@ jobs:
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
+ cache: yarn
- name: Install dependencies
run: yarn --immutable
- working-directory: '.github/scripts'
- name: Check Bitrise E2E Status
env:
@@ -56,4 +56,3 @@ jobs:
# The status check created under this workflow may be bucketed under another check suite in Github actions. This is a result of workflows with the same triggers.
# For example, the status check may show as `CLA Signature Bot / Bitrise E2E Status`. This is a bug on Github's UI. https://github.com/orgs/community/discussions/24616
run: yarn run run-bitrise-e2e-check
- working-directory: '.github/scripts'
diff --git a/.js.env.example b/.js.env.example
index 68e8316f034..768f29a7f82 100644
--- a/.js.env.example
+++ b/.js.env.example
@@ -92,5 +92,3 @@ export MM_ENABLE_SETTINGS_PAGE_DEV_OPTIONS="true"
# Multichain Feature flag
export MULTICHAIN_V1=""
-#Multichain feature flag specific to UI changes
-export MM_MULTICHAIN_V1_ENABLED=""
diff --git a/.storybook/main.js b/.storybook/main.js
index fd51e44c137..64a3f3cf602 100644
--- a/.storybook/main.js
+++ b/.storybook/main.js
@@ -3,7 +3,6 @@ module.exports = {
'../app/component-library/components/**/*.stories.?(ts|tsx|js|jsx)',
'../app/component-library/base-components/**/*.stories.?(ts|tsx|js|jsx)',
'../app/component-library/components-temp/TagColored/**/*.stories.?(ts|tsx|js|jsx)',
- '../app/component-library/components-temp/KeyValueRow/**/*.stories.?(ts|tsx|js|jsx)',
],
addons: ['@storybook/addon-ondevice-controls'],
framework: '@storybook/react-native',
diff --git a/.storybook/storybook.requires.js b/.storybook/storybook.requires.js
index 3b26602e64d..ae2e8f2d55b 100644
--- a/.storybook/storybook.requires.js
+++ b/.storybook/storybook.requires.js
@@ -30,13 +30,6 @@ global.STORIES = [
importPathMatcher:
"^\\.[\\\\/](?:app\\/component-library\\/components-temp\\/TagColored(?:\\/(?!\\.)(?:(?:(?!(?:^|\\/)\\.).)*?)\\/|\\/|$)(?!\\.)(?=.)[^/]*?\\.stories\\.(?:ts|tsx|js|jsx)?)$",
},
- {
- titlePrefix: "",
- directory: "./app/component-library/components-temp/KeyValueRow",
- files: "**/*.stories.?(ts|tsx|js|jsx)",
- importPathMatcher:
- "^\\.[\\\\/](?:app\\/component-library\\/components-temp\\/KeyValueRow(?:\\/(?!\\.)(?:(?:(?!(?:^|\\/)\\.).)*?)\\/|\\/|$)(?!\\.)(?=.)[^/]*?\\.stories\\.(?:ts|tsx|js|jsx)?)$",
- },
];
import "@storybook/addon-ondevice-controls/register";
@@ -122,7 +115,8 @@ const getStories = () => {
"./app/component-library/components/Toast/Toast.stories.tsx": require("../app/component-library/components/Toast/Toast.stories.tsx"),
"./app/component-library/base-components/TagBase/TagBase.stories.tsx": require("../app/component-library/base-components/TagBase/TagBase.stories.tsx"),
"./app/component-library/components-temp/TagColored/TagColored.stories.tsx": require("../app/component-library/components-temp/TagColored/TagColored.stories.tsx"),
- "./app/component-library/components-temp/KeyValueRow/KeyValueRow.stories.tsx": require("../app/component-library/components-temp/KeyValueRow/KeyValueRow.stories.tsx"),
+ "./app/components/Views/AssetDetails/AssetDetailsActions/AssetDetailsActions.stories.tsx": require("../app/components/Views/AssetDetails/AssetDetailsActions/AssetDetailsActions.stories.tsx"),
+
};
};
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b50b2835a3..97b9b7095f6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,96 @@
## Current Main Branch
+## 7.32.0 - Sep 19, 2024
+
+### Added
+
+- [#10294](https://github.com/MetaMask/metamask-mobile/pull/10294): feat: create redux slice for featureFlags (#10294)
+- [#11314](https://github.com/MetaMask/metamask-mobile/pull/11314): feat: reject connection properly (#11314)
+- [#11132](https://github.com/MetaMask/metamask-mobile/pull/11132): feat: Add performance tracing infrastructure (#11132)
+- [#10061](https://github.com/MetaMask/metamask-mobile/pull/10061): feat: new receive flow (#10061)
+- [#11174](https://github.com/MetaMask/metamask-mobile/pull/11174): feat(2796): behind feature flag permission settings multichain 2of2 (#11174)
+- [#11019](https://github.com/MetaMask/metamask-mobile/pull/11019): feat(2793): mocked UI screen displaying multichain dapp permission summary 2of2 (#11019)
+- [#10988](https://github.com/MetaMask/metamask-mobile/pull/10988): feat(2808): add a mocked UI checkbox list that will later allow adding the ability to edit network permission (#10988)
+- [#11168](https://github.com/MetaMask/metamask-mobile/pull/11168): feat: add pooled staking input flow screen (#11168)
+- [#10964](https://github.com/MetaMask/metamask-mobile/pull/10964): feat: build your earnings component stub in eth token details (#10964)
+- [#11051](https://github.com/MetaMask/metamask-mobile/pull/11051): feat: add brand evo font files (#11051)
+- [#11285](https://github.com/MetaMask/metamask-mobile/pull/11285): feat: notifications add analytics (#11285)
+- [#10755](https://github.com/MetaMask/metamask-mobile/pull/10755): feat: ledger account selection screen add hd options to sync with extension (#10755)
+- [#11195](https://github.com/MetaMask/metamask-mobile/pull/11195): feat: add AppState dependency to load notifications (#11195)
+- [#11175](https://github.com/MetaMask/metamask-mobile/pull/11175): feat: add product announcements toggle (#11175)
+
+### Changed
+- [#11148](https://github.com/MetaMask/metamask-mobile/pull/11148): chore: remove animation and add new splash screen (#11148)
+- [#11306](https://github.com/MetaMask/metamask-mobile/pull/11306): chore: update @sentry/react-native to version 5.33.0 (#11306)
+- [#11144](https://github.com/MetaMask/metamask-mobile/pull/11144): test: E2E Mocking Setup For Detox Tests (#11144)
+- [#11212](https://github.com/MetaMask/metamask-mobile/pull/11212): chore: Update CI workflow triggers to support release branches (#11212)
+- [#11243](https://github.com/MetaMask/metamask-mobile/pull/11243): chore(js-ts): Convert ModalNavbarTitle to TypeScript (#11243)
+- [#11213](https://github.com/MetaMask/metamask-mobile/pull/11213): test: Appium separate and optimize app launch time measurements (#11213)
+- [#11264](https://github.com/MetaMask/metamask-mobile/pull/11264): chore: remove triggers for actions not needed during the merge-queue CI (#11264)
+- [#11222](https://github.com/MetaMask/metamask-mobile/pull/11222): chore: add bitrise document link to the bitrise failed comment (#11222)
+- [#11145](https://github.com/MetaMask/metamask-mobile/pull/11145): chore: update performance for new allocation (#11145)
+- [#11184](https://github.com/MetaMask/metamask-mobile/pull/11184): test: remove notifications launch arg in E2E (#11184)
+- [#11186](https://github.com/MetaMask/metamask-mobile/pull/11186): ci: prevent detox E2E lock failure (#11186)
+- [#11141](https://github.com/MetaMask/metamask-mobile/pull/11141): chore: update express for all the packages (#11141)
+- [#11124](https://github.com/MetaMask/metamask-mobile/pull/11124): docs: Update Appium documentation (#11124)
+- [#10865](https://github.com/MetaMask/metamask-mobile/pull/10865): chore: update eslint v^8.44 (#10865)
+- [#11096](https://github.com/MetaMask/metamask-mobile/pull/11096): test: detox black list gas api endpoint (#11096)
+- [#11246](https://github.com/MetaMask/metamask-mobile/pull/11246): chore: Remove `eth-sign` (#11246)
+- [#11220](https://github.com/MetaMask/metamask-mobile/pull/11220): chore: Update package @blockaid/ppom_release to version 1.5.3 (#11220)
+- [#11244](https://github.com/MetaMask/metamask-mobile/pull/11244): chore(js-ts): Convert useInterval.js to TypeScript (#11244)
+- [#11089](https://github.com/MetaMask/metamask-mobile/pull/11089): chore: add staking team to codeowners file (#11089)
+- [#11049](https://github.com/MetaMask/metamask-mobile/pull/11049): chore: update balance design (#11049)
+- [#11011](https://github.com/MetaMask/metamask-mobile/pull/11011): chore: Capture currency change in MetaMetrics (#11011)
+- [#10468](https://github.com/MetaMask/metamask-mobile/pull/10468): chore: Capture custom rpc url in `trackEvent` (#10468)
+- [#11207](https://github.com/MetaMask/metamask-mobile/pull/11207): chore(deps): Bump `@metamask/base-controller` from `^6.0.0` to `^7.0.0` (#11207)
+- [#11235](https://github.com/MetaMask/metamask-mobile/pull/11235): ci: avoid running release pipeline on every commit to the release branch (#11235)
+- [#11094](https://github.com/MetaMask/metamask-mobile/pull/11094): chore: chore/7.31.0-Changelog (#11094)
+- [#10788](https://github.com/MetaMask/metamask-mobile/pull/10788): chore: Add `@metamask/selected-network-controller` & integrate (#10788)
+- [#11122](https://github.com/MetaMask/metamask-mobile/pull/11122): test: e2e for auto-lock (#11122)
+- [#11143](https://github.com/MetaMask/metamask-mobile/pull/11143): chore: bump react native webview to 14.0.3 version (#11143)
+- [#11284](https://github.com/MetaMask/metamask-mobile/pull/11284): chore: add notifications state awareness inapp badge (#11284)
+- [#11209](https://github.com/MetaMask/metamask-mobile/pull/11209): chore(runway): cherry-pick fix: freeze during swap with approval (#11209)
+- [#11157](https://github.com/MetaMask/metamask-mobile/pull/11157): chore(runway): cherry-pick chore: bump send for all the packages (#11157)
+- [#11082](https://github.com/MetaMask/metamask-mobile/pull/11082): chore: bump network controller 20.0.0 (#11082)
+- [#11095](https://github.com/MetaMask/metamask-mobile/pull/11095): chore(runway): cherry-pick fix: Intermittent Display Issue of Fiat Currency on Main Wallet View (#11095)
+- [#11181](https://github.com/MetaMask/metamask-mobile/pull/11181): chore(runway): cherry-pick fix: fix check token balance is zero (#11181)
+- [#11208](https://github.com/MetaMask/metamask-mobile/pull/11208): chore(runway): cherry-pick chore: update performance for new allocation (#11208)
+- [#10821](https://github.com/MetaMask/metamask-mobile/pull/10821): chore(deps): bump `accounts-controller` to v18.1.0 and `keyring-api` to v8.1.0 (#10821)
+
+### Fixed
+- [#11117](https://github.com/MetaMask/metamask-mobile/pull/11117): fix: add feat flag (#11117)
+- [#11084](https://github.com/MetaMask/metamask-mobile/pull/11084): fix: locks api spec version for api spec tests (#11084)
+- [#11310](https://github.com/MetaMask/metamask-mobile/pull/11310): fix: quick fix on feature flag & notification state (#11310)
+- [#11200](https://github.com/MetaMask/metamask-mobile/pull/11200): fix: add feature flag on profile sync (#11200)
+- [#11302](https://github.com/MetaMask/metamask-mobile/pull/11302): fix: cp & resolve merge conflict (#11302)
+- [#11130](https://github.com/MetaMask/metamask-mobile/pull/11130): fix(action): add a workaround for known bots (#11130)
+- [#11173](https://github.com/MetaMask/metamask-mobile/pull/11173): fix: dset version (#11173)
+- [#10899](https://github.com/MetaMask/metamask-mobile/pull/10899): fix: Android crash when svgs use the " html entity (#10899)
+- [#11126](https://github.com/MetaMask/metamask-mobile/pull/11126): fix: Skip sonar cloud gate in step instead (#11126)
+- [#11121](https://github.com/MetaMask/metamask-mobile/pull/11121): fix: Add new job to verify ""All jobs pass"" job for required PR check (#11121)
+- [#11266](https://github.com/MetaMask/metamask-mobile/pull/11266): fix: notification permission flow (#11266)
+- [#11252](https://github.com/MetaMask/metamask-mobile/pull/11252): fix: notification permission request message (#11252)
+- [#11155](https://github.com/MetaMask/metamask-mobile/pull/11155): fix: android crashing on date formating Intl usage. (#11155)
+- [#11137](https://github.com/MetaMask/metamask-mobile/pull/11137): fix: notifications bugs (#11137)
+- [#11110](https://github.com/MetaMask/metamask-mobile/pull/11110): fix: accounts notifications switch (#11110)
+- [#11146](https://github.com/MetaMask/metamask-mobile/pull/11146): fix: update nativesdk with improved concurrency handling (#11146)
+- [#11165](https://github.com/MetaMask/metamask-mobile/pull/11165): fix: freeze during swap with approval (#11165)
+- [#11161](https://github.com/MetaMask/metamask-mobile/pull/11161): fix: blockaid loader on confirmation pages (#11161)
+- [#10989](https://github.com/MetaMask/metamask-mobile/pull/10989): fix: closing of gas info tooltip (#10989)
+- [#10348](https://github.com/MetaMask/metamask-mobile/pull/10348): fix: confirmations UI adjustments (#10348)
+- [#10842](https://github.com/MetaMask/metamask-mobile/pull/10842): fix: app crash due to minimal input must be string error (#10842)
+- [#11112](https://github.com/MetaMask/metamask-mobile/pull/11112): fix: update token details monetization button (#11112)
+- [#11172](https://github.com/MetaMask/metamask-mobile/pull/11172): fix: fix check token balance is zero (#11172)
+- [#11087](https://github.com/MetaMask/metamask-mobile/pull/11087): fix: Intermittent Display Issue of Fiat Currency on Main Wallet View (#11087)
+- [#11176](https://github.com/MetaMask/metamask-mobile/pull/11176): fix: switch from bundled to url EE (#11176)
+- [#11281](https://github.com/MetaMask/metamask-mobile/pull/11281): fix: Fix the styling issue of link in SearchingForDeviceStep component (#11281)
+- [#11265](https://github.com/MetaMask/metamask-mobile/pull/11265): fix: notification account syncing (#11265)
+- [#11218](https://github.com/MetaMask/metamask-mobile/pull/11218): fix: close icon on notifications list screen (#11218)
+- [#11193](https://github.com/MetaMask/metamask-mobile/pull/11193): fix: ItemMenu crash using dayjs (#11193)
+- [#11098](https://github.com/MetaMask/metamask-mobile/pull/11098): fix: badge count and ui polishing (#11098)
+
+
## 7.31.0 - Sep 6, 2024
### Added
- [#10747](https://github.com/MetaMask/metamask-mobile/pull/10747): feat: 2805 grant permission to network with missmatching rpc url (#10747)
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 00000000000..3c02108f3ce
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,10 @@
+source 'https://rubygems.org'
+
+# Recommended to use http://rbenv.org/ to install and use this version
+ruby '>= 3.1.5'
+
+# Allow minor version updates up to but excluding 2.0.0
+gem 'cocoapods', '~> 1.12'
+
+# Allow all version updates up to but excluding 7.1.0
+gem 'activesupport', '>= 6.1.7.3', '< 7.1.0'
diff --git a/ios/Gemfile.lock b/Gemfile.lock
similarity index 100%
rename from ios/Gemfile.lock
rename to Gemfile.lock
diff --git a/README.md b/README.md
index 2c4a6de0445..64eff4aa1df 100644
--- a/README.md
+++ b/README.md
@@ -41,20 +41,15 @@ cd metamask-mobile
**Firebase Messaging Setup**
-Before running the app, keep in mind that MetaMask uses FCM (Firebase Cloud Message) to empower communications. Based on this, as an external contributor you would preferably need to provide your own FREE Firebase project config file with a matching client for package name `io.metamask`, and update your `google-services.json` file in the `android/app` directory as well your `.env` files (`.ios.env`, `.js.env`, `.android.env`), adding `GOOGLE_SERVICES_B64` variable depending on the environment you are running the app (ios/android).
+Before running the app, keep in mind that MetaMask uses FCM (Firebase Cloud Message) to empower communications. Based on this, would be preferable that you provide your own Firebase project config file and update your `google-services.json` file in the `android/app` directory as well your .env files (ios.env, js.env, android.env), adding GOOGLE_SERVICES_B64 variable depending on the environment you are running the app (ios/android).
-The value you should provide to `GOOGLE_SERVICES_B64` is the base64 encoded version of your Firebase project config file, which can be generated as follows:
+ATTENTION: In case you don't provide your own Firebase project config file, you can make usage of a mock file at `android/app/google-services-example.json`, following the steps below from the root of the project:
```bash
base64 -i ./android/app/google-services-example.json
```
-Copy the result to your clipboard and paste it in the `GOOGLE_SERVICES_B64` variable in the `.env` file you are running the app.
-
-> [!CAUTION]
-> In case you don't provide your own Firebase project config file, you will face the error `No matching client found for package name 'io.metamask'`.
-
-You can make usage of a mock file at `android/app/google-services-example.json`, following the same steps above from the root of the project.
+Copy the result to your clipboard and paste it in the GOOGLE_SERVICES_B64 variable in the .env file you are running the app.
In case of any doubt, please follow the instructions in the link below to get your Firebase project config file.
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 2363b3c882e..c103d97dbf1 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -173,8 +173,8 @@ android {
applicationId "io.metamask"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 1432
- versionName "7.31.0"
+ versionCode 1441
+ versionName "7.32.0"
testBuildType System.getProperty('testBuildType', 'debug')
missingDimensionStrategy 'react-native-camera', 'general'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
diff --git a/android/app/google-services-example.json b/android/app/google-services-example.json
index f7765f0ad2a..56d9bd48e3c 100644
--- a/android/app/google-services-example.json
+++ b/android/app/google-services-example.json
@@ -7,20 +7,15 @@
},
"client": [
{
- "api_key": [
- {
- "current_key": ""
- }
- ],
"client_info": {
"mobilesdk_app_id": "1:123456789000:android:f1bf012572b04063",
- "client_id": "android:io.metamask",
+ "client_id": "android:com.google.samples.quickstart.admobexample",
"client_type": 1,
"android_client_info": {
- "package_name": "io.metamask",
+ "package_name": "com.google.samples.quickstart.admobexample",
"certificate_hash": []
}
- }
+ },
}
],
"configuration_version": "1"
diff --git a/app/actions/onboarding/index.js b/app/actions/onboarding/index.js
new file mode 100644
index 00000000000..244cac54e16
--- /dev/null
+++ b/app/actions/onboarding/index.js
@@ -0,0 +1,20 @@
+/**
+ * Saves an onboarding analytics event in state
+ *
+ * @param {object} event - Event object
+ */
+export function saveOnboardingEvent(event) {
+ return {
+ type: 'SAVE_EVENT',
+ event,
+ };
+}
+
+/**
+ * Erases any event stored in state
+ */
+export function clearOnboardingEvents() {
+ return {
+ type: 'CLEAR_EVENTS',
+ };
+}
diff --git a/app/actions/onboarding/index.ts b/app/actions/onboarding/index.ts
deleted file mode 100644
index 9866cf8d17b..00000000000
--- a/app/actions/onboarding/index.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { IMetaMetricsEvent } from '../../core/Analytics';
-
-export const SAVE_EVENT = 'SAVE_EVENT';
-export const CLEAR_EVENTS = 'CLEAR_EVENTS';
-
-interface SaveEventAction {
- type: typeof SAVE_EVENT;
- event: [IMetaMetricsEvent];
-}
-
-interface ClearEventsAction {
- type: typeof CLEAR_EVENTS;
-}
-
-export type OnboardingActionTypes = SaveEventAction | ClearEventsAction;
-
-export function saveOnboardingEvent(eventArgs: [IMetaMetricsEvent]): SaveEventAction {
- return {
- type: SAVE_EVENT,
- event: eventArgs,
- };
-}
-
-export function clearOnboardingEvents(): ClearEventsAction {
- return {
- type: CLEAR_EVENTS,
- };
-}
\ No newline at end of file
diff --git a/app/component-library/base-components/TagBase/__snapshots__/TagBase.test.tsx.snap b/app/component-library/base-components/TagBase/__snapshots__/TagBase.test.tsx.snap
index a3be1a262ef..3959ad45459 100644
--- a/app/component-library/base-components/TagBase/__snapshots__/TagBase.test.tsx.snap
+++ b/app/component-library/base-components/TagBase/__snapshots__/TagBase.test.tsx.snap
@@ -7,12 +7,14 @@ exports[`TagBase should render TagBase 1`] = `
onLayout={[Function]}
style={
{
+ "alignItems": "center",
"alignSelf": "flex-start",
"backgroundColor": "#ffffff",
"borderColor": "#bbc0c5",
"borderRadius": 999,
"borderWidth": 0,
"color": "#141618",
+ "flexDirection": "row",
"padding": 16,
"paddingHorizontal": 8,
"paddingVertical": 2,
@@ -20,72 +22,63 @@ exports[`TagBase should render TagBase 1`] = `
}
testID="tagbase"
>
-
-
-
+
-
+
- Sample TagBase Children
-
-
+ Sample TagBase Children
+
+
-
+
-
+ }
+ width={20}
+ />
`;
diff --git a/app/component-library/components-temp/CellSelectWithMenu/__snapshots__/CellSelectWithMenu.test.tsx.snap b/app/component-library/components-temp/CellSelectWithMenu/__snapshots__/CellSelectWithMenu.test.tsx.snap
index 3e2ad44c55f..5b97c76e34c 100644
--- a/app/component-library/components-temp/CellSelectWithMenu/__snapshots__/CellSelectWithMenu.test.tsx.snap
+++ b/app/component-library/components-temp/CellSelectWithMenu/__snapshots__/CellSelectWithMenu.test.tsx.snap
@@ -44,7 +44,6 @@ exports[`CellSelectWithMenu should render with default settings correctly 1`] =
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ 0.33873792024529126,
+ 0.9408807689542256,
+ -0.9408807689542256,
+ 0.33873792024529126,
+ 50.8847515111068,
+ 5.120153954341607,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
-
+
+
+ Orangefox.eth
+
+
@@ -211,78 +248,32 @@ exports[`CellSelectWithMenu should render with default settings correctly 1`] =
numberOfLines={1}
style={
{
- "color": "#141618",
+ "color": "#6a737d",
"fontFamily": "EuclidCircularB-Regular",
- "fontSize": 16,
+ "fontSize": 14,
"fontWeight": "400",
"letterSpacing": 0,
- "lineHeight": 24,
+ "lineHeight": 22,
}
}
- testID="cellbase-avatar-title"
>
- Orangefox.eth
+ 0x2990079bcdEe240329a520d2444386FC119da21a
-
-
- 0x2990079bcdEe240329a520d2444386FC119da21a
-
-
-
-
+ width={10}
+ />
+
diff --git a/app/component-library/components-temp/KeyValueRow/KeyValueLabel/KeyValueLabel.styles.tsx b/app/component-library/components-temp/KeyValueRow/KeyValueLabel/KeyValueLabel.styles.tsx
deleted file mode 100644
index ae5c4860b71..00000000000
--- a/app/component-library/components-temp/KeyValueRow/KeyValueLabel/KeyValueLabel.styles.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-const styleSheet = () =>
- StyleSheet.create({
- labelContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- });
-
-export default styleSheet;
diff --git a/app/component-library/components-temp/KeyValueRow/KeyValueLabel/KeyValueLabel.tsx b/app/component-library/components-temp/KeyValueRow/KeyValueLabel/KeyValueLabel.tsx
deleted file mode 100644
index aefa71eecbc..00000000000
--- a/app/component-library/components-temp/KeyValueRow/KeyValueLabel/KeyValueLabel.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import ButtonIcon from '../../../../component-library/components/Buttons/ButtonIcon';
-import Label from '../../../../component-library/components/Form/Label';
-import {
- IconColor,
- IconName,
-} from '../../../../component-library/components/Icons/Icon';
-import {
- TextVariant,
- TextColor,
-} from '../../../../component-library/components/Texts/Text';
-import { useStyles } from '../../../../component-library/hooks';
-import useTooltipModal from '../../../../components/hooks/useTooltipModal';
-import React from 'react';
-import { View } from 'react-native';
-import { KeyValueRowLabelProps, TooltipSizes } from '../KeyValueRow.types';
-import styleSheet from './KeyValueLabel.styles';
-
-/**
- * A label and tooltip component.
- *
- * @param {Object} props - Component props.
- * @param {TextVariant} [props.variant] - Optional text variant. Defaults to TextVariant.BodyMDMedium.
- * @param {TextVariant} [props.color] - Optional text color. Defaults to TextColor.Default.
- * @param {TextVariant} [props.tooltip] - Optional tooltip to render to the right of the label text.
- *
- * @returns {JSX.Element} The rendered KeyValueRowLabel component.
- */
-const KeyValueRowLabel = ({
- label,
- variant = TextVariant.BodyMDMedium,
- color = TextColor.Default,
- tooltip,
-}: KeyValueRowLabelProps) => {
- const { styles } = useStyles(styleSheet, {});
-
- const { openTooltipModal } = useTooltipModal();
-
- const hasTooltip = tooltip?.title && tooltip?.text;
-
- const onNavigateToTooltipModal = () => {
- if (!hasTooltip) return;
- openTooltipModal(tooltip.title, tooltip.text);
- };
-
- return (
-
-
- {hasTooltip && (
-
- )}
-
- );
-};
-
-export default KeyValueRowLabel;
diff --git a/app/component-library/components-temp/KeyValueRow/KeyValueRoot/KeyValueRoot.styles.tsx b/app/component-library/components-temp/KeyValueRow/KeyValueRoot/KeyValueRoot.styles.tsx
deleted file mode 100644
index a3de4af351f..00000000000
--- a/app/component-library/components-temp/KeyValueRow/KeyValueRoot/KeyValueRoot.styles.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-const styleSheet = () =>
- StyleSheet.create({
- rootContainer: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- overflow: 'hidden',
- },
- });
-
-export default styleSheet;
diff --git a/app/component-library/components-temp/KeyValueRow/KeyValueRoot/KeyValueRoot.tsx b/app/component-library/components-temp/KeyValueRow/KeyValueRoot/KeyValueRoot.tsx
deleted file mode 100644
index e308488648a..00000000000
--- a/app/component-library/components-temp/KeyValueRow/KeyValueRoot/KeyValueRoot.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import { useStyles } from '../../../hooks';
-import React from 'react';
-import { View } from 'react-native';
-import { KeyValueRowRootProps } from '../KeyValueRow.types';
-import styleSheet from './KeyValueRoot.styles';
-
-/**
- * The main container for the KeyValueRow component.
- * When creating custom KeyValueRow components, this must be the outermost component wrapping the two components.
- *
- * e.g.
- * ```
- *
- *
- *
- *
- * ```
- *
- * @component
- * @param {Object} props - Component props.
- * @param {Array} props.children - The two children.
- * @param {ViewProps} [props.style] - Optional styling
- *
- * @returns {JSX.Element} The rendered Root component.
- */
-const KeyValueRowRoot = ({
- children,
- style: customStyles,
-}: KeyValueRowRootProps) => {
- const { styles: defaultStyles } = useStyles(styleSheet, {});
-
- const styles = [defaultStyles.rootContainer, customStyles];
-
- return {children};
-};
-
-export default KeyValueRowRoot;
diff --git a/app/component-library/components-temp/KeyValueRow/KeyValueRow.stories.tsx b/app/component-library/components-temp/KeyValueRow/KeyValueRow.stories.tsx
deleted file mode 100644
index e5a440b3276..00000000000
--- a/app/component-library/components-temp/KeyValueRow/KeyValueRow.stories.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import React from 'react';
-import { withNavigation } from '../../../../storybook/decorators';
-import { View, StyleSheet } from 'react-native';
-import KeyValueRowComponent, {
- KeyValueRowFieldIconSides,
- TooltipSizes,
-} from './index';
-import Text, { TextColor, TextVariant } from '../../components/Texts/Text';
-import Title from '../../../components/Base/Title';
-import { IconColor, IconName, IconSize } from '../../components/Icons/Icon';
-
-const KeyValueRowMeta = {
- title: 'Components Temp / KeyValueRow',
- component: KeyValueRowComponent,
- decorators: [withNavigation],
-};
-
-export default KeyValueRowMeta;
-
-const styles = StyleSheet.create({
- container: {
- padding: 16,
- },
- listItem: {
- marginVertical: 16,
- gap: 16,
- },
-});
-
-export const KeyValueRow = {
- render: () => (
-
- KeyValueRow Component
-
- Prebuilt component displayed below but KeyValueRow stubs are available
- to create new KeyValueRow variants.
-
-
-
-
-
-
-
-
- ),
-};
diff --git a/app/component-library/components-temp/KeyValueRow/KeyValueRow.styles.ts b/app/component-library/components-temp/KeyValueRow/KeyValueRow.styles.ts
deleted file mode 100644
index 33999c44819..00000000000
--- a/app/component-library/components-temp/KeyValueRow/KeyValueRow.styles.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-const styleSheet = () =>
- StyleSheet.create({
- flexRow: {
- flexDirection: 'row',
- alignItems: 'center',
- gap: 8,
- },
- });
-
-export default styleSheet;
diff --git a/app/component-library/components-temp/KeyValueRow/KeyValueRow.test.tsx b/app/component-library/components-temp/KeyValueRow/KeyValueRow.test.tsx
deleted file mode 100644
index a9b00ece7e8..00000000000
--- a/app/component-library/components-temp/KeyValueRow/KeyValueRow.test.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-import React from 'react';
-import { render } from '@testing-library/react-native';
-import KeyValueRow from './KeyValueRow';
-import { IconName } from '../../components/Icons/Icon';
-
-jest.mock('@react-navigation/native', () => {
- const actualNav = jest.requireActual('@react-navigation/native');
- return {
- ...actualNav,
- useNavigation: () => ({
- navigate: jest.fn(),
- }),
- };
-});
-
-describe('KeyValueRow', () => {
- describe('Prebuilt Component', () => {
- describe('KeyValueRow', () => {
- it('should render when there is only text', () => {
- const { toJSON } = render(
- ,
- );
-
- expect(toJSON()).toMatchSnapshot();
- });
-
- it('should render text with tooltips', () => {
- const { toJSON } = render(
- ,
- );
-
- expect(toJSON()).toMatchSnapshot();
- });
-
- it('should render text with icons', () => {
- const { toJSON } = render(
- ,
- );
-
- expect(toJSON()).toMatchSnapshot();
- });
-
- it('should render text with icons and tooltips', () => {
- const { toJSON } = render(
- ,
- );
-
- expect(toJSON()).toMatchSnapshot();
- });
- });
- });
-});
diff --git a/app/component-library/components-temp/KeyValueRow/KeyValueRow.tsx b/app/component-library/components-temp/KeyValueRow/KeyValueRow.tsx
deleted file mode 100644
index 18c6300b1cf..00000000000
--- a/app/component-library/components-temp/KeyValueRow/KeyValueRow.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import { useStyles } from '../../hooks';
-import React from 'react';
-import stylesheet from './KeyValueRow.styles';
-import {
- KeyValueRowProps,
- KeyValueRowFieldIconSides,
- KeyValueRowSectionAlignments,
-} from './KeyValueRow.types';
-import Icon from '../../components/Icons/Icon';
-import { View } from 'react-native';
-import { areKeyValueRowPropsEqual } from './KeyValueRow.utils';
-import KeyValueSection from './KeyValueSection/KeyValueSection';
-import KeyValueRowLabel from './KeyValueLabel/KeyValueLabel';
-import KeyValueRowRoot from './KeyValueRoot/KeyValueRoot';
-
-/**
- * Prebuilt convenience component to format and render a key/value KeyValueRowLabel pair.
- * The KeyValueRowLabel component has props to display a tooltip and icon.
- *
- * Examples are in the Storybook: [StorybookLink](./KeyValueRow.stories.tsx)
- *
- * @param {Object} props - Component props
- * @param {KeyValueRowField} props.field - Represents the left side of the key value row pair
- * @param {KeyValueRowField} props.value - Represents the right side of the key value row pair
- * @param {ViewProps} [props.style] - Optional styling
- *
- * @returns {JSX.Element} The rendered KeyValueRow component.
- */
-const KeyValueRow = React.memo(({ field, value, style }: KeyValueRowProps) => {
- const { styles } = useStyles(stylesheet, {});
-
- // Field (left side)
- const fieldIcon = field?.icon;
- const shouldShowFieldIcon = fieldIcon?.name;
-
- // Value (right side)
- const valueIcon = value?.icon;
- const shouldShowValueIcon = valueIcon?.name;
-
- return (
-
-
-
- {shouldShowFieldIcon &&
- (fieldIcon.side === KeyValueRowFieldIconSides.LEFT ||
- fieldIcon.side === KeyValueRowFieldIconSides.BOTH ||
- !fieldIcon?.side) && }
-
- {shouldShowFieldIcon &&
- (fieldIcon?.side === KeyValueRowFieldIconSides.RIGHT ||
- fieldIcon?.side === KeyValueRowFieldIconSides.BOTH) && (
-
- )}
-
-
-
-
- {shouldShowValueIcon &&
- (valueIcon?.side === KeyValueRowFieldIconSides.LEFT ||
- valueIcon?.side === KeyValueRowFieldIconSides.BOTH ||
- !valueIcon?.side) && }
-
- {shouldShowValueIcon &&
- (valueIcon?.side === KeyValueRowFieldIconSides.RIGHT ||
- valueIcon?.side === KeyValueRowFieldIconSides.BOTH) && (
-
- )}
-
-
-
- );
-}, areKeyValueRowPropsEqual);
-
-/**
- * Exported sub-components to provide a base for new KeyValueRow variants.
- */
-export const KeyValueRowStubs = {
- Root: KeyValueRowRoot,
- Section: KeyValueSection,
- Label: KeyValueRowLabel,
-};
-
-export default KeyValueRow;
diff --git a/app/component-library/components-temp/KeyValueRow/KeyValueRow.types.ts b/app/component-library/components-temp/KeyValueRow/KeyValueRow.types.ts
deleted file mode 100644
index 0e9c864d309..00000000000
--- a/app/component-library/components-temp/KeyValueRow/KeyValueRow.types.ts
+++ /dev/null
@@ -1,157 +0,0 @@
-import {
- IconProps,
- IconSize,
-} from '../../../component-library/components/Icons/Icon';
-import { ButtonIconSizes } from '../../components/Buttons/ButtonIcon';
-import { ReactNode } from 'react';
-import { TextProps } from '../../components/Texts/Text/Text.types';
-import { ViewProps } from 'react-native';
-
-/**
- * The optional tooltip tha can be displayed within a KeyValueRowField or KeyValueRowLabel.
- *
- * @see KeyValueRowField
- * @see KeyValueRowLabel
- */
-interface KeyValueRowTooltip {
- /**
- * The title displayed at the top of the tooltip.
- */
- title: string;
- /**
- * The text displayed within the tooltip body.
- */
- text: string;
- /**
- * Optional size of the tooltip icon.
- * @default TooltipSizes.Md
- */
- size?: ButtonIconSizes;
-}
-
-/**
- * Used to position icon in KeyValueRowField
- *
- * @see KeyValueRowField
- */
-export enum KeyValueRowFieldIconSides {
- LEFT = 'LEFT',
- RIGHT = 'RIGHT',
- BOTH = 'BOTH',
-}
-
-/**
- * Represents a field displayed within KeyValueRowProps.
- *
- * @see KeyValueRowProps
- */
-interface KeyValueRowField {
- /**
- * The text to display.
- */
- text: string;
- /**
- * Optional text variant.
- * @default TextVariant.BodyMDMedium
- */
- variant?: TextProps['variant'];
- /**
- * Optional text color.
- * @default TextColor.Default
- */
- color?: TextProps['color'];
- /**
- * Optional icon to display. If undefined, no icon is displayed.
- */
- icon?: IconProps & { side?: KeyValueRowFieldIconSides };
- /**
- * Optional tooltip to display. If undefined, no tooltip is displayed.
- */
- tooltip?: KeyValueRowTooltip;
-}
-
-export const IconSizes = IconSize;
-
-export const TooltipSizes = ButtonIconSizes;
-
-/**
- * The KeyValueRowLabel prop interface.
- *
- * @see KeyValueRowLabel in ./KeyValueRow.tsx
- */
-export interface KeyValueRowLabelProps {
- /**
- * Text to display.
- */
- label: string;
- /**
- * Optional text variant.
- * @default TextVariant.BodyMDMedium
- */
- variant?: TextProps['variant'];
- /**
- * Optional text color.
- * @default TextColor.Default
- */
- color?: TextProps['color'];
- /**
- * Optional tooltip. If undefined, the tooltip won't be displayed.
- */
- tooltip?: KeyValueRowTooltip;
-}
-
-/**
- * Represents the main container for the KeyValueRow component.
- */
-export interface KeyValueRowRootProps {
- /**
- * Must have exactly two children. Adding more will lead to an undesired outcome.
- */
- children: [ReactNode, ReactNode];
- /**
- * Optional styles. Useful for controlling padding and margins.
- */
- style?: ViewProps['style'];
-}
-
-/**
- * Represents the valid KeyValueSection alignments.
- */
-export enum KeyValueRowSectionAlignments {
- LEFT = 'flex-start',
- RIGHT = 'flex-end',
-}
-
-/**
- * The KeyValueSection component props.
- */
-export interface KeyValueSectionProps {
- /**
- * Child components.
- */
- children: ReactNode;
- /**
- * Optional content alignment.
- * @default KeyValueRowSectionAlignments.RIGHT
- */
- align?: KeyValueRowSectionAlignments;
-}
-
-/**
- * The KeyValueRow component props.
- */
-export interface KeyValueRowProps {
- /**
- * The "key" portion of the KeyValueRow (left side).
- * Using the variable name field because key is reserved.
- */
- field: KeyValueRowField;
- /**
- * The "value" portion of the KeyValueRow (right side).
- */
- value: KeyValueRowField;
- /**
- * Optional styles. E.g. specifying padding or margins.
- */
- style?: ViewProps['style'];
-}
diff --git a/app/component-library/components-temp/KeyValueRow/KeyValueRow.utils.ts b/app/component-library/components-temp/KeyValueRow/KeyValueRow.utils.ts
deleted file mode 100644
index bf4c38843e4..00000000000
--- a/app/component-library/components-temp/KeyValueRow/KeyValueRow.utils.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { KeyValueRowProps } from './KeyValueRow.types';
-
-export const areKeyValueRowPropsEqual = (
- prevProps: KeyValueRowProps,
- newProps: KeyValueRowProps,
-) =>
- JSON.stringify(prevProps.field) === JSON.stringify(newProps.field) &&
- JSON.stringify(prevProps.value) === JSON.stringify(newProps.value);
diff --git a/app/component-library/components-temp/KeyValueRow/KeyValueSection/KeyValueSection.styles.tsx b/app/component-library/components-temp/KeyValueRow/KeyValueSection/KeyValueSection.styles.tsx
deleted file mode 100644
index f8d8adf679d..00000000000
--- a/app/component-library/components-temp/KeyValueRow/KeyValueSection/KeyValueSection.styles.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-const styleSheet = () =>
- StyleSheet.create({
- keyValueSectionContainer: {
- flex: 1,
- },
- });
-
-export default styleSheet;
diff --git a/app/component-library/components-temp/KeyValueRow/KeyValueSection/KeyValueSection.tsx b/app/component-library/components-temp/KeyValueRow/KeyValueSection/KeyValueSection.tsx
deleted file mode 100644
index 7c01d2b438a..00000000000
--- a/app/component-library/components-temp/KeyValueRow/KeyValueSection/KeyValueSection.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react';
-import { useStyles } from '../../../hooks';
-import { View } from 'react-native';
-import {
- KeyValueRowSectionAlignments,
- KeyValueSectionProps,
-} from '../KeyValueRow.types';
-import stylesSheet from './KeyValueSection.styles';
-
-/**
- * A container representing either the left or right side of the KeyValueRow.
- * For desired results, use only two components within the .
- *
- * @component
- * @param {Object} props - Component props.
- * @param {ReactNode} props.children - The child components.
- * @param {KeyValueRowSectionAlignments} [props.align] - The alignment of the KeyValueSection. Defaults to KeyValueRowSectionAlignments.RIGHT
- *
- * @returns {JSX.Element} The rendered KeyValueSection component.
- */
-const KeyValueSection = ({
- children,
- align = KeyValueRowSectionAlignments.LEFT,
-}: KeyValueSectionProps) => {
- const { styles } = useStyles(stylesSheet, {});
-
- return (
-
- {children}
-
- );
-};
-
-export default KeyValueSection;
diff --git a/app/component-library/components-temp/KeyValueRow/__snapshots__/KeyValueRow.test.tsx.snap b/app/component-library/components-temp/KeyValueRow/__snapshots__/KeyValueRow.test.tsx.snap
deleted file mode 100644
index c83d3a4fd5f..00000000000
--- a/app/component-library/components-temp/KeyValueRow/__snapshots__/KeyValueRow.test.tsx.snap
+++ /dev/null
@@ -1,609 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`KeyValueRow Prebuilt Component KeyValueRow should render text with icons 1`] = `
-
-
-
-
-
-
- Key Text
-
-
-
-
-
-
-
-
-
- Value Text
-
-
-
-
-
-`;
-
-exports[`KeyValueRow Prebuilt Component KeyValueRow should render text with icons and tooltips 1`] = `
-
-
-
-
-
-
- Key Text
-
-
-
-
-
-
-
-
-
-
-
-
- Value Text
-
-
-
-
-
-
-
-
-`;
-
-exports[`KeyValueRow Prebuilt Component KeyValueRow should render text with tooltips 1`] = `
-
-
-
-
-
- Key Text
-
-
-
-
-
-
-
-
-
-
-
- Value Text
-
-
-
-
-
-
-
-
-`;
-
-exports[`KeyValueRow Prebuilt Component KeyValueRow should render when there is only text 1`] = `
-
-
-
-
-
- Sample Key Text
-
-
-
-
-
-
-
-
- Sample Value Text
-
-
-
-
-
-`;
diff --git a/app/component-library/components-temp/KeyValueRow/index.tsx b/app/component-library/components-temp/KeyValueRow/index.tsx
deleted file mode 100644
index ee4a0c18825..00000000000
--- a/app/component-library/components-temp/KeyValueRow/index.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-export { KeyValueRowStubs, default } from './KeyValueRow';
-export {
- KeyValueRowSectionAlignments,
- KeyValueRowFieldIconSides,
- TooltipSizes,
- IconSizes,
-} from './KeyValueRow.types';
-export type * from './KeyValueRow.types';
diff --git a/app/component-library/components-temp/ListItemMultiSelectButton/__snapshots__/ListItemMultiSelectButton.test.tsx.snap b/app/component-library/components-temp/ListItemMultiSelectButton/__snapshots__/ListItemMultiSelectButton.test.tsx.snap
index 0baaf1dd60b..278ed889370 100644
--- a/app/component-library/components-temp/ListItemMultiSelectButton/__snapshots__/ListItemMultiSelectButton.test.tsx.snap
+++ b/app/component-library/components-temp/ListItemMultiSelectButton/__snapshots__/ListItemMultiSelectButton.test.tsx.snap
@@ -40,16 +40,7 @@ exports[`ListItemMultiSelectButton should render correctly with default props 1`
}
}
>
-
-
-
+
diff --git a/app/component-library/components/Cells/Cell/__snapshots__/Cell.test.tsx.snap b/app/component-library/components/Cells/Cell/__snapshots__/Cell.test.tsx.snap
index a3d36f307dd..310992b6f65 100644
--- a/app/component-library/components/Cells/Cell/__snapshots__/Cell.test.tsx.snap
+++ b/app/component-library/components/Cells/Cell/__snapshots__/Cell.test.tsx.snap
@@ -270,6 +270,7 @@ exports[`Cell should render CellMultiSelect given the type MultiSelect 1`] = `
disabled={false}
style={
{
+ "alignItems": "flex-start",
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
@@ -283,252 +284,275 @@ exports[`Cell should render CellMultiSelect given the type MultiSelect 1`] = `
accessible={true}
style={
{
+ "alignItems": "center",
+ "flexDirection": "row",
"padding": 0,
}
}
>
-
-
-
-
+
+
+
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ 0.33873792024529126,
+ 0.9408807689542256,
+ -0.9408807689542256,
+ 0.33873792024529126,
+ 50.8847515111068,
+ 5.120153954341607,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
-
+
+
-
+
- Orangefox.eth
-
-
+ 0x2990079bcdEe240329a520d2444386FC119da21a
+
+
- 0x2990079bcdEe240329a520d2444386FC119da21a
-
+ }
+ >
+ Updated 1 sec ago
+
+
- Updated 1 sec ago
+ Imported
-
-
- Imported
-
-
@@ -584,6 +578,7 @@ exports[`Cell should render CellSelect given the type Select 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -594,14 +589,15 @@ exports[`Cell should render CellSelect given the type Select 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ 0.33873792024529126,
+ 0.9408807689542256,
+ -0.9408807689542256,
+ 0.33873792024529126,
+ 50.8847515111068,
+ 5.120153954341607,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
-
+
+
-
+
- Orangefox.eth
-
-
+ 0x2990079bcdEe240329a520d2444386FC119da21a
+
+
- 0x2990079bcdEe240329a520d2444386FC119da21a
-
+ }
+ >
+ Updated 1 sec ago
+
+
- Updated 1 sec ago
+ Imported
-
-
- Imported
-
-
diff --git a/app/component-library/components/Cells/Cell/variants/CellMultiSelect/__snapshots__/CellMultiSelect.test.tsx.snap b/app/component-library/components/Cells/Cell/variants/CellMultiSelect/__snapshots__/CellMultiSelect.test.tsx.snap
index ab5aef4d21f..81a2ddfbd11 100644
--- a/app/component-library/components/Cells/Cell/variants/CellMultiSelect/__snapshots__/CellMultiSelect.test.tsx.snap
+++ b/app/component-library/components/Cells/Cell/variants/CellMultiSelect/__snapshots__/CellMultiSelect.test.tsx.snap
@@ -5,6 +5,7 @@ exports[`CellMultiSelect should render default settings correctly 1`] = `
disabled={false}
style={
{
+ "alignItems": "flex-start",
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
@@ -18,252 +19,275 @@ exports[`CellMultiSelect should render default settings correctly 1`] = `
accessible={true}
style={
{
+ "alignItems": "center",
+ "flexDirection": "row",
"padding": 0,
}
}
>
-
-
-
-
+
+
+
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ 0.33873792024529126,
+ 0.9408807689542256,
+ -0.9408807689542256,
+ 0.33873792024529126,
+ 50.8847515111068,
+ 5.120153954341607,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
-
+
+
-
+
- Orangefox.eth
-
-
+ 0x2990079bcdEe240329a520d2444386FC119da21a
+
+
- 0x2990079bcdEe240329a520d2444386FC119da21a
-
+ }
+ >
+ Updated 1 sec ago
+
+
- Updated 1 sec ago
+ Imported
-
-
- Imported
-
-
diff --git a/app/component-library/components/Cells/Cell/variants/CellSelect/__snapshots__/CellSelect.test.tsx.snap b/app/component-library/components/Cells/Cell/variants/CellSelect/__snapshots__/CellSelect.test.tsx.snap
index 615ce5c9784..abdabeda711 100644
--- a/app/component-library/components/Cells/Cell/variants/CellSelect/__snapshots__/CellSelect.test.tsx.snap
+++ b/app/component-library/components/Cells/Cell/variants/CellSelect/__snapshots__/CellSelect.test.tsx.snap
@@ -8,6 +8,7 @@ exports[`CellSelect should render default settings correctly 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -18,14 +19,15 @@ exports[`CellSelect should render default settings correctly 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ 0.33873792024529126,
+ 0.9408807689542256,
+ -0.9408807689542256,
+ 0.33873792024529126,
+ 50.8847515111068,
+ 5.120153954341607,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
-
+
+
-
+
- Orangefox.eth
-
-
+ 0x2990079bcdEe240329a520d2444386FC119da21a
+
+
- 0x2990079bcdEe240329a520d2444386FC119da21a
-
+ }
+ >
+ Updated 1 sec ago
+
+
- Updated 1 sec ago
+ Imported
-
-
- Imported
-
-
diff --git a/app/component-library/components/List/ListItem/ListItem.constants.ts b/app/component-library/components/List/ListItem/ListItem.constants.ts
index 8ee1fb524fd..3003517c37c 100644
--- a/app/component-library/components/List/ListItem/ListItem.constants.ts
+++ b/app/component-library/components/List/ListItem/ListItem.constants.ts
@@ -9,13 +9,9 @@ export const TESTID_LISTITEM_GAP = 'listitem-gap';
// Defaults
export const DEFAULT_LISTITEM_GAP = 16;
export const DEFAULT_LISTITEM_VERTICALALIGNMENT = VerticalAlignment.Center;
-export const DEFAULT_LISTITEM_TOPACCESSORYGAP = 0;
-export const DEFAULT_LISTITEM_BOTTOMACCESSORYGAP = 0;
// Sample consts
export const SAMPLE_LISTITEM_PROPS: ListItemProps = {
gap: DEFAULT_LISTITEM_GAP,
verticalAlignment: DEFAULT_LISTITEM_VERTICALALIGNMENT,
- topAccessoryGap: DEFAULT_LISTITEM_TOPACCESSORYGAP,
- bottomAccessoryGap: DEFAULT_LISTITEM_BOTTOMACCESSORYGAP,
};
diff --git a/app/component-library/components/List/ListItem/ListItem.stories.tsx b/app/component-library/components/List/ListItem/ListItem.stories.tsx
index 083abaee263..d6e35986846 100644
--- a/app/component-library/components/List/ListItem/ListItem.stories.tsx
+++ b/app/component-library/components/List/ListItem/ListItem.stories.tsx
@@ -26,36 +26,6 @@ const ListItemMeta = {
},
defaultValue: SAMPLE_LISTITEM_PROPS.verticalAlignment,
},
- topAccessory: {
- control: { type: 'boolean' },
- defaultValue: false,
- mapping: {
- false: null,
- true: (
- Sample Top Accessory
- ),
- },
- },
- bottomAccessory: {
- control: { type: 'boolean' },
- defaultValue: false,
- mapping: {
- false: null,
- true: (
-
- Sample Bottom Accessory
-
- ),
- },
- },
- topAccessoryGap: {
- control: { type: 'number' },
- defaultValue: SAMPLE_LISTITEM_PROPS.topAccessoryGap,
- },
- bottomAccessoryGap: {
- control: { type: 'number' },
- defaultValue: SAMPLE_LISTITEM_PROPS.bottomAccessoryGap,
- },
},
};
export default ListItemMeta;
diff --git a/app/component-library/components/List/ListItem/ListItem.styles.ts b/app/component-library/components/List/ListItem/ListItem.styles.ts
index df6305cdc20..25d5fc68d40 100644
--- a/app/component-library/components/List/ListItem/ListItem.styles.ts
+++ b/app/component-library/components/List/ListItem/ListItem.styles.ts
@@ -17,8 +17,7 @@ import { VerticalAlignment, ListItemStyleSheetVars } from './ListItem.types';
*/
const styleSheet = (params: { theme: Theme; vars: ListItemStyleSheetVars }) => {
const { vars } = params;
- const { style, verticalAlignment, topAccessoryGap, bottomAccessoryGap } =
- vars;
+ const { style, verticalAlignment } = vars;
let alignItems;
switch (verticalAlignment) {
case VerticalAlignment.Center:
@@ -36,19 +35,11 @@ const styleSheet = (params: { theme: Theme; vars: ListItemStyleSheetVars }) => {
base: Object.assign(
{
padding: 16,
+ flexDirection: 'row',
+ alignItems,
} as ViewStyle,
style,
) as ViewStyle,
- item: {
- flexDirection: 'row',
- alignItems,
- } as ViewStyle,
- topAccessory: {
- marginBottom: topAccessoryGap ?? 0,
- },
- bottomAccessory: {
- marginTop: bottomAccessoryGap ?? 0,
- },
});
};
diff --git a/app/component-library/components/List/ListItem/ListItem.test.tsx b/app/component-library/components/List/ListItem/ListItem.test.tsx
index 34a40bacfd4..84211b78cd4 100644
--- a/app/component-library/components/List/ListItem/ListItem.test.tsx
+++ b/app/component-library/components/List/ListItem/ListItem.test.tsx
@@ -23,51 +23,6 @@ describe('ListItem', () => {
expect(wrapper).toMatchSnapshot();
});
- it('should render the top accessory', () => {
- const wrapper = render(
- }>
-
- ,
- );
- expect(wrapper).toMatchSnapshot();
- });
-
- it('should render the bottom accessory', () => {
- const wrapper = render(
- }>
-
- ,
- );
- expect(wrapper).toMatchSnapshot();
- });
-
- it('should render the correct topAccessoryGap', () => {
- const givenTopAccessoryGap = 20;
- const { getByRole } = render(
- } topAccessoryGap={givenTopAccessoryGap}>
-
- ,
- );
- expect(getByRole('none').props.children[0].props.style.marginBottom).toBe(
- givenTopAccessoryGap,
- );
- });
-
- it('should render the correct bottomAccessoryGap', () => {
- const givenBottomAccessoryGap = 20;
- const { getByRole } = render(
- }
- bottomAccessoryGap={givenBottomAccessoryGap}
- >
-
- ,
- );
- expect(getByRole('none').props.children[2].props.style.marginTop).toBe(
- givenBottomAccessoryGap,
- );
- });
-
it('should render the correct default gap', () => {
const { getByTestId } = render(
@@ -106,9 +61,7 @@ describe('ListItem', () => {
,
);
- expect(getByRole('none').props.children[1].props.style.alignItems).toBe(
- 'center',
- );
+ expect(getByRole('none').props.style.alignItems).toBe('center');
});
it('should render the given verticalAlignment', () => {
@@ -117,9 +70,6 @@ describe('ListItem', () => {
,
);
-
- expect(getByRole('none').props.children[1].props.style.alignItems).toBe(
- 'flex-start',
- );
+ expect(getByRole('none').props.style.alignItems).toBe('flex-start');
});
});
diff --git a/app/component-library/components/List/ListItem/ListItem.tsx b/app/component-library/components/List/ListItem/ListItem.tsx
index 7c86fbfbe53..5c96fec91d5 100644
--- a/app/component-library/components/List/ListItem/ListItem.tsx
+++ b/app/component-library/components/List/ListItem/ListItem.tsx
@@ -19,10 +19,6 @@ import {
const ListItem: React.FC = ({
style,
children,
- topAccessory,
- bottomAccessory,
- topAccessoryGap,
- bottomAccessoryGap,
gap = DEFAULT_LISTITEM_GAP,
verticalAlignment = DEFAULT_LISTITEM_VERTICALALIGNMENT,
...props
@@ -30,32 +26,24 @@ const ListItem: React.FC = ({
const { styles } = useStyles(styleSheet, {
style,
verticalAlignment,
- topAccessoryGap,
- bottomAccessoryGap,
});
return (
- {topAccessory && {topAccessory}}
-
- {React.Children.toArray(children)
- .filter((child) => !!child)
- .map((child, index) => (
-
- {index > 0 && (
-
- )}
- {child}
-
- ))}
-
- {bottomAccessory && (
- {bottomAccessory}
- )}
+ {React.Children.toArray(children)
+ .filter((child) => !!child)
+ .map((child, index) => (
+
+ {index > 0 && (
+
+ )}
+ {child}
+
+ ))}
);
};
diff --git a/app/component-library/components/List/ListItem/ListItem.types.ts b/app/component-library/components/List/ListItem/ListItem.types.ts
index 18a905c72e1..1ab097d5935 100644
--- a/app/component-library/components/List/ListItem/ListItem.types.ts
+++ b/app/component-library/components/List/ListItem/ListItem.types.ts
@@ -18,22 +18,6 @@ export interface ListItemProps extends ViewProps {
* Content to wrap to display.
*/
children?: React.ReactNode;
- /**
- * Optional prop to include content to be displayed above the ListItem.
- */
- topAccessory?: React.ReactNode;
- /**
- * Optional prop to include content to be displayed below the ListItem.
- */
- bottomAccessory?: React.ReactNode;
- /**
- * Optional prop to configure the gap between the topAccessory and the ListItem.
- */
- topAccessoryGap?: number;
- /**
- * Optional prop to configure the gap between the bottomAccessory and the ListItem.
- */
- bottomAccessoryGap?: number;
/**
* Optional prop to configure the gap between items inside the ListItem.
*/
@@ -53,5 +37,5 @@ export interface ListItemProps extends ViewProps {
*/
export type ListItemStyleSheetVars = Pick<
ListItemProps,
- 'style' | 'verticalAlignment' | 'topAccessoryGap' | 'bottomAccessoryGap'
+ 'style' | 'verticalAlignment'
>;
diff --git a/app/component-library/components/List/ListItem/__snapshots__/ListItem.test.tsx.snap b/app/component-library/components/List/ListItem/__snapshots__/ListItem.test.tsx.snap
index e23d2a00c93..a197bdd692f 100644
--- a/app/component-library/components/List/ListItem/__snapshots__/ListItem.test.tsx.snap
+++ b/app/component-library/components/List/ListItem/__snapshots__/ListItem.test.tsx.snap
@@ -6,83 +6,12 @@ exports[`ListItem should render snapshot correctly 1`] = `
accessible={true}
style={
{
+ "alignItems": "center",
+ "flexDirection": "row",
"padding": 16,
}
}
>
-
-
-
-
-`;
-
-exports[`ListItem should render the bottom accessory 1`] = `
-
-
-
-
-
-
-
-
-`;
-
-exports[`ListItem should render the top accessory 1`] = `
-
-
-
-
-
-
-
+
`;
diff --git a/app/component-library/components/List/ListItemMultiSelect/ListItemMultiSelect.styles.ts b/app/component-library/components/List/ListItemMultiSelect/ListItemMultiSelect.styles.ts
index 8bd81b3bb46..e317e812ab8 100644
--- a/app/component-library/components/List/ListItemMultiSelect/ListItemMultiSelect.styles.ts
+++ b/app/component-library/components/List/ListItemMultiSelect/ListItemMultiSelect.styles.ts
@@ -27,6 +27,7 @@ const styleSheet = (params: {
{
padding: 16,
borderRadius: 4,
+ alignItems: 'flex-start',
backgroundColor: colors.background.default,
opacity: isDisabled ? 0.5 : 1,
} as ViewStyle,
diff --git a/app/component-library/components/List/ListItemMultiSelect/__snapshots__/ListItemMultiSelect.test.tsx.snap b/app/component-library/components/List/ListItemMultiSelect/__snapshots__/ListItemMultiSelect.test.tsx.snap
index 853d6669561..8ad70722b3d 100644
--- a/app/component-library/components/List/ListItemMultiSelect/__snapshots__/ListItemMultiSelect.test.tsx.snap
+++ b/app/component-library/components/List/ListItemMultiSelect/__snapshots__/ListItemMultiSelect.test.tsx.snap
@@ -5,6 +5,7 @@ exports[`ListItemMultiSelect should render snapshot correctly 1`] = `
disabled={false}
style={
{
+ "alignItems": "flex-start",
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
@@ -17,57 +18,50 @@ exports[`ListItemMultiSelect should render snapshot correctly 1`] = `
accessible={true}
style={
{
+ "alignItems": "center",
+ "flexDirection": "row",
"padding": 0,
}
}
>
-
-
-
-
-
-
+
+
+
`;
diff --git a/app/component-library/components/List/ListItemSelect/ListItemSelect.styles.ts b/app/component-library/components/List/ListItemSelect/ListItemSelect.styles.ts
index 5e590a70708..f7ef433fe81 100644
--- a/app/component-library/components/List/ListItemSelect/ListItemSelect.styles.ts
+++ b/app/component-library/components/List/ListItemSelect/ListItemSelect.styles.ts
@@ -27,6 +27,7 @@ const styleSheet = (params: {
{
position: 'relative',
opacity: isDisabled ? 0.5 : 1,
+ padding: 16,
borderRadius: 4,
backgroundColor: colors.background.default,
} as ViewStyle,
@@ -38,7 +39,7 @@ const styleSheet = (params: {
backgroundColor: colors.primary.muted,
},
listItem: {
- padding: 16,
+ padding: 0,
},
underlayBar: {
marginVertical: 4,
diff --git a/app/component-library/components/List/ListItemSelect/__snapshots__/ListItemSelect.test.tsx.snap b/app/component-library/components/List/ListItemSelect/__snapshots__/ListItemSelect.test.tsx.snap
index 0fca6037af1..4199cc4eb07 100644
--- a/app/component-library/components/List/ListItemSelect/__snapshots__/ListItemSelect.test.tsx.snap
+++ b/app/component-library/components/List/ListItemSelect/__snapshots__/ListItemSelect.test.tsx.snap
@@ -8,6 +8,7 @@ exports[`ListItemSelect should render snapshot correctly 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -17,20 +18,13 @@ exports[`ListItemSelect should render snapshot correctly 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
-
-
+
`;
diff --git a/app/component-library/components/Pickers/PickerAccount/PickerAccount.tsx b/app/component-library/components/Pickers/PickerAccount/PickerAccount.tsx
index ce9a9ab9014..5a4c293777f 100644
--- a/app/component-library/components/Pickers/PickerAccount/PickerAccount.tsx
+++ b/app/component-library/components/Pickers/PickerAccount/PickerAccount.tsx
@@ -51,7 +51,7 @@ const PickerAccount: React.ForwardRefRenderFunction<
/>
{accountName}
diff --git a/app/component-library/components/Pickers/PickerAccount/__snapshots__/PickerAccount.test.tsx.snap b/app/component-library/components/Pickers/PickerAccount/__snapshots__/PickerAccount.test.tsx.snap
index 0afdb8affeb..f2c7d176b13 100644
--- a/app/component-library/components/Pickers/PickerAccount/__snapshots__/PickerAccount.test.tsx.snap
+++ b/app/component-library/components/Pickers/PickerAccount/__snapshots__/PickerAccount.test.tsx.snap
@@ -178,11 +178,11 @@ exports[`PickerAccount should render correctly 1`] = `
style={
{
"color": "#141618",
- "fontFamily": "EuclidCircularB-Medium",
- "fontSize": 14,
- "fontWeight": "500",
+ "fontFamily": "EuclidCircularB-Regular",
+ "fontSize": 16,
+ "fontWeight": "400",
"letterSpacing": 0,
- "lineHeight": 22,
+ "lineHeight": 24,
}
}
testID="account-label"
@@ -207,7 +207,7 @@ exports[`PickerAccount should render correctly 1`] = `
diff --git a/app/component-library/components/Pickers/PickerBase/__snapshots__/PickerBase.test.tsx.snap b/app/component-library/components/Pickers/PickerBase/__snapshots__/PickerBase.test.tsx.snap
index f93ccf2a438..6d3ac0d242b 100644
--- a/app/component-library/components/Pickers/PickerBase/__snapshots__/PickerBase.test.tsx.snap
+++ b/app/component-library/components/Pickers/PickerBase/__snapshots__/PickerBase.test.tsx.snap
@@ -17,7 +17,7 @@ exports[`PickerBase should render correctly 1`] = `
>
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ -0.9438009515832294,
+ 0.3305143927132231,
+ -0.3305143927132231,
+ -0.9438009515832294,
+ 58.82830600098727,
+ 40.20612936020905,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
-
+
+
+
-
+ Sample SelectButton label
+
+
-
- Sample SelectButton label
-
-
- Sample SelectButton description
-
-
+ Sample SelectButton description
+
diff --git a/app/component-library/components/Select/SelectButton/foundation/__snapshots__/SelectButtonBase.test.tsx.snap b/app/component-library/components/Select/SelectButton/foundation/__snapshots__/SelectButtonBase.test.tsx.snap
index b73b687f331..6506738d46b 100644
--- a/app/component-library/components/Select/SelectButton/foundation/__snapshots__/SelectButtonBase.test.tsx.snap
+++ b/app/component-library/components/Select/SelectButton/foundation/__snapshots__/SelectButtonBase.test.tsx.snap
@@ -16,7 +16,9 @@ exports[`SelectButtonBase should render snapshot correctly 1`] = `
accessible={true}
style={
{
+ "alignItems": "center",
"flex": 1,
+ "flexDirection": "row",
"marginRight": 8,
"padding": 0,
}
@@ -25,209 +27,200 @@ exports[`SelectButtonBase should render snapshot correctly 1`] = `
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ -0.9438009515832294,
+ 0.3305143927132231,
+ -0.3305143927132231,
+ -0.9438009515832294,
+ 58.82830600098727,
+ 40.20612936020905,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
-
+
+
+
-
+ Sample SelectValue label
+
+
-
- Sample SelectValue label
-
-
- Sample SelectValue description
-
-
+ Sample SelectValue description
+
diff --git a/app/component-library/components/Select/SelectOption/__snapshots__/SelectOption.test.tsx.snap b/app/component-library/components/Select/SelectOption/__snapshots__/SelectOption.test.tsx.snap
index 3e2315357d9..f4844f6ac4a 100644
--- a/app/component-library/components/Select/SelectOption/__snapshots__/SelectOption.test.tsx.snap
+++ b/app/component-library/components/Select/SelectOption/__snapshots__/SelectOption.test.tsx.snap
@@ -9,6 +9,7 @@ exports[`SelectOption should render snapshot correctly 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -18,235 +19,220 @@ exports[`SelectOption should render snapshot correctly 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
+
-
-
+
-
+
-
-
-
-
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ -0.9438009515832294,
+ 0.3305143927132231,
+ -0.3305143927132231,
+ -0.9438009515832294,
+ 58.82830600098727,
+ 40.20612936020905,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
-
+
+
+
+
-
+ Sample SelectOption label
+
+
-
- Sample SelectOption label
-
-
- Sample SelectOption description
-
-
-
+ }
+ >
+ Sample SelectOption description
+
diff --git a/app/component-library/components/Select/SelectValue/SelectValue.styles.ts b/app/component-library/components/Select/SelectValue/SelectValue.styles.ts
index d8eead46b1a..090e2954d54 100644
--- a/app/component-library/components/Select/SelectValue/SelectValue.styles.ts
+++ b/app/component-library/components/Select/SelectValue/SelectValue.styles.ts
@@ -26,7 +26,6 @@ const styleSheet = (params: {
base: Object.assign(
{
padding: 0,
- flex: 1,
} as ViewStyle,
style,
) as ViewStyle,
diff --git a/app/component-library/components/Select/SelectValue/__snapshots__/SelectValue.test.tsx.snap b/app/component-library/components/Select/SelectValue/__snapshots__/SelectValue.test.tsx.snap
index 9db4ae456c2..f70bceb8b37 100644
--- a/app/component-library/components/Select/SelectValue/__snapshots__/SelectValue.test.tsx.snap
+++ b/app/component-library/components/Select/SelectValue/__snapshots__/SelectValue.test.tsx.snap
@@ -6,7 +6,8 @@ exports[`SelectValue should render snapshot correctly 1`] = `
accessible={true}
style={
{
- "flex": 1,
+ "alignItems": "center",
+ "flexDirection": "row",
"padding": 0,
}
}
@@ -14,209 +15,200 @@ exports[`SelectValue should render snapshot correctly 1`] = `
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ -0.9438009515832294,
+ 0.3305143927132231,
+ -0.3305143927132231,
+ -0.9438009515832294,
+ 58.82830600098727,
+ 40.20612936020905,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
-
+
+
+
-
+ Sample SelectValue label
+
+
-
- Sample SelectValue label
-
-
- Sample SelectValue description
-
-
+ Sample SelectValue description
+
`;
diff --git a/app/components/Base/ModalDragger.tsx b/app/components/Base/ModalDragger.js
similarity index 80%
rename from app/components/Base/ModalDragger.tsx
rename to app/components/Base/ModalDragger.js
index cb0ea3f1fd1..3e1c58daf26 100644
--- a/app/components/Base/ModalDragger.tsx
+++ b/app/components/Base/ModalDragger.js
@@ -1,14 +1,10 @@
import React from 'react';
+import PropTypes from 'prop-types';
import { StyleSheet, View } from 'react-native';
import { useTheme } from '../../util/theme';
-import { Theme } from '@metamask/design-tokens';
import { colors as importedColors } from '../../styles/common';
-interface ModalDraggerProps {
- borderless?: boolean;
-}
-
-const createStyles = (colors: Theme['colors']) =>
+const createStyles = (colors) =>
StyleSheet.create({
draggerWrapper: {
width: '100%',
@@ -29,7 +25,7 @@ const createStyles = (colors: Theme['colors']) =>
},
});
-function ModalDragger({ borderless }: ModalDraggerProps) {
+function ModalDragger({ borderless }) {
const { colors } = useTheme();
const styles = createStyles(colors);
@@ -40,4 +36,8 @@ function ModalDragger({ borderless }: ModalDraggerProps) {
);
}
+ModalDragger.propTypes = {
+ borderless: PropTypes.bool,
+};
+
export default ModalDragger;
diff --git a/app/components/Base/SelectorButton.tsx b/app/components/Base/SelectorButton.js
similarity index 65%
rename from app/components/Base/SelectorButton.tsx
rename to app/components/Base/SelectorButton.js
index b886dd85ce9..1c3d2d79444 100644
--- a/app/components/Base/SelectorButton.tsx
+++ b/app/components/Base/SelectorButton.js
@@ -1,16 +1,10 @@
import React from 'react';
-import { View, StyleSheet, TouchableOpacity, TouchableOpacityProps, GestureResponderEvent } from 'react-native';
+import PropTypes from 'prop-types';
+import { View, StyleSheet, TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import { useTheme } from '../../util/theme';
-import { Theme } from '@metamask/design-tokens';
-interface SelectorButtonProps {
- onPress: (event: GestureResponderEvent) => void;
- disabled?: boolean;
- children: React.ReactNode;
-}
-
-const createStyles = (colors: Theme['colors']) =>
+const createStyles = (colors) =>
StyleSheet.create({
container: {
backgroundColor: colors.background.alternative,
@@ -29,7 +23,7 @@ const createStyles = (colors: Theme['colors']) =>
},
});
-const SelectorButton: React.FC = ({ onPress, disabled, children, ...props }) => {
+function SelectorButton({ onPress, disabled, children, ...props }) {
const { colors } = useTheme();
const styles = createStyles(colors);
@@ -41,6 +35,12 @@ const SelectorButton: React.FC = ({
);
+}
+
+SelectorButton.propTypes = {
+ children: PropTypes.node,
+ onPress: PropTypes.func,
+ disabled: PropTypes.bool,
};
export default SelectorButton;
diff --git a/app/components/Base/Summary.tsx b/app/components/Base/Summary.js
similarity index 58%
rename from app/components/Base/Summary.tsx
rename to app/components/Base/Summary.js
index 6a953cf82bd..2b89be27a0c 100644
--- a/app/components/Base/Summary.tsx
+++ b/app/components/Base/Summary.js
@@ -1,9 +1,9 @@
import React from 'react';
-import { View, StyleSheet, ViewStyle, StyleProp } from 'react-native';
+import PropTypes from 'prop-types';
+import { View, StyleSheet } from 'react-native';
import { useTheme } from '../../util/theme';
-import type { Theme as DesignTokenTheme } from '@metamask/design-tokens';
-const createStyles = (colors: DesignTokenTheme['colors']) =>
+const createStyles = (colors) =>
StyleSheet.create({
wrapper: {
borderWidth: 1,
@@ -40,33 +40,11 @@ const useGetStyles = () => {
return createStyles(colors);
};
-interface SummaryProps {
- style?: StyleProp;
-}
-
-interface SummaryRowProps extends SummaryProps {
- end?: boolean;
- last?: boolean;
-}
-
-interface SummaryColProps extends SummaryProps {
- end?: boolean;
-}
-
-interface SummarySeparatorProps extends SummaryProps {}
-
-interface SummaryComponent extends React.FC {
- Row: React.FC;
- Col: React.FC;
- Separator: React.FC;
-}
-
-const Summary: SummaryComponent = ({ style, ...props }) => {
+const Summary = ({ style, ...props }) => {
const styles = useGetStyles();
return ;
};
-
-const SummaryRow: React.FC = ({ style, end, last, ...props }) => {
+const SummaryRow = ({ style, end, last, ...props }) => {
const styles = useGetStyles();
return (
= ({ style, end, last, ...props }) =
/>
);
};
-
-const SummaryCol: React.FC = ({ style, end, ...props }) => {
+const SummaryCol = ({ style, end, ...props }) => {
const styles = useGetStyles();
return ;
};
-
-const SummarySeparator: React.FC = ({ style, ...props }) => {
+const SummarySeparator = ({ style, ...props }) => {
const styles = useGetStyles();
return ;
};
@@ -89,5 +65,34 @@ const SummarySeparator: React.FC = ({ style, ...props })
Summary.Row = SummaryRow;
Summary.Col = SummaryCol;
Summary.Separator = SummarySeparator;
-
export default Summary;
+
+/**
+ * Any other external style defined in props will be applied
+ */
+const stylePropType = PropTypes.oneOfType([PropTypes.object, PropTypes.array]);
+
+Summary.propTypes = {
+ style: stylePropType,
+};
+SummaryRow.propTypes = {
+ style: stylePropType,
+ /**
+ * Aligns content to the end of the row
+ */
+ end: PropTypes.bool,
+ /**
+ * Add style to the last row of the summary
+ */
+ last: PropTypes.bool,
+};
+SummaryCol.propTypes = {
+ style: stylePropType,
+ /**
+ * Aligns content to the end of the row
+ */
+ end: PropTypes.bool,
+};
+SummarySeparator.propTypes = {
+ style: stylePropType,
+};
diff --git a/app/components/Nav/Main/MainNavigator.js b/app/components/Nav/Main/MainNavigator.js
index dd7342adc58..b73c245d6cf 100644
--- a/app/components/Nav/Main/MainNavigator.js
+++ b/app/components/Nav/Main/MainNavigator.js
@@ -89,7 +89,7 @@ import NftDetails from '../../Views/NftDetails';
import NftDetailsFullImage from '../../Views/NftDetails/NFtDetailsFullImage';
import AccountPermissions from '../../../components/Views/AccountPermissions';
import { AccountPermissionsScreens } from '../../../components/Views/AccountPermissions/AccountPermissions.types';
-import { StakeModalStack, StakeScreenStack } from '../../UI/Stake/routes';
+import StakeInputView from '../../UI/Stake/Views/InputView/StakeInputView';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
@@ -694,6 +694,16 @@ const Swaps = () => (
);
+const Stake = () => (
+
+
+
+);
+
const SetPasswordFlow = () => (
(
{() => }
-
-
+
{
return (
-
{
const [showDeprecatedAlert, setShowDeprecatedAlert] = useState(true);
const { colors } = useTheme();
const styles = createStyles(colors);
+
const backgroundMode = useRef(false);
const locale = useRef(I18n.locale);
const removeConnectionStatusListener = useRef();
const removeNotVisibleNotifications = props.removeNotVisibleNotifications;
- useNotificationHandler(props.navigation);
+
useEnableAutomaticSecurityChecks();
useMinimumVersions();
-
-
-
useEffect(() => {
if (DEPRECATED_NETWORKS.includes(props.chainId)) {
setShowDeprecatedAlert(true);
@@ -268,9 +267,24 @@ const Main = (props) => {
initForceReload();
return;
}
-
});
+ const bootstrapAndroidInitialNotification = useCallback(async () => {
+ if (Device.isAndroid()) {
+ const initialNotification = await notifee.getInitialNotification();
+
+ if (
+ initialNotification?.data?.action === 'tx' &&
+ initialNotification.data.id
+ ) {
+ NotificationManager.setTransactionToView(initialNotification.data.id);
+ props.navigation.navigate(Routes.TRANSACTIONS_VIEW);
+ }
+ }
+ }, [props.navigation]);
+
+ useNotificationHandler(bootstrapAndroidInitialNotification, props.navigation);
+
// Remove all notifications that aren't visible
useEffect(() => {
removeNotVisibleNotifications();
diff --git a/app/components/UI/AccountSelectorList/AccountSelector.test.tsx b/app/components/UI/AccountSelectorList/AccountSelector.test.tsx
index 306c88825c4..0cb8daa6c9b 100644
--- a/app/components/UI/AccountSelectorList/AccountSelector.test.tsx
+++ b/app/components/UI/AccountSelectorList/AccountSelector.test.tsx
@@ -8,12 +8,7 @@ import { View } from 'react-native';
import { ACCOUNT_BALANCE_BY_ADDRESS_TEST_ID } from '../../../../wdio/screen-objects/testIDs/Components/AccountListComponent.testIds';
import { backgroundState } from '../../../util/test/initial-root-state';
import { regex } from '../../../../app/util/regex';
-import {
- createMockAccountsControllerState,
- createMockAccountsControllerStateWithSnap,
- MOCK_ADDRESS_1,
- MOCK_ADDRESS_2,
-} from '../../../util/test/accountsControllerTestUtils';
+import { createMockAccountsControllerState } from '../../../util/test/accountsControllerTestUtils';
import { mockNetworkState } from '../../../util/test/network';
import { CHAIN_IDS } from '@metamask/transaction-controller';
@@ -205,28 +200,4 @@ describe('AccountSelectorList', () => {
expect(within(accountNameItems[1]).getByText('Account 2')).toBeDefined();
});
});
- it('renders "Snaps (beta)" tag for Snap accounts', async () => {
- const mockAccountsWithSnap = createMockAccountsControllerStateWithSnap([
- MOCK_ADDRESS_1,
- MOCK_ADDRESS_2,
- ]);
-
- const stateWithSnapAccount = {
- ...initialState,
- engine: {
- ...initialState.engine,
- backgroundState: {
- ...initialState.engine.backgroundState,
- AccountsController: mockAccountsWithSnap,
- },
- },
- };
-
- const { queryByText } = renderComponent(stateWithSnapAccount);
-
- await waitFor(async () => {
- const snapTag = await queryByText('Snaps (beta)');
- expect(snapTag).toBeDefined();
- });
- });
});
diff --git a/app/components/UI/AccountSelectorList/__snapshots__/AccountSelector.test.tsx.snap b/app/components/UI/AccountSelectorList/__snapshots__/AccountSelector.test.tsx.snap
index 49c9d6e96b1..da19df64fb1 100644
--- a/app/components/UI/AccountSelectorList/__snapshots__/AccountSelector.test.tsx.snap
+++ b/app/components/UI/AccountSelectorList/__snapshots__/AccountSelector.test.tsx.snap
@@ -65,6 +65,7 @@ exports[`AccountSelectorList renders all accounts with balances 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -75,243 +76,236 @@ exports[`AccountSelectorList renders all accounts with balances 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ 0.7046342099635947,
+ -0.7095707365365209,
+ 0.7095707365365209,
+ 0.7046342099635947,
+ -25.225718686778755,
+ -4.611026307883787,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
+
+
+
+ Account 1
+
+
+ 0xC495...D272
+
+
+
- Account 1
-
-
- 0xC495...D272
-
-
-
-
-
- $3200.00
+ $3200.00
1 ETH
-
-
+
@@ -358,6 +352,7 @@ exports[`AccountSelectorList renders all accounts with balances 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -368,243 +363,236 @@ exports[`AccountSelectorList renders all accounts with balances 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ 0.6704266189587993,
+ -0.7419758409756162,
+ 0.7419758409756162,
+ 0.6704266189587993,
+ 8.152366664413217,
+ -0.7899246956676649,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
+
+
+
+ Account 2
+
+
+ 0xd018...78E7
+
+
+
- Account 2
-
-
- 0xd018...78E7
-
-
-
-
-
- $6400.00
+ $6400.00
2 ETH
-
-
+
@@ -681,6 +669,7 @@ exports[`AccountSelectorList renders all accounts with right accessory 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -691,222 +680,215 @@ exports[`AccountSelectorList renders all accounts with right accessory 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ 0.7046342099635947,
+ -0.7095707365365209,
+ 0.7095707365365209,
+ 0.7046342099635947,
+ -25.225718686778755,
+ -4.611026307883787,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
-
+
+
-
- Account 1
-
-
- 0xC495...D272
-
-
-
+
-
- 0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272 - Account 1
-
+ 0xC495...D272
+
+
+
+
+ 0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272 - Account 1
@@ -927,6 +909,7 @@ exports[`AccountSelectorList renders all accounts with right accessory 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -937,222 +920,215 @@ exports[`AccountSelectorList renders all accounts with right accessory 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ 0.6704266189587993,
+ -0.7419758409756162,
+ 0.7419758409756162,
+ 0.6704266189587993,
+ 8.152366664413217,
+ -0.7899246956676649,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
-
+
+
-
- Account 2
-
-
- 0xd018...78E7
-
-
-
+
-
- 0xd018538C87232FF95acbCe4870629b75640a78E7 - Account 2
-
+ 0xd018...78E7
+
+
+
+
+ 0xd018538C87232FF95acbCe4870629b75640a78E7 - Account 2
@@ -1228,6 +1204,7 @@ exports[`AccountSelectorList renders correctly 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -1238,243 +1215,236 @@ exports[`AccountSelectorList renders correctly 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ 0.7046342099635947,
+ -0.7095707365365209,
+ 0.7095707365365209,
+ 0.7046342099635947,
+ -25.225718686778755,
+ -4.611026307883787,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
+
+
+
+ Account 1
+
+
+ 0xC495...D272
+
+
+
- Account 1
-
-
- 0xC495...D272
-
-
-
-
-
- $3200.00
+ $3200.00
1 ETH
-
-
+
@@ -1521,6 +1491,7 @@ exports[`AccountSelectorList renders correctly 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -1531,243 +1502,236 @@ exports[`AccountSelectorList renders correctly 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
-
-
-
-
-
+
+
-
-
-
+ }
+ height={32}
+ matrix={
+ [
+ 0.6704266189587993,
+ -0.7419758409756162,
+ 0.7419758409756162,
+ 0.6704266189587993,
+ 8.152366664413217,
+ -0.7899246956676649,
+ ]
+ }
+ propList={
+ [
+ "fill",
+ ]
+ }
+ width={32}
+ x={0}
+ y={0}
+ />
+
+
+
+
+
+ Account 2
+
+
+ 0xd018...78E7
+
+
+
- Account 2
-
-
- 0xd018...78E7
-
-
-
-
-
- $6400.00
+ $6400.00
2 ETH
-
-
+
@@ -1840,6 +1804,7 @@ exports[`AccountSelectorList should render all accounts but only the balance for
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -1850,127 +1815,120 @@ exports[`AccountSelectorList should render all accounts but only the balance for
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
-
+
+
+
+ Account 1
+
+
-
+ }
+ >
+ 0xC495...D272
+
+
+
- Account 1
-
-
- 0xC495...D272
-
-
-
-
-
- $3200.00
+ $3200.00
1 ETH
-
-
+
@@ -2017,6 +1975,7 @@ exports[`AccountSelectorList should render all accounts but only the balance for
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -2027,94 +1986,87 @@ exports[`AccountSelectorList should render all accounts but only the balance for
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
-
-
-
-
+
+
+
- Account 2
-
-
+ Account 2
+
+
- 0xd018...78E7
-
-
+ }
+ >
+ 0xd018...78E7
+
diff --git a/app/components/UI/AddCustomCollectible/index.tsx b/app/components/UI/AddCustomCollectible/index.tsx
index be364abd6c8..a0bf94fd374 100644
--- a/app/components/UI/AddCustomCollectible/index.tsx
+++ b/app/components/UI/AddCustomCollectible/index.tsx
@@ -6,6 +6,7 @@ import {
TextInput,
View,
StyleSheet,
+ Platform,
} from 'react-native';
import { fontStyles } from '../../../styles/common';
import Engine from '../../../core/Engine';
@@ -17,7 +18,14 @@ import Device from '../../../util/device';
import { MetaMetricsEvents } from '../../../core/Analytics';
import { useTheme } from '../../../util/theme';
-import { NFTImportScreenSelectorsIDs } from '../../../../e2e/selectors/wallet/ImportNFTView.selectors';
+import { CUSTOM_TOKEN_CONTAINER_ID } from '../../../../wdio/screen-objects/testIDs/Screens/AddCustomToken.testIds';
+import generateTestId from '../../../../wdio/utils/generateTestId';
+import {
+ NFT_ADDRESS_INPUT_BOX_ID,
+ NFT_IDENTIFIER_WARNING_MESSAGE_ID,
+ NFT_ADDRESS_WARNING_MESSAGE_ID,
+ NFT_IDENTIFIER_INPUT_BOX_ID,
+} from '../../../../wdio/screen-objects/testIDs/Screens/NFTImportScreen.testIds';
import { selectChainId } from '../../../selectors/networkController';
import { selectSelectedInternalAccountChecksummedAddress } from '../../../selectors/accountsController';
import { getDecimalChainId } from '../../../util/networks';
@@ -225,7 +233,7 @@ const AddCustomCollectible = ({
return (
{warningAddress}
@@ -274,7 +282,7 @@ const AddCustomCollectible = ({
keyboardType="numeric"
onChangeText={onTokenIdChange}
onBlur={validateCustomCollectibleTokenId}
- testID={NFTImportScreenSelectorsIDs.IDENTIFIER_INPUT_BOX}
+ {...generateTestId(Platform, NFT_IDENTIFIER_INPUT_BOX_ID)}
ref={assetTokenIdInput}
onSubmitEditing={addNft}
returnKeyType={'done'}
@@ -284,7 +292,7 @@ const AddCustomCollectible = ({
/>
{warningTokenId}
diff --git a/app/components/UI/AddCustomToken/__snapshots__/index.test.tsx.snap b/app/components/UI/AddCustomToken/__snapshots__/index.test.tsx.snap
index 4ad291a7d08..e0596fa1cff 100644
--- a/app/components/UI/AddCustomToken/__snapshots__/index.test.tsx.snap
+++ b/app/components/UI/AddCustomToken/__snapshots__/index.test.tsx.snap
@@ -8,6 +8,7 @@ exports[`AddCustomToken render matches previous snapshot 1`] = `
"flex": 1,
}
}
+ testID="add-custom-token-screen"
>
@@ -225,7 +226,7 @@ exports[`AddCustomToken render matches previous snapshot 1`] = `
"width": "90%",
}
}
- testID="token-import-next-button"
+ testID="next-button-custom-import"
>
+
{this.renderBanner()}
@@ -529,14 +540,14 @@ class AddCustomToken extends PureComponent {
onBlur={() => {
this.handleBlurAddress();
}}
- testID={ImportTokenViewSelectorsIDs.ADDRESS_INPUT}
+ {...generateTestId(Platform, TOKEN_ADDRESS_INPUT_BOX_ID)}
onSubmitEditing={this.jumpToAssetSymbol}
returnKeyType={'next'}
keyboardAppearance={themeAppearance}
/>
{this.state.warningAddress}
@@ -554,7 +565,7 @@ class AddCustomToken extends PureComponent {
value={this.state.symbol}
onChangeText={this.onSymbolChange}
onBlur={this.validateCustomTokenSymbol}
- testID={ImportTokenViewSelectorsIDs.SYMBOL_INPUT}
+ {...generateTestId(Platform, TOKEN_ADDRESS_SYMBOL_ID)}
ref={this.assetSymbolInput}
onSubmitEditing={this.jumpToAssetPrecision}
returnKeyType={'next'}
@@ -579,7 +590,7 @@ class AddCustomToken extends PureComponent {
placeholderTextColor={colors.text.muted}
onChangeText={this.onDecimalsChange}
onBlur={this.validateCustomTokenDecimals}
- testID={ImportTokenViewSelectorsIDs.DECIMAL_INPUT}
+ {...generateTestId(Platform, NFT_IDENTIFIER_INPUT_BOX_ID)}
ref={this.assetPrecisionInput}
onSubmitEditing={this.addToken}
returnKeyType={'done'}
@@ -590,7 +601,10 @@ class AddCustomToken extends PureComponent {
{this.state.warningDecimals ? (
{this.state.warningDecimals}{' '}
diff --git a/app/components/UI/AddressCopy/AddressCopy.styles.ts b/app/components/UI/AddressCopy/AddressCopy.styles.ts
index 089c48d5136..5a79e70438d 100644
--- a/app/components/UI/AddressCopy/AddressCopy.styles.ts
+++ b/app/components/UI/AddressCopy/AddressCopy.styles.ts
@@ -7,18 +7,14 @@ const styleSheet = (params: { theme: Theme }) => {
const { colors } = theme;
return StyleSheet.create({
- address: {
- flexDirection: 'row',
- alignItems: 'center',
- },
+ address: { flexDirection: 'row' },
copyButton: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.primary.muted,
borderRadius: 20,
- paddingHorizontal: 12,
- padding: 4,
- marginLeft: 12,
+ paddingHorizontal: 8,
+ marginLeft: 8,
},
icon: { marginLeft: 4 },
});
diff --git a/app/components/UI/AddressCopy/AddressCopy.tsx b/app/components/UI/AddressCopy/AddressCopy.tsx
index d295c5f75ad..17f8dbd1550 100644
--- a/app/components/UI/AddressCopy/AddressCopy.tsx
+++ b/app/components/UI/AddressCopy/AddressCopy.tsx
@@ -79,7 +79,7 @@ const AddressCopy = ({ formatAddressType = 'full' }: AddressCopyProps) => {
>
{selectedInternalAccount
diff --git a/app/components/UI/ApprovalTagUrl/ApprovalTagUrl.tsx b/app/components/UI/ApprovalTagUrl/ApprovalTagUrl.tsx
index e7980f2848d..c66a2390c3d 100644
--- a/app/components/UI/ApprovalTagUrl/ApprovalTagUrl.tsx
+++ b/app/components/UI/ApprovalTagUrl/ApprovalTagUrl.tsx
@@ -4,12 +4,12 @@ import { useSelector } from 'react-redux';
import { strings } from '../../../../locales/i18n';
import TagUrl from '../../../component-library/components/Tags/TagUrl';
-import { useStyles } from '../../../component-library/hooks';
import AppConstants from '../../../core/AppConstants';
-import { selectInternalAccounts } from '../../../selectors/accountsController';
import { selectAccountsByChainId } from '../../../selectors/accountTrackerController';
import { prefixUrlWithProtocol } from '../../../util/browser';
import useFavicon from '../../hooks/useFavicon/useFavicon';
+import { selectInternalAccounts } from '../../../selectors/accountsController';
+import { useStyles } from '../../../component-library/hooks';
import stylesheet from './ApprovalTagUrl.styles';
const { ORIGIN_DEEPLINK, ORIGIN_QR_CODE } = AppConstants.DEEPLINKS;
@@ -51,14 +51,12 @@ const ApprovalTagUrl = ({
const domainTitle = useMemo(() => {
let title = '';
- if (url || currentEnsName || origin) {
- title = prefixUrlWithProtocol(currentEnsName || origin || url);
- } else {
- title = '';
+ if (url || currentEnsName) {
+ title = prefixUrlWithProtocol(currentEnsName || url || '');
}
return title;
- }, [currentEnsName, origin, url]);
+ }, [currentEnsName, url]);
const faviconSource = useFavicon(origin as string) as
| { uri: string }
diff --git a/app/components/UI/AssetList/index.js b/app/components/UI/AssetList/index.js
index f5f736236de..c5c89f3709d 100644
--- a/app/components/UI/AssetList/index.js
+++ b/app/components/UI/AssetList/index.js
@@ -6,6 +6,7 @@ import StyledButton from '../StyledButton'; // eslint-disable-line import/no-un
import AssetIcon from '../AssetIcon';
import { fontStyles } from '../../../styles/common';
import Text from '../../Base/Text';
+import { ImportTokenViewSelectorsIDs } from '../../../../e2e/selectors/ImportTokenView.selectors';
const styles = StyleSheet.create({
rowWrapper: {
@@ -79,6 +80,7 @@ export default class AssetList extends PureComponent {
containerStyle={styles.item}
onPress={() => handleSelectAsset(searchResults[i])} // eslint-disable-line
key={i}
+ testID={ImportTokenViewSelectorsIDs.CONTAINER}
>
diff --git a/app/components/UI/AssetOverview/Balance/Balance.styles.tsx b/app/components/UI/AssetOverview/Balance/Balance.styles.tsx
index 335718d5c39..ce76a5f26ea 100644
--- a/app/components/UI/AssetOverview/Balance/Balance.styles.tsx
+++ b/app/components/UI/AssetOverview/Balance/Balance.styles.tsx
@@ -24,7 +24,9 @@ const styleSheet = (params: { theme: Theme }) => {
overflow: 'hidden',
},
title: {
- paddingVertical: 4,
+ ...typography.sHeadingSM,
+ marginVertical: 0,
+ paddingVertical: 8,
paddingHorizontal: 15,
} as TextStyle,
text: {
diff --git a/app/components/UI/AssetOverview/Balance/Balance.tsx b/app/components/UI/AssetOverview/Balance/Balance.tsx
index 02c89071051..75fe3d4efac 100644
--- a/app/components/UI/AssetOverview/Balance/Balance.tsx
+++ b/app/components/UI/AssetOverview/Balance/Balance.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import { View } from 'react-native';
import { strings } from '../../../../../locales/i18n';
+import Title from '../../../Base/Title';
import { useStyles } from '../../../../component-library/hooks';
import styleSheet from './Balance.styles';
import AssetElement from '../../AssetElement';
@@ -25,9 +26,6 @@ import Text, {
} from '../../../../component-library/components/Texts/Text';
import { TokenI } from '../../Tokens/types';
import { useNavigation } from '@react-navigation/native';
-import { isPooledStakingFeatureEnabled } from '../../Stake/constants';
-import StakingBalance from '../../Stake/components/StakingBalance/StakingBalance';
-
interface BalanceProps {
asset: TokenI;
mainBalance: string;
@@ -55,9 +53,9 @@ const Balance = ({ asset, mainBalance, secondaryBalance }: BalanceProps) => {
return (
-
+
{strings('asset_overview.your_balance')}
-
+
{
{asset.name || asset.symbol}
- {isPooledStakingFeatureEnabled() && asset?.isETH && }
);
};
diff --git a/app/components/UI/AssetOverview/Balance/__snapshots__/index.test.tsx.snap b/app/components/UI/AssetOverview/Balance/__snapshots__/index.test.tsx.snap
index d233e8e44fd..af6fcda4d9e 100644
--- a/app/components/UI/AssetOverview/Balance/__snapshots__/index.test.tsx.snap
+++ b/app/components/UI/AssetOverview/Balance/__snapshots__/index.test.tsx.snap
@@ -9,18 +9,60 @@ exports[`Balance should render correctly with a fiat balance 1`] = `
}
>
Your balance
@@ -237,18 +279,60 @@ exports[`Balance should render correctly without a fiat balance 1`] = `
}
>
Your balance
diff --git a/app/components/UI/AssetOverview/ChartNavigationButton/ChartNavigationButton.styles.tsx b/app/components/UI/AssetOverview/ChartNavigationButton/ChartNavigationButton.styles.tsx
index ae8fdabdd06..d468e7e2502 100644
--- a/app/components/UI/AssetOverview/ChartNavigationButton/ChartNavigationButton.styles.tsx
+++ b/app/components/UI/AssetOverview/ChartNavigationButton/ChartNavigationButton.styles.tsx
@@ -15,7 +15,7 @@ const styleSheet = (params: {
return StyleSheet.create({
button: {
backgroundColor: selected
- ? colors.background.pressed
+ ? colors.primary.default
: colors.background.default,
borderRadius: 40,
paddingVertical: 2,
@@ -27,6 +27,7 @@ const styleSheet = (params: {
},
label: {
letterSpacing: 3,
+ color: selected ? colors.background.default : colors.primary.default,
textAlign: 'center',
} as TextStyle,
});
diff --git a/app/components/UI/AssetOverview/ChartNavigationButton/ChartNavigationButton.tsx b/app/components/UI/AssetOverview/ChartNavigationButton/ChartNavigationButton.tsx
index cc33cec08c8..7aae8909651 100644
--- a/app/components/UI/AssetOverview/ChartNavigationButton/ChartNavigationButton.tsx
+++ b/app/components/UI/AssetOverview/ChartNavigationButton/ChartNavigationButton.tsx
@@ -2,7 +2,6 @@ import React from 'react';
import { TouchableOpacity } from 'react-native';
import { useStyles } from '../../../../component-library/hooks';
import Text, {
- TextColor,
TextVariant,
} from '../../../../component-library/components/Texts/Text';
import styleSheet from './ChartNavigationButton.styles';
@@ -21,11 +20,7 @@ const ChartNavigationButton = ({
const { styles } = useStyles(styleSheet, { selected });
return (
-
+
{label}
diff --git a/app/components/UI/AssetOverview/Price/Price.styles.tsx b/app/components/UI/AssetOverview/Price/Price.styles.tsx
index 5510e7fd774..0330cf4e6d0 100644
--- a/app/components/UI/AssetOverview/Price/Price.styles.tsx
+++ b/app/components/UI/AssetOverview/Price/Price.styles.tsx
@@ -11,22 +11,33 @@ const styleSheet = (params: {
theme,
vars: { priceDiff },
} = params;
- const { colors } = theme;
+ const { colors, typography } = theme;
return StyleSheet.create({
wrapper: {
paddingHorizontal: 16,
},
+ name: {
+ fontWeight: '500',
+ } as TextStyle,
+ price: {
+ ...typography.lHeadingLG,
+ } as TextStyle,
priceDiff: {
+ ...typography.lBodyMDMedium,
color:
priceDiff > 0
? colors.success.default
: priceDiff < 0
? colors.error.default
: colors.text.alternative,
+ lineHeight: 22,
} as TextStyle,
priceDiffIcon: {
marginTop: 10,
},
+ priceDiffDateText: {
+ ...typography.lBodyMDMedium,
+ } as TextStyle,
loadingPrice: {
paddingTop: 8,
},
diff --git a/app/components/UI/AssetOverview/Price/Price.tsx b/app/components/UI/AssetOverview/Price/Price.tsx
index f297a40115f..694f2215b75 100644
--- a/app/components/UI/AssetOverview/Price/Price.tsx
+++ b/app/components/UI/AssetOverview/Price/Price.tsx
@@ -11,9 +11,9 @@ import { useStyles } from '../../../../component-library/hooks';
import { toDateFormat } from '../../../../util/date';
import { addCurrencySymbol } from '../../../../util/number';
import Text, {
- TextColor,
TextVariant,
} from '../../../../component-library/components/Texts/Text';
+import Title from '../../../Base/Title';
import PriceChart from '../PriceChart/PriceChart';
import { distributeDataPoints } from '../PriceChart/utils';
import styleSheet from './Price.styles';
@@ -80,17 +80,14 @@ const Price = ({
<>
{asset.name ? (
-
+
{asset.name} ({asset.symbol})
) : (
- {asset.symbol}
+ {asset.symbol}
)}
{!isNaN(price) && (
-
+
{isLoading ? (
@@ -104,7 +101,7 @@ const Price = ({
) : (
addCurrencySymbol(price, currentCurrency, true)
)}
-
+
)}
{isLoading ? (
@@ -118,7 +115,7 @@ const Price = ({
) : distributedPriceData.length > 0 ? (
-
+
{
0 ? '+' : ''}
{diff === 0 ? '0' : ((diff / comparePrice) * 100).toFixed(2)}
- %){' '}
-
- {date}
-
+ %) {date}
) : null}
diff --git a/app/components/UI/AssetOverview/PriceChart/PriceChart.tsx b/app/components/UI/AssetOverview/PriceChart/PriceChart.tsx
index 2af05649ef6..aeb34cc7a93 100644
--- a/app/components/UI/AssetOverview/PriceChart/PriceChart.tsx
+++ b/app/components/UI/AssetOverview/PriceChart/PriceChart.tsx
@@ -69,9 +69,9 @@ const PriceChart = ({
const chartColor =
priceDiff > 0
- ? theme.colors.primary.default
+ ? theme.colors.success.default
: priceDiff < 0
- ? theme.colors.primary.default
+ ? theme.colors.error.default
: theme.colors.text.alternative;
const apx = (size = 0) => {
diff --git a/app/components/UI/AssetOverview/StakingEarnings/StakingEarnings.styles.tsx b/app/components/UI/AssetOverview/StakingEarnings/StakingEarnings.styles.tsx
index e5961497522..b9fc494e5ca 100644
--- a/app/components/UI/AssetOverview/StakingEarnings/StakingEarnings.styles.tsx
+++ b/app/components/UI/AssetOverview/StakingEarnings/StakingEarnings.styles.tsx
@@ -3,13 +3,14 @@ import { StyleSheet, TextStyle } from 'react-native';
const styleSheet = (params: { theme: Theme }) => {
const { theme } = params;
- const { colors } = theme;
+ const { colors, typography } = theme;
return StyleSheet.create({
stakingEarningsContainer: {
paddingTop: 16,
},
title: {
+ ...typography.sHeadingSM,
paddingBottom: 8,
} as TextStyle,
keyValueRow: {
diff --git a/app/components/UI/AssetOverview/StakingEarnings/__snapshots__/StakingEarnings.test.tsx.snap b/app/components/UI/AssetOverview/StakingEarnings/__snapshots__/StakingEarnings.test.tsx.snap
index 3f9bd60d677..766fc24aac4 100644
--- a/app/components/UI/AssetOverview/StakingEarnings/__snapshots__/StakingEarnings.test.tsx.snap
+++ b/app/components/UI/AssetOverview/StakingEarnings/__snapshots__/StakingEarnings.test.tsx.snap
@@ -9,17 +9,58 @@ exports[`Staking Earnings should render correctly 1`] = `
}
>
Your earnings
diff --git a/app/components/UI/AssetOverview/StakingEarnings/index.tsx b/app/components/UI/AssetOverview/StakingEarnings/index.tsx
index d9538aaa50a..99bb62812ab 100644
--- a/app/components/UI/AssetOverview/StakingEarnings/index.tsx
+++ b/app/components/UI/AssetOverview/StakingEarnings/index.tsx
@@ -16,6 +16,7 @@ import ButtonIcon, {
import useTooltipModal from '../../../../components/hooks/useTooltipModal';
import { strings } from '../../../../../locales/i18n';
import { isPooledStakingFeatureEnabled } from '../../Stake/constants';
+import Title from '../../../Base/Title';
// TODO: Remove mock data when connecting component to backend.
const MOCK_DATA = {
@@ -49,9 +50,7 @@ const StakingEarnings = () => {
return (
-
- {strings('stake.your_earnings')}
-
+ {strings('stake.your_earnings')}
{/* Annual Rate */}
diff --git a/app/components/UI/AssetOverview/TokenDetails/MarketDetailsList/MarketDetailsList.tsx b/app/components/UI/AssetOverview/TokenDetails/MarketDetailsList/MarketDetailsList.tsx
index 6edd8fd5ca2..ffd3713470e 100644
--- a/app/components/UI/AssetOverview/TokenDetails/MarketDetailsList/MarketDetailsList.tsx
+++ b/app/components/UI/AssetOverview/TokenDetails/MarketDetailsList/MarketDetailsList.tsx
@@ -2,12 +2,10 @@ import React from 'react';
import { View } from 'react-native';
import { strings } from '../../../../../../locales/i18n';
import { useStyles } from '../../../../../component-library/hooks';
+import Title from '../../../../Base/Title';
import styleSheet from '../TokenDetails.styles';
import { MarketDetails } from '../TokenDetails';
import TokenDetailsListItem from '../TokenDetailsListItem';
-import Text, {
- TextVariant,
-} from '../../../../../component-library/components/Texts/Text';
interface MarketDetailsListProps {
marketDetails: MarketDetails;
@@ -20,9 +18,7 @@ const MarketDetailsList: React.FC = ({
return (
-
- {strings('token.market_details')}
-
+ {strings('token.market_details')}
{marketDetails.marketCap && (
Market details
diff --git a/app/components/UI/AssetOverview/TokenDetails/TokenDetails.styles.tsx b/app/components/UI/AssetOverview/TokenDetails/TokenDetails.styles.tsx
index 2d4b7023f4e..7a087b99206 100644
--- a/app/components/UI/AssetOverview/TokenDetails/TokenDetails.styles.tsx
+++ b/app/components/UI/AssetOverview/TokenDetails/TokenDetails.styles.tsx
@@ -3,7 +3,7 @@ import { StyleSheet, TextStyle } from 'react-native';
const styleSheet = (params: { theme: Theme }) => {
const { theme } = params;
- const { colors } = theme;
+ const { typography, colors } = theme;
return StyleSheet.create({
tokenDetailsContainer: {
marginTop: 24,
@@ -13,6 +13,8 @@ const styleSheet = (params: { theme: Theme }) => {
paddingVertical: 4,
},
title: {
+ ...typography.sHeadingSM,
+ marginVertical: 0,
paddingVertical: 8,
} as TextStyle,
icon: { marginLeft: 4 },
diff --git a/app/components/UI/AssetOverview/TokenDetails/TokenDetailsList/TokenDetailsList.tsx b/app/components/UI/AssetOverview/TokenDetails/TokenDetailsList/TokenDetailsList.tsx
index b218361c5ff..17622c974ce 100644
--- a/app/components/UI/AssetOverview/TokenDetails/TokenDetailsList/TokenDetailsList.tsx
+++ b/app/components/UI/AssetOverview/TokenDetails/TokenDetailsList/TokenDetailsList.tsx
@@ -8,6 +8,7 @@ import Text, {
TextColor,
TextVariant,
} from '../../../../../component-library/components/Texts/Text';
+import Title from '../../../../Base/Title';
import styleSheet from '../TokenDetails.styles';
import Icon, {
IconColor,
@@ -48,9 +49,7 @@ const TokenDetailsList: React.FC = ({
return (
-
- {strings('token.token_details')}
-
+ {strings('token.token_details')}
{tokenDetails.contractAddress && (
Token details
diff --git a/app/components/UI/AssetOverview/TokenDetails/__snapshots__/TokenDetails.test.tsx.snap b/app/components/UI/AssetOverview/TokenDetails/__snapshots__/TokenDetails.test.tsx.snap
index 1fc8c179d6c..677b5a82edd 100644
--- a/app/components/UI/AssetOverview/TokenDetails/__snapshots__/TokenDetails.test.tsx.snap
+++ b/app/components/UI/AssetOverview/TokenDetails/__snapshots__/TokenDetails.test.tsx.snap
@@ -11,17 +11,59 @@ exports[`TokenDetails should render correctly 1`] = `
>
Token details
@@ -195,17 +237,59 @@ exports[`TokenDetails should render correctly 1`] = `
Market details
diff --git a/app/components/UI/AssetOverview/__snapshots__/AssetOverview.test.tsx.snap b/app/components/UI/AssetOverview/__snapshots__/AssetOverview.test.tsx.snap
index 5ec60e73b6f..28af7970a2c 100644
--- a/app/components/UI/AssetOverview/__snapshots__/AssetOverview.test.tsx.snap
+++ b/app/components/UI/AssetOverview/__snapshots__/AssetOverview.test.tsx.snap
@@ -21,12 +21,12 @@ exports[`AssetOverview should render correctly 1`] = `
accessibilityRole="text"
style={
{
- "color": "#6a737d",
+ "color": "#141618",
"fontFamily": "EuclidCircularB-Medium",
- "fontSize": 14,
+ "fontSize": 18,
"fontWeight": "500",
"letterSpacing": 0,
- "lineHeight": 22,
+ "lineHeight": 24,
}
}
>
@@ -36,16 +36,57 @@ exports[`AssetOverview should render correctly 1`] = `
)
@@ -188,7 +229,7 @@ exports[`AssetOverview should render correctly 1`] = `
style={
{
"alignItems": "center",
- "backgroundColor": "#00000014",
+ "backgroundColor": "#0376c9",
"borderRadius": 40,
"justifyContent": "center",
"paddingHorizontal": 8,
@@ -201,7 +242,7 @@ exports[`AssetOverview should render correctly 1`] = `
accessibilityRole="text"
style={
{
- "color": "#141618",
+ "color": "#ffffff",
"fontFamily": "EuclidCircularB-Regular",
"fontSize": 12,
"fontWeight": "400",
@@ -232,7 +273,7 @@ exports[`AssetOverview should render correctly 1`] = `
accessibilityRole="text"
style={
{
- "color": "#6a737d",
+ "color": "#0376c9",
"fontFamily": "EuclidCircularB-Regular",
"fontSize": 12,
"fontWeight": "400",
@@ -263,7 +304,7 @@ exports[`AssetOverview should render correctly 1`] = `
accessibilityRole="text"
style={
{
- "color": "#6a737d",
+ "color": "#0376c9",
"fontFamily": "EuclidCircularB-Regular",
"fontSize": 12,
"fontWeight": "400",
@@ -294,7 +335,7 @@ exports[`AssetOverview should render correctly 1`] = `
accessibilityRole="text"
style={
{
- "color": "#6a737d",
+ "color": "#0376c9",
"fontFamily": "EuclidCircularB-Regular",
"fontSize": 12,
"fontWeight": "400",
@@ -325,7 +366,7 @@ exports[`AssetOverview should render correctly 1`] = `
accessibilityRole="text"
style={
{
- "color": "#6a737d",
+ "color": "#0376c9",
"fontFamily": "EuclidCircularB-Regular",
"fontSize": 12,
"fontWeight": "400",
@@ -356,7 +397,7 @@ exports[`AssetOverview should render correctly 1`] = `
accessibilityRole="text"
style={
{
- "color": "#6a737d",
+ "color": "#0376c9",
"fontFamily": "EuclidCircularB-Regular",
"fontSize": 12,
"fontWeight": "400",
@@ -864,18 +905,60 @@ exports[`AssetOverview should render correctly 1`] = `
}
>
Your balance
diff --git a/app/components/UI/AssetSearch/index.tsx b/app/components/UI/AssetSearch/index.tsx
index b1cfd4be105..dfc06c6462d 100644
--- a/app/components/UI/AssetSearch/index.tsx
+++ b/app/components/UI/AssetSearch/index.tsx
@@ -3,6 +3,7 @@ import {
TextInput,
View,
StyleSheet,
+ Platform,
TextStyle,
DimensionValue,
} from 'react-native';
@@ -13,7 +14,8 @@ import { toLowerCaseEquals } from '../../../util/general';
import { useSelector } from 'react-redux';
import { TokenListToken } from '@metamask/assets-controllers';
import { useTheme } from '../../../util/theme';
-import { ImportTokenViewSelectorsIDs } from '../../../../e2e/selectors/wallet/ImportTokenView.selectors';
+import generateTestId from '../../../../wdio/utils/generateTestId';
+import { TOKEN_INPUT_BOX_ID } from '../../../../wdio/screen-objects/testIDs/Screens/AssetSearch.testIds';
import { TokenViewSelectors } from '../../../../e2e/selectors/AddTokenView.selectors';
import { selectTokenListArray } from '../../../selectors/tokenListController';
import Icon, {
@@ -164,7 +166,7 @@ const AssetSearch = memo(({ onSearch, onFocus, onBlur }: Props) => {
placeholder={strings('token.search_tokens_placeholder')}
placeholderTextColor={colors.text.muted}
onChangeText={handleSearch}
- testID={ImportTokenViewSelectorsIDs.SEARCH_BAR}
+ {...generateTestId(Platform, TOKEN_INPUT_BOX_ID)}
keyboardAppearance={themeAppearance}
/>
diff --git a/app/components/UI/BasicFunctionality/BasicFunctionalityModal/BasicFunctionalityModal.tsx b/app/components/UI/BasicFunctionality/BasicFunctionalityModal/BasicFunctionalityModal.tsx
index e01d3afdccb..b2d26f68838 100644
--- a/app/components/UI/BasicFunctionality/BasicFunctionalityModal/BasicFunctionalityModal.tsx
+++ b/app/components/UI/BasicFunctionality/BasicFunctionalityModal/BasicFunctionalityModal.tsx
@@ -26,7 +26,10 @@ import Icon, {
IconSize,
} from '../../../../component-library/components/Icons/Icon';
import Routes from '../../../../constants/navigation/Routes';
-import NotificationsService from '../../../../util/notifications/services/NotificationService';
+import {
+ asyncAlert,
+ requestPushNotificationsPermission,
+} from '../../../../util/notifications';
import { MetaMetricsEvents } from '../../../../core/Analytics';
import { useEnableNotifications } from '../../../../util/notifications/hooks/useNotifications';
import { useMetrics } from '../../../hooks/useMetrics';
@@ -34,6 +37,7 @@ import {
selectIsProfileSyncingEnabled,
selectIsMetamaskNotificationsEnabled,
} from '../../../../selectors/notifications';
+import { AuthorizationStatus } from '@notifee/react-native';
interface Props {
route: {
@@ -61,11 +65,18 @@ const BasicFunctionalityModal = ({ route }: Props) => {
const { enableNotifications } = useEnableNotifications();
const enableNotificationsFromModal = useCallback(async () => {
- const { permission } = await NotificationsService.getAllPermissions(false);
- if (permission !== 'authorized') {
- return;
- }
+ const nativeNotificationStatus = await requestPushNotificationsPermission(
+ asyncAlert,
+ );
+
+ if (nativeNotificationStatus?.authorizationStatus === AuthorizationStatus.AUTHORIZED) {
+ /**
+ * Although this is an async function, we are dispatching an action (firing & forget)
+ * to emulate optimistic UI.
+ *
+ */
enableNotifications();
+ }
}, [enableNotifications]);
const closeBottomSheet = async () => {
diff --git a/app/components/UI/BlockingActionModal/index.tsx b/app/components/UI/BlockingActionModal/index.js
similarity index 80%
rename from app/components/UI/BlockingActionModal/index.tsx
rename to app/components/UI/BlockingActionModal/index.js
index c29a8f34e29..b126438edf0 100644
--- a/app/components/UI/BlockingActionModal/index.tsx
+++ b/app/components/UI/BlockingActionModal/index.js
@@ -1,11 +1,11 @@
import React from 'react';
+import PropTypes from 'prop-types';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import Modal from 'react-native-modal';
import { baseStyles } from '../../../styles/common';
import { useTheme } from '../../../util/theme';
-import { Theme } from '@metamask/design-tokens';
-const createStyles = (colors: Theme['colors']) =>
+const createStyles = (colors) =>
StyleSheet.create({
modal: {
margin: 0,
@@ -26,25 +26,6 @@ const createStyles = (colors: Theme['colors']) =>
},
});
-interface BlockingActionModalProps {
- /**
- * Whether modal is shown
- */
- modalVisible: boolean;
- /**
- * Whether a spinner is shown
- */
- isLoadingAction: boolean;
- /**
- * Content to display above the action buttons
- */
- children: React.ReactNode;
- /**
- * Callback function when modal animation is completed
- */
- onAnimationCompleted?: () => void;
-}
-
/**
* View that renders an action modal
*/
@@ -53,7 +34,7 @@ export default function BlockingActionModal({
modalVisible,
isLoadingAction,
onAnimationCompleted,
-}: BlockingActionModalProps) {
+}) {
const { colors } = useTheme();
const styles = createStyles(colors);
@@ -77,3 +58,20 @@ export default function BlockingActionModal({
);
}
+
+BlockingActionModal.propTypes = {
+ /**
+ * Whether modal is shown
+ */
+ modalVisible: PropTypes.bool,
+ /**
+ * Whether a spinner is shown
+ */
+ isLoadingAction: PropTypes.bool,
+ /**
+ * Content to display above the action buttons
+ */
+ children: PropTypes.node,
+
+ onAnimationCompleted: PropTypes.func,
+};
diff --git a/app/components/UI/BottomModal/BottomModal.test.tsx b/app/components/UI/BottomModal/BottomModal.test.tsx
deleted file mode 100644
index e5204501b33..00000000000
--- a/app/components/UI/BottomModal/BottomModal.test.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import React from 'react';
-import { Text, View } from 'react-native';
-
-import renderWithProvider from '../../../util/test/renderWithProvider';
-import BottomModal from '.';
-
-describe('BottomModal', () => {
- it('should match snapshot', async () => {
- const container = renderWithProvider(
-
-
- DUMMY
-
- ,
- { state: {} },
- );
- expect(container).toMatchSnapshot();
- });
-});
diff --git a/app/components/UI/BottomModal/BottomModal.tsx b/app/components/UI/BottomModal/BottomModal.tsx
deleted file mode 100644
index 7dbeb15ede4..00000000000
--- a/app/components/UI/BottomModal/BottomModal.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import React, { ReactChild } from 'react';
-import Modal from 'react-native-modal';
-import { StyleSheet } from 'react-native';
-
-import { useTheme } from '../../../util/theme';
-
-interface BottomModalProps {
- children: ReactChild;
- onClose?: () => void;
-}
-
-const styles = StyleSheet.create({
- bottomModal: {
- justifyContent: 'flex-end',
- margin: 0,
- },
-});
-
-const BottomModal = ({ children, onClose }: BottomModalProps) => {
- const { colors } = useTheme();
-
- return (
-
- {children}
-
- );
-};
-
-export default BottomModal;
diff --git a/app/components/UI/BottomModal/__snapshots__/BottomModal.test.tsx.snap b/app/components/UI/BottomModal/__snapshots__/BottomModal.test.tsx.snap
deleted file mode 100644
index 6325a757835..00000000000
--- a/app/components/UI/BottomModal/__snapshots__/BottomModal.test.tsx.snap
+++ /dev/null
@@ -1,124 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`BottomModal should match snapshot 1`] = `
-
-
-
-
-
- DUMMY
-
-
-
-
-`;
diff --git a/app/components/UI/BottomModal/index.tsx b/app/components/UI/BottomModal/index.tsx
deleted file mode 100644
index 3e78bd6309c..00000000000
--- a/app/components/UI/BottomModal/index.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './BottomModal';
diff --git a/app/components/UI/ComponentErrorBoundary/index.js b/app/components/UI/ComponentErrorBoundary/index.js
new file mode 100644
index 00000000000..d532728cdc3
--- /dev/null
+++ b/app/components/UI/ComponentErrorBoundary/index.js
@@ -0,0 +1,58 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Logger from '../../../util/Logger';
+import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics';
+
+class ComponentErrorBoundary extends React.Component {
+ state = { error: null };
+
+ static propTypes = {
+ /**
+ * Component to be used when there is no error
+ */
+ children: PropTypes.oneOfType([
+ PropTypes.arrayOf(PropTypes.node),
+ PropTypes.node,
+ ]),
+ /**
+ * Component label for logging
+ */
+ componentLabel: PropTypes.string.isRequired,
+ /**
+ * Function to be called when there is an error
+ */
+ onError: PropTypes.func,
+ /**
+ * Will not track as an error, but still log to analytics
+ */
+ dontTrackAsError: PropTypes.bool,
+ };
+
+ static getDerivedStateFromError(error) {
+ return { error };
+ }
+
+ componentDidCatch(error, errorInfo) {
+ // eslint-disable-next-line no-unused-expressions
+ this.props.onError?.();
+
+ const { componentLabel, dontTrackAsError } = this.props;
+
+ if (dontTrackAsError) {
+ return trackErrorAsAnalytics(
+ `Component Error Boundary: ${componentLabel}`,
+ error?.message,
+ );
+ }
+ Logger.error(error, { View: this.props.componentLabel, ...errorInfo });
+ }
+
+ getErrorMessage = () =>
+ `Component: ${this.props.componentLabel}\n${this.state.error.toString()}`;
+
+ render() {
+ return this.state.error ? null : this.props.children;
+ }
+}
+
+export default ComponentErrorBoundary;
diff --git a/app/components/UI/ComponentErrorBoundary/index.tsx b/app/components/UI/ComponentErrorBoundary/index.tsx
deleted file mode 100644
index 788c502297c..00000000000
--- a/app/components/UI/ComponentErrorBoundary/index.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import React from 'react';
-import Logger from '../../../util/Logger';
-import trackErrorAsAnalytics from '../../../util/metrics/TrackError/trackErrorAsAnalytics';
-
-interface ComponentErrorBoundaryProps {
- /**
- * Component to be used when there is no error
- */
- children: React.ReactNode;
- /**
- * Component label for logging
- */
- componentLabel: string;
- /**
- * Function to be called when there is an error
- */
- onError?: () => void;
- /**
- * Will not track as an error, but still log to analytics
- */
- dontTrackAsError?: boolean;
-}
-
-interface ComponentErrorBoundaryState {
- error: Error | null;
-}
-
-class ComponentErrorBoundary extends React.Component {
- state: ComponentErrorBoundaryState = { error: null };
-
- static getDerivedStateFromError(error: Error): ComponentErrorBoundaryState {
- return { error };
- }
-
- componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
- this.props.onError?.();
-
- const { componentLabel, dontTrackAsError } = this.props;
-
- if (dontTrackAsError) {
- return trackErrorAsAnalytics(
- `Component Error Boundary: ${componentLabel}`,
- error?.message,
- );
- }
- Logger.error(error, { View: this.props.componentLabel, ...errorInfo });
- }
-
- getErrorMessage = (): string =>
- `Component: ${this.props.componentLabel}\n${this.state.error?.toString()}`;
-
- render(): React.ReactNode {
- return this.state.error ? null : this.props.children;
- }
-}
-
-export default ComponentErrorBoundary;
diff --git a/app/components/UI/ConfirmAddAsset/ConfirmAddAsset.tsx b/app/components/UI/ConfirmAddAsset/ConfirmAddAsset.tsx
index 097b76baf44..e6701455b55 100644
--- a/app/components/UI/ConfirmAddAsset/ConfirmAddAsset.tsx
+++ b/app/components/UI/ConfirmAddAsset/ConfirmAddAsset.tsx
@@ -1,5 +1,5 @@
import React, { useCallback, useEffect } from 'react';
-import { View } from 'react-native';
+import { View, Platform } from 'react-native';
import Text, {
TextVariant,
} from '../../../component-library/components/Texts/Text';
@@ -36,7 +36,11 @@ import Modal from 'react-native-modal';
import Box from '../Ramp/components/Box';
import SheetHeader from '../../../component-library/components/Sheet/SheetHeader';
import Routes from '../../../constants/navigation/Routes';
-import { ImportTokenViewSelectorsIDs } from '../../../../e2e/selectors/wallet/ImportTokenView.selectors';
+import generateTestId from '../../../../wdio/utils/generateTestId';
+import {
+ ADD_CANCEL_ADD_CUSTOM_ASSET_MODAL,
+ ADD_CONFIRM_CUSTOM_ASSET,
+} from '../../../../wdio/screen-objects/testIDs/Screens/AddCustomToken.testIds';
import { TOKEN_TITLE } from '../../../components/Views/AddAsset/AddAsset.constants';
const RenderBalance = (asset: {
@@ -125,7 +129,7 @@ const ConfirmAddAsset = () => {
>
@@ -171,7 +175,7 @@ const ConfirmAddAsset = () => {
return (
{strings('wallet.import_token')}
diff --git a/app/components/UI/ConnectHeader/index.js b/app/components/UI/ConnectHeader/index.js
new file mode 100644
index 00000000000..acd3af74261
--- /dev/null
+++ b/app/components/UI/ConnectHeader/index.js
@@ -0,0 +1,61 @@
+import React, { Component } from 'react';
+import { View, StyleSheet, TouchableOpacity } from 'react-native';
+import PropTypes from 'prop-types';
+import IonicIcon from 'react-native-vector-icons/Ionicons';
+import Text, {
+ TextVariant,
+} from '../../../component-library/components/Texts/Text';
+import { ThemeContext, mockTheme } from '../../../util/theme';
+
+const createStyles = (colors) =>
+ StyleSheet.create({
+ header: {
+ width: '100%',
+ position: 'relative',
+ paddingBottom: 20,
+ },
+ title: {
+ color: colors.text.default,
+ fontSize: 16,
+ textAlign: 'center',
+ paddingVertical: 12,
+ },
+ back: {
+ position: 'absolute',
+ zIndex: 1,
+ paddingVertical: 10,
+ paddingRight: 10,
+ },
+ });
+
+class ConnectHeader extends Component {
+ static propTypes = {
+ action: PropTypes.func.isRequired,
+ title: PropTypes.string.isRequired,
+ };
+
+ render() {
+ const { title, action } = this.props;
+ const colors = this.context.colors || mockTheme.colors;
+ const styles = createStyles(colors);
+
+ return (
+
+
+
+
+
+ {title}
+
+
+ );
+ }
+}
+
+ConnectHeader.contextType = ThemeContext;
+
+export default ConnectHeader;
diff --git a/app/components/UI/ConnectHeader/index.tsx b/app/components/UI/ConnectHeader/index.tsx
deleted file mode 100644
index ec7042ccc3d..00000000000
--- a/app/components/UI/ConnectHeader/index.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import React from 'react';
-import { View, StyleSheet, TouchableOpacity } from 'react-native';
-import IonicIcon from 'react-native-vector-icons/Ionicons';
-import Text, {
- TextVariant,
-} from '../../../component-library/components/Texts/Text';
-import { ThemeContext, mockTheme } from '../../../util/theme';
-import { Theme } from '@metamask/design-tokens';
-
-interface ConnectHeaderProps {
- action: () => void;
- title: string;
-}
-
-const createStyles = (colors: Theme['colors']) =>
- StyleSheet.create({
- header: {
- width: '100%',
- position: 'relative',
- paddingBottom: 20,
- },
- title: {
- color: colors.text.default,
- fontSize: 16,
- textAlign: 'center',
- paddingVertical: 12,
- },
- back: {
- position: 'absolute',
- zIndex: 1,
- paddingVertical: 10,
- paddingRight: 10,
- },
- });
-
-const ConnectHeader: React.FC = ({ title, action }) => {
- const context = React.useContext(ThemeContext);
- const colors = context?.colors || mockTheme.colors;
- const styles = createStyles(colors);
-
- return (
-
-
-
-
-
- {title}
-
-
- );
-};
-
-export default ConnectHeader;
diff --git a/app/components/UI/FadeView/index.tsx b/app/components/UI/FadeView/index.js
similarity index 62%
rename from app/components/UI/FadeView/index.tsx
rename to app/components/UI/FadeView/index.js
index 8fe7b419621..592d5ee2549 100644
--- a/app/components/UI/FadeView/index.tsx
+++ b/app/components/UI/FadeView/index.js
@@ -1,39 +1,30 @@
-import React, { PureComponent, ReactNode } from 'react';
-import { Animated, StyleProp, ViewStyle } from 'react-native';
-
-interface FadeViewPropTypes {
- /**
- * Determines to show / hide the children components
- */
- visible: boolean;
- /**
- * Children components of the FadeView
- * it can be a text node, an image, or an icon
- * or an Array with a combination of them
- */
- children?: ReactNode;
- /**
- * Styles to be applied to the FadeView
- */
- style?: StyleProp;
-}
-
-interface FadeViewState {
- visible: boolean;
-}
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import { Animated, ViewPropTypes } from 'react-native';
/**
* View that has the ability to fade in / out
* his children by using the visible prop
*/
-export default class FadeView extends PureComponent<
- FadeViewPropTypes,
- FadeViewState
-> {
- visibility: Animated.Value;
- mounted: boolean = false;
+export default class FadeView extends PureComponent {
+ static propTypes = {
+ /**
+ * Determines to show / hide the children components
+ */
+ visible: PropTypes.bool,
+ /**
+ * Children components of the FadeView
+ * it can be a text node, an image, or an icon
+ * or an Array with a combination of them
+ */
+ children: PropTypes.any,
+ /**
+ * Styles to be applied to the FadeView
+ */
+ style: ViewPropTypes.style,
+ };
- constructor(props: FadeViewPropTypes) {
+ constructor(props) {
super(props);
this.state = {
visible: props.visible,
diff --git a/app/components/UI/GenericButton/index.android.tsx b/app/components/UI/GenericButton/index.android.js
similarity index 74%
rename from app/components/UI/GenericButton/index.android.tsx
rename to app/components/UI/GenericButton/index.android.js
index 31c0f904e48..d715f3567a7 100644
--- a/app/components/UI/GenericButton/index.android.tsx
+++ b/app/components/UI/GenericButton/index.android.js
@@ -1,5 +1,6 @@
import React from 'react';
-import { View, TouchableNativeFeedback, StyleProp, ViewStyle, GestureResponderEvent } from 'react-native';
+import PropTypes from 'prop-types';
+import { View, ViewPropTypes, TouchableNativeFeedback } from 'react-native';
/**
* @deprecated The `` component has been deprecated in favor of the new `
);
}
return (
-
+
-
- {renderHeader()}
-
-
- {!isAlreadyConnected || isNetworkSwitch
- ? strings('permissions.title_dapp_url_wants_to', {
- dappUrl: new URL(currentPageInformation.url).hostname,
- })
- : strings('permissions.title_dapp_url_has_approval_to', {
- dappUrl: new URL(currentPageInformation.url).hostname,
- })}
-
-
- {!isNetworkSwitch && renderAccountPermissionsRequestInfoCard()}
- {renderNetworkPermissionsRequestInfoCard()}
-
-
- {isAlreadyConnected && isDisconnectAllShown && (
-
-
-
- )}
- {showActionButtons && (
-
-
- {strings('permissions.cancel')}
-
-
- {strings('confirmation_modal.confirm_cta')}
-
-
- )}
+ {renderHeader()}
+
+
+ {isInitialDappConnection
+ ? strings('permissions.title_dapp_url_wants_to', {
+ dappUrl: new URL(currentPageInformation.url).hostname,
+ })
+ : strings('permissions.title_dapp_url_has_approval_to', {
+ dappUrl: new URL(currentPageInformation.url).hostname,
+ })}
+
+ {renderAccountPermissionsRequestInfoCard()}
+ {renderNetworkPermissionsRequestInfoCard()}
+ {showActionButtons && (
+
+
+ {strings('permissions.cancel')}
+
+
+ {strings('confirmation_modal.confirm_cta')}
+
+
+ )}
);
diff --git a/app/components/UI/PermissionsSummary/PermissionsSummary.types.ts b/app/components/UI/PermissionsSummary/PermissionsSummary.types.ts
index c80d27e198a..949ff145f5c 100644
--- a/app/components/UI/PermissionsSummary/PermissionsSummary.types.ts
+++ b/app/components/UI/PermissionsSummary/PermissionsSummary.types.ts
@@ -11,8 +11,6 @@ export interface PermissionsSummaryProps {
onBack?: () => void;
onUserAction?: React.Dispatch>;
showActionButtons?: boolean;
+ isInitialDappConnection?: boolean;
isAlreadyConnected?: boolean;
- isRenderedAsBottomSheet?: boolean;
- isDisconnectAllShown?: boolean;
- isNetworkSwitch?: boolean;
}
diff --git a/app/components/UI/PermissionsSummary/__snapshots__/PermissionsSummary.test.tsx.snap b/app/components/UI/PermissionsSummary/__snapshots__/PermissionsSummary.test.tsx.snap
index 07ce968d48c..07551011cc3 100644
--- a/app/components/UI/PermissionsSummary/__snapshots__/PermissionsSummary.test.tsx.snap
+++ b/app/components/UI/PermissionsSummary/__snapshots__/PermissionsSummary.test.tsx.snap
@@ -1,118 +1,481 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PermissionsSummary should render correctly 1`] = `
-
+
-
+
+
+
+
+ a
+
+
+
+
+
+
+
+
+ app.uniswap.org wants to:
+
+
+
+
+
+
+
+
+ See your accounts and suggest transactions
+
-
+
+ Requesting for
+
+
+ Account 2
+
+
+
+
+
-
- a
-
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
@@ -121,68 +484,35 @@ exports[`PermissionsSummary should render correctly 1`] = `
style={
{
"color": "#141618",
- "fontFamily": "EuclidCircularB-Bold",
- "fontSize": 16,
- "fontWeight": "700",
+ "fontFamily": "EuclidCircularB-Regular",
+ "fontSize": 14,
+ "fontWeight": "400",
"letterSpacing": 0,
- "lineHeight": 24,
+ "lineHeight": 22,
}
}
>
- app.uniswap.org has approval to:
+ Use your enabled networks
-
-
-
-
-
- See your accounts and suggest transactions
+
+ Requesting for
+
+
+ Ethereum Main Network
+
+
+
-
-
+
+
+
+
+
- Requesting for
-
-
- Account 2
-
-
+ testID="token-avatar-image"
+ />
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Use your enabled networks
-
-
-
-
-
- Requesting for
-
-
- Ethereum Main Network
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- +1
-
-
-
+ +1
+
-
-
-
-
-
-
-
+
+
-
- Disconnect all
-
-
+
+
-
-
+
+
- Cancel
-
-
-
+
-
- Confirm
-
-
-
+ Confirm
+
+
diff --git a/app/components/UI/Ramp/Views/Settings/__snapshots__/Settings.test.tsx.snap b/app/components/UI/Ramp/Views/Settings/__snapshots__/Settings.test.tsx.snap
index a80c9ed4655..daa7e9670b4 100644
--- a/app/components/UI/Ramp/Views/Settings/__snapshots__/Settings.test.tsx.snap
+++ b/app/components/UI/Ramp/Views/Settings/__snapshots__/Settings.test.tsx.snap
@@ -656,6 +656,8 @@ exports[`Settings Activation Keys renders correctly when is loading 1`] = `
accessible={true}
style={
{
+ "alignItems": "center",
+ "flexDirection": "row",
"padding": 16,
}
}
@@ -663,211 +665,202 @@ exports[`Settings Activation Keys renders correctly when is loading 1`] = `
-
-
-
-
+
+
+
+
-
+ test key 1
+
+
-
- test key 1
-
-
- testKey1
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+ width={24}
+ />
+
-
-
-
-
+
+
+
+
-
+ test key 2
+
+
-
- test key 2
-
-
- testKey2
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+ width={24}
+ />
+
-
-
-
-
+
+
+
+
-
+ test key 1
+
+
-
- test key 1
-
-
- testKey1
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+ width={24}
+ />
+
-
-
-
-
+
+
+
+
-
+ test key 2
+
+
-
- test key 2
-
-
- testKey2
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+ width={24}
+ />
+
{
label={strings('transaction.next')}
onPress={goToConfirmAddToken}
isDisabled={selectedAsset.length < 1}
- testID={ImportTokenViewSelectorsIDs.NEXT_BUTTON}
+ testID="token-import-next-button"
/>
diff --git a/app/components/UI/Stake/Views/InputView/StakeInputView.test.tsx b/app/components/UI/Stake/Views/InputView/StakeInputView.test.tsx
index dca7cbb8ca8..81e2a74aa00 100644
--- a/app/components/UI/Stake/Views/InputView/StakeInputView.test.tsx
+++ b/app/components/UI/Stake/Views/InputView/StakeInputView.test.tsx
@@ -10,7 +10,7 @@ function render(Component: React.ComponentType) {
return renderScreen(
Component,
{
- name: Routes.STAKING.STAKE,
+ name: Routes.STAKE.STAKE,
},
{
state: {
@@ -127,13 +127,5 @@ describe('StakeInputView', () => {
fireEvent.press(screen.getByText('4'));
expect(screen.queryAllByText('Not enough ETH')).toHaveLength(2);
});
-
- it('navigates to Learn more modal when learn icon is pressed', () => {
- render(StakeInputView);
- fireEvent.press(screen.getByLabelText('Learn More'));
- expect(mockNavigate).toHaveBeenCalledWith('StakeModals', {
- screen: Routes.STAKING.MODALS.LEARN_MORE,
- });
- });
});
});
diff --git a/app/components/UI/Stake/Views/InputView/StakeInputView.tsx b/app/components/UI/Stake/Views/InputView/StakeInputView.tsx
index d9c1f63afd0..89f12bb48f4 100644
--- a/app/components/UI/Stake/Views/InputView/StakeInputView.tsx
+++ b/app/components/UI/Stake/Views/InputView/StakeInputView.tsx
@@ -35,7 +35,6 @@ import QuickAmounts from '../../components/QuickAmounts';
import useBalance from '../../hooks/useBalance';
import styleSheet from './StakeInputView.styles';
import EstimatedAnnualRewardsCard from '../../components/EstimatedAnnualRewardsCard';
-import Routes from '../../../../../constants/navigation/Routes';
const StakeInputView = () => {
const navigation = useNavigation();
@@ -57,12 +56,6 @@ const StakeInputView = () => {
const currentCurrency = useSelector(selectCurrentCurrency);
const conversionRate = useSelector(selectConversionRate) || 1;
- const navigateToLearnMoreModal = () => {
- navigation.navigate('StakeModals', {
- screen: Routes.STAKING.MODALS.LEARN_MORE,
- });
- };
-
const balanceText = isEth
? `${balance} ETH`
: `${balanceFiatNumber?.toString()} ${currentCurrency.toUpperCase()}`;
@@ -226,7 +219,9 @@ const StakeInputView = () => {
{
+ // TODO: Add tooltip modal
+ }}
/>
{strings('stake.metamask_pool')}
-
+
diff --git a/app/components/UI/Stake/components/LearnMoreModal/LearnMoreModal.styles.ts b/app/components/UI/Stake/components/LearnMoreModal/LearnMoreModal.styles.ts
deleted file mode 100644
index bb2ca5c6278..00000000000
--- a/app/components/UI/Stake/components/LearnMoreModal/LearnMoreModal.styles.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-const createLearnMoreModalStyles = () =>
- StyleSheet.create({
- container: {
- paddingHorizontal: 24,
- paddingTop: 24,
- },
- imageContainer: {
- alignItems: 'center',
- paddingBottom: 16,
- },
- bannerImage: {
- width: '100%',
- height: 200,
- borderRadius: 12,
- },
- textContainer: {
- paddingVertical: 8,
- },
- buttonContainer: {
- flexDirection: 'row',
- gap: 16,
- paddingHorizontal: 16,
- paddingTop: 16,
- },
- button: {
- flex: 1,
- },
- italicText: {
- fontStyle: 'italic',
- },
- });
-
-export default createLearnMoreModalStyles;
diff --git a/app/components/UI/Stake/components/LearnMoreModal/LearnMoreModal.test.tsx b/app/components/UI/Stake/components/LearnMoreModal/LearnMoreModal.test.tsx
deleted file mode 100644
index e290bea31e5..00000000000
--- a/app/components/UI/Stake/components/LearnMoreModal/LearnMoreModal.test.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { fireEvent, screen } from '@testing-library/react-native';
-import LearnMoreModal from '.';
-import { POOLED_STAKING_FAQ_URL } from '../../constants';
-import Routes from '../../../../../constants/navigation/Routes';
-import { renderScreen } from '../../../../../util/test/renderWithProvider';
-import { strings } from '../../../../../../locales/i18n';
-
-const renderLearnMoreModal = () =>
- renderScreen(LearnMoreModal, { name: Routes.STAKING.MODALS.LEARN_MORE });
-
-const mockNavigate = jest.fn();
-
-jest.mock('@react-navigation/native', () => {
- const actualReactNavigation = jest.requireActual('@react-navigation/native');
- return {
- ...actualReactNavigation,
- useNavigation: () => ({
- navigate: mockNavigate,
- }),
- };
-});
-
-describe('LearnMoreModal', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it('render matches snapshot', () => {
- const { toJSON } = renderLearnMoreModal();
- expect(toJSON()).toMatchSnapshot();
- });
-
- it('navigates to FAQ page when "Learn More" button is pressed', () => {
- renderLearnMoreModal();
- // Simulate pressing the "Learn More" button
- fireEvent.press(screen.getByText(strings('stake.learn_more')));
-
- // Assert that navigate is called with the correct parameters
- expect(mockNavigate).toHaveBeenCalledWith('Webview', {
- screen: 'SimpleWebview',
- params: { url: POOLED_STAKING_FAQ_URL },
- });
- });
-});
diff --git a/app/components/UI/Stake/components/LearnMoreModal/__snapshots__/LearnMoreModal.test.tsx.snap b/app/components/UI/Stake/components/LearnMoreModal/__snapshots__/LearnMoreModal.test.tsx.snap
deleted file mode 100644
index a5b380a956d..00000000000
--- a/app/components/UI/Stake/components/LearnMoreModal/__snapshots__/LearnMoreModal.test.tsx.snap
+++ /dev/null
@@ -1,726 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`LearnMoreModal render matches snapshot 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
- LearnMore
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Stake ETH and earn
-
-
-
-
- Stake any amount of ETH
-
-
- No minimum required.
-
-
-
-
- Earn ETH rewards
-
-
- Start earning as soon as you stake. Rewards compound automatically.
-
-
-
-
- Flexible unstaking
-
-
- Unstake anytime. Typically takes up to 11 days to process.
-
-
-
-
- Staking does not guarantee rewards, and involves risks including a loss of funds.
-
-
-
-
-
-
-
- Learn more
-
-
-
-
-
-
- Got it
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`;
diff --git a/app/components/UI/Stake/components/LearnMoreModal/index.tsx b/app/components/UI/Stake/components/LearnMoreModal/index.tsx
deleted file mode 100644
index 87e41a1a714..00000000000
--- a/app/components/UI/Stake/components/LearnMoreModal/index.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import React, { useRef } from 'react';
-import { View, Image } from 'react-native';
-import BottomSheet, {
- type BottomSheetRef,
-} from '../../../../../component-library/components/BottomSheets/BottomSheet';
-import Text, {
- TextColor,
- TextVariant,
-} from '../../../../../component-library/components/Texts/Text';
-import LearnMoreImage from '../images/LearnMoreEthBanner.png';
-import Button, {
- ButtonSize,
- ButtonVariants,
- ButtonWidthTypes,
-} from '../../../../../component-library/components/Buttons/Button';
-import { useNavigation } from '@react-navigation/native';
-import { strings } from '../../../../../../locales/i18n';
-import { POOLED_STAKING_FAQ_URL } from '../../constants';
-import createLearnMoreModalStyles from './LearnMoreModal.styles';
-
-const styles = createLearnMoreModalStyles();
-
-const ModalTextBlock = ({
- heading,
- body,
- bodyColor = TextColor.Alternative,
-}: {
- heading: string;
- body: string;
- bodyColor?: TextColor;
-}) => (
-
- {heading}
-
- {body}
-
-
-);
-
-const LearnMoreModal = () => {
- const sheetRef = useRef(null);
-
- const navigation = useNavigation();
-
- const handleClose = () => {
- sheetRef.current?.onCloseBottomSheet();
- };
-
- return (
-
-
-
-
-
-
-
-
- {strings('stake.stake_eth_and_earn')}
-
-
-
-
-
-
-
-
- {strings('stake.disclaimer')}
-
-
-
-
-
- {
- navigation.navigate('Webview', {
- screen: 'SimpleWebview',
- params: {
- url: POOLED_STAKING_FAQ_URL,
- },
- });
- }} // Take to the faq page
- label={strings('stake.learn_more')}
- variant={ButtonVariants.Secondary}
- width={ButtonWidthTypes.Full}
- size={ButtonSize.Lg}
- />
-
-
-
-
-
-
- );
-};
-
-export default LearnMoreModal;
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingBalance.styles.ts b/app/components/UI/Stake/components/StakingBalance/StakingBalance.styles.ts
deleted file mode 100644
index 7f653872905..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingBalance.styles.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-const styleSheet = () =>
- StyleSheet.create({
- container: {
- paddingHorizontal: 16,
- },
- badgeWrapper: {
- alignSelf: 'center',
- },
- balances: {
- flex: 1,
- justifyContent: 'center',
- marginLeft: 20,
- alignSelf: 'center',
- },
- ethLogo: {
- width: 32,
- height: 32,
- borderRadius: 16,
- overflow: 'hidden',
- },
- bannerStyles: {
- marginVertical: 8,
- },
- buttonsContainer: {
- paddingTop: 8,
- },
- stakingCta: {
- paddingBottom: 8,
- },
- });
-
-export default styleSheet;
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingBalance.test.tsx b/app/components/UI/Stake/components/StakingBalance/StakingBalance.test.tsx
deleted file mode 100644
index cb8074032cb..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingBalance.test.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import React from 'react';
-import { fireEvent, screen } from '@testing-library/react-native';
-import { renderScreen } from '../../../../../util/test/renderWithProvider';
-import { backgroundState } from '../../../../../util/test/initial-root-state';
-import StakingBalance from './StakingBalance';
-import { strings } from '../../../../../../locales/i18n';
-import Routes from '../../../../../constants/navigation/Routes';
-
-function render(Component: React.ComponentType) {
- return renderScreen(
- Component,
- {
- name: 'Asset',
- },
- {
- state: {
- engine: {
- backgroundState,
- },
- },
- },
- );
-}
-
-const mockNavigate = jest.fn();
-
-jest.mock('@react-navigation/native', () => {
- const actualReactNavigation = jest.requireActual('@react-navigation/native');
- return {
- ...actualReactNavigation,
- useNavigation: () => ({
- navigate: mockNavigate,
- }),
- };
-});
-describe('StakingBalance', () => {
- it('render matches snapshot', () => {
- render(StakingBalance);
- expect(screen.toJSON()).toMatchSnapshot();
- });
-
- it('redirects to StakeInputView on stake button click', () => {
- render(StakingBalance);
-
- fireEvent.press(screen.getByText(strings('stake.stake_more')));
-
- expect(mockNavigate).toHaveBeenCalledTimes(1);
- expect(mockNavigate).toHaveBeenCalledWith(Routes.STAKING.STAKE);
- });
-});
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingBalance.tsx b/app/components/UI/Stake/components/StakingBalance/StakingBalance.tsx
deleted file mode 100644
index 8884688694b..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingBalance.tsx
+++ /dev/null
@@ -1,159 +0,0 @@
-import React, { useMemo, useState } from 'react';
-import Badge, {
- BadgeVariant,
-} from '../../../../../component-library/components/Badges/Badge';
-import BadgeWrapper from '../../../../../component-library/components/Badges/BadgeWrapper';
-import Text, {
- TextVariant,
-} from '../../../../../component-library/components/Texts/Text';
-import { useStyles } from '../../../../../component-library/hooks';
-import AssetElement from '../../../AssetElement';
-import NetworkMainAssetLogo from '../../../NetworkMainAssetLogo';
-import { selectNetworkName } from '../../../../../selectors/networkInfos';
-import { useSelector } from 'react-redux';
-import images from '../../../../../images/image-icons';
-import styleSheet from './StakingBalance.styles';
-import { View } from 'react-native';
-import StakingButtons from './StakingButtons/StakingButtons';
-import ClaimBanner from './StakingBanners/ClaimBanner/ClaimBanner';
-import UnstakingBanner from './StakingBanners/UnstakeBanner/UnstakeBanner';
-import Banner, {
- BannerAlertSeverity,
- BannerVariant,
-} from '../../../../../component-library/components/Banners/Banner';
-import { strings } from '../../../../../../locales/i18n';
-import { renderFromWei } from '../../../../../util/number';
-import { getTimeDifferenceFromNow } from '../../../../../util/date';
-import { filterExitRequests } from './utils';
-import { BN } from 'ethereumjs-util';
-import bn from 'bignumber.js';
-import {
- CommonPercentageInputUnits,
- fixDisplayAmount,
- formatPercent,
- PercentageOutputFormat,
-} from '../../utils/value';
-import { multiplyValueByPowerOfTen } from '../../utils/bignumber';
-import StakingCta from './StakingCta/StakingCta';
-import {
- MOCK_GET_POOLED_STAKES_API_RESPONSE,
- MOCK_GET_VAULT_RESPONSE,
- MOCK_STAKED_ETH_ASSET,
-} from './mockData';
-
-const StakingBalance = () => {
- const { styles } = useStyles(styleSheet, {});
-
- const networkName = useSelector(selectNetworkName);
-
- const [isGeoBlocked] = useState(false);
- const [hasStakedPositions] = useState(false);
-
- const { unstakingRequests, claimableRequests } = useMemo(
- () =>
- filterExitRequests(
- MOCK_GET_POOLED_STAKES_API_RESPONSE.accounts[0].exitRequests,
- MOCK_GET_POOLED_STAKES_API_RESPONSE.exchangeRate,
- ),
- [],
- );
-
- const claimableEth = useMemo(
- () =>
- renderFromWei(
- claimableRequests.reduce(
- (acc, { claimedAssets }) =>
- claimedAssets ? acc.add(new BN(claimedAssets)) : acc,
- new BN(0),
- ),
- ),
- [claimableRequests],
- );
-
- const hasClaimableEth = !!Number(claimableEth);
-
- return (
-
- {Boolean(MOCK_STAKED_ETH_ASSET.balance) && !isGeoBlocked && (
-
-
- }
- >
-
-
-
- {MOCK_STAKED_ETH_ASSET.name || MOCK_STAKED_ETH_ASSET.symbol}
-
-
- )}
-
-
- {isGeoBlocked ? (
-
- ) : (
- <>
- {unstakingRequests.map(
- ({ positionTicket, withdrawalTimestamp, assetsToDisplay }) =>
- assetsToDisplay && (
-
- ),
- )}
-
- {hasClaimableEth && (
-
- )}
-
- {!hasStakedPositions && (
-
- )}
-
-
- >
- )}
-
-
- );
-};
-
-export default StakingBalance;
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingBalance.types.ts b/app/components/UI/Stake/components/StakingBalance/StakingBalance.types.ts
deleted file mode 100644
index dec8e420060..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingBalance.types.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-export interface ExitRequest {
- positionTicket: string; // BigInt!
- timestamp: string; // BigInt!
- totalShares: string; // BigInt!
- receiver: string; // Bytes!
- /**
- * If `withdrawalTimestamp` is null, it means the request hasn't been processed yet.
- * If `withdrawalTimestamp` is "0", funds are withdrawable.
- * Else, `withdrawalTimestamp` shows an approximate UTC timestamp of when funds will be withdrawable.
- * `withdrawalTimestamp` is updated every 6 hours.
- */
- withdrawalTimestamp: string | null; // BigInt
-}
-
-export type ExitRequestWithClaimedAssetInfo = Pick<
- ExitRequest,
- 'positionTicket' | 'timestamp' | 'totalShares' | 'withdrawalTimestamp'
-> & {
- exitQueueIndex: string;
- claimedAssets: string | null;
- leftShares: string | null;
-};
-
-interface StakeByAccount {
- account: string;
- lifetimeRewards: string;
- assets: string;
- exitRequests: ExitRequestWithClaimedAssetInfo[];
-}
-
-export interface GetStakesApiResponse {
- accounts: StakeByAccount[];
- exchangeRate: string;
-}
-
-export interface Vault {
- apy: string; //BigDecimal!
- capacity: string; //BigInt!
- displayName: string | null; //String
- feePercent: number; // Int!
- totalAssets: string; //BigInt!
- addressString: string; //String!
-}
-
-export interface GetVaultDataApiResponse
- extends Pick {
- vaultAddress: string;
-}
-
-export interface UnstakingRequest extends ExitRequestWithClaimedAssetInfo {
- assetsToDisplay: string;
-}
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingBanners/ClaimBanner/ClaimBanner.styles.ts b/app/components/UI/Stake/components/StakingBalance/StakingBanners/ClaimBanner/ClaimBanner.styles.ts
deleted file mode 100644
index d8450cebf83..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingBanners/ClaimBanner/ClaimBanner.styles.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-const styleSheet = () =>
- StyleSheet.create({
- claimButton: {
- alignSelf: 'flex-start',
- },
- });
-
-export default styleSheet;
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingBanners/ClaimBanner/ClaimBanner.tsx b/app/components/UI/Stake/components/StakingBalance/StakingBanners/ClaimBanner/ClaimBanner.tsx
deleted file mode 100644
index 906be56cbc0..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingBanners/ClaimBanner/ClaimBanner.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import React from 'react';
-import Banner, {
- BannerAlertSeverity,
- BannerVariant,
-} from '../../../../../../../component-library/components/Banners/Banner';
-import Text, {
- TextVariant,
- TextColor,
-} from '../../../../../../../component-library/components/Texts/Text';
-import { strings } from '../../../../../../../../locales/i18n';
-import Button, {
- ButtonVariants,
-} from '../../../../../../../component-library/components/Buttons/Button';
-import useTooltipModal from '../../../../../../hooks/useTooltipModal';
-import { BannerProps } from '../../../../../../../component-library/components/Banners/Banner/Banner.types';
-import { useStyles } from '../../../../../../../component-library/hooks';
-import styleSheet from './ClaimBanner.styles';
-
-type StakeBannerProps = Pick & {
- claimableAmount: string;
-};
-
-const ClaimBanner = ({ claimableAmount, style }: StakeBannerProps) => {
- const { styles } = useStyles(styleSheet, {});
-
- const { openTooltipModal } = useTooltipModal();
-
- const onClaimPress = () => openTooltipModal('TODO', 'Connect to claim flow');
-
- return (
-
-
- {strings('stake.banner_text.has_claimable_eth', {
- amountEth: claimableAmount,
- })}
-
-
- {strings('stake.claim')} ETH
-
- }
- onPress={onClaimPress}
- />
- >
- }
- />
- );
-};
-
-export default ClaimBanner;
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingBanners/UnstakeBanner/UnstakeBanner.tsx b/app/components/UI/Stake/components/StakingBalance/StakingBanners/UnstakeBanner/UnstakeBanner.tsx
deleted file mode 100644
index 42941ec3a59..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingBanners/UnstakeBanner/UnstakeBanner.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react';
-import Banner, {
- BannerAlertSeverity,
- BannerVariant,
-} from '../../../../../../../component-library/components/Banners/Banner';
-import Text from '../../../../../../../component-library/components/Texts/Text';
-import { renderUnstakingTimeRemaining } from './utils';
-import { BannerProps } from '../../../../../../../component-library/components/Banners/Banner/Banner.types';
-
-export type UnstakingBannerProps = Pick & {
- timeRemaining: {
- days: number;
- hours: number;
- minutes: number;
- };
- amountEth: string;
-};
-
-const UnstakingBanner = ({
- timeRemaining,
- amountEth,
- style,
-}: UnstakingBannerProps) => (
- {renderUnstakingTimeRemaining(timeRemaining, amountEth)}
- }
- />
-);
-
-export default UnstakingBanner;
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingBanners/UnstakeBanner/utils/index.test.ts b/app/components/UI/Stake/components/StakingBalance/StakingBanners/UnstakeBanner/utils/index.test.ts
deleted file mode 100644
index a432837ce1a..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingBanners/UnstakeBanner/utils/index.test.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-import { renderUnstakingTimeRemaining } from '.';
-
-describe('Unstake Banner Utils', () => {
- describe('Renders the unstaking time remaining', () => {
- const MOCK_ETH_AMOUNT = '0.0012';
-
- it('returns default text when days and hours = 0', () => {
- const result = renderUnstakingTimeRemaining(
- { days: 0, hours: 0, minutes: 0 },
- MOCK_ETH_AMOUNT,
- );
-
- expect(result).toBe(
- `Unstaking ${MOCK_ETH_AMOUNT} ETH in progress. Come back later to claim it.`,
- );
- });
-
- it('returns "day" only when days = 1 and hours = 0', () => {
- const result = renderUnstakingTimeRemaining(
- { days: 1, hours: 0, minutes: 0 },
- MOCK_ETH_AMOUNT,
- );
-
- expect(result).toBe(
- `Unstaking ${MOCK_ETH_AMOUNT} ETH in progress. Come back in 1 day to claim it.`,
- );
- });
-
- it('returns "days" only when days > 1 and hours = 0', () => {
- const result = renderUnstakingTimeRemaining(
- { days: 2, hours: 0, minutes: 0 },
- MOCK_ETH_AMOUNT,
- );
-
- expect(result).toBe(
- `Unstaking ${MOCK_ETH_AMOUNT} ETH in progress. Come back in 2 days to claim it.`,
- );
- });
-
- it('returns "hour" only when days = 0 and hours = 1', () => {
- const result = renderUnstakingTimeRemaining(
- { days: 0, hours: 1, minutes: 0 },
- MOCK_ETH_AMOUNT,
- );
-
- expect(result).toBe(
- `Unstaking ${MOCK_ETH_AMOUNT} ETH in progress. Come back in 1 hour to claim it.`,
- );
- });
-
- it('returns "hours" only when days = 0 and hours > 1', () => {
- const result = renderUnstakingTimeRemaining(
- { days: 0, hours: 2, minutes: 0 },
- MOCK_ETH_AMOUNT,
- );
-
- expect(result).toBe(
- `Unstaking ${MOCK_ETH_AMOUNT} ETH in progress. Come back in 2 hours to claim it.`,
- );
- });
-
- it('returns "day" and "hour" text when days and hours = 1', () => {
- const result = renderUnstakingTimeRemaining(
- { days: 1, hours: 1, minutes: 0 },
- MOCK_ETH_AMOUNT,
- );
-
- expect(result).toBe(
- `Unstaking ${MOCK_ETH_AMOUNT} ETH in progress. Come back in 1 day and 1 hour to claim it.`,
- );
- });
-
- it('returns "days" and "hours" text when days and hours > 1', () => {
- const result = renderUnstakingTimeRemaining(
- { days: 5, hours: 2, minutes: 0 },
- MOCK_ETH_AMOUNT,
- );
-
- expect(result).toBe(
- `Unstaking ${MOCK_ETH_AMOUNT} ETH in progress. Come back in 5 days and 2 hours to claim it.`,
- );
- });
-
- it('returns "minutes" if days and hours = 0', () => {
- const result = renderUnstakingTimeRemaining(
- { days: 0, hours: 0, minutes: 35 },
- MOCK_ETH_AMOUNT,
- );
-
- expect(result).toBe(
- `Unstaking ${MOCK_ETH_AMOUNT} ETH in progress. Come back in approximately 35 minutes to claim it.`,
- );
- });
-
- it('return "minute" if days and hours = 0 and minutes = 1', () => {
- const result = renderUnstakingTimeRemaining(
- { days: 0, hours: 0, minutes: 1 },
- MOCK_ETH_AMOUNT,
- );
-
- expect(result).toBe(
- `Unstaking ${MOCK_ETH_AMOUNT} ETH in progress. Come back in approximately 1 minute to claim it.`,
- );
- });
- });
-});
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingBanners/UnstakeBanner/utils/index.ts b/app/components/UI/Stake/components/StakingBalance/StakingBanners/UnstakeBanner/utils/index.ts
deleted file mode 100644
index 5445df9825f..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingBanners/UnstakeBanner/utils/index.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { strings } from '../../../../../../../../../locales/i18n';
-
-/**
- * Allows for flexible rendering of unstaking banner countdown.
- *
- * Examples:
- * - Unstaking 0.0172 ETH in progress. Come back later to claim it. (days: 0, hours: 0) (default)
- * - Unstaking 0.0172 ETH in progress. Come back in 1 day to claim it. (days: 1, hours: 0).
- * - Unstaking 0.0172 ETH in progress. Come back in 1 hour to claim it. (days: 0, hours: 1).
- * - Unstaking 0.0172 ETH in progress. Come back in 2 days and 3 hours. (days: 2, hours: 3).
- */
-export const renderUnstakingTimeRemaining = (
- { days, hours, minutes }: { days: number; hours: number; minutes: number },
- amountEth: string,
-) => {
- if (!days && !hours && !minutes)
- return strings('stake.banner_text.unstaking_in_progress.default', {
- amountEth,
- });
-
- const baseCopy = strings('stake.banner_text.unstaking_in_progress.base', {
- amountEth,
- });
-
- const minuteString = minutes
- ? `${strings('stake.banner_text.approximately')} ${minutes} ${strings(
- 'stake.minute',
- {
- count: minutes,
- },
- )}`
- : '';
-
- const dayString = days
- ? `${days} ${strings('stake.day', { count: days })}`
- : '';
-
- const hourString = hours
- ? `${hours} ${strings('stake.hour', { count: hours })}`
- : '';
-
- const andString =
- days && hours
- ? ` ${strings('stake.banner_text.unstaking_in_progress.and')} `
- : '';
-
- const toClaimString = strings(
- 'stake.banner_text.unstaking_in_progress.to_claim_it',
- );
-
- if (!days && !hours && minutes) {
- return `${baseCopy} ${minuteString} ${toClaimString}`.trim();
- }
-
- return `${baseCopy} ${dayString}${andString}${hourString} ${toClaimString}`.trim();
-};
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingButtons/StakingButtons.styles.tsx b/app/components/UI/Stake/components/StakingBalance/StakingButtons/StakingButtons.styles.tsx
deleted file mode 100644
index 57448c632eb..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingButtons/StakingButtons.styles.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-const styleSheet = () =>
- StyleSheet.create({
- balanceButtonsContainer: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- gap: 8,
- },
- balanceActionButton: {
- flex: 1,
- },
- });
-
-export default styleSheet;
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingButtons/StakingButtons.tsx b/app/components/UI/Stake/components/StakingBalance/StakingButtons/StakingButtons.tsx
deleted file mode 100644
index 5b843165cf0..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingButtons/StakingButtons.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import React, { useState } from 'react';
-import Button, {
- ButtonVariants,
-} from '../../../../../../component-library/components/Buttons/Button';
-import { strings } from '../../../../../../../locales/i18n';
-import { View, ViewProps } from 'react-native';
-import { useStyles } from '../../../../../../component-library/hooks';
-import styleSheet from './StakingButtons.styles';
-import useTooltipModal from '../../../../../hooks/useTooltipModal';
-import { useNavigation } from '@react-navigation/native';
-
-interface StakingButtonsProps extends Pick {}
-
-const StakingButtons = ({ style }: StakingButtonsProps) => {
- const [hasStakedPosition] = useState(true);
- const [hasEthToUnstake] = useState(true);
-
- const { openTooltipModal } = useTooltipModal();
-
- const { navigate } = useNavigation();
-
- const { styles } = useStyles(styleSheet, {});
-
- // TODO: Connect to unstaking flow when it's ready
- const onUnstakePress = () =>
- openTooltipModal('TODO', 'Connect to unstaking flow');
-
- const onStakePress = () => navigate('Stake');
-
- return (
-
- {hasEthToUnstake && (
-
- )}
-
-
- );
-};
-
-export default StakingButtons;
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.styles.tsx b/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.styles.tsx
deleted file mode 100644
index 5c635d99dc0..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.styles.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-const styleSheet = () =>
- StyleSheet.create({
- title: {
- paddingBottom: 16,
- paddingTop: 14,
- },
- contentMain: {
- flexDirection: 'row',
- flexWrap: 'wrap',
- },
- rightPad: {
- paddingRight: 3,
- },
- });
-
-export default styleSheet;
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.test.tsx b/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.test.tsx
deleted file mode 100644
index 8415d7bd14e..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.test.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import React from 'react';
-import { screen, render } from '@testing-library/react-native';
-import StakingCta from './StakingCta';
-
-const mockNavigate = jest.fn();
-
-jest.mock('@react-navigation/native', () => {
- const actualReactNavigation = jest.requireActual('@react-navigation/native');
- return {
- ...actualReactNavigation,
- useNavigation: () => ({
- navigate: mockNavigate,
- }),
- };
-});
-describe('StakingCta', () => {
- it('render matches snapshot', () => {
- render();
- expect(screen.toJSON()).toMatchSnapshot();
- });
-});
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.tsx b/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.tsx
deleted file mode 100644
index 1ab816b649d..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import { View, ViewProps } from 'react-native';
-import { useStyles } from '../../../../../../component-library/hooks';
-import styleSheet from './StakingCta.styles';
-import Text, {
- TextVariant,
- TextColor,
-} from '../../../../../../component-library/components/Texts/Text';
-import Button, {
- ButtonVariants,
-} from '../../../../../../component-library/components/Buttons/Button';
-import { strings } from '../../../../../../../locales/i18n';
-import { useNavigation } from '@react-navigation/native';
-import Routes from '../../../../../../constants/navigation/Routes';
-
-interface StakingCtaProps extends Pick {
- estimatedRewardRate: string;
-}
-
-const StakingCta = ({ estimatedRewardRate, style }: StakingCtaProps) => {
- const { styles } = useStyles(styleSheet, {});
-
- const { navigate } = useNavigation();
-
- const navigateToLearnMoreModal = () =>
- navigate('StakeModals', {
- screen: Routes.STAKING.MODALS.LEARN_MORE,
- });
-
- return (
-
-
- {strings('stake.stake_eth_and_earn')}
-
-
-
- {strings('stake.stake_your_eth_cta.base')}
-
- {estimatedRewardRate}
-
- {strings('stake.stake_your_eth_cta.annually')}
-
-
-
-
- );
-};
-
-export default StakingCta;
diff --git a/app/components/UI/Stake/components/StakingBalance/StakingCta/__snapshots__/StakingCta.test.tsx.snap b/app/components/UI/Stake/components/StakingBalance/StakingCta/__snapshots__/StakingCta.test.tsx.snap
deleted file mode 100644
index 82348eab2d1..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/StakingCta/__snapshots__/StakingCta.test.tsx.snap
+++ /dev/null
@@ -1,114 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`StakingCta render matches snapshot 1`] = `
-
-
- Stake ETH and earn
-
-
-
- Stake your ETH with MetaMask Pool and earn
-
-
- 2.6%
-
-
- annually.
-
-
-
- Learn more.
-
-
-
-
-`;
diff --git a/app/components/UI/Stake/components/StakingBalance/__snapshots__/StakingBalance.test.tsx.snap b/app/components/UI/Stake/components/StakingBalance/__snapshots__/StakingBalance.test.tsx.snap
deleted file mode 100644
index 1d017c20f6b..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/__snapshots__/StakingBalance.test.tsx.snap
+++ /dev/null
@@ -1,924 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`StakingBalance render matches snapshot 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
- Asset
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Staked Ethereum
-
-
-
- $13,292.20
-
-
- 4.9999 ETH
-
-
-
-
-
-
-
-
-
-
- Unstaking 0.0010 ETH in progress. Come back in 11 days to claim it.
-
-
-
-
-
-
-
-
-
- You can claim 0.00214 ETH. Once claimed, you'll get ETH back in your wallet.
-
-
-
- Claim
- ETH
-
-
-
-
-
-
- Stake ETH and earn
-
-
-
- Stake your ETH with MetaMask Pool and earn
-
-
- 2.9%
-
-
- annually.
-
-
-
- Learn more.
-
-
-
-
-
-
-
- Unstake
-
-
-
-
- Stake more
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-`;
diff --git a/app/components/UI/Stake/components/StakingBalance/mockData.ts b/app/components/UI/Stake/components/StakingBalance/mockData.ts
deleted file mode 100644
index ae8b3cdf39a..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/mockData.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import { TokenI } from '../../../../UI/Tokens/types';
-import {
- GetStakesApiResponse,
- GetVaultDataApiResponse,
-} from './StakingBalance.types';
-
-// TODO: Replace mock data when connecting to backend.
-export const MOCK_STAKED_ETH_ASSET = {
- balance: '4.9999 ETH',
- balanceFiat: '$13,292.20',
- name: 'Staked Ethereum',
- symbol: 'ETH',
-} as TokenI;
-
-// TODO: Replace mock data when connecting to backend.
-export const MOCK_GET_POOLED_STAKES_API_RESPONSE: GetStakesApiResponse = {
- accounts: [
- {
- account: '0x0123456789abcdef0123456789abcdef01234567',
- lifetimeRewards: '43927049303048',
- assets: '17913326707142320',
- exitRequests: [
- {
- // Unstaking
- positionTicket: '2153260738145148336740',
- timestamp: '1727110415000',
- totalShares: '989278156820374',
- withdrawalTimestamp: null,
- exitQueueIndex: '-1',
- claimedAssets: null,
- leftShares: null,
- },
- // Requests below are claimable.
- {
- positionTicket: '515964521392314631201',
- timestamp: '1720539311000',
- totalShares: '99473618267007',
- withdrawalTimestamp: '0',
- exitQueueIndex: '57',
- claimedAssets: '100006626507361',
- leftShares: '0',
- },
- {
- positionTicket: '515964620865932898208',
- timestamp: '1720541495000',
- totalShares: '99473618267007',
- withdrawalTimestamp: '0',
- exitQueueIndex: '57',
- claimedAssets: '100006626507361',
- leftShares: '0',
- },
- {
- positionTicket: '516604671289934191921',
- timestamp: '1720607327000',
- totalShares: '1929478758729790',
- withdrawalTimestamp: '0',
- exitQueueIndex: '58',
- claimedAssets: '1939870510970987',
- leftShares: '0',
- },
- ],
- },
- ],
- exchangeRate: '1.010906701603882254',
-};
-
-// TODO: See if this data is available yet. If not, mock backend response.
-export const MOCK_GET_VAULT_RESPONSE: GetVaultDataApiResponse = {
- apy: '2.853065141088762750393474836309926',
- capacity:
- '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456',
- feePercent: 1500,
- totalAssets: '7723070453364602130892',
- vaultAddress: '0x0a1b2c3d4e5f6a7b8c9dabecfd0123456789abcd',
-};
diff --git a/app/components/UI/Stake/components/StakingBalance/utils/index.ts b/app/components/UI/Stake/components/StakingBalance/utils/index.ts
deleted file mode 100644
index 45d74039ff7..00000000000
--- a/app/components/UI/Stake/components/StakingBalance/utils/index.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import {
- ExitRequestWithClaimedAssetInfo,
- UnstakingRequest,
-} from '../StakingBalance.types';
-import bn from 'bignumber.js';
-
-// An exitQueueIndex of -1 means that the request has not yet entered the exit queue
-// A leftShares value of 0 means that all shares have exited the exit queue and are therefore claimable
-// A leftShares value of 1 is due to a rounding error and means the same as a value of 0
-export const isExitRequestClaimable = (
- exitRequest: ExitRequestWithClaimedAssetInfo,
-) =>
- exitRequest.exitQueueIndex !== '-1' &&
- (exitRequest.leftShares === '0' || exitRequest.leftShares === '1');
-
-export const filterExitRequests = (
- exitRequests: ExitRequestWithClaimedAssetInfo[],
- exchangeRate: string,
-) =>
- exitRequests.reduce<{
- unstakingRequests: UnstakingRequest[];
- claimableRequests: ExitRequestWithClaimedAssetInfo[];
- }>(
- (acc, request) => {
- if (isExitRequestClaimable(request)) {
- acc.claimableRequests.push(request);
- return acc;
- }
-
- // determine current asset value of withdrawing or partially withdrawn request
- const assetsToDisplay = request.claimedAssets
- ? new bn(request.leftShares ?? 0)
- .multipliedBy(exchangeRate)
- .plus(request.claimedAssets)
- : new bn(request.totalShares).multipliedBy(exchangeRate);
-
- acc.unstakingRequests.push({
- ...request,
- assetsToDisplay: assetsToDisplay.toString(),
- });
- return acc;
- },
- { unstakingRequests: [], claimableRequests: [] },
- );
diff --git a/app/components/UI/Stake/components/images/LearnMoreEthBanner.png b/app/components/UI/Stake/components/images/LearnMoreEthBanner.png
deleted file mode 100644
index 8304e61cf61..00000000000
Binary files a/app/components/UI/Stake/components/images/LearnMoreEthBanner.png and /dev/null differ
diff --git a/app/components/UI/Stake/constants/index.ts b/app/components/UI/Stake/constants/index.ts
index 4d59090ccc0..776a25a8d76 100644
--- a/app/components/UI/Stake/constants/index.ts
+++ b/app/components/UI/Stake/constants/index.ts
@@ -1,6 +1,3 @@
/* eslint-disable import/prefer-default-export */
export const isPooledStakingFeatureEnabled = () =>
process.env.MM_POOLED_STAKING_UI_ENABLED === 'true';
-
-export const POOLED_STAKING_FAQ_URL =
- 'https://support.metamask.io/metamask-portfolio/move-crypto/stake/staking-pool/';
diff --git a/app/components/UI/Stake/routes/index.tsx b/app/components/UI/Stake/routes/index.tsx
deleted file mode 100644
index 372547838b4..00000000000
--- a/app/components/UI/Stake/routes/index.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import React from 'react';
-import { createStackNavigator } from '@react-navigation/stack';
-import StakeInputView from '../Views/InputView/StakeInputView';
-import LearnMoreModal from '../components/LearnMoreModal';
-import Routes from '../../../../constants/navigation/Routes';
-const Stack = createStackNavigator();
-const ModalStack = createStackNavigator();
-
-const clearStackNavigatorOptions = {
- headerShown: false,
- cardStyle: {
- backgroundColor: 'transparent',
- },
- animationEnabled: false,
-};
-
-// Regular Stack for Screens
-const StakeScreenStack = () => (
-
-
-
-);
-
-// Modal Stack for Modals
-const StakeModalStack = () => (
-
-
-
-);
-
-export { StakeScreenStack, StakeModalStack };
diff --git a/app/components/UI/Stake/utils/bignumber/bignumber.test.ts b/app/components/UI/Stake/utils/bignumber/bignumber.test.ts
deleted file mode 100644
index d2f3f98be8d..00000000000
--- a/app/components/UI/Stake/utils/bignumber/bignumber.test.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-import BigNumber from 'bignumber.js';
-import { getPowerOfTen, multiplyValueByPowerOfTen } from './index';
-
-describe('bignumber utils', () => {
- describe('getPowerOfTen', () => {
- it('handles a number of inputs and returns a BigNumber', () => {
- expect(getPowerOfTen(0)).toEqual(new BigNumber(1));
- expect(getPowerOfTen(1)).toEqual(new BigNumber(10));
- expect(getPowerOfTen(2)).toEqual(new BigNumber(100));
- expect(getPowerOfTen(3)).toEqual(new BigNumber(1000));
- expect(getPowerOfTen(-1)).toEqual(new BigNumber(0.1));
- expect(getPowerOfTen(-2)).toEqual(new BigNumber(0.01));
- expect(getPowerOfTen(-3)).toEqual(new BigNumber(0.001));
- expect(getPowerOfTen(Number.POSITIVE_INFINITY)).toEqual(
- new BigNumber('Infinity'),
- );
- expect(getPowerOfTen(Number.NEGATIVE_INFINITY)).toEqual(new BigNumber(0));
- });
- });
- describe('multiplyValueByPowerOfTen', () => {
- it('multiplies small numbers by the power of ten provided and returns a BigNumber', () => {
- const input = 0.000004577280038473;
- expect(multiplyValueByPowerOfTen(input, 0)).toEqual(new BigNumber(input));
- expect(multiplyValueByPowerOfTen(input, 1)).toEqual(
- new BigNumber(0.00004577280038473),
- );
- expect(multiplyValueByPowerOfTen(input, 2)).toEqual(
- new BigNumber(0.0004577280038473),
- );
- expect(multiplyValueByPowerOfTen(input, 3)).toEqual(
- new BigNumber(0.004577280038473),
- );
- expect(multiplyValueByPowerOfTen(input, 10)).toEqual(
- new BigNumber(45772.80038473),
- );
- expect(multiplyValueByPowerOfTen(input, 30)).toEqual(
- new BigNumber('4577280038473000000000000'),
- );
- expect(
- multiplyValueByPowerOfTen(input, Number.POSITIVE_INFINITY),
- ).toEqual(new BigNumber('Infinity'));
- expect(multiplyValueByPowerOfTen(input, -1)).toEqual(
- new BigNumber(0.0000004577280038473),
- );
- expect(multiplyValueByPowerOfTen(input, -10)).toEqual(
- new BigNumber(0.0000000000000004577280038473),
- );
- expect(
- multiplyValueByPowerOfTen(input, Number.NEGATIVE_INFINITY),
- ).toEqual(new BigNumber(0));
- });
- it('multiplies large numbers by the power of ten provided', () => {
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- const input = 393859854789998.4883;
- expect(multiplyValueByPowerOfTen(input, 0)).toEqual(new BigNumber(input));
-
- expect(multiplyValueByPowerOfTen(input, 1)).toEqual(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- new BigNumber(3938598547899984.883),
- );
-
- expect(multiplyValueByPowerOfTen(input, 2)).toEqual(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- new BigNumber(39385985478999848.83),
- );
-
- expect(multiplyValueByPowerOfTen(input, 10)).toEqual(
- new BigNumber('3.938598547899985e+24'),
- );
- expect(multiplyValueByPowerOfTen(input, 30)).toEqual(
- new BigNumber('3.938598547899985e+44'),
- );
- expect(
- multiplyValueByPowerOfTen(input, Number.POSITIVE_INFINITY),
- ).toEqual(new BigNumber(Infinity));
- expect(multiplyValueByPowerOfTen(input, -1)).toEqual(
- new BigNumber('39385985478999.85'),
- );
- expect(multiplyValueByPowerOfTen(input, -10)).toEqual(
- new BigNumber('39385.98547899985'),
- );
- expect(
- multiplyValueByPowerOfTen(input, Number.NEGATIVE_INFINITY),
- ).toEqual(new BigNumber(0));
- });
- it('multiplies zero to the power of ten provided and returns a BigNumber', () => {
- expect(multiplyValueByPowerOfTen(0, 10)).toEqual(new BigNumber(0));
- expect(multiplyValueByPowerOfTen(0, 0)).toEqual(new BigNumber(0));
- expect(multiplyValueByPowerOfTen(0, -10)).toEqual(new BigNumber(0));
- expect(multiplyValueByPowerOfTen(0, Number.POSITIVE_INFINITY)).toEqual(
- new BigNumber(0),
- );
- expect(multiplyValueByPowerOfTen(0, Number.NEGATIVE_INFINITY)).toEqual(
- new BigNumber(0),
- );
- });
- it('multiplies infinity to the power of ten provided and returns a BigNumber', () => {
- const input = Number.POSITIVE_INFINITY;
- expect(multiplyValueByPowerOfTen(input, 10)).toEqual(
- new BigNumber('Infinity'),
- );
- expect(multiplyValueByPowerOfTen(input, 0)).toEqual(
- new BigNumber('Infinity'),
- );
- expect(multiplyValueByPowerOfTen(input, -10)).toEqual(
- new BigNumber('Infinity'),
- );
- expect(
- multiplyValueByPowerOfTen(input, Number.POSITIVE_INFINITY),
- ).toEqual(new BigNumber(Number.POSITIVE_INFINITY));
- expect(
- multiplyValueByPowerOfTen(input, Number.NEGATIVE_INFINITY),
- ).toEqual(new BigNumber(0));
- });
- it('multiplies negative infinity to the power of ten provided and returns a BigNumber', () => {
- const input = Number.NEGATIVE_INFINITY;
- expect(multiplyValueByPowerOfTen(input, 10)).toEqual(
- new BigNumber(Number.NEGATIVE_INFINITY),
- );
- expect(multiplyValueByPowerOfTen(input, 0)).toEqual(
- new BigNumber(Number.NEGATIVE_INFINITY),
- );
- expect(multiplyValueByPowerOfTen(input, -10)).toEqual(
- new BigNumber(Number.NEGATIVE_INFINITY),
- );
- expect(
- multiplyValueByPowerOfTen(input, Number.POSITIVE_INFINITY),
- ).toEqual(new BigNumber(Number.NEGATIVE_INFINITY));
- expect(
- multiplyValueByPowerOfTen(input, Number.NEGATIVE_INFINITY),
- ).toEqual(new BigNumber(0));
- });
- });
-});
diff --git a/app/components/UI/Stake/utils/bignumber/index.ts b/app/components/UI/Stake/utils/bignumber/index.ts
deleted file mode 100644
index 480933c9b2b..00000000000
--- a/app/components/UI/Stake/utils/bignumber/index.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import BigNumber from 'bignumber.js';
-
-export enum BigNumberUtilsReturnFormat {
- NUMBER = 'NUMBER',
- BN = 'BN',
- STRING = 'STRING',
-}
-
-export type BigNumberUtilsReturnType = BigNumber | number | string;
-
-export const bnZero = new BigNumber(0);
-export const bnOne = new BigNumber(1);
-export const bnTen = new BigNumber(10);
-
-export const getPowerOfTen = (pow: number): BigNumber => bnTen.pow(pow);
-
-export const getValueAsBn = (value: BigNumber | string | number): BigNumber =>
- typeof value === 'string' || typeof value === 'number'
- ? new BigNumber(value)
- : value;
-
-export const multiplyValueByPowerOfTen = (
- value: BigNumber | string | number,
- pow: number,
-): BigNumber => {
- const valueAsBn = getValueAsBn(value);
- const power = getPowerOfTen(pow);
-
- let override;
- // 0 * Number.POSITIVE_INFINITY is NaN, but this is a weird outcome so let's say it equals 0
- if (valueAsBn.eq(0) && power.eq(Number.POSITIVE_INFINITY)) override = bnZero;
- if (valueAsBn.eq(Number.POSITIVE_INFINITY) && power.eq(0)) override = bnZero;
- if (valueAsBn.eq(Number.NEGATIVE_INFINITY) && power.eq(0)) override = bnZero;
-
- const calculated = override || valueAsBn.multipliedBy(power);
- return calculated;
-};
diff --git a/app/components/UI/Stake/utils/value/index.ts b/app/components/UI/Stake/utils/value/index.ts
deleted file mode 100644
index 737f1462603..00000000000
--- a/app/components/UI/Stake/utils/value/index.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import BigNumber from 'bignumber.js';
-import {
- getPowerOfTen,
- bnOne,
- getValueAsBn,
- bnZero,
- multiplyValueByPowerOfTen,
-} from '../bignumber';
-
-export enum CommonPercentageInputUnits {
- BASIS_POINTS = 'BASIS_POINTS',
- DECIMALS = 'DECIMALS',
- PERCENTAGE = 'PERCENTAGE',
-}
-
-export enum PercentageOutputFormat {
- BASIS_POINTS = 'BASIS_POINTS',
- DECIMAL = 'DECIMAL',
- PERCENT_SIGN = 'PERCENT_SIGN',
-}
-
-// 1bp = 0.01% = 0.0001 -> 10bp = 0.1% = 0.001 -> 100bp = 1% = 0.01
-const CommonPercentageInputUnitsAsE: Record<
- CommonPercentageInputUnits,
- number
-> = {
- [CommonPercentageInputUnits.BASIS_POINTS]: 2, // 100 bp
- [CommonPercentageInputUnits.DECIMALS]: -2, // .01
- [CommonPercentageInputUnits.PERCENTAGE]: 0, // 1% or 1 percentage point
-};
-
-const percentageOutputE = {
- [PercentageOutputFormat.BASIS_POINTS]:
- CommonPercentageInputUnitsAsE[CommonPercentageInputUnits.BASIS_POINTS],
- [PercentageOutputFormat.DECIMAL]:
- CommonPercentageInputUnitsAsE[CommonPercentageInputUnits.DECIMALS],
- [PercentageOutputFormat.PERCENT_SIGN]:
- CommonPercentageInputUnitsAsE[CommonPercentageInputUnits.PERCENTAGE],
-};
-
-// Determine if a value is equal to or above an evaluated `exponent`, or equal to or below its evaluated inverse
-export const isEqualOrGreaterOrderOfMagnitude = (
- value: BigNumber,
- exponent: number,
-): boolean => {
- if (value.eq(bnZero)) return false;
- return (
- (value.gte(bnOne) && value.gte(getPowerOfTen(exponent))) ||
- value.lte(bnOne.dividedBy(getPowerOfTen(exponent)))
- );
-};
-
-// Fix a value to a `fixed` value of decimal places either in number or scientific notation depending on the `exponentLimit`
-export const fixDisplayAmount = (
- value: BigNumber | string | number,
- fixed = 2,
- exponentLimit = 21,
- roundingMode: BigNumber.RoundingMode = BigNumber.ROUND_DOWN,
-): string => {
- const valueAsBn = getValueAsBn(value);
- const absoluteExponent = Math.abs(exponentLimit);
- if (isEqualOrGreaterOrderOfMagnitude(valueAsBn, absoluteExponent)) {
- return valueAsBn.toExponential(fixed);
- }
- return valueAsBn.toFixed(fixed, roundingMode);
-};
-
-export const formatPercent = (
- value: string | number,
- {
- inputFormat,
- outputFormat,
- fixed,
- }: {
- inputFormat: CommonPercentageInputUnits | number;
- outputFormat: PercentageOutputFormat;
- fixed?: number;
- },
-): string => {
- if (typeof fixed === 'number' && fixed < 0) {
- throw new Error(
- `Cannot convert a number to negative number of fixed places. Tried: ${fixed}`,
- );
- }
-
- const inputE =
- typeof inputFormat === 'number'
- ? inputFormat
- : CommonPercentageInputUnitsAsE[inputFormat];
- const scientificNotation = multiplyValueByPowerOfTen(value, inputE * -1);
- const displayE = percentageOutputE[outputFormat];
- const resultValue = multiplyValueByPowerOfTen(scientificNotation, displayE);
- const displayValue =
- typeof fixed === 'number'
- ? resultValue.toFixed(fixed, BigNumber.ROUND_HALF_UP)
- : resultValue.toString();
-
- switch (outputFormat) {
- case PercentageOutputFormat.BASIS_POINTS:
- return `${displayValue} ${
- new BigNumber(displayValue).abs().eq(1) ? 'bp' : 'bps'
- }`;
- case PercentageOutputFormat.DECIMAL:
- return displayValue;
- case PercentageOutputFormat.PERCENT_SIGN:
- default:
- return `${displayValue}%`;
- }
-};
diff --git a/app/components/UI/Stake/utils/value/value.test.ts b/app/components/UI/Stake/utils/value/value.test.ts
deleted file mode 100644
index d78cea2c15f..00000000000
--- a/app/components/UI/Stake/utils/value/value.test.ts
+++ /dev/null
@@ -1,1905 +0,0 @@
-import bn from 'bignumber.js';
-import {
- CommonPercentageInputUnits,
- PercentageOutputFormat,
- fixDisplayAmount,
- formatPercent,
- isEqualOrGreaterOrderOfMagnitude,
-} from './index';
-
-describe('fixDisplayAmount', () => {
- it('handles different input formats and returns a string', () => {
- expect(fixDisplayAmount(5)).toBe('5.00');
- expect(fixDisplayAmount('5')).toBe('5.00');
- expect(fixDisplayAmount(new bn(5))).toBe('5.00');
- });
- it('rounds number to 4 decimals when decimals is set to 4', () => {
- expect(fixDisplayAmount(5.45446, 4)).toBe('5.4544');
- expect(fixDisplayAmount('5.45446', 4)).toBe('5.4544');
- expect(fixDisplayAmount(new bn(5.45446), 4)).toBe('5.4544');
- });
- it('fixes decimals regardless of trailing zeroes', () => {
- expect(fixDisplayAmount('5.40000', 2)).toBe('5.40');
- expect(fixDisplayAmount(new bn('5.40000'), 2)).toBe('5.40');
- });
- it('fixes exponential decimals in scientific notation when input is over default evaluated exponent', () => {
- expect(fixDisplayAmount(1000000000000000000000, 2)).toBe('1.00e+21');
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- expect(fixDisplayAmount(1234567891234567891234, 2)).toBe('1.23e+21');
- expect(fixDisplayAmount('1234567891234567891234', 2)).toBe('1.23e+21');
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- expect(fixDisplayAmount(new bn(1234567891234567891234), 2)).toBe(
- '1.23e+21',
- );
- });
- it('fixes exponential decimals in scientific notation when input is over custom evaluated exponent', () => {
- expect(fixDisplayAmount(100000, 2, 5)).toBe('1.00e+5');
- expect(fixDisplayAmount(123456, 2, 5)).toBe('1.23e+5');
- expect(fixDisplayAmount('123456', 2, 5)).toBe('1.23e+5');
- expect(fixDisplayAmount(new bn(123456), 2, 5)).toBe('1.23e+5');
- });
- it('fixes decimals in scientific notation when decimal input is below 1 but over default inverse evaluated exponent', () => {
- expect(fixDisplayAmount(0.123, 2)).toBe('0.12');
- expect(fixDisplayAmount('0.123', 2)).toBe('0.12');
- expect(fixDisplayAmount(new bn(0.123), 2)).toBe('0.12');
- });
- it('fixes exponential decimals in scientific notation when decimal input is under custom inverse evaluated exponent', () => {
- expect(fixDisplayAmount(0.001, 2, 3)).toBe('1.00e-3');
- expect(fixDisplayAmount(0.0001234, 2, 3)).toBe('1.23e-4');
- expect(fixDisplayAmount('0.0001234', 2, 3)).toBe('1.23e-4');
- expect(fixDisplayAmount(new bn(0.0001234), 2, 3)).toBe('1.23e-4');
- });
- it('defaults to rounding down', () => {
- expect(fixDisplayAmount('210.626398728671935147', 4, undefined)).toBe(
- '210.6263',
- );
- });
- it('obeys a passed rounding instruction', () => {
- expect(
- fixDisplayAmount(
- '210.626398728671935147',
- 4,
- undefined,
- bn.ROUND_HALF_UP,
- ),
- ).toBe('210.6264');
- });
-});
-
-describe('isEqualOrGreaterOrderOfMagnitude', () => {
- it('returns true when value is larger than evaluated exponent', () => {
- expect(isEqualOrGreaterOrderOfMagnitude(new bn(1000), 2)).toBe(true);
- });
- it('returns true when value is same order of magnitude to evaluated exponent', () => {
- expect(isEqualOrGreaterOrderOfMagnitude(new bn(100), 2)).toBe(true);
- expect(isEqualOrGreaterOrderOfMagnitude(new bn(0.01), 2)).toBe(true);
- });
- it('returns true when value is smaller than inverse evaluated exponent', () => {
- expect(isEqualOrGreaterOrderOfMagnitude(new bn(0.001), 2)).toBe(true);
- });
- it('returns false when value is 1 or larger but smaller than evaluated exponent', () => {
- expect(isEqualOrGreaterOrderOfMagnitude(new bn(1), 2)).toBe(false);
- });
- it('returns false when value is smaller than 1 but larger than inverse evaluated exponent', () => {
- expect(isEqualOrGreaterOrderOfMagnitude(new bn(0.1), 2)).toBe(false);
- });
-});
-
-describe('formatPercent', () => {
- it('converts basis points provided as a number to a formatted basis point string', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('0 bps');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('Infinity bps');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-Infinity bps');
-
- expect(
- formatPercent(1, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('1 bp');
-
- expect(
- formatPercent(-1, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-1 bp');
-
- expect(
- formatPercent(2, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('2 bps');
-
- expect(
- formatPercent(200, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('200 bps');
-
- expect(
- formatPercent(-200, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-200 bps');
-
- expect(
- formatPercent(0.92739292, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('0.92739292 bps');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-475478383.388298292, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-475478383.3882983 bps');
- });
-
- it('converts basis points provided as a string to a formatted basis point string', () => {
- expect(
- formatPercent('0', {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('0 bps');
-
- expect(
- formatPercent('Infinity', {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('Infinity bps');
-
- expect(
- formatPercent('-Infinity', {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-Infinity bps');
-
- expect(
- formatPercent('1', {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('1 bp');
-
- expect(
- formatPercent('-1', {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-1 bp');
-
- expect(
- formatPercent('2', {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('2 bps');
-
- expect(
- formatPercent('200', {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('200 bps');
-
- expect(
- formatPercent('-200', {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-200 bps');
-
- expect(
- formatPercent('0.92739292', {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('0.92739292 bps');
-
- expect(
- formatPercent('-475478383.388298292', {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-475478383.388298292 bps');
- });
-
- it('converts basis points provided as a number to a formatted basis point string with a fixed number of decimals', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 2,
- }),
- ).toEqual('0.00 bps');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 2,
- }),
- ).toEqual('Infinity bps');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 2,
- }),
- ).toEqual('-Infinity bps');
-
- expect(
- formatPercent(1, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 2,
- }),
- ).toEqual('1.00 bp');
-
- expect(
- formatPercent(-1, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 2,
- }),
- ).toEqual('-1.00 bp');
-
- expect(
- formatPercent(2, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 2,
- }),
- ).toEqual('2.00 bps');
-
- expect(
- formatPercent(200, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 2,
- }),
- ).toEqual('200.00 bps');
-
- expect(
- formatPercent(-200, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 2,
- }),
- ).toEqual('-200.00 bps');
-
- expect(
- formatPercent(0.92739292, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 2,
- }),
- ).toEqual('0.93 bps');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-475478383.388298292, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 2,
- }),
- ).toEqual('-475478383.39 bps');
- });
-
- it('converts basis points provided as a string to a formatted basis point string with a fixed number of decimals', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 0,
- }),
- ).toEqual('0 bps');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 0,
- }),
- ).toEqual('Infinity bps');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 0,
- }),
- ).toEqual('-Infinity bps');
-
- expect(
- formatPercent(1, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 0,
- }),
- ).toEqual('1 bp');
-
- expect(
- formatPercent(-1, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 0,
- }),
- ).toEqual('-1 bp');
-
- expect(
- formatPercent(2, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 0,
- }),
- ).toEqual('2 bps');
-
- expect(
- formatPercent(200, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 0,
- }),
- ).toEqual('200 bps');
-
- expect(
- formatPercent(-200, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 0,
- }),
- ).toEqual('-200 bps');
-
- expect(
- formatPercent(0.92739292, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 0,
- }),
- ).toEqual('1 bp');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-475478383.388298292, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 0,
- }),
- ).toEqual('-475478383 bps');
- });
-
- it('converts decimals provided as a number to a formatted basis point string', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('0 bps');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('Infinity bps');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-Infinity bps');
-
- expect(
- formatPercent(0.0001, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('1 bp');
-
- expect(
- formatPercent(-0.0001, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-1 bp');
-
- expect(
- formatPercent(0.0002, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('2 bps');
-
- expect(
- formatPercent(0.01, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('100 bps');
-
- expect(
- formatPercent(-0.01, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-100 bps');
-
- expect(
- formatPercent(0.00092739292, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('9.2739292 bps');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-4754783833882.98292, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-47547838338829830 bps');
- });
-
- it('converts decimals provided as a string to a formatted basis point string', () => {
- expect(
- formatPercent('0', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('0 bps');
-
- expect(
- formatPercent('Infinity', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('Infinity bps');
-
- expect(
- formatPercent('-Infinity', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-Infinity bps');
-
- expect(
- formatPercent('0.0001', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('1 bp');
-
- expect(
- formatPercent('-0.0001', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-1 bp');
-
- expect(
- formatPercent('0.0002', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('2 bps');
-
- expect(
- formatPercent('0.01', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('100 bps');
-
- expect(
- formatPercent('-0.01', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-100 bps');
-
- expect(
- formatPercent('0.00092739292', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('9.2739292 bps');
-
- expect(
- formatPercent('-4754783833882.98292', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-47547838338829829.2 bps');
- });
-
- it('converts decimals provided as a number to a formatted basis point string with a fixed number of decimals', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('0.0 bps');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('Infinity bps');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('-Infinity bps');
-
- expect(
- formatPercent(0.0001, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('1.0 bp');
-
- expect(
- formatPercent(-0.0001, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('-1.0 bp');
-
- expect(
- formatPercent(0.0002, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('2.0 bps');
-
- expect(
- formatPercent(0.01, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('100.0 bps');
-
- expect(
- formatPercent(-0.01, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('-100.0 bps');
-
- expect(
- formatPercent(0.00092739292, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('9.3 bps');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-4754783833882.98292, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('-47547838338829830.0 bps');
- });
-
- it('converts decimals provided as a string to a formatted basis point string with a fixed number of decimals', () => {
- expect(
- formatPercent('0', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('0.0 bps');
-
- expect(
- formatPercent('Infinity', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('Infinity bps');
-
- expect(
- formatPercent('-Infinity', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('-Infinity bps');
-
- expect(
- formatPercent('0.0001', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('1.0 bp');
-
- expect(
- formatPercent('-0.0001', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('-1.0 bp');
-
- expect(
- formatPercent('0.0002', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('2.0 bps');
-
- expect(
- formatPercent('0.01', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('100.0 bps');
-
- expect(
- formatPercent('-0.01', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('-100.0 bps');
-
- expect(
- formatPercent('0.00092739292', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('9.3 bps');
-
- expect(
- formatPercent('-4754783833882.98292', {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 1,
- }),
- ).toEqual('-47547838338829829.2 bps');
- });
-
- it('converts a percentage provided as a number to a formatted basis point string', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('0 bps');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('Infinity bps');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-Infinity bps');
-
- expect(
- formatPercent(0.01, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('1 bp');
-
- expect(
- formatPercent(-0.01, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-1 bp');
-
- expect(
- formatPercent(0.02, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('2 bps');
-
- expect(
- formatPercent(1, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('100 bps');
-
- expect(
- formatPercent(-1, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-100 bps');
-
- expect(
- formatPercent(0.092739292, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('9.2739292 bps');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-475478383388298.292, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-47547838338829830 bps');
- });
-
- it('converts a percentage provided as a string to a formatted basis point string', () => {
- expect(
- formatPercent('0', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('0 bps');
-
- expect(
- formatPercent('Infinity', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('Infinity bps');
-
- expect(
- formatPercent('-Infinity', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-Infinity bps');
-
- expect(
- formatPercent('0.01', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('1 bp');
-
- expect(
- formatPercent('-0.01', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-1 bp');
-
- expect(
- formatPercent('0.02', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('2 bps');
-
- expect(
- formatPercent('1', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('100 bps');
-
- expect(
- formatPercent('-1', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-100 bps');
-
- expect(
- formatPercent('0.092739292', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('9.2739292 bps');
-
- expect(
- formatPercent('-475478383388298.292', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- }),
- ).toEqual('-47547838338829829.2 bps');
- });
-
- it('converts a percentage provided as a number to a formatted basis point string with a fixed number of decimals', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('0.0000 bps');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('Infinity bps');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('-Infinity bps');
-
- expect(
- formatPercent(0.01, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('1.0000 bp');
-
- expect(
- formatPercent(-0.01, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('-1.0000 bp');
-
- expect(
- formatPercent(0.02, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('2.0000 bps');
-
- expect(
- formatPercent(1, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('100.0000 bps');
-
- expect(
- formatPercent(-1, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('-100.0000 bps');
-
- expect(
- formatPercent(0.092739292, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('9.2739 bps');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-475478383388298.292, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('-47547838338829830.0000 bps');
- });
-
- it('converts a percentage provided as a string to a formatted basis point string with a fixed number of decimals', () => {
- expect(
- formatPercent('0', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('0.0000 bps');
-
- expect(
- formatPercent('Infinity', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('Infinity bps');
-
- expect(
- formatPercent('-Infinity', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('-Infinity bps');
-
- expect(
- formatPercent('0.01', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('1.0000 bp');
-
- expect(
- formatPercent('-0.01', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('-1.0000 bp');
-
- expect(
- formatPercent('0.02', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('2.0000 bps');
-
- expect(
- formatPercent('1', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('100.0000 bps');
-
- expect(
- formatPercent('-1', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('-100.0000 bps');
-
- expect(
- formatPercent('0.092739292', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('9.2739 bps');
-
- expect(
- formatPercent('-475478383388298.292', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.BASIS_POINTS,
- fixed: 4,
- }),
- ).toEqual('-47547838338829829.2000 bps');
- });
-
- it('converts basis points provided as a number to a formatted decimal', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('0');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('Infinity');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-Infinity');
-
- expect(
- formatPercent(1, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('0.0001');
-
- expect(
- formatPercent(-1, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-0.0001');
-
- expect(
- formatPercent(10, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('0.001');
-
- expect(
- formatPercent(100, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('0.01');
-
- expect(
- formatPercent(-1000, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-0.1');
-
- expect(
- formatPercent(2000, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('0.2');
-
- expect(
- formatPercent(20000, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('2');
-
- expect(
- formatPercent(-200000, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-20');
-
- expect(
- formatPercent(0.00092739292, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('9.2739292e-8');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-4754783833882.98292, {
- inputFormat: CommonPercentageInputUnits.BASIS_POINTS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-475478383.3882983');
- });
-
- it('converts decimals provided as a decimal to a formatted decimal', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('0');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('Infinity');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-Infinity');
-
- expect(
- formatPercent(1, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('1');
-
- expect(
- formatPercent(-1, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-1');
-
- expect(
- formatPercent(2, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('2');
-
- expect(
- formatPercent(100, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('100');
-
- expect(
- formatPercent(-100, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-100');
-
- expect(
- formatPercent(0.000000092739292, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('9.2739292e-8');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-47547.8383388298292, {
- inputFormat: CommonPercentageInputUnits.DECIMALS,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-47547.83833882983');
- });
-
- it('converts a percentage provided as a number to a formatted decimal', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('0');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('Infinity');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-Infinity');
-
- expect(
- formatPercent(100, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('1');
-
- expect(
- formatPercent(-100, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-1');
-
- expect(
- formatPercent(200, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('2');
-
- expect(
- formatPercent(10000, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('100');
-
- expect(
- formatPercent(-10000, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-100');
-
- expect(
- formatPercent(0.0000092739292, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('9.2739292e-8');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-4754783.83388298292, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-47547.83833882983');
- });
-
- it('converts a percentage provided as a string to a formatted decimal', () => {
- expect(
- formatPercent('0', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('0');
-
- expect(
- formatPercent('Infinity', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('Infinity');
-
- expect(
- formatPercent('-Infinity', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-Infinity');
-
- expect(
- formatPercent('100', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('1');
-
- expect(
- formatPercent('-100', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-1');
-
- expect(
- formatPercent('200', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('2');
-
- expect(
- formatPercent('10000', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('100');
-
- expect(
- formatPercent('-10000', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-100');
-
- expect(
- formatPercent('0.0000092739292', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('9.2739292e-8');
-
- expect(
- formatPercent('-4754783.83388298292', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-47547.8383388298292');
- });
-
- it('converts a percentage provided as a number to a formatted decimal with a fixed number of decimals', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('0');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('Infinity');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('-Infinity');
-
- expect(
- formatPercent(100, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('1');
-
- expect(
- formatPercent(-100, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('-1');
-
- expect(
- formatPercent(200, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('2');
-
- expect(
- formatPercent(10000, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('100');
-
- expect(
- formatPercent(-10000, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('-100');
-
- expect(
- formatPercent(0.0000092739292, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('0');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-4754783.83388298292, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('-47548');
- });
-
- it('converts a percentage provided as a string to a formatted decimal with a fixed number of decimals', () => {
- expect(
- formatPercent('0', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('0');
-
- expect(
- formatPercent('Infinity', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('Infinity');
-
- expect(
- formatPercent('-Infinity', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('-Infinity');
-
- expect(
- formatPercent('100', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('1');
-
- expect(
- formatPercent('-100', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('-1');
-
- expect(
- formatPercent('200', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('2');
-
- expect(
- formatPercent('10000', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('100');
-
- expect(
- formatPercent('-10000', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('-100');
-
- expect(
- formatPercent('0.0000092739292', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('0');
-
- expect(
- formatPercent('-4754783.83388298292', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: 0,
- }),
- ).toEqual('-47548');
- });
-
- it('converts a percentage provided as a number to a formatted percentage', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('0%');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('Infinity%');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('-Infinity%');
-
- expect(
- formatPercent(1, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('1%');
-
- expect(
- formatPercent(-1, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('-1%');
-
- expect(
- formatPercent(2, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('2%');
-
- expect(
- formatPercent(100, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('100%');
-
- expect(
- formatPercent(-100, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('-100%');
-
- expect(
- formatPercent(0.000000092739292, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('9.2739292e-8%');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-4754783.83388298292, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('-4754783.833882983%');
- });
-
- it('converts a percentage provided as a string to a formatted percentage', () => {
- expect(
- formatPercent('0', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('0%');
-
- expect(
- formatPercent('Infinity', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('Infinity%');
-
- expect(
- formatPercent('-Infinity', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('-Infinity%');
-
- expect(
- formatPercent('1', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('1%');
-
- expect(
- formatPercent('-1', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('-1%');
-
- expect(
- formatPercent('2', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('2%');
-
- expect(
- formatPercent('100.0000', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('100%');
-
- expect(
- formatPercent('-100', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('-100%');
-
- expect(
- formatPercent('0.000000092739292', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('9.2739292e-8%');
-
- expect(
- formatPercent('-4754783.83388298292', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- }),
- ).toEqual('-4754783.83388298292%');
- });
-
- it('converts a percentage provided as a number to a formatted percentage with a fixed number of decimals', () => {
- expect(
- formatPercent(0, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('0.0%');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('Infinity%');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('-Infinity%');
-
- expect(
- formatPercent(1, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('1.0%');
-
- expect(
- formatPercent(-1, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('-1.0%');
-
- expect(
- formatPercent(2, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('2.0%');
-
- expect(
- formatPercent(100, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('100.0%');
-
- expect(
- formatPercent(-100, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('-100.0%');
-
- expect(
- formatPercent(0.000000092739292, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('0.0%');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-4754783.83388298292, {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('-4754783.8%');
- });
-
- it('converts a percentage provided as a string to a formatted percentage with a fixed number of decimals', () => {
- expect(
- formatPercent('0', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('0.0%');
-
- expect(
- formatPercent('Infinity', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('Infinity%');
-
- expect(
- formatPercent('-Infinity', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('-Infinity%');
-
- expect(
- formatPercent('1.000', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('1.0%');
-
- expect(
- formatPercent('-1', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('-1.0%');
-
- expect(
- formatPercent('2', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('2.0%');
-
- expect(
- formatPercent('100', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('100.0%');
-
- expect(
- formatPercent('-100', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('-100.0%');
-
- expect(
- formatPercent('0.000000092739292', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('0.0%');
-
- expect(
- formatPercent('-4754783.83388298292', {
- inputFormat: CommonPercentageInputUnits.PERCENTAGE,
- outputFormat: PercentageOutputFormat.PERCENT_SIGN,
- fixed: 1,
- }),
- ).toEqual('-4754783.8%');
- });
-
- it('converts inputs with arbitrary formats to decimal format', () => {
- expect(
- formatPercent(0, {
- inputFormat: -1,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('0');
-
- expect(
- formatPercent(Number.POSITIVE_INFINITY, {
- inputFormat: -1,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('Infinity');
-
- expect(
- formatPercent(Number.NEGATIVE_INFINITY, {
- inputFormat: -1,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-Infinity');
-
- expect(
- formatPercent(10, {
- inputFormat: -1,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('1');
-
- expect(
- formatPercent(-10, {
- inputFormat: -1,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-1');
-
- expect(
- formatPercent(20, {
- inputFormat: -1,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('2');
-
- expect(
- formatPercent(1000, {
- inputFormat: -1,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('100');
-
- expect(
- formatPercent(-1000, {
- inputFormat: -1,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-100');
-
- expect(
- formatPercent(0.00000092739292, {
- inputFormat: -1,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('9.2739292e-8');
-
- expect(
- // disabling no-loss-of-precision because excessively precise inputs are possible
- // eslint-disable-next-line @typescript-eslint/no-loss-of-precision
- formatPercent(-475478.383388298292, {
- inputFormat: -1,
- outputFormat: PercentageOutputFormat.DECIMAL,
- }),
- ).toEqual('-47547.83833882983');
- });
-
- it('throws if provided a negative number as the decimal toFixed', () => {
- expect(() =>
- formatPercent(1000, {
- inputFormat: -1,
- outputFormat: PercentageOutputFormat.DECIMAL,
- fixed: -2,
- }),
- ).toThrow(
- 'Cannot convert a number to negative number of fixed places. Tried: -2',
- );
- });
-});
diff --git a/app/components/UI/Swaps/components/Ratio.tsx b/app/components/UI/Swaps/components/Ratio.js
similarity index 73%
rename from app/components/UI/Swaps/components/Ratio.tsx
rename to app/components/UI/Swaps/components/Ratio.js
index 10f8e90a654..a9a4060d6f5 100644
--- a/app/components/UI/Swaps/components/Ratio.tsx
+++ b/app/components/UI/Swaps/components/Ratio.js
@@ -1,13 +1,12 @@
import React, { useMemo, useState } from 'react';
+import PropTypes from 'prop-types';
import { StyleSheet, TouchableOpacity } from 'react-native';
import FA5Icon from 'react-native-vector-icons/FontAwesome5';
import { useRatio } from '../utils';
import Text from '../../../Base/Text';
import { useTheme } from '../../../../util/theme';
-import BigNumber from 'bignumber.js';
-import { Theme } from '@metamask/design-tokens';
-const createStyles = (colors: Theme['colors']) =>
+const createStyles = (colors) =>
StyleSheet.create({
infoIcon: {
fontSize: 12,
@@ -15,29 +14,15 @@ const createStyles = (colors: Theme['colors']) =>
color: colors.primary.default,
},
});
-
-interface TokenInfo {
- symbol: string;
- decimals: number;
-}
-
-interface RatioProps {
- sourceAmount: string;
- sourceToken: TokenInfo;
- destinationAmount: string;
- destinationToken: TokenInfo;
- boldSymbol?: boolean;
-}
-
function Ratio({
sourceAmount,
sourceToken,
destinationAmount,
destinationToken,
boldSymbol = false,
-}: RatioProps) {
+}) {
/* Get the ratio between the assets given the selected quote*/
- const [ratioAsSource, setRatioAsSource] = useState(true);
+ const [ratioAsSource, setRatioAsSource] = useState(true);
const { colors } = useTheme();
const styles = createStyles(colors);
@@ -54,14 +39,14 @@ function Ratio({
sourceToken,
]);
- const ratio: BigNumber = useRatio(
+ const ratio = useRatio(
numerator?.amount,
numerator?.decimals,
denominator?.amount,
denominator?.decimals,
);
- const handleRatioSwitch = (): void => setRatioAsSource((isSource) => !isSource);
+ const handleRatioSwitch = () => setRatioAsSource((isSource) => !isSource);
return (
@@ -80,4 +65,18 @@ function Ratio({
);
}
+Ratio.propTypes = {
+ sourceAmount: PropTypes.string,
+ sourceToken: PropTypes.shape({
+ symbol: PropTypes.string,
+ decimals: PropTypes.number,
+ }),
+ destinationAmount: PropTypes.string,
+ destinationToken: PropTypes.shape({
+ symbol: PropTypes.string,
+ decimals: PropTypes.number,
+ }),
+ boldSymbol: PropTypes.bool,
+};
+
export default Ratio;
diff --git a/app/components/UI/SwitchCustomNetwork/index.js b/app/components/UI/SwitchCustomNetwork/index.js
index a6a55fb526c..4bb83a8ffa6 100644
--- a/app/components/UI/SwitchCustomNetwork/index.js
+++ b/app/components/UI/SwitchCustomNetwork/index.js
@@ -1,4 +1,4 @@
-import React, { useEffect, useMemo } from 'react';
+import React from 'react';
import PropTypes from 'prop-types';
import StyledButton from '../StyledButton';
import { StyleSheet, View } from 'react-native';
@@ -8,14 +8,8 @@ import Device from '../../../util/device';
import Text from '../../Base/Text';
import { useTheme } from '../../../util/theme';
import { CommonSelectorsIDs } from '../../../../e2e/selectors/Common.selectors';
-import {
- isMultichainVersion1Enabled,
- getDecimalChainId,
-} from '../../../util/networks';
+import { isMutichainVersion1Enabled } from '../../../util/networks';
import PermissionSummary from '../PermissionsSummary';
-import { MetaMetricsEvents } from '../../../core/Analytics';
-import { useNetworkInfo } from '../../../selectors/selectedNetworkController';
-import { useMetrics } from '../../../components/hooks/useMetrics';
const createStyles = (colors) =>
StyleSheet.create({
@@ -96,33 +90,11 @@ const SwitchCustomNetwork = ({
}) => {
const { colors } = useTheme();
const styles = createStyles(colors);
- const { trackEvent } = useMetrics();
-
- const { networkName } = useNetworkInfo(
- new URL(currentPageInformation.url).hostname,
- );
-
- const trackingData = useMemo(
- () => ({
- chain_id: getDecimalChainId(customNetworkInformation.chainId),
- from_network: networkName,
- to_network: customNetworkInformation.chainName,
- }),
- [customNetworkInformation, networkName],
- );
-
- useEffect(() => {
- trackEvent(
- MetaMetricsEvents.NETWORK_SWITCH_REQUESTED_AND_MODAL_SHOWN,
- trackingData,
- );
- }, [trackEvent, trackingData]);
/**
* Calls onConfirm callback and analytics to track connect confirmed event
*/
const confirm = () => {
- trackEvent(MetaMetricsEvents.NETWORK_SWITCH_CONFIRM_PRESSED, trackingData);
onConfirm && onConfirm();
};
@@ -200,12 +172,10 @@ const SwitchCustomNetwork = ({
currentPageInformation={currentPageInformation}
onCancel={onCancel}
onConfirm={onConfirm}
- isDisconnectAllShown={false}
- isNetworkSwitch
/>
);
- return isMultichainVersion1Enabled
+ return isMutichainVersion1Enabled
? renderPermissionSummary()
: renderNetworkSwitchingNotice();
};
diff --git a/app/components/UI/SwitchCustomNetwork/index.test.tsx b/app/components/UI/SwitchCustomNetwork/index.test.tsx
index 4a338b39b96..daec075e1e3 100644
--- a/app/components/UI/SwitchCustomNetwork/index.test.tsx
+++ b/app/components/UI/SwitchCustomNetwork/index.test.tsx
@@ -1,29 +1,13 @@
import React from 'react';
import SwitchCustomNetwork from './';
import renderWithProvider from '../../../util/test/renderWithProvider';
-import { backgroundState } from '../../../util/test/initial-root-state';
-import { MOCK_ACCOUNTS_CONTROLLER_STATE } from '../../../util/test/accountsControllerTestUtils';
-
-const mockInitialState = {
- wizard: {
- step: 1,
- },
- engine: {
- backgroundState: {
- ...backgroundState,
- AccountsController: MOCK_ACCOUNTS_CONTROLLER_STATE,
- },
- },
-};
describe('SwitchCustomNetwork', () => {
it('should render correctly', () => {
const { toJSON } = renderWithProvider(
,
- { state: mockInitialState },
);
expect(toJSON()).toMatchSnapshot();
});
diff --git a/app/components/UI/Tokens/index.tsx b/app/components/UI/Tokens/index.tsx
index 35bdc59990e..6b93f2ba8e7 100644
--- a/app/components/UI/Tokens/index.tsx
+++ b/app/components/UI/Tokens/index.tsx
@@ -246,7 +246,7 @@ const Tokens: React.FC = ({ tokens }) => {
const renderStakeButton = (asset: TokenI) => {
const onStakeButtonPress = () => {
if (isPooledStakingFeatureEnabled()) {
- navigation.navigate('StakeScreens', { screen: Routes.STAKING.STAKE });
+ navigation.navigate(Routes.STAKE.STAKE);
} else {
const existingStakeTab = browserTabs.find((tab: BrowserTab) =>
tab.url.includes(AppConstants.STAKE.URL),
diff --git a/app/components/UI/TransactionHeader/index.js b/app/components/UI/TransactionHeader/index.js
index 6e0241ad900..456637771ab 100644
--- a/app/components/UI/TransactionHeader/index.js
+++ b/app/components/UI/TransactionHeader/index.js
@@ -6,7 +6,7 @@ import { connect } from 'react-redux';
import WebsiteIcon from '../WebsiteIcon';
import { getHost, getUrlObj } from '../../../util/browser';
import networkList, {
- isMultichainVersion1Enabled,
+ isMutichainVersion1Enabled,
} from '../../../util/networks';
import FontAwesome from 'react-native-vector-icons/FontAwesome';
import AppConstants from '../../../core/AppConstants';
@@ -27,9 +27,9 @@ const createStyles = (colors) =>
alignItems: 'center',
},
domainLogo: {
- width: isMultichainVersion1Enabled ? 32 : 56,
- height: isMultichainVersion1Enabled ? 32 : 56,
- borderRadius: isMultichainVersion1Enabled ? 16 : 32,
+ width: isMutichainVersion1Enabled ? 32 : 56,
+ height: isMutichainVersion1Enabled ? 32 : 56,
+ borderRadius: isMutichainVersion1Enabled ? 16 : 32,
},
assetLogo: {
alignItems: 'center',
@@ -213,8 +213,8 @@ const TransactionHeader = (props) => {
return (
{renderTopIcon()}
- {isMultichainVersion1Enabled ? null : renderDomainUrlContainer()}
- {isMultichainVersion1Enabled ? null : renderNetworkContainer()}
+ {isMutichainVersion1Enabled ? null : renderDomainUrlContainer()}
+ {isMutichainVersion1Enabled ? null : renderNetworkContainer()}
);
};
diff --git a/app/components/UI/WalletAccount/WalletAccount.styles.ts b/app/components/UI/WalletAccount/WalletAccount.styles.ts
index dcda47cc4c3..4b28809c4e0 100644
--- a/app/components/UI/WalletAccount/WalletAccount.styles.ts
+++ b/app/components/UI/WalletAccount/WalletAccount.styles.ts
@@ -36,7 +36,7 @@ const styleSheet = (params: {
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: colors.background.default,
- padding: 16,
+ padding: 20,
borderWidth: 0,
borderColor: colors.border.default,
borderRadius: 8,
diff --git a/app/components/UI/WalletAccount/__snapshots__/WalletAccount.test.tsx.snap b/app/components/UI/WalletAccount/__snapshots__/WalletAccount.test.tsx.snap
index 7b228b6ae9f..c98281f9ef8 100644
--- a/app/components/UI/WalletAccount/__snapshots__/WalletAccount.test.tsx.snap
+++ b/app/components/UI/WalletAccount/__snapshots__/WalletAccount.test.tsx.snap
@@ -190,11 +190,11 @@ exports[`WalletAccount renders correctly 1`] = `
style={
{
"color": "#141618",
- "fontFamily": "EuclidCircularB-Medium",
- "fontSize": 14,
- "fontWeight": "500",
+ "fontFamily": "EuclidCircularB-Regular",
+ "fontSize": 16,
+ "fontWeight": "400",
"letterSpacing": 0,
- "lineHeight": 22,
+ "lineHeight": 24,
}
}
testID="account-label"
@@ -204,7 +204,7 @@ exports[`WalletAccount renders correctly 1`] = `
@@ -294,9 +292,9 @@ exports[`WalletAccount renders correctly 1`] = `
style={
{
"color": "#0376c9",
- "fontFamily": "EuclidCircularB-Medium",
+ "fontFamily": "EuclidCircularB-Regular",
"fontSize": 12,
- "fontWeight": "500",
+ "fontWeight": "400",
"letterSpacing": 0,
"lineHeight": 20,
}
diff --git a/app/components/Views/AccountConnect/AccountConnect.tsx b/app/components/Views/AccountConnect/AccountConnect.tsx
index a2a348c4dd9..065a9787740 100644
--- a/app/components/Views/AccountConnect/AccountConnect.tsx
+++ b/app/components/Views/AccountConnect/AccountConnect.tsx
@@ -71,8 +71,7 @@ import AccountConnectSingle from './AccountConnectSingle';
import AccountConnectSingleSelector from './AccountConnectSingleSelector';
import { PermissionsSummaryProps } from '../../../components/UI/PermissionsSummary/PermissionsSummary.types';
import PermissionsSummary from '../../../components/UI/PermissionsSummary';
-import { isMultichainVersion1Enabled } from '../../../util/networks';
-import NetworkConnectMultiSelector from '../NetworkConnect/NetworkConnectMultiSelector';
+import { isMutichainVersion1Enabled } from '../../../util/networks';
const createStyles = () =>
StyleSheet.create({
@@ -174,7 +173,7 @@ const AccountConnect = (props: AccountConnectProps) => {
channelIdOrHostname,
]);
- const urlWithProtocol = (hostname && !isUUID(hostname))
+ const urlWithProtocol = hostname
? prefixUrlWithProtocol(hostname)
: domainTitle;
@@ -557,8 +556,6 @@ const AccountConnect = (props: AccountConnectProps) => {
onEdit: () => {
setScreen(AccountConnectScreens.MultiConnectSelector);
},
- onEditNetworks: () =>
- setScreen(AccountConnectScreens.MultiConnectNetworkSelector),
onUserAction: setUserIntent,
isAlreadyConnected: false,
};
@@ -618,20 +615,6 @@ const AccountConnect = (props: AccountConnectProps) => {
],
);
- const renderMultiConnectNetworkSelectorScreen = useCallback(
- () => (
- setScreen(AccountConnectScreens.SingleConnect)}
- />
- ),
- [isLoading, urlWithProtocol, hostname],
- );
-
const renderPhishingModal = useCallback(
() => (
{
const renderConnectScreens = useCallback(() => {
switch (screen) {
case AccountConnectScreens.SingleConnect:
- return isMultichainVersion1Enabled
+ return isMutichainVersion1Enabled
? renderPermissionsSummaryScreen()
: renderSingleConnectScreen();
case AccountConnectScreens.SingleConnectSelector:
return renderSingleConnectSelectorScreen();
case AccountConnectScreens.MultiConnectSelector:
return renderMultiConnectSelectorScreen();
- case AccountConnectScreens.MultiConnectNetworkSelector:
- return renderMultiConnectNetworkSelectorScreen();
}
}, [
screen,
@@ -687,7 +668,6 @@ const AccountConnect = (props: AccountConnectProps) => {
renderPermissionsSummaryScreen,
renderSingleConnectSelectorScreen,
renderMultiConnectSelectorScreen,
- renderMultiConnectNetworkSelectorScreen,
]);
return (
diff --git a/app/components/Views/AccountConnect/AccountConnect.types.ts b/app/components/Views/AccountConnect/AccountConnect.types.ts
index dd94ca045b5..73c01a194f5 100644
--- a/app/components/Views/AccountConnect/AccountConnect.types.ts
+++ b/app/components/Views/AccountConnect/AccountConnect.types.ts
@@ -7,7 +7,6 @@ export enum AccountConnectScreens {
SingleConnect = 'SingleConnect',
SingleConnectSelector = 'SingleConnectSelector',
MultiConnectSelector = 'MultiConnectSelector',
- MultiConnectNetworkSelector = 'MultiConnectNetworkSelector',
}
export interface AccountConnectParams {
diff --git a/app/components/Views/AccountConnect/AccountConnectMultiSelector/AccountConnectMultiSelector.styles.ts b/app/components/Views/AccountConnect/AccountConnectMultiSelector/AccountConnectMultiSelector.styles.ts
index c255f107de9..2438c7b6944 100644
--- a/app/components/Views/AccountConnect/AccountConnectMultiSelector/AccountConnectMultiSelector.styles.ts
+++ b/app/components/Views/AccountConnect/AccountConnectMultiSelector/AccountConnectMultiSelector.styles.ts
@@ -3,19 +3,15 @@ import { StyleSheet } from 'react-native';
// External dependencies.
import { Theme } from '../../../../util/theme/models';
-import { isMultichainVersion1Enabled } from '../../../../util/networks';
+import { isMutichainVersion1Enabled } from '../../../../util/networks';
/**
* Style sheet function for AccountConnectMultiSelector screen.
* @returns StyleSheet object.
*/
-
-const styleSheet = (params: {
- theme: Theme;
- vars: { isRenderedAsBottomSheet: boolean | undefined };
-}) => {
+const styleSheet = (params: { theme: Theme }) => {
const { colors } = params.theme;
- const { vars } = params;
+
return StyleSheet.create({
container: {
height: '100%',
@@ -25,12 +21,12 @@ const styleSheet = (params: {
},
description: {
textAlign: 'center',
- marginVertical: isMultichainVersion1Enabled ? 8 : 16,
+ marginVertical: isMutichainVersion1Enabled ? 8 : 16,
color: colors.text.alternative,
},
ctaButtonsContainer: {
- marginTop: isMultichainVersion1Enabled ? 0 : 24,
- marginBottom: vars.isRenderedAsBottomSheet ? 0 : 16,
+ marginTop: isMutichainVersion1Enabled ? 0 : 24,
+ marginBottom: 16,
},
connectOrUpdateButtonContainer: { flexDirection: 'row' },
button: { flex: 1 },
diff --git a/app/components/Views/AccountConnect/AccountConnectMultiSelector/AccountConnectMultiSelector.tsx b/app/components/Views/AccountConnect/AccountConnectMultiSelector/AccountConnectMultiSelector.tsx
index 91d505d7cec..d2474c6d27f 100644
--- a/app/components/Views/AccountConnect/AccountConnectMultiSelector/AccountConnectMultiSelector.tsx
+++ b/app/components/Views/AccountConnect/AccountConnectMultiSelector/AccountConnectMultiSelector.tsx
@@ -34,7 +34,7 @@ import {
} from './AccountConnectMultiSelector.types';
import { useNavigation } from '@react-navigation/native';
import Routes from '../../../../constants/navigation/Routes';
-import { isMultichainVersion1Enabled } from '../../../../util/networks';
+import { isMutichainVersion1Enabled } from '../../../../util/networks';
import Checkbox from '../../../../component-library/components/Checkbox';
const AccountConnectMultiSelector = ({
@@ -52,9 +52,8 @@ const AccountConnectMultiSelector = ({
connection,
onBack,
screenTitle,
- isRenderedAsBottomSheet = true,
}: AccountConnectMultiSelectorProps) => {
- const { styles } = useStyles(styleSheet, { isRenderedAsBottomSheet });
+ const { styles } = useStyles(styleSheet, {});
const { navigate } = useNavigation();
const [screen, setScreen] = useState(
AccountConnectMultiSelectorScreens.AccountMultiSelector,
@@ -93,7 +92,7 @@ const AccountConnectMultiSelector = ({
const renderSelectAllButton = useCallback(
() =>
Boolean(accounts.length) &&
- !isMultichainVersion1Enabled && (
+ !isMutichainVersion1Enabled && (
{
@@ -117,7 +116,7 @@ const AccountConnectMultiSelector = ({
const renderUnselectAllButton = useCallback(
() =>
Boolean(accounts.length) &&
- !isMultichainVersion1Enabled && (
+ !isMutichainVersion1Enabled && (
{
@@ -188,7 +187,7 @@ const AccountConnectMultiSelector = ({
return (
- {!isMultichainVersion1Enabled && (
+ {!isMutichainVersion1Enabled && (
)}
- {!isMultichainVersion1Enabled && (
+ {!isMutichainVersion1Enabled && (
)}
{areAnyAccountsSelected && (
)}
- {isMultichainVersion1Enabled && areNoAccountsSelected && (
+ {isMutichainVersion1Enabled && areNoAccountsSelected && (
@@ -269,14 +268,14 @@ const AccountConnectMultiSelector = ({
- {!isMultichainVersion1Enabled && (
+ {!isMutichainVersion1Enabled && (
)}
- {isMultichainVersion1Enabled
+ {isMutichainVersion1Enabled
? strings('accounts.select_accounts_description')
: strings('accounts.connect_description')}
- {isMultichainVersion1Enabled && renderSelectAllCheckbox()}
+ {isMutichainVersion1Enabled && renderSelectAllCheckbox()}
{areAllAccountsSelected
? renderUnselectAllButton()
: renderSelectAllButton()}
diff --git a/app/components/Views/AccountConnect/AccountConnectMultiSelector/AccountConnectMultiSelector.types.ts b/app/components/Views/AccountConnect/AccountConnectMultiSelector/AccountConnectMultiSelector.types.ts
index e9180a09ae7..1e9883a61e9 100644
--- a/app/components/Views/AccountConnect/AccountConnectMultiSelector/AccountConnectMultiSelector.types.ts
+++ b/app/components/Views/AccountConnect/AccountConnectMultiSelector/AccountConnectMultiSelector.types.ts
@@ -31,5 +31,4 @@ export interface AccountConnectMultiSelectorProps extends UseAccounts {
onBack: () => void;
connection?: ConnectionProps;
screenTitle?: string;
- isRenderedAsBottomSheet?: boolean;
}
diff --git a/app/components/Views/AccountPermissions/AccountPermissions.tsx b/app/components/Views/AccountPermissions/AccountPermissions.tsx
index 78a3555fdca..9d39a57deae 100755
--- a/app/components/Views/AccountPermissions/AccountPermissions.tsx
+++ b/app/components/Views/AccountPermissions/AccountPermissions.tsx
@@ -53,7 +53,7 @@ import { useMetrics } from '../../../components/hooks/useMetrics';
import { selectInternalAccounts } from '../../../selectors/accountsController';
import { selectPermissionControllerState } from '../../../selectors/snaps/permissionController';
import { RootState } from '../../../reducers';
-import { isMultichainVersion1Enabled } from '../../../util/networks';
+import { isMutichainVersion1Enabled } from '../../../util/networks';
import PermissionsSummary from '../../../components/UI/PermissionsSummary';
import { PermissionsSummaryProps } from '../../../components/UI/PermissionsSummary/PermissionsSummary.types';
@@ -67,6 +67,7 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
isRenderedAsBottomSheet = true,
initialScreen = AccountPermissionsScreens.Connected,
} = props.route.params;
+
const accountAvatarType = useSelector((state: RootState) =>
state.settings.useBlockieIcon
? AvatarAccountType.Blockies
@@ -152,7 +153,7 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
};
toastRef?.current?.showToast(
- isMultichainVersion1Enabled ? networkToastProps : plainToastProps,
+ isMutichainVersion1Enabled ? networkToastProps : plainToastProps,
);
previousPermittedAccounts.current = permittedAccountsByHostname.length;
@@ -392,7 +393,7 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
isRenderedAsBottomSheet
? setPermissionsScreen(AccountPermissionsScreens.Connected)
: navigation.navigate('PermissionsManager'),
- isRenderedAsBottomSheet,
+ isInitialDappConnection: false,
};
return ;
}, [faviconSource, urlWithProtocol, isRenderedAsBottomSheet, navigation]);
@@ -415,7 +416,6 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
setPermissionsScreen(AccountPermissionsScreens.PermissionsSummary)
}
screenTitle={strings('accounts.edit_accounts_title')}
- isRenderedAsBottomSheet={isRenderedAsBottomSheet}
/>
),
[
@@ -428,7 +428,6 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
urlWithProtocol,
secureIcon,
hostname,
- isRenderedAsBottomSheet,
],
);
@@ -474,16 +473,9 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
onBack={() =>
setPermissionsScreen(AccountPermissionsScreens.PermissionsSummary)
}
- isRenderedAsBottomSheet={isRenderedAsBottomSheet}
/>
),
- [
- isLoading,
- setUserIntent,
- urlWithProtocol,
- hostname,
- isRenderedAsBottomSheet,
- ],
+ [isLoading, setUserIntent, urlWithProtocol, hostname],
);
const renderRevokeScreen = useCallback(
@@ -514,7 +506,6 @@ const AccountPermissions = (props: AccountPermissionsProps) => {
accountAvatarType,
],
);
-
const renderPermissionsScreens = useCallback(() => {
switch (permissionsScreen) {
case AccountPermissionsScreens.Connected:
diff --git a/app/components/Views/AccountPermissions/AccountPermissionsConnected/AccountPermissionsConnected.tsx b/app/components/Views/AccountPermissions/AccountPermissionsConnected/AccountPermissionsConnected.tsx
index 45712047ef3..6e71b5dfb62 100644
--- a/app/components/Views/AccountPermissions/AccountPermissionsConnected/AccountPermissionsConnected.tsx
+++ b/app/components/Views/AccountPermissions/AccountPermissionsConnected/AccountPermissionsConnected.tsx
@@ -12,7 +12,7 @@ import TagUrl from '../../../../component-library/components/Tags/TagUrl';
import PickerNetwork from '../../../../component-library/components/Pickers/PickerNetwork';
import {
getDecimalChainId,
- isMultichainVersion1Enabled,
+ isMutichainVersion1Enabled,
} from '../../../../util/networks';
import AccountSelectorList from '../../../../components/UI/AccountSelectorList';
import { AccountPermissionsScreens } from '../AccountPermissions.types';
@@ -148,10 +148,10 @@ const AccountPermissionsConnected = ({
return (
<>
- {!isMultichainVersion1Enabled && (
+ {!isMutichainVersion1Enabled && (
)}
- {isMultichainVersion1Enabled && (
+ {isMutichainVersion1Enabled && (
)}
- {!isMultichainVersion1Enabled && (
+ {!isMutichainVersion1Enabled && (
)}
- {isMultichainVersion1Enabled && (
+ {isMutichainVersion1Enabled && (
{strings('accounts.connected_accounts_title')}
)}
- {!isMultichainVersion1Enabled && (
+ {!isMutichainVersion1Enabled && (
{renderSheetAction()}
- {isMultichainVersion1Enabled && (
+ {isMutichainVersion1Enabled && (
- StyleSheet.create({
+const styleSheet = (params: { theme: Theme }) => {
+ const { theme } = params;
+ const { typography } = theme;
+ return StyleSheet.create({
wrapper: {
+ marginTop: 20,
paddingHorizontal: 16,
+ marginBottom: 16,
+ width: '100%',
},
title: {
- paddingVertical: 8,
+ ...typography.sHeadingSM,
+ marginVertical: 0,
+ marginBottom: 4,
} as TextStyle,
});
+};
export default styleSheet;
diff --git a/app/components/Views/Asset/ActivityHeader.tsx b/app/components/Views/Asset/ActivityHeader.tsx
index c3dee3829be..0b4e3620779 100644
--- a/app/components/Views/Asset/ActivityHeader.tsx
+++ b/app/components/Views/Asset/ActivityHeader.tsx
@@ -2,10 +2,8 @@ import React from 'react';
import { View } from 'react-native';
import { strings } from '../../../../locales/i18n';
import { useStyles } from '../../../component-library/hooks';
+import Title from '../../Base/Title';
import { Asset } from '../../UI/AssetOverview/AssetOverview.types';
-import Text, {
- TextVariant,
-} from '../../../component-library/components/Texts/Text';
import styleSheet from './ActivityHeader.styles';
interface ActivityHeaderProps {
@@ -16,11 +14,11 @@ const ActivityHeader = ({ asset }: ActivityHeaderProps) => {
const { styles } = useStyles(styleSheet, {});
return (
-
+
{strings('asset_overview.activity', {
symbol: asset.name || asset.symbol,
})}
-
+
);
};
diff --git a/app/components/Views/GasEducationCarousel/__snapshots__/index.test.tsx.snap b/app/components/Views/GasEducationCarousel/__snapshots__/index.test.tsx.snap
index 6603f9b568e..c65587fd9fc 100644
--- a/app/components/Views/GasEducationCarousel/__snapshots__/index.test.tsx.snap
+++ b/app/components/Views/GasEducationCarousel/__snapshots__/index.test.tsx.snap
@@ -16,116 +16,117 @@ exports[`GasEducationCarousel should render correctly 1`] = `
}
}
>
-
-
-
+
+ }
+ >
+
-
-
-
+
+ }
+ directionalLockEnabled={true}
+ horizontal={true}
+ keyboardDismissMode="on-drag"
+ onMomentumScrollBegin={[Function]}
+ onMomentumScrollEnd={[Function]}
+ onScroll={[Function]}
+ pagingEnabled={true}
+ scrollEnabled={true}
+ scrollEventThrottle={16}
+ scrollsToTop={false}
+ showsHorizontalScrollIndicator={false}
+ style={{}}
+ >
+
-
-
-
-
- Before you purchase ETH, understand gas fees
-
-
- If you plan to swap or transfer your ETH, purchase extra to cover gas fees.
-
-
+
+
+
- Gas fees for transactions are separate from the cost of purchasing ETH.
-
+ Before you purchase ETH, understand gas fees
+
+
+ If you plan to swap or transfer your ETH, purchase extra to cover gas fees.
+
+
- MetaMask does not profit from gas fees.
+ Gas fees for transactions are separate from the cost of purchasing ETH.
+
+
+ MetaMask does not profit from gas fees.
+
-
+
-
-
-
-
-
+
+
+
-
+ >
+
+
-
-
-
-
-
-
+
+
+ >
+
+
+
+
-
-
+
+
{
- const { vars } = params;
- return StyleSheet.create({
+const styleSheet = () =>
+ StyleSheet.create({
bottomSheetContainer: {
height: '100%',
},
@@ -13,8 +10,8 @@ const styleSheet = (params: {
paddingHorizontal: 16,
},
buttonsContainer: {
- marginTop: isMultichainVersion1Enabled ? 0 : 24,
- marginBottom: vars.isRenderedAsBottomSheet ? 0 : 16,
+ marginTop: isMutichainVersion1Enabled ? 0 : 24,
+ marginBottom: 16,
},
updateButtonContainer: { flexDirection: 'row' },
buttonPositioning: { flex: 1 },
@@ -36,6 +33,5 @@ const styleSheet = (params: {
flexDirection: 'row',
},
});
-};
export default styleSheet;
diff --git a/app/components/Views/NetworkConnect/NetworkConnectMultiSelector/NetworkConnectMultiSelector.tsx b/app/components/Views/NetworkConnect/NetworkConnectMultiSelector/NetworkConnectMultiSelector.tsx
index 181b86d44d7..5ba09617094 100644
--- a/app/components/Views/NetworkConnect/NetworkConnectMultiSelector/NetworkConnectMultiSelector.tsx
+++ b/app/components/Views/NetworkConnect/NetworkConnectMultiSelector/NetworkConnectMultiSelector.tsx
@@ -34,9 +34,8 @@ const NetworkConnectMultiSelector = ({
urlWithProtocol,
hostname,
onBack,
- isRenderedAsBottomSheet = true,
}: NetworkConnectMultiSelectorProps) => {
- const { styles } = useStyles(styleSheet, { isRenderedAsBottomSheet });
+ const { styles } = useStyles(styleSheet, {});
const { navigate } = useNavigation();
const [selectedNetworkIds, setSelectedNetworkIds] = useState([]);
diff --git a/app/components/Views/NetworkConnect/NetworkConnectMultiSelector/NetworkConnectMultiSelector.types.ts b/app/components/Views/NetworkConnect/NetworkConnectMultiSelector/NetworkConnectMultiSelector.types.ts
index 1b3406946ac..6780f896f1b 100644
--- a/app/components/Views/NetworkConnect/NetworkConnectMultiSelector/NetworkConnectMultiSelector.types.ts
+++ b/app/components/Views/NetworkConnect/NetworkConnectMultiSelector/NetworkConnectMultiSelector.types.ts
@@ -12,5 +12,4 @@ export interface NetworkConnectMultiSelectorProps {
urlWithProtocol: string;
hostname: string;
onBack: () => void;
- isRenderedAsBottomSheet?: boolean;
}
diff --git a/app/components/Views/NetworkSelector/__snapshots__/NetworkSelector.test.tsx.snap b/app/components/Views/NetworkSelector/__snapshots__/NetworkSelector.test.tsx.snap
index e7858e18e3e..d8ceabfffba 100644
--- a/app/components/Views/NetworkSelector/__snapshots__/NetworkSelector.test.tsx.snap
+++ b/app/components/Views/NetworkSelector/__snapshots__/NetworkSelector.test.tsx.snap
@@ -475,6 +475,7 @@ exports[`Network Selector renders correctly 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -485,7 +486,9 @@ exports[`Network Selector renders correctly 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
@@ -501,68 +504,59 @@ exports[`Network Selector renders correctly 1`] = `
style={
{
"alignItems": "center",
- "flexDirection": "row",
+ "backgroundColor": "#ffffff",
+ "borderRadius": 16,
+ "height": 32,
+ "justifyContent": "center",
+ "marginRight": 16,
+ "overflow": "hidden",
+ "width": 32,
}
}
+ testID="cellbase-avatar"
>
-
-
-
-
+
+
+
-
- Ethereum Main Network
-
-
+ Ethereum Main Network
+
@@ -602,6 +596,7 @@ exports[`Network Selector renders correctly 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -612,14 +607,15 @@ exports[`Network Selector renders correctly 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
-
-
-
-
+
+
+
-
- Linea Main Network
-
-
+ Linea Main Network
+
@@ -702,6 +690,7 @@ exports[`Network Selector renders correctly 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -712,7 +701,9 @@ exports[`Network Selector renders correctly 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
@@ -728,70 +719,61 @@ exports[`Network Selector renders correctly 1`] = `
style={
{
"alignItems": "center",
- "flexDirection": "row",
+ "backgroundColor": "#ffffff",
+ "borderRadius": 16,
+ "height": 32,
+ "justifyContent": "center",
+ "marginRight": 16,
+ "overflow": "hidden",
+ "width": 32,
}
}
+ testID="cellbase-avatar"
>
-
-
-
-
+
+
+
-
- Avalanche Mainnet C-Chain
-
-
+ Avalanche Mainnet C-Chain
+
@@ -805,6 +787,7 @@ exports[`Network Selector renders correctly 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -815,7 +798,9 @@ exports[`Network Selector renders correctly 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
@@ -831,70 +816,61 @@ exports[`Network Selector renders correctly 1`] = `
style={
{
"alignItems": "center",
- "flexDirection": "row",
+ "backgroundColor": "#ffffff",
+ "borderRadius": 16,
+ "height": 32,
+ "justifyContent": "center",
+ "marginRight": 16,
+ "overflow": "hidden",
+ "width": 32,
}
}
+ testID="cellbase-avatar"
>
-
-
-
-
+
+
+
-
- Polygon Mainnet
-
-
+ Polygon Mainnet
+
@@ -908,6 +884,7 @@ exports[`Network Selector renders correctly 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -918,7 +895,9 @@ exports[`Network Selector renders correctly 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
@@ -934,70 +913,61 @@ exports[`Network Selector renders correctly 1`] = `
style={
{
"alignItems": "center",
- "flexDirection": "row",
+ "backgroundColor": "#ffffff",
+ "borderRadius": 16,
+ "height": 32,
+ "justifyContent": "center",
+ "marginRight": 16,
+ "overflow": "hidden",
+ "width": 32,
}
}
+ testID="cellbase-avatar"
>
-
-
-
-
+
+
+
-
- Optimism
-
-
+ Optimism
+
@@ -1011,6 +981,7 @@ exports[`Network Selector renders correctly 1`] = `
"backgroundColor": "#ffffff",
"borderRadius": 4,
"opacity": 1,
+ "padding": 16,
"position": "relative",
}
}
@@ -1021,7 +992,9 @@ exports[`Network Selector renders correctly 1`] = `
accessible={true}
style={
{
- "padding": 16,
+ "alignItems": "center",
+ "flexDirection": "row",
+ "padding": 0,
}
}
>
@@ -1037,68 +1010,59 @@ exports[`Network Selector renders correctly 1`] = `
style={
{
"alignItems": "center",
- "flexDirection": "row",
+ "backgroundColor": "#f2f4f6",
+ "borderRadius": 16,
+ "borderWidth": 1,
+ "height": 32,
+ "justifyContent": "center",
+ "marginRight": 16,
+ "overflow": "hidden",
+ "width": 32,
}
}
+ testID="cellbase-avatar"
>
-
-
- G
-
-
-
+
+
+
-
- Gnosis Chain
-
-
+ Gnosis Chain
+
diff --git a/app/components/Views/Notifications/Details/Fields/NetworkFeeField.test.tsx b/app/components/Views/Notifications/Details/Fields/NetworkFeeField.test.tsx
index 7d2b8274b0d..de32edec86b 100644
--- a/app/components/Views/Notifications/Details/Fields/NetworkFeeField.test.tsx
+++ b/app/components/Views/Notifications/Details/Fields/NetworkFeeField.test.tsx
@@ -1,10 +1,17 @@
import React from 'react';
import { render } from '@testing-library/react-native';
import NetworkFeeField from './NetworkFeeField';
-import { ModalFieldType } from '../../../../../util/notifications';
-import { processNotification } from '@metamask/notification-services-controller/notification-services';
-import { createMockNotificationEthReceived } from '@metamask/notification-services-controller/notification-services/mocks';
+import { OnChainRawNotificationsWithNetworkFields } from '@metamask/notification-services-controller/dist/types/NotificationServicesController/types';
+import {
+ ModalFieldType,
+ type Notification,
+} from '../../../../../util/notifications';
+import { NotificationServicesController } from '@metamask/notification-services-controller';
+const {
+ Processors: { processNotification },
+ Mocks,
+} = NotificationServicesController;
jest.mock('../../../../../util/notifications/methods/common', () => ({
getNetworkFees: () =>
Promise.resolve({
@@ -20,8 +27,8 @@ jest.mock('../../../../../util/notifications/methods/common', () => ({
}));
const MOCK_NOTIFICATION = processNotification(
- createMockNotificationEthReceived(),
-);
+ Mocks.createMockNotificationEthReceived(),
+) as OnChainRawNotificationsWithNetworkFields;
describe('NetworkFeeField', () => {
const setIsCollapsed = jest.fn();
@@ -35,6 +42,7 @@ describe('NetworkFeeField', () => {
const { toJSON } = render(
@@ -50,7 +58,7 @@ describe('NetworkFeeField', () => {
chainId: '0x1',
})
}
- notification={MOCK_NOTIFICATION}
+ notification={MOCK_NOTIFICATION as Notification}
/>,
);
diff --git a/app/components/Views/Notifications/OptIn/index.test.tsx b/app/components/Views/Notifications/OptIn/index.test.tsx
index b643daaa7fb..bd62c3d961e 100644
--- a/app/components/Views/Notifications/OptIn/index.test.tsx
+++ b/app/components/Views/Notifications/OptIn/index.test.tsx
@@ -1,16 +1,13 @@
import React from 'react';
-import OptIn from '.';
+import OptIn from './';
import { RootState } from '../../../../reducers';
import { backgroundState } from '../../../../util/test/initial-root-state';
import renderWithProvider, {
DeepPartial,
} from '../../../../util/test/renderWithProvider';
-import { strings } from '../../../../../locales/i18n';
const mockedDispatch = jest.fn();
-
-
const mockInitialState: DeepPartial = {
settings: {},
engine: {
@@ -21,12 +18,14 @@ const mockInitialState: DeepPartial = {
},
},
},
- };
+};
- jest.mock('react-redux', () => ({
- ...jest.requireActual('react-redux'),
- useSelector: jest.fn().mockImplementation((selector) => selector(mockInitialState)),
- }));
+jest.mock('react-redux', () => ({
+ ...jest.requireActual('react-redux'),
+ // TODO: Replace "any" with type
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ useSelector: (fn: any) => fn(mockInitialState),
+}));
jest.mock('@react-navigation/native', () => {
const actualNav = jest.requireActual('@react-navigation/native');
@@ -39,82 +38,9 @@ jest.mock('@react-navigation/native', () => {
};
});
-jest.mock('../../../../actions/notification/helpers', () => ({
- enableNotificationServices: jest.fn(),
-}));
-
-jest.mock('../../../../components/hooks/useMetrics', () => ({
- useMetrics: () => ({
- trackEvent: jest.fn(),
- }),
-}));
-
-jest.mock('../../../../util/notifications/hooks/useNotifications', () => ({
- useEnableNotifications: () => ({
- enableNotifications: jest.fn(),
- }),
-}));
-
-jest.mock('react-native', () => ({
- Linking: {
- openURL: jest.fn(),
- },
-}));
-
-jest.mock('../../../../selectors/notifications', () => ({
- selectIsMetamaskNotificationsEnabled: jest.fn(),
-}));
-
-jest.mock('../../../../core/Analytics', () => ({
- MetaMetricsEvents: {
- NOTIFICATIONS_ACTIVATED: 'notifications_activated',
- },
-}));
-
-jest.mock('../../../../util/theme', () => ({
- useTheme: jest.fn(),
-}));
-
-jest.mock('../../../../selectors/notifications', () => ({
- selectIsProfileSyncingEnabled: jest.fn(),
-}));
-
describe('OptIn', () => {
-
- beforeEach(() => {
- jest.resetAllMocks();
- });
-
it('should render correctly', () => {
const { toJSON } = renderWithProvider();
expect(toJSON()).toMatchSnapshot();
});
-
- it('calls enableNotifications when the button is pressed', async () => {
- const { getByText } = renderWithProvider(
-
- );
-
- const button = getByText(strings('notifications.activation_card.cta'));
- expect(button).toBeDefined();
- });
-
- it('calls navigate when the cancel button is pressed', async () => {
- const { getByText } = renderWithProvider(
-
- );
-
- const button = getByText(strings('notifications.activation_card.cancel'));
- expect(button).toBeDefined();
- });
-
- it('calls trackEvent when the button is pressed', async () => {
- const { getByText } = renderWithProvider(
-
- );
-
- const button = getByText(strings('notifications.activation_card.cta'));
- expect(button).toBeDefined();
- });
});
-
diff --git a/app/components/Views/Notifications/OptIn/index.tsx b/app/components/Views/Notifications/OptIn/index.tsx
index 4c69eb7d7dd..d256d687df8 100644
--- a/app/components/Views/Notifications/OptIn/index.tsx
+++ b/app/components/Views/Notifications/OptIn/index.tsx
@@ -4,6 +4,7 @@ import { useNavigation } from '@react-navigation/native';
import { useMetrics } from '../../../../components/hooks/useMetrics';
import { MetaMetricsEvents } from '../../../../core/Analytics';
+import { AuthorizationStatus } from '@notifee/react-native';
import Button, {
ButtonVariants,
} from '../../../../component-library/components/Buttons/Button';
@@ -17,7 +18,10 @@ import EnableNotificationsCardPlaceholder from '../../../../images/enableNotific
import { createStyles } from './styles';
import Routes from '../../../../constants/navigation/Routes';
import { useSelector } from 'react-redux';
-import NotificationsService from '../../../../util/notifications/services/NotificationService';
+import {
+ asyncAlert,
+ requestPushNotificationsPermission,
+} from '../../../../util/notifications';
import AppConstants from '../../../../core/AppConstants';
import { RootState } from '../../../../reducers';
import { useEnableNotifications } from '../../../../util/notifications/hooks/useNotifications';
@@ -72,11 +76,14 @@ const OptIn = () => {
},
});
} else {
- const { permission } = await NotificationsService.getAllPermissions();
-
- if (permission !== 'authorized') {
- return;
- }
+ const nativeNotificationStatus = await requestPushNotificationsPermission(
+ asyncAlert,
+ );
+
+ if (
+ nativeNotificationStatus?.authorizationStatus ===
+ AuthorizationStatus.AUTHORIZED
+ ) {
/**
* Although this is an async function, we are dispatching an action (firing & forget)
* to emulate optimistic UI.
@@ -96,6 +103,7 @@ const OptIn = () => {
action_type: 'activated',
is_profile_syncing_enabled: isProfileSyncingEnabled,
});
+ }
}, [
basicFunctionalityEnabled,
enableNotifications,
diff --git a/app/components/Views/Notifications/index.tsx b/app/components/Views/Notifications/index.tsx
index 77d736afa4a..a38ffeb1b50 100644
--- a/app/components/Views/Notifications/index.tsx
+++ b/app/components/Views/Notifications/index.tsx
@@ -1,6 +1,7 @@
import React, { useCallback, useMemo } from 'react';
import { View } from 'react-native';
import { useSelector } from 'react-redux';
+import notifee from '@notifee/react-native';
import { useMetrics } from '../../../components/hooks/useMetrics';
import { NotificationsViewSelectorsIDs } from '../../../../e2e/selectors/NotificationsView.selectors';
import styles from './styles';
@@ -28,7 +29,6 @@ import {
useMarkNotificationAsRead,
} from '../../../util/notifications/hooks/useNotifications';
import { NavigationProp, ParamListBase } from '@react-navigation/native';
-import NotificationsService from '../../../util/notifications/services/NotificationService';
import ButtonIcon, {
ButtonIconSizes,
} from '../../../component-library/components/Buttons/ButtonIcon';
@@ -53,7 +53,7 @@ const NotificationsView = ({
const handleMarkAllAsRead = useCallback(() => {
markNotificationAsRead(notifications);
- NotificationsService.setBadgeCount(0);
+ notifee.setBadgeCount(0);
trackEvent(MetaMetricsEvents.NOTIFICATIONS_MARKED_ALL_AS_READ);
}, [markNotificationAsRead, notifications, trackEvent]);
@@ -143,7 +143,7 @@ NotificationsView.navigationOptions = ({
/>
),
headerTitle: () => (
-
+
{strings('app_settings.notifications_title')}
),
diff --git a/app/components/Views/Notifications/styles.ts b/app/components/Views/Notifications/styles.ts
index a803a435637..fa14c20c8d2 100644
--- a/app/components/Views/Notifications/styles.ts
+++ b/app/components/Views/Notifications/styles.ts
@@ -12,7 +12,6 @@ const styles = StyleSheet.create({
position: 'absolute',
},
icon: { marginHorizontal: 16 },
- title: { alignSelf: 'center' },
});
export default styles;
diff --git a/app/components/Views/OnboardingCarousel/__snapshots__/index.test.tsx.snap b/app/components/Views/OnboardingCarousel/__snapshots__/index.test.tsx.snap
index 761bd88f873..b400a2c0740 100644
--- a/app/components/Views/OnboardingCarousel/__snapshots__/index.test.tsx.snap
+++ b/app/components/Views/OnboardingCarousel/__snapshots__/index.test.tsx.snap
@@ -1,713 +1,32 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`OnboardingCarousel should render correctly 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
-
- Welcome to MetaMask
-
-
-
- Trusted by millions, MetaMask is a secure wallet making the world of web3 accessible to all.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Get started
-
-
-
-
-
-
-
-`;
-
-exports[`OnboardingCarousel should render the App Start Time text when isTest is true 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
-
- Welcome to MetaMask
-
-
-
- Trusted by millions, MetaMask is a secure wallet making the world of web3 accessible to all.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Get started
-
-
-
-
-
-
-
+
+
`;
diff --git a/app/components/Views/OnboardingCarousel/index.js b/app/components/Views/OnboardingCarousel/index.js
new file mode 100644
index 00000000000..a2c4b29d44b
--- /dev/null
+++ b/app/components/Views/OnboardingCarousel/index.js
@@ -0,0 +1,273 @@
+import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import {
+ Text,
+ View,
+ ScrollView,
+ StyleSheet,
+ Image,
+ Dimensions,
+ Platform,
+} from 'react-native';
+import { MetaMetricsEvents } from '../../../core/Analytics';
+import StyledButton from '../../UI/StyledButton';
+import { fontStyles, baseStyles } from '../../../styles/common';
+import { strings } from '../../../../locales/i18n';
+import FadeOutOverlay from '../../UI/FadeOutOverlay';
+import ScrollableTabView from 'react-native-scrollable-tab-view';
+import { getTransparentOnboardingNavbarOptions } from '../../UI/Navbar';
+import OnboardingScreenWithBg from '../../UI/OnboardingScreenWithBg';
+import Device from '../../../util/device';
+import { saveOnboardingEvent } from '../../../actions/onboarding';
+import { connect } from 'react-redux';
+import { ThemeContext, mockTheme } from '../../../util/theme';
+import { WELCOME_SCREEN_CAROUSEL_TITLE_ID } from '../../../../wdio/screen-objects/testIDs/Screens/WelcomeScreen.testIds';
+import { OnboardingCarouselSelectorIDs } from '../../../../e2e/selectors/Onboarding/OnboardingCarousel.selectors';
+import generateTestId from '../../../../wdio/utils/generateTestId';
+import trackOnboarding from '../../../util/metrics/TrackOnboarding/trackOnboarding';
+
+const IMAGE_3_RATIO = 215 / 315;
+const IMAGE_2_RATIO = 222 / 239;
+const IMAGE_1_RATIO = 285 / 203;
+const DEVICE_WIDTH = Dimensions.get('window').width;
+
+const IMG_PADDING = Device.isIphoneX() ? 100 : Device.isIphone5S() ? 180 : 220;
+
+const createStyles = (colors) =>
+ StyleSheet.create({
+ scroll: {
+ flexGrow: 1,
+ },
+ wrapper: {
+ paddingVertical: 30,
+ flex: 1,
+ },
+ title: {
+ fontSize: 24,
+ marginBottom: 12,
+ color: colors.text.default,
+ justifyContent: 'center',
+ textAlign: 'center',
+ ...fontStyles.bold,
+ },
+ subtitle: {
+ fontSize: 14,
+ lineHeight: 19,
+ marginTop: 12,
+ marginBottom: 25,
+ color: colors.text.alternative,
+ justifyContent: 'center',
+ textAlign: 'center',
+ ...fontStyles.normal,
+ },
+ ctas: {
+ paddingHorizontal: 40,
+ paddingBottom: Device.isIphoneX() ? 40 : 20,
+ flexDirection: 'column',
+ },
+ ctaWrapper: {
+ justifyContent: 'flex-end',
+ },
+ carouselImage: {},
+ // eslint-disable-next-line react-native/no-unused-styles
+ carouselImage1: {
+ marginTop: 30,
+ width: DEVICE_WIDTH - IMG_PADDING,
+ height: (DEVICE_WIDTH - IMG_PADDING) * IMAGE_1_RATIO,
+ },
+ // eslint-disable-next-line react-native/no-unused-styles
+ carouselImage2: {
+ width: DEVICE_WIDTH - IMG_PADDING,
+ height: (DEVICE_WIDTH - IMG_PADDING) * IMAGE_2_RATIO,
+ },
+ // eslint-disable-next-line react-native/no-unused-styles
+ carouselImage3: {
+ width: DEVICE_WIDTH - 60,
+ height: (DEVICE_WIDTH - 60) * IMAGE_3_RATIO,
+ },
+ carouselImageWrapper: {
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ circle: {
+ width: 8,
+ height: 8,
+ borderRadius: 8 / 2,
+ backgroundColor: colors.icon.default,
+ opacity: 0.4,
+ marginHorizontal: 8,
+ },
+ solidCircle: {
+ opacity: 1,
+ },
+ progessContainer: {
+ flexDirection: 'row',
+ alignSelf: 'center',
+ marginVertical: 36,
+ },
+ tab: {
+ marginHorizontal: 30,
+ },
+ });
+
+const onboarding_carousel_1 = require('../../../images/onboarding-carousel-1.png'); // eslint-disable-line
+const onboarding_carousel_2 = require('../../../images/onboarding-carousel-2.png'); // eslint-disable-line
+const onboarding_carousel_3 = require('../../../images/onboarding-carousel-3.png'); // eslint-disable-line
+
+const carousel_images = [
+ onboarding_carousel_1,
+ onboarding_carousel_2,
+ onboarding_carousel_3,
+];
+
+/**
+ * View that is displayed to first time (new) users
+ */
+class OnboardingCarousel extends PureComponent {
+ static propTypes = {
+ /**
+ * The navigator object
+ */
+ navigation: PropTypes.object,
+ /**
+ * Save onboarding event to state
+ */
+ saveOnboardingEvent: PropTypes.func,
+ };
+
+ state = {
+ currentTab: 1,
+ };
+
+ track = (event, properties) => {
+ trackOnboarding(event, properties, this.props.saveOnboardingEvent);
+ };
+
+ onPressGetStarted = () => {
+ this.props.navigation.navigate('Onboarding');
+ this.track(MetaMetricsEvents.ONBOARDING_STARTED);
+ };
+
+ renderTabBar = () => ;
+
+ onChangeTab = (obj) => {
+ this.setState({ currentTab: obj.i + 1 });
+ this.track(MetaMetricsEvents.ONBOARDING_WELCOME_SCREEN_ENGAGEMENT, {
+ message_title: strings(`onboarding_carousel.title${[obj.i + 1]}`, {
+ locale: 'en',
+ }),
+ });
+ };
+
+ updateNavBar = () => {
+ const colors = this.context.colors || mockTheme.colors;
+ this.props.navigation.setOptions(
+ getTransparentOnboardingNavbarOptions(colors),
+ );
+ };
+
+ componentDidMount = () => {
+ this.updateNavBar();
+ this.track(MetaMetricsEvents.ONBOARDING_WELCOME_MESSAGE_VIEWED);
+ };
+
+ componentDidUpdate = () => {
+ this.updateNavBar();
+ };
+
+ render() {
+ const { currentTab } = this.state;
+ const colors = this.context.colors || mockTheme.colors;
+ const styles = createStyles(colors);
+
+ return (
+
+
+
+
+
+ {['one', 'two', 'three'].map((value, index) => {
+ const key = index + 1;
+ const imgStyleKey = `carouselImage${key}`;
+ return (
+
+
+
+ {strings(`onboarding_carousel.title${key}`)}
+
+
+ {strings(`onboarding_carousel.subtitle${key}`)}
+
+
+
+
+
+
+ );
+ })}
+
+
+
+ {[1, 2, 3].map((i) => (
+
+ ))}
+
+
+
+
+
+
+ {strings('onboarding_carousel.get_started')}
+
+
+
+
+
+
+ );
+ }
+}
+
+OnboardingCarousel.contextType = ThemeContext;
+
+const mapDispatchToProps = (dispatch) => ({
+ saveOnboardingEvent: (...eventArgs) =>
+ dispatch(saveOnboardingEvent(eventArgs)),
+});
+
+export default connect(null, mapDispatchToProps)(OnboardingCarousel);
diff --git a/app/components/Views/OnboardingCarousel/index.test.tsx b/app/components/Views/OnboardingCarousel/index.test.tsx
index a6521a3c26a..8d6ca7d7aa2 100644
--- a/app/components/Views/OnboardingCarousel/index.test.tsx
+++ b/app/components/Views/OnboardingCarousel/index.test.tsx
@@ -1,41 +1,20 @@
import React from 'react';
-import { waitFor } from '@testing-library/react-native';
+import { shallow } from 'enzyme';
import OnboardingCarousel from './';
-import { NavigationProp, ParamListBase } from '@react-navigation/native';
-import { PerformanceRegressionSelectorIDs } from '../../../../e2e/selectors/PerformanceRegression.selectors';
-import renderWithProvider from '../../../util/test/renderWithProvider';
-
-jest.mock('../../../util/metrics/TrackOnboarding/trackOnboarding');
-jest.mock('../../../util/test/utils', () => ({
- isTest: true,
-}));
-
-const mockNavigate: jest.Mock = jest.fn();
-const mockSetOptions: jest.Mock = jest.fn();
-const mockNavigation = {
- navigate: mockNavigate,
- setOptions: mockSetOptions,
-} as unknown as NavigationProp;
+import { Provider } from 'react-redux';
+import createMockStore from 'redux-mock-store';
+const mockStore = createMockStore();
+const initialState = {};
+const store = mockStore(initialState);
describe('OnboardingCarousel', () => {
it('should render correctly', () => {
- const { toJSON } = renderWithProvider(
-
+ const wrapper = shallow(
+
+
+ ,
);
- expect(toJSON()).toMatchSnapshot();
- });
-
- it('should render the App Start Time text when isTest is true', async () => {
- const { toJSON, getByTestId } = renderWithProvider(
-
- );
- expect(toJSON()).toMatchSnapshot();
-
- await waitFor(() => {
- expect(
- getByTestId(PerformanceRegressionSelectorIDs.APP_START_TIME_ID),
- ).toBeTruthy();
- });
+ expect(wrapper).toMatchSnapshot();
});
});
diff --git a/app/components/Views/OnboardingCarousel/index.tsx b/app/components/Views/OnboardingCarousel/index.tsx
deleted file mode 100644
index 0641aa1e98b..00000000000
--- a/app/components/Views/OnboardingCarousel/index.tsx
+++ /dev/null
@@ -1,302 +0,0 @@
-import React, { useState, useEffect, useContext, useCallback } from 'react';
-import {
- Text,
- View,
- ScrollView,
- StyleSheet,
- Image,
- Dimensions,
- Platform,
-} from 'react-native';
-import type { ThemeColors } from '@metamask/design-tokens/dist/types/js/themes/types';
-import { NavigationProp, ParamListBase } from '@react-navigation/native';
-import { MetaMetricsEvents, IMetaMetricsEvent } from '../../../core/Analytics';
-import StyledButton from '../../UI/StyledButton';
-import { fontStyles, baseStyles } from '../../../styles/common';
-import { strings } from '../../../../locales/i18n';
-import FadeOutOverlay from '../../UI/FadeOutOverlay';
-import ScrollableTabView from 'react-native-scrollable-tab-view';
-import { getTransparentOnboardingNavbarOptions } from '../../UI/Navbar';
-import OnboardingScreenWithBg from '../../UI/OnboardingScreenWithBg';
-import Device from '../../../util/device';
-import { connect } from 'react-redux';
-import { ThemeContext, mockTheme } from '../../../util/theme';
-import { WELCOME_SCREEN_CAROUSEL_TITLE_ID } from '../../../../wdio/screen-objects/testIDs/Screens/WelcomeScreen.testIds';
-import { OnboardingCarouselSelectorIDs } from '../../../../e2e/selectors/Onboarding/OnboardingCarousel.selectors';
-import generateTestId from '../../../../wdio/utils/generateTestId';
-import trackOnboarding from '../../../util/metrics/TrackOnboarding/trackOnboarding';
-import { isTest } from '../../../util/test/utils';
-import StorageWrapper from '../../../store/storage-wrapper';
-import { PerformanceRegressionSelectorIDs } from '../../../../e2e/selectors/PerformanceRegression.selectors';
-import { JsonMap } from '@segment/analytics-react-native';
-import { Dispatch } from 'redux';
-import {
- saveOnboardingEvent as SaveEvent,
- OnboardingActionTypes,
-} from '../../../actions/onboarding';
-
-const IMAGE_3_RATIO = 215 / 315;
-const IMAGE_2_RATIO = 222 / 239;
-const IMAGE_1_RATIO = 285 / 203;
-const DEVICE_WIDTH = Dimensions.get('window').width;
-
-const IMG_PADDING = Device.isIphoneX() ? 100 : Device.isIphone5S() ? 180 : 220;
-
-const createStyles = (colors: ThemeColors) =>
- StyleSheet.create({
- scroll: {
- flexGrow: 1,
- },
- wrapper: {
- paddingVertical: 30,
- flex: 1,
- },
- title: {
- fontSize: 24,
- marginBottom: 12,
- color: colors.text.default,
- justifyContent: 'center',
- textAlign: 'center',
- ...fontStyles.bold,
- },
- subtitle: {
- fontSize: 14,
- lineHeight: 19,
- marginTop: 12,
- marginBottom: 25,
- color: colors.text.alternative,
- justifyContent: 'center',
- textAlign: 'center',
- ...fontStyles.normal,
- },
- ctas: {
- paddingHorizontal: 40,
- paddingBottom: Device.isIphoneX() ? 40 : 20,
- flexDirection: 'column',
- },
- ctaWrapper: {
- justifyContent: 'flex-end',
- },
- carouselImage: {},
- // eslint-disable-next-line react-native/no-unused-styles
- carouselImage1: {
- marginTop: 30,
- width: DEVICE_WIDTH - IMG_PADDING,
- height: (DEVICE_WIDTH - IMG_PADDING) * IMAGE_1_RATIO,
- },
- // eslint-disable-next-line react-native/no-unused-styles
- carouselImage2: {
- width: DEVICE_WIDTH - IMG_PADDING,
- height: (DEVICE_WIDTH - IMG_PADDING) * IMAGE_2_RATIO,
- },
- // eslint-disable-next-line react-native/no-unused-styles
- carouselImage3: {
- width: DEVICE_WIDTH - 60,
- height: (DEVICE_WIDTH - 60) * IMAGE_3_RATIO,
- },
- carouselImageWrapper: {
- flex: 1,
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'center',
- },
- circle: {
- width: 8,
- height: 8,
- borderRadius: 8 / 2,
- backgroundColor: colors.icon.default,
- opacity: 0.4,
- marginHorizontal: 8,
- },
- solidCircle: {
- opacity: 1,
- },
- progessContainer: {
- flexDirection: 'row',
- alignSelf: 'center',
- marginVertical: 36,
- },
- tab: {
- marginHorizontal: 30,
- },
- metricsWrapper: {
- flex: 1,
- marginTop: 12,
- marginBottom: 25,
- },
- metricsData: {
- fontSize: 10,
- color: colors.text.alternative,
- textAlign: 'center',
- },
- });
-
-const onboarding_carousel_1 = require('../../../images/onboarding-carousel-1.png'); // eslint-disable-line
-const onboarding_carousel_2 = require('../../../images/onboarding-carousel-2.png'); // eslint-disable-line
-const onboarding_carousel_3 = require('../../../images/onboarding-carousel-3.png'); // eslint-disable-line
-
-const carousel_images = [
- onboarding_carousel_1,
- onboarding_carousel_2,
- onboarding_carousel_3,
-];
-
-interface OnboardingCarouselProps {
- navigation: NavigationProp;
- saveOnboardingEvent: (...eventArgs: [IMetaMetricsEvent]) => void;
-}
-/**
- * View that is displayed to first time (new) users
- */
-export const OnboardingCarousel: React.FC = ({
- navigation,
- saveOnboardingEvent,
-}) => {
- const [currentTab, setCurrentTab] = useState(1);
- const [appStartTime, setAppStartTime] = useState(
- undefined,
- );
- const themeContext = useContext(ThemeContext);
- const colors = themeContext.colors || mockTheme.colors;
- const styles = createStyles(colors);
-
- const track = useCallback(
- (event: IMetaMetricsEvent, properties: JsonMap = {}) => {
- trackOnboarding(event, properties, saveOnboardingEvent);
- },
- [saveOnboardingEvent],
- );
-
- const onPressGetStarted = () => {
- navigation.navigate('Onboarding');
- track(MetaMetricsEvents.ONBOARDING_STARTED);
- };
-
- const renderTabBar = () => ;
-
- const onChangeTab = (obj: { i: number }) => {
- setCurrentTab(obj.i + 1);
- track(MetaMetricsEvents.ONBOARDING_WELCOME_SCREEN_ENGAGEMENT, {
- message_title: strings(`onboarding_carousel.title${[obj.i + 1]}`, {
- locale: 'en',
- }),
- });
- };
-
- const updateNavBar = useCallback(() => {
- navigation.setOptions(getTransparentOnboardingNavbarOptions(colors));
- }, [navigation, colors]);
-
- const initialize = useCallback(async () => {
- updateNavBar();
- track(MetaMetricsEvents.ONBOARDING_WELCOME_MESSAGE_VIEWED);
- const newAppStartTime = await StorageWrapper.getItem('appStartTime');
- setAppStartTime(newAppStartTime);
- }, [updateNavBar, track]);
-
- useEffect(() => {
- initialize();
- }, [initialize]);
-
- useEffect(() => {
- updateNavBar();
- }, [colors, updateNavBar]);
-
- return (
-
-
-
-
-
- {['one', 'two', 'three'].map((value, index) => {
- const key = index + 1;
- const imgStyleKey =
- `carouselImage${key}` as keyof typeof styles;
- return (
-
-
-
- {strings(`onboarding_carousel.title${key}`)}
-
- {isTest && (
- // This Text component is used to grab the App Start Time for our E2E test
- // ColdStartToOnboardingScreen.feature
-
- {appStartTime}
-
- )}
-
- {strings(`onboarding_carousel.subtitle${key}`)}
-
-
-
-
-
-
- );
- })}
-
-
-
- {[1, 2, 3].map((i) => (
-
- ))}
-
-
-
-
-
-
- {strings('onboarding_carousel.get_started')}
-
-
-
-
-
-
- );
-};
-
-const mapDispatchToProps = (dispatch: Dispatch) => ({
- saveOnboardingEvent: (...eventArgs: [IMetaMetricsEvent]) =>
- dispatch(SaveEvent(eventArgs)),
-});
-
-export default connect(null, mapDispatchToProps)(OnboardingCarousel);
diff --git a/app/components/Views/PickComponent/index.tsx b/app/components/Views/PickComponent/index.js
similarity index 65%
rename from app/components/Views/PickComponent/index.tsx
rename to app/components/Views/PickComponent/index.js
index bfebabcff60..e35cccdbf3b 100644
--- a/app/components/Views/PickComponent/index.tsx
+++ b/app/components/Views/PickComponent/index.js
@@ -1,11 +1,11 @@
import React, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
import { StyleSheet, View } from 'react-native';
import { baseStyles } from '../../../styles/common';
import { ThemeContext, mockTheme } from '../../../util/theme';
import RadioButton from '../../../component-library/components/RadioButton/RadioButton';
-import { Theme } from '@metamask/design-tokens';
-const createStyles = (_colors: Theme['colors']) =>
+const createStyles = (colors) =>
StyleSheet.create({
root: {
...baseStyles.flexGrow,
@@ -16,38 +16,36 @@ const createStyles = (_colors: Theme['colors']) =>
},
});
-interface PickComponentProps {
- /**
- * Callback to pick an option
- */
- pick?: (value: string) => void;
- /**
- * Text to first option
- */
- textFirst: string;
- /**
- * Value of first option
- */
- valueFirst: string;
- /**
- * Text to second option
- */
- textSecond: string;
- /**
- * Value of second option
- */
- valueSecond: string;
- /**
- * Current selected value
- */
- selectedValue: string;
-}
-
/**
- * Component that allows to select clicking two options
+ * Componets that allows to select clicking two options
*/
-export default class PickComponent extends PureComponent {
- static contextType = ThemeContext;
+export default class PickComponent extends PureComponent {
+ static propTypes = {
+ /**
+ * Callback to pick an option
+ */
+ pick: PropTypes.func,
+ /**
+ * Text to first option
+ */
+ textFirst: PropTypes.string,
+ /**
+ * Value of first option
+ */
+ valueFirst: PropTypes.string,
+ /**
+ * Text to second option
+ */
+ textSecond: PropTypes.string,
+ /**
+ * Value of second option
+ */
+ valueSecond: PropTypes.string,
+ /**
+ * Current selected value
+ */
+ selectedValue: PropTypes.string,
+ };
pickFirst = () => {
const { pick, valueFirst } = this.props;
@@ -85,3 +83,5 @@ export default class PickComponent extends PureComponent {
);
};
}
+
+PickComponent.contextType = ThemeContext;
diff --git a/app/components/Views/Settings/AdvancedSettings/__snapshots__/index.test.tsx.snap b/app/components/Views/Settings/AdvancedSettings/__snapshots__/index.test.tsx.snap
index 1f1923e0c31..943cc45a4d4 100644
--- a/app/components/Views/Settings/AdvancedSettings/__snapshots__/index.test.tsx.snap
+++ b/app/components/Views/Settings/AdvancedSettings/__snapshots__/index.test.tsx.snap
@@ -456,6 +456,7 @@ exports[`AdvancedSettings should render correctly 1`] = `
"marginTop": 32,
}
}
+ testID="token-detection-toggle"
>
- Autodetect tokens
+ Enhanced Token Detection
StyleSheet.create({
@@ -169,6 +169,14 @@ class AdvancedSettings extends PureComponent {
* Entire redux state used to generate state logs
*/
fullState: PropTypes.object,
+ /**
+ * ChainID of network
+ */
+ chainId: PropTypes.string,
+ /**
+ * Boolean that checks if token detection is enabled
+ */
+ isTokenDetectionEnabled: PropTypes.bool,
/**
* Object that represents the current route info like params passed to it
*/
@@ -255,6 +263,47 @@ class AdvancedSettings extends PureComponent {
PreferencesController.setUseTokenDetection(detectionStatus);
};
+ renderTokenDetectionSection = () => {
+ const { isTokenDetectionEnabled, chainId } = this.props;
+ const { styles, colors } = this.getStyles();
+ const theme = this.context || mockTheme;
+ if (!isTokenDetectionSupportedForNetwork(chainId)) {
+ return null;
+ }
+ return (
+
+
+
+ {strings('app_settings.token_detection_title')}
+
+
+
+
+
+
+ {strings('app_settings.token_detection_description')}
+
+
+ );
+ };
+
toggleSmartTransactionsOptInStatus = (smartTransactionsOptInStatus) => {
const { PreferencesController } = Engine.context;
PreferencesController.setSmartTransactionsOptInStatus(
@@ -428,7 +477,7 @@ class AdvancedSettings extends PureComponent {
{strings('app_settings.custom_nonce_desc')}
-
+ {this.renderTokenDetectionSection()}
diff --git a/app/components/Views/Settings/AutoDetectNFTSettings/__snapshots__/index.test.tsx.snap b/app/components/Views/Settings/AutoDetectNFTSettings/__snapshots__/index.test.tsx.snap
deleted file mode 100644
index 16f08a0bb24..00000000000
--- a/app/components/Views/Settings/AutoDetectNFTSettings/__snapshots__/index.test.tsx.snap
+++ /dev/null
@@ -1,89 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`AutoDetectNFTSettings should render correctly 1`] = `
-
-
-
- Autodetect NFTs
-
-
-
-
-
-
- Let MetaMask add NFTs you own using third-party services (like OpenSea). Autodetecting NFTs exposes your IP and account address to these services. Enabling this feature could associate your IP address with your Ethereum address and display fake NFTs airdropped by scammers. You can add tokens manually to avoid this risk.
-
-
-`;
diff --git a/app/components/Views/Settings/AutoDetectNFTSettings/index.constants.ts b/app/components/Views/Settings/AutoDetectNFTSettings/index.constants.ts
deleted file mode 100644
index f3047aa77ef..00000000000
--- a/app/components/Views/Settings/AutoDetectNFTSettings/index.constants.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export const NFT_AUTO_DETECT_MODE_SECTION =
- 'nft-opensea-autodetect-mode-section';
diff --git a/app/components/Views/Settings/AutoDetectNFTSettings/index.styles.ts b/app/components/Views/Settings/AutoDetectNFTSettings/index.styles.ts
deleted file mode 100644
index a7b2f4d82a8..00000000000
--- a/app/components/Views/Settings/AutoDetectNFTSettings/index.styles.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-const createStyles = () =>
- StyleSheet.create({
- titleContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- title: {
- flex: 1,
- },
- switchElement: {
- marginLeft: 16,
- },
- switch: {
- alignSelf: 'flex-start',
- },
- halfSetting: {
- marginTop: 16,
- },
- desc: {
- marginTop: 8,
- },
- setting: {
- marginTop: 32,
- },
- });
-
-export default createStyles;
diff --git a/app/components/Views/Settings/AutoDetectNFTSettings/index.test.tsx b/app/components/Views/Settings/AutoDetectNFTSettings/index.test.tsx
deleted file mode 100644
index fa84909edfa..00000000000
--- a/app/components/Views/Settings/AutoDetectNFTSettings/index.test.tsx
+++ /dev/null
@@ -1,158 +0,0 @@
-// Third party dependencies
-import React from 'react';
-import { fireEvent } from '@testing-library/react-native';
-import { useNavigation } from '@react-navigation/native';
-
-// External dependencies
-import Engine from '../../../../core/Engine';
-import renderWithProvider from '../../../../util/test/renderWithProvider';
-import { backgroundState } from '../../../../util/test/initial-root-state';
-
-// Internal dependencies
-import AutoDetectNFTSettings from './index';
-import { NFT_AUTO_DETECT_MODE_SECTION } from './index.constants';
-
-let mockSetDisplayNftMedia: jest.Mock;
-let mockSetUseNftDetection: jest.Mock;
-let mockAddTraitsToUser: jest.Mock;
-let mockTrackEvent: jest.Mock;
-
-beforeEach(() => {
- mockSetDisplayNftMedia.mockClear();
- mockSetUseNftDetection.mockClear();
- mockAddTraitsToUser.mockClear();
- mockTrackEvent.mockClear();
-});
-
-const mockEngine = Engine;
-
-jest.mock('../../../../core/Engine', () => {
- mockSetDisplayNftMedia = jest.fn();
- mockSetUseNftDetection = jest.fn();
- mockAddTraitsToUser = jest.fn();
- mockTrackEvent = jest.fn();
- return {
- init: () => mockEngine.init({}),
- context: {
- PreferencesController: {
- setDisplayNftMedia: mockSetDisplayNftMedia,
- setUseNftDetection: mockSetUseNftDetection,
- },
- },
- };
-});
-
-const mockNavigation = {
- goBack: jest.fn(),
- setOptions: jest.fn(),
-};
-
-jest.mock('@react-navigation/native', () => ({
- ...jest.requireActual('@react-navigation/native'),
- useNavigation: jest.fn(() => mockNavigation),
-}));
-
-jest.mock('../../../hooks/useMetrics', () => ({
- useMetrics: () => ({
- addTraitsToUser: mockAddTraitsToUser,
- trackEvent: mockTrackEvent,
- }),
- MetaMetricsEvents: {
- NFT_AUTO_DETECTION_ENABLED: 'NFT_AUTO_DETECTION_ENABLED',
- },
-}));
-
-jest.mock('../../../../util/general', () => ({
- timeoutFetch: jest.fn(),
-}));
-
-describe('AutoDetectNFTSettings', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- (useNavigation as jest.Mock).mockImplementation(() => mockNavigation);
- });
-
- const initialState = {
- engine: {
- backgroundState: {
- ...backgroundState,
- PreferencesController: {
- ...backgroundState.PreferencesController,
- useTokenDetection: true,
- displayNftMedia: false,
- useNftDetection: false,
- },
- },
- },
- network: {
- provider: {
- chainId: '1',
- },
- },
- };
-
- it('should render correctly', () => {
- const tree = renderWithProvider(, {
- state: initialState,
- });
- expect(tree).toMatchSnapshot();
- });
-
- describe('NFT Autodetection', () => {
- it('should render NFT autodetection switch', () => {
- const { getByTestId } = renderWithProvider(, {
- state: initialState,
- });
- const autoDetectSwitch = getByTestId(NFT_AUTO_DETECT_MODE_SECTION);
- expect(autoDetectSwitch).toBeTruthy();
- });
-
- it('should toggle NFT autodetection when switch is pressed', () => {
- const { getByTestId } = renderWithProvider(, {
- state: initialState,
- });
- const autoDetectSwitch = getByTestId(NFT_AUTO_DETECT_MODE_SECTION);
- fireEvent(autoDetectSwitch, 'onValueChange', true);
-
- expect(
- Engine.context.PreferencesController.setUseNftDetection,
- ).toHaveBeenCalledWith(true);
- expect(
- Engine.context.PreferencesController.setDisplayNftMedia,
- ).toHaveBeenCalledWith(true);
- expect(mockAddTraitsToUser).toHaveBeenCalledWith({
- 'NFT Autodetection': 'ON',
- });
- expect(mockTrackEvent).toHaveBeenCalledWith(
- 'NFT_AUTO_DETECTION_ENABLED',
- {
- 'NFT Autodetection': 'ON',
- location: 'app_settings',
- },
- );
- });
-
- it('should not enable display NFT media when autodetection is turned off', () => {
- const { getByTestId } = renderWithProvider(, {
- state: initialState,
- });
- const autoDetectSwitch = getByTestId(NFT_AUTO_DETECT_MODE_SECTION);
- expect(autoDetectSwitch).toBeTruthy();
-
- fireEvent(autoDetectSwitch, 'onValueChange', false);
-
- expect(mockSetUseNftDetection).toHaveBeenCalledWith(false);
- expect(mockSetDisplayNftMedia).not.toHaveBeenCalled();
- expect(mockAddTraitsToUser).toHaveBeenCalledWith({
- 'NFT Autodetection': 'OFF',
- });
- expect(mockTrackEvent).toHaveBeenCalledWith(
- 'NFT_AUTO_DETECTION_ENABLED',
- {
- 'NFT Autodetection': 'OFF',
- location: 'app_settings',
- },
- );
- });
- });
-});
diff --git a/app/components/Views/Settings/AutoDetectNFTSettings/index.tsx b/app/components/Views/Settings/AutoDetectNFTSettings/index.tsx
deleted file mode 100644
index 0cc7ecbe88d..00000000000
--- a/app/components/Views/Settings/AutoDetectNFTSettings/index.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-// Third party dependencies
-import React, { useCallback } from 'react';
-import { View, Switch } from 'react-native';
-import { useSelector } from 'react-redux';
-
-// External dependencies
-import Engine from '../../../../core/Engine';
-import { selectUseNftDetection } from '../../../../selectors/preferencesController';
-import { useTheme } from '../../../../util/theme';
-import { strings } from '../../../../../locales/i18n';
-import { MetaMetricsEvents, useMetrics } from '../../../hooks/useMetrics';
-import Text, {
- TextVariant,
- TextColor,
-} from '../../../../component-library/components/Texts/Text';
-import { UserProfileProperty } from '../../../../util/metrics/UserSettingsAnalyticsMetaData/UserProfileAnalyticsMetaData.types';
-
-// Internal dependencies
-import createStyles from './index.styles';
-import { NFT_AUTO_DETECT_MODE_SECTION } from './index.constants';
-
-const AutoDetectNFTSettings = () => {
- const { trackEvent, addTraitsToUser } = useMetrics();
- const theme = useTheme();
- const { colors } = theme;
- const styles = createStyles();
-
- const useNftDetection = useSelector(selectUseNftDetection);
-
- const toggleNftAutodetect = useCallback(
- (value: boolean) => {
- const { PreferencesController } = Engine.context;
- if (value) {
- PreferencesController.setDisplayNftMedia(value);
- }
- PreferencesController.setUseNftDetection(value);
- const traits = {
- [UserProfileProperty.NFT_AUTODETECTION]: value
- ? UserProfileProperty.ON
- : UserProfileProperty.OFF,
- };
- addTraitsToUser(traits);
- trackEvent(MetaMetricsEvents.NFT_AUTO_DETECTION_ENABLED, {
- ...traits,
- location: 'app_settings',
- });
- },
- [addTraitsToUser, trackEvent],
- );
-
- return (
-
-
-
- {strings('app_settings.nft_autodetect_mode')}
-
-
-
-
-
-
- {strings('app_settings.autodetect_nft_desc')}
-
-
- );
-};
-
-export default AutoDetectNFTSettings;
diff --git a/app/components/Views/Settings/AutoDetectTokensSettings/__snapshots__/index.test.tsx.snap b/app/components/Views/Settings/AutoDetectTokensSettings/__snapshots__/index.test.tsx.snap
deleted file mode 100644
index 180daab2501..00000000000
--- a/app/components/Views/Settings/AutoDetectTokensSettings/__snapshots__/index.test.tsx.snap
+++ /dev/null
@@ -1,91 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`AssetSettings should render correctly 1`] = `
-
-
-
- Autodetect tokens
-
-
-
-
-
-
- We use third-party APIs to detect and display new tokens sent to your wallet. Turn off if you don’t want the app to pull data from those services.
-
-
-`;
diff --git a/app/components/Views/Settings/AutoDetectTokensSettings/index.constants.ts b/app/components/Views/Settings/AutoDetectTokensSettings/index.constants.ts
deleted file mode 100644
index e3ae2d01629..00000000000
--- a/app/components/Views/Settings/AutoDetectTokensSettings/index.constants.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const TOKEN_DETECTION_TOGGLE = 'token-detection-toggle';
diff --git a/app/components/Views/Settings/AutoDetectTokensSettings/index.styles.ts b/app/components/Views/Settings/AutoDetectTokensSettings/index.styles.ts
deleted file mode 100644
index 6a79c3498eb..00000000000
--- a/app/components/Views/Settings/AutoDetectTokensSettings/index.styles.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-// Third party dependencies.
-import { StyleSheet } from 'react-native';
-
-const styleSheet = () =>
- StyleSheet.create({
- setting: {
- marginTop: 32,
- },
- titleContainer: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- title: {
- flex: 1,
- },
- toggle: {
- flexDirection: 'row',
- alignItems: 'center',
- marginLeft: 16,
- },
- switch: {
- alignSelf: 'flex-start',
- },
- desc: {
- marginTop: 8,
- },
- });
-
-export default styleSheet;
diff --git a/app/components/Views/Settings/AutoDetectTokensSettings/index.test.tsx b/app/components/Views/Settings/AutoDetectTokensSettings/index.test.tsx
deleted file mode 100644
index c463bb86e38..00000000000
--- a/app/components/Views/Settings/AutoDetectTokensSettings/index.test.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-// Third party dependencies
-import React from 'react';
-import { fireEvent } from '@testing-library/react-native';
-
-// External dependencies
-import Engine from '../../../../core/Engine';
-import renderWithProvider from '../../../../util/test/renderWithProvider';
-import { backgroundState } from '../../../../util/test/initial-root-state';
-
-// Internal dependencies
-import AssetSettings from '.';
-import { TOKEN_DETECTION_TOGGLE } from './index.constants';
-
-let mockSetUseTokenDetection: jest.Mock;
-
-beforeEach(() => {
- mockSetUseTokenDetection.mockClear();
-});
-
-const mockEngine = Engine;
-
-jest.mock('../../../../core/Engine', () => {
- mockSetUseTokenDetection = jest.fn();
- return {
- init: () => mockEngine.init({}),
- context: {
- PreferencesController: {
- setUseTokenDetection: mockSetUseTokenDetection,
- },
- },
- };
-});
-
-describe('AssetSettings', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- const initialState = {
- engine: {
- backgroundState: {
- ...backgroundState,
- PreferencesController: {
- ...backgroundState.PreferencesController,
- useTokenDetection: true,
- },
- },
- },
- };
-
- it('should render correctly', () => {
- const tree = renderWithProvider(, {
- state: initialState,
- });
- expect(tree).toMatchSnapshot();
- });
-
- describe('Token Detection', () => {
- it('should toggle token detection when switch is pressed', () => {
- const { getByTestId } = renderWithProvider(, {
- state: initialState,
- });
- const toggleSwitch = getByTestId(TOKEN_DETECTION_TOGGLE);
- fireEvent(toggleSwitch, 'onValueChange', false);
- expect(mockSetUseTokenDetection).toHaveBeenCalledWith(false);
- });
- });
-});
diff --git a/app/components/Views/Settings/AutoDetectTokensSettings/index.tsx b/app/components/Views/Settings/AutoDetectTokensSettings/index.tsx
deleted file mode 100644
index d6ba8bb075e..00000000000
--- a/app/components/Views/Settings/AutoDetectTokensSettings/index.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-// Third party dependencies
-import React, { useCallback } from 'react';
-import { View, Switch } from 'react-native';
-import { useSelector } from 'react-redux';
-
-// External dependencies
-import { useStyles } from '../../../../component-library/hooks';
-import Engine from '../../../../core/Engine';
-import { selectUseTokenDetection } from '../../../../selectors/preferencesController';
-import { useTheme } from '../../../../util/theme';
-import { strings } from '../../../../../locales/i18n';
-import Text, {
- TextVariant,
- TextColor,
-} from '../../../../component-library/components/Texts/Text';
-
-// Internal dependencies
-import styleSheet from './index.styles';
-import { TOKEN_DETECTION_TOGGLE } from './index.constants';
-
-const AutoDetectTokensSettings = () => {
- const theme = useTheme();
- const { colors } = theme;
- const { styles } = useStyles(styleSheet, {});
-
- const isTokenDetectionEnabled = useSelector(selectUseTokenDetection);
-
- const toggleTokenDetection = useCallback((value: boolean) => {
- Engine.context.PreferencesController.setUseTokenDetection(value);
- }, []);
-
- return (
-
-
-
- {strings('app_settings.token_detection_title')}
-
-
-
-
-
-
- {strings('app_settings.token_detection_description')}
-
-
- );
-};
-
-export default AutoDetectTokensSettings;
diff --git a/app/components/Views/Settings/NotificationsSettings/index.test.tsx b/app/components/Views/Settings/NotificationsSettings/index.test.tsx
index ff8b0928e70..292c0906062 100644
--- a/app/components/Views/Settings/NotificationsSettings/index.test.tsx
+++ b/app/components/Views/Settings/NotificationsSettings/index.test.tsx
@@ -1,16 +1,11 @@
-import React, { useCallback } from 'react';
-import { renderHook, act } from '@testing-library/react-hooks';
+import React from 'react';
import renderWithProvider from '../../../../util/test/renderWithProvider';
-import NotificationsService from '../../../../util/notifications/services/NotificationService';
+
import { backgroundState } from '../../../../util/test/initial-root-state';
import NotificationsSettings from '.';
-
-import Routes from '../../../../constants/navigation/Routes';
import { Props } from './NotificationsSettings.types';
import { MOCK_ACCOUNTS_CONTROLLER_STATE } from '../../../../util/test/accountsControllerTestUtils';
-import { MetaMetricsEvents } from '../../../../core/Analytics/MetaMetrics.events';
-import { NavigationProp, ParamListBase } from '@react-navigation/native';
// Mock store.getState
let mockGetState: jest.Mock;
@@ -43,142 +38,8 @@ const mockInitialState = {
},
};
-jest.mock('@react-navigation/native', () => {
- const actualNav = jest.requireActual('@react-navigation/native');
- return {
- ...actualNav,
- useNavigation: () => ({
- navigate: jest.fn(),
- }),
- };
-});
-
-jest.mock('../../../../util/notifications/services/NotificationService', () => ({
- getAllPermissions: jest.fn(),
-}));
-
-jest.mock('../../../../core/Analytics/MetaMetrics.events', () => ({
- MetaMetricsEvents: {
- NOTIFICATIONS_SETTINGS_UPDATED: 'NOTIFICATIONS_SETTINGS_UPDATED',
- },
-}));
-
-const mockDisableNotifications = jest.fn();
-const mockEnableNotifications = jest.fn();
-const mockSetUiNotificationStatus = jest.fn();
-const mockTrackEvent = jest.fn();
-
-const mockNavigation = {
- navigate: jest.fn(),
-} as unknown as NavigationProp;
-
const setOptions = jest.fn();
-describe('toggleNotificationsEnabled', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- const setup = (basicFunctionalityEnabled: boolean, isMetamaskNotificationsEnabled: boolean, isProfileSyncingEnabled: boolean) => renderHook(() =>
- useCallback(async () => {
- if (!basicFunctionalityEnabled) {
- mockNavigation.navigate(Routes.MODAL.ROOT_MODAL_FLOW, {
- screen: Routes.SHEET.BASIC_FUNCTIONALITY,
- params: {
- caller: Routes.SETTINGS.NOTIFICATIONS,
- },
- });
- } else if (isMetamaskNotificationsEnabled) {
- mockDisableNotifications();
- mockSetUiNotificationStatus(false);
- } else {
- const { permission } = await NotificationsService.getAllPermissions(false);
- if (permission !== 'authorized') {
- return;
- }
-
- mockEnableNotifications();
- mockSetUiNotificationStatus(true);
- }
- mockTrackEvent(MetaMetricsEvents.NOTIFICATIONS_SETTINGS_UPDATED, {
- settings_type: 'notifications',
- old_value: isMetamaskNotificationsEnabled,
- new_value: !isMetamaskNotificationsEnabled,
- was_profile_syncing_on: isMetamaskNotificationsEnabled ? true : isProfileSyncingEnabled,
- });
- }, [])
- );
-
- it('should navigate to basic functionality screen if basicFunctionalityEnabled is false', async () => {
- const { result } = setup(false, false, false);
-
- await act(async () => {
- await result.current();
- });
-
- expect(mockNavigation.navigate).toHaveBeenCalledWith(Routes.MODAL.ROOT_MODAL_FLOW, {
- screen: Routes.SHEET.BASIC_FUNCTIONALITY,
- params: {
- caller: Routes.SETTINGS.NOTIFICATIONS,
- },
- });
- });
-
- it('should disable notifications if isMetamaskNotificationsEnabled is true', async () => {
- const { result } = setup(true, true, false);
-
- await act(async () => {
- await result.current();
- });
-
- expect(mockDisableNotifications).toHaveBeenCalled();
- expect(mockSetUiNotificationStatus).toHaveBeenCalledWith(false);
- });
-
- it('should enable notifications if isMetamaskNotificationsEnabled is false and permission is authorized', async () => {
- (NotificationsService.getAllPermissions as jest.Mock).mockResolvedValue({ permission: 'authorized' });
-
- const { result } = setup(true, false, false);
-
- await act(async () => {
- await result.current();
- });
-
- expect(mockEnableNotifications).toHaveBeenCalled();
- expect(mockSetUiNotificationStatus).toHaveBeenCalledWith(true);
- });
-
- it('should not enable notifications if permission is not authorized', async () => {
- (NotificationsService.getAllPermissions as jest.Mock).mockResolvedValue({ permission: 'denied' });
-
- const { result } = setup(true, false, false);
-
- await act(async () => {
- await result.current();
- });
-
- expect(mockEnableNotifications).not.toHaveBeenCalled();
- expect(mockSetUiNotificationStatus).not.toHaveBeenCalled();
- });
-
- it('should track event when notifications settings are updated', async () => {
- (NotificationsService.getAllPermissions as jest.Mock).mockResolvedValue({ permission: 'authorized' });
-
- const { result } = setup(true, false, true);
-
- await act(async () => {
- await result.current();
- });
-
- expect(mockTrackEvent).toHaveBeenCalledWith(MetaMetricsEvents.NOTIFICATIONS_SETTINGS_UPDATED, {
- settings_type: 'notifications',
- old_value: false,
- new_value: true,
- was_profile_syncing_on: true,
- });
- });
-});
-
describe('NotificationsSettings', () => {
it('should render correctly', () => {
mockGetState.mockImplementation(() => ({
@@ -199,21 +60,4 @@ describe('NotificationsSettings', () => {
);
expect(toJSON()).toMatchSnapshot();
});
-
- it('should toggle notifications and handle permission correctly', async () => {
- const isMetamaskNotificationsEnabled = true;
- const basicFunctionalityEnabled = true;
- const isProfileSyncingEnabled = true;
-
- const toggleNotificationsEnabledImpl = jest.fn(() => Promise.resolve({
- isMetamaskNotificationsEnabled,
- basicFunctionalityEnabled,
- isProfileSyncingEnabled,
- }));
-
- await toggleNotificationsEnabledImpl();
-
- expect(NotificationsService.getAllPermissions).toHaveBeenCalledTimes(1);
- expect(NotificationsService.getAllPermissions).toHaveBeenCalledWith(false);
- });
});
diff --git a/app/components/Views/Settings/NotificationsSettings/index.tsx b/app/components/Views/Settings/NotificationsSettings/index.tsx
index 707d2a24b7e..9dd493d57e6 100644
--- a/app/components/Views/Settings/NotificationsSettings/index.tsx
+++ b/app/components/Views/Settings/NotificationsSettings/index.tsx
@@ -31,7 +31,10 @@ import {
selectIsProfileSyncingEnabled,
} from '../../../../selectors/notifications';
-import NotificationsService from '../../../../util/notifications/services/NotificationService';
+import {
+ requestPushNotificationsPermission,
+ asyncAlert,
+} from '../../../../util/notifications';
import Routes from '../../../../constants/navigation/Routes';
import ButtonIcon, {
@@ -51,6 +54,7 @@ import AppConstants from '../../../../core/AppConstants';
import notificationsRows from './notificationsRows';
import { IconName } from '../../../../component-library/components/Icons/Icon';
import { MetaMetricsEvents } from '../../../../core/Analytics/MetaMetrics.events';
+import { AuthorizationStatus } from '@notifee/react-native';
interface MainNotificationSettingsProps extends Props {
toggleNotificationsEnabled: () => void;
@@ -183,17 +187,18 @@ const NotificationsSettings = ({ navigation, route }: Props) => {
disableNotifications();
setUiNotificationStatus(false);
} else {
- const { permission } = await NotificationsService.getAllPermissions(false);
- if (permission !== 'authorized') {
- return;
- }
+ const nativeNotificationStatus = await requestPushNotificationsPermission(
+ asyncAlert,
+ );
+ if (nativeNotificationStatus?.authorizationStatus === AuthorizationStatus.AUTHORIZED) {
/**
* Although this is an async function, we are dispatching an action (firing & forget)
* to emulate optimistic UI.
*/
enableNotifications();
setUiNotificationStatus(true);
+ }
}
trackEvent(MetaMetricsEvents.NOTIFICATIONS_SETTINGS_UPDATED, {
settings_type: 'notifications',
diff --git a/app/components/Views/Settings/PermissionsSettings/PermissionsManager.tsx b/app/components/Views/Settings/PermissionsSettings/PermissionsManager.tsx
index 0e5f26cb4ff..1d835b161ac 100644
--- a/app/components/Views/Settings/PermissionsSettings/PermissionsManager.tsx
+++ b/app/components/Views/Settings/PermissionsSettings/PermissionsManager.tsx
@@ -14,7 +14,7 @@ import Icon, {
import Text, {
TextVariant,
} from '../../../../component-library/components/Texts/Text';
-import { isMultichainVersion1Enabled } from '../../../../util/networks';
+import { isMutichainVersion1Enabled } from '../../../../util/networks';
import { getNavigationOptionsTitle } from '../../../UI/Navbar';
import PermissionItem from './PermissionItem';
import mockPermissionItems from './PermissionItem/PermissionItem.constants';
@@ -82,7 +82,7 @@ const PermissionsManager = (props: SDKSessionsManagerProps) => {
{
/* TODO: replace mock data with real data once available */
- isMultichainVersion1Enabled &&
+ isMutichainVersion1Enabled &&
mockPermissionItems.map((mockPermissionItem, _index) => (
{
style={styles.perissionsWrapper}
testID={SDKSelectorsIDs.SESSION_MANAGER_CONTAINER}
>
- {isMultichainVersion1Enabled && mockPermissionItems.length
+ {isMutichainVersion1Enabled && mockPermissionItems.length
? renderPermissions()
: renderEmptyResult()}
diff --git a/app/components/Views/Settings/SecuritySettings/SecuritySettings.tsx b/app/components/Views/Settings/SecuritySettings/SecuritySettings.tsx
index 5dda58ef252..f7ae9d6eff5 100644
--- a/app/components/Views/Settings/SecuritySettings/SecuritySettings.tsx
+++ b/app/components/Views/Settings/SecuritySettings/SecuritySettings.tsx
@@ -58,6 +58,7 @@ import {
selectIsIpfsGatewayEnabled,
selectIsMultiAccountBalancesEnabled,
selectDisplayNftMedia,
+ selectUseNftDetection,
selectShowIncomingTransactionNetworks,
selectShowTestNetworks,
selectUseSafeChainsListValidation,
@@ -88,6 +89,7 @@ import {
HASH_STRING,
HASH_TO_TEST,
IPFS_GATEWAY_SECTION,
+ NFT_AUTO_DETECT_MODE_SECTION,
NFT_DISPLAY_MEDIA_MODE_SECTION,
PASSCODE_CHOICE_STRING,
SDK_SECTION,
@@ -119,6 +121,7 @@ import ProfileSyncingComponent from '../../../UI/ProfileSyncing/ProfileSyncing';
import Routes from '../../../../constants/navigation/Routes';
import { MetaMetrics } from '../../../../core/Analytics';
import MetaMetricsAndDataCollectionSection from './Sections/MetaMetricsAndDataCollectionSection/MetaMetricsAndDataCollectionSection';
+import { UserProfileProperty } from '../../../../util/metrics/UserSettingsAnalyticsMetaData/UserProfileAnalyticsMetaData.types';
import {
selectIsMetamaskNotificationsEnabled,
selectIsProfileSyncingEnabled,
@@ -129,8 +132,6 @@ import { RootState } from '../../../../reducers';
import { EtherscanSupportedHexChainId } from '@metamask/preferences-controller';
import { useDisableNotifications } from '../../../../util/notifications/hooks/useNotifications';
import { isNotificationsFeatureEnabled } from '../../../../util/notifications';
-import AutoDetectNFTSettings from '../../Settings/AutoDetectNFTSettings';
-
const Heading: React.FC = ({ children, first }) => {
const { colors } = useTheme();
const styles = createStyles(colors);
@@ -144,7 +145,7 @@ const Heading: React.FC = ({ children, first }) => {
};
const Settings: React.FC = () => {
- const { trackEvent, isEnabled } = useMetrics();
+ const { trackEvent, isEnabled, addTraitsToUser } = useMetrics();
const theme = useTheme();
const { colors } = theme;
const styles = createStyles(colors);
@@ -197,6 +198,8 @@ const Settings: React.FC = () => {
selectUseTransactionSimulations,
);
+ const useNftDetection = useSelector(selectUseNftDetection);
+
const isNotificationEnabled = useSelector(
selectIsMetamaskNotificationsEnabled,
);
@@ -583,6 +586,27 @@ const Settings: React.FC = () => {
if (!value) PreferencesController?.setUseNftDetection(value);
};
+ const toggleNftAutodetect = useCallback(
+ (value) => {
+ const { PreferencesController } = Engine.context;
+ if (value) {
+ PreferencesController.setDisplayNftMedia(value);
+ }
+ PreferencesController.setUseNftDetection(value);
+ const traits = {
+ [UserProfileProperty.NFT_AUTODETECTION]: value
+ ? UserProfileProperty.ON
+ : UserProfileProperty.OFF,
+ };
+ addTraitsToUser(traits);
+ trackEvent(MetaMetricsEvents.NFT_AUTO_DETECTION_ENABLED, {
+ ...traits,
+ location: 'app_settings',
+ });
+ },
+ [addTraitsToUser, trackEvent],
+ );
+
const renderDisplayNftMedia = useCallback(
() => (
@@ -708,6 +732,43 @@ const Settings: React.FC = () => {
[colors, styles, useTransactionSimulations, theme.brandColors.white],
);
+ const renderAutoDetectNft = useCallback(
+ () => (
+
+
+
+ {strings('app_settings.nft_autodetect_mode')}
+
+
+
+
+
+
+ {strings('app_settings.autodetect_nft_desc')}
+
+
+ ),
+ [colors, styles, useNftDetection, theme, toggleNftAutodetect],
+ );
+
const setIpfsGateway = (gateway: string) => {
const { PreferencesController } = Engine.context;
PreferencesController.setIpfsGateway(gateway);
@@ -1093,11 +1154,7 @@ const Settings: React.FC = () => {
{strings('app_settings.token_nft_ens_subheading')}
{renderDisplayNftMedia()}
- {isMainnet && (
-
-
-
- )}
+ {isMainnet && renderAutoDetectNft()}
{renderIpfsGateway()}
-
+
-
-
+
- Autodetect NFTs
-
-
+
-
-
-
-
- Let MetaMask add NFTs you own using third-party services (like OpenSea). Autodetecting NFTs exposes your IP and account address to these services. Enabling this feature could associate your IP address with your Ethereum address and display fake NFTs airdropped by scammers. You can add tokens manually to avoid this risk.
-
+ thumbTintColor="#ffffff"
+ tintColor="#bbc0c566"
+ value={true}
+ />
+
+
+ Let MetaMask add NFTs you own using third-party services (like OpenSea). Autodetecting NFTs exposes your IP and account address to these services. Enabling this feature could associate your IP address with your Ethereum address and display fake NFTs airdropped by scammers. You can add tokens manually to avoid this risk.
+
StyleSheet.create({
@@ -202,7 +202,7 @@ const Settings = () => {
testID={SettingsViewSelectorsIDs.NOTIFICATIONS}
/>
)}
- {isMultichainVersion1Enabled && (
+ {isMutichainVersion1Enabled && (
{
const snapGetBip44EntropyTitle = (protocol: string) =>
`Control your ${protocol} accounts and assets`;
const snapGetEntropyTitle = 'Derive arbitrary keys unique to this snap';
- const endowmentKeyringTitle =
- 'Allow requests for adding and controlling Ethereum accounts';
+ const endowmentKeyringTitle = 'endowment:keyring';
const endowmentWebassemblyTitle = 'Support for WebAssembly';
const endowmentEthereumProviderTitle = 'Access the Ethereum provider';
const endowmentUnknownTitle = 'Unknown permission';
@@ -43,7 +42,6 @@ describe('SnapPermissions', () => {
'See the origins of websites that suggest transactions';
const endowmentExtendRuntimeTitle = 'Extend runtime';
const snapDialogTitle = 'Display custom dialogs';
- const snapManageAccountsTitle = 'Add and control Ethereum accounts';
const mockCoinTypes = [
{ coinType: 0 }, // Bitcoin
@@ -401,13 +399,6 @@ describe('SnapPermissions', () => {
caveats: null,
date: mockDate2,
},
- snap_manageAccounts: {
- caveats: null,
- date: mockDate2,
- id: '_jJdNdRnD5pXD728ngmHY',
- invoker: 'npm:@metamask/snap-simple-keyring-snap',
- parentCapability: 'snap_manageAccounts',
- },
};
const { getAllByTestId } = render(
@@ -418,7 +409,7 @@ describe('SnapPermissions', () => {
const permissionCellTitles = getAllByTestId(SNAP_PERMISSIONS_TITLE);
const permissionCellDates = getAllByTestId(SNAP_PERMISSIONS_DATE);
- expect(permissionCells.length).toBe(22);
+ expect(permissionCells.length).toBe(21);
expect(permissionCellTitles[0].props.children).toBe(longRunningTitle);
expect(permissionCellDates[0].props.children).toBe(
'Approved on May 24 at 5:35 pm',
@@ -519,10 +510,6 @@ describe('SnapPermissions', () => {
expect(permissionCellDates[20].props.children).toBe(
'Approved on Jun 6 at 4:02 pm',
);
- expect(permissionCellTitles[21].props.children).toBe(snapManageAccountsTitle);
- expect(permissionCellDates[21].props.children).toBe(
- 'Approved on Jun 6 at 4:02 pm',
- );
});
it('renders correct installed date', () => {
diff --git a/app/components/Views/Wallet/__snapshots__/index.test.tsx.snap b/app/components/Views/Wallet/__snapshots__/index.test.tsx.snap
index 3ec9cf2c174..31493f40c76 100644
--- a/app/components/Views/Wallet/__snapshots__/index.test.tsx.snap
+++ b/app/components/Views/Wallet/__snapshots__/index.test.tsx.snap
@@ -769,11 +769,11 @@ exports[`Wallet should render correctly 1`] = `
style={
{
"color": "#141618",
- "fontFamily": "EuclidCircularB-Medium",
- "fontSize": 14,
- "fontWeight": "500",
+ "fontFamily": "EuclidCircularB-Regular",
+ "fontSize": 16,
+ "fontWeight": "400",
"letterSpacing": 0,
- "lineHeight": 22,
+ "lineHeight": 24,
}
}
testID="account-label"
@@ -783,7 +783,7 @@ exports[`Wallet should render correctly 1`] = `
@@ -873,9 +871,9 @@ exports[`Wallet should render correctly 1`] = `
style={
{
"color": "#0376c9",
- "fontFamily": "EuclidCircularB-Medium",
+ "fontFamily": "EuclidCircularB-Regular",
"fontSize": 12,
- "fontWeight": "500",
+ "fontWeight": "400",
"letterSpacing": 0,
"lineHeight": 20,
}
diff --git a/app/components/Views/Wallet/index.tsx b/app/components/Views/Wallet/index.tsx
index 886a36d645a..e127c3ca2d3 100644
--- a/app/components/Views/Wallet/index.tsx
+++ b/app/components/Views/Wallet/index.tsx
@@ -35,7 +35,9 @@ import {
ToastContext,
ToastVariants,
} from '../../../component-library/components/Toast';
-import NotificationsService from '../../../util/notifications/services/NotificationService';
+import {
+ isDeviceNotificationEnabled,
+} from '../../../util/notifications';
import Engine from '../../../core/Engine';
import CollectibleContracts from '../../UI/CollectibleContracts';
import { MetaMetricsEvents } from '../../../core/Analytics';
@@ -341,7 +343,7 @@ const Wallet = ({
}
async function checkIfNotificationsAreEnabled() {
- await NotificationsService.isDeviceNotificationEnabled();
+ await isDeviceNotificationEnabled();
}
checkIfNotificationsAreEnabled();
});
diff --git a/app/components/Views/confirmations/Confirm/Confirm.test.tsx b/app/components/Views/confirmations/Confirm/Confirm.test.tsx
deleted file mode 100644
index af02b70c2fa..00000000000
--- a/app/components/Views/confirmations/Confirm/Confirm.test.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react';
-
-import renderWithProvider from '../../../../util/test/renderWithProvider';
-import { personalSignatureConfirmationState } from '../../../../util/test/confirm-data-helpers';
-import Confirm from './index';
-
-describe('Confirm', () => {
- it('should match snapshot for personal sign', async () => {
- const container = renderWithProvider(, {
- state: personalSignatureConfirmationState,
- });
- expect(container).toMatchSnapshot();
- });
-});
diff --git a/app/components/Views/confirmations/Confirm/Confirm.tsx b/app/components/Views/confirmations/Confirm/Confirm.tsx
deleted file mode 100644
index bf2c015f929..00000000000
--- a/app/components/Views/confirmations/Confirm/Confirm.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from 'react';
-import { View } from 'react-native';
-
-import BottomModal from '../../../../components/UI/BottomModal';
-import { useTheme } from '../../../../util/theme';
-import Footer from '../components/Confirm/Footer';
-import Title from '../components/Confirm/Title';
-import useConfirmationRedesignEnabled from '../hooks/useConfirmationRedesignEnabled';
-import createStyles from './style';
-
-const Confirm = () => {
- const { colors } = useTheme();
- const { isRedesignedEnabled } = useConfirmationRedesignEnabled();
-
- if (!isRedesignedEnabled) {
- return null;
- }
-
- const styles = createStyles(colors);
-
- return (
-
-
-
-
-
-
- );
-};
-
-export default Confirm;
diff --git a/app/components/Views/confirmations/Confirm/__snapshots__/Confirm.test.tsx.snap b/app/components/Views/confirmations/Confirm/__snapshots__/Confirm.test.tsx.snap
deleted file mode 100644
index 9a19ed18971..00000000000
--- a/app/components/Views/confirmations/Confirm/__snapshots__/Confirm.test.tsx.snap
+++ /dev/null
@@ -1,279 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Confirm should match snapshot for personal sign 1`] = `
-
-
-
-
-
-
- Signature request
-
-
-
-
-
- Reject
-
-
-
-
-
- Confirm
-
-
-
-
-
-
-`;
diff --git a/app/components/Views/confirmations/Confirm/index.tsx b/app/components/Views/confirmations/Confirm/index.tsx
deleted file mode 100644
index a4cf806e5d8..00000000000
--- a/app/components/Views/confirmations/Confirm/index.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './Confirm';
diff --git a/app/components/Views/confirmations/Confirm/style.tsx b/app/components/Views/confirmations/Confirm/style.tsx
deleted file mode 100644
index b1ac082a128..00000000000
--- a/app/components/Views/confirmations/Confirm/style.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-import Device from '../../../../util/device';
-import { Colors } from '../../../../util/theme/models';
-
-const createStyles = (colors: Colors) =>
- StyleSheet.create({
- container: {
- backgroundColor: colors.background.alternative,
- paddingTop: 24,
- minHeight: '90%',
- borderTopLeftRadius: 20,
- borderTopRightRadius: 20,
- paddingBottom: Device.isIphoneX() ? 20 : 0,
- alignItems: 'center',
- },
- });
-
-export default createStyles;
diff --git a/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/types.ts b/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/types.ts
index bf83928a2bd..4e7da1e8f0b 100644
--- a/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/types.ts
+++ b/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/types.ts
@@ -1,4 +1,4 @@
-import { AddressBookControllerState } from '@metamask/address-book-controller';
+import { AddressBookState } from '@metamask/address-book-controller';
import { NetworkType } from '@metamask/controller-utils';
import { InternalAccount } from '@metamask/keyring-api';
import type { NetworkState } from '@metamask/network-controller';
@@ -17,6 +17,6 @@ export interface AddNicknameProps {
providerChainId: Hex;
providerNetwork: string;
providerRpcTarget: string;
- addressBook: AddressBookControllerState['addressBook'];
+ addressBook: AddressBookState['addressBook'];
internalAccounts: InternalAccount[];
}
diff --git a/app/components/Views/confirmations/components/Confirm/Footer/Footer.test.tsx b/app/components/Views/confirmations/components/Confirm/Footer/Footer.test.tsx
deleted file mode 100644
index 4139a44c779..00000000000
--- a/app/components/Views/confirmations/components/Confirm/Footer/Footer.test.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import React from 'react';
-
-import renderWithProvider from '../../../../../../util/test/renderWithProvider';
-import { personalSignatureConfirmationState } from '../../../../../../util/test/confirm-data-helpers';
-import Footer from './index';
-import { fireEvent } from '@testing-library/react-native';
-
-const mockConfirmSpy = jest.fn();
-const mockRejectSpy = jest.fn();
-jest.mock('../../../hooks/useApprovalRequest', () => () => ({
- onConfirm: mockConfirmSpy,
- onReject: mockRejectSpy,
-}));
-
-describe('Footer', () => {
- it('should match snapshot for personal sign', async () => {
- const container = renderWithProvider(, {
- state: personalSignatureConfirmationState,
- });
- expect(container).toMatchSnapshot();
- });
-
- it('should call onConfirm when confirm button is clicked', async () => {
- const { getByText } = renderWithProvider(, {
- state: personalSignatureConfirmationState,
- });
- fireEvent.press(getByText('Confirm'));
- expect(mockConfirmSpy).toHaveBeenCalledTimes(1);
- });
-
- it('should call onReject when reject button is clicked', async () => {
- const { getByText } = renderWithProvider(, {
- state: personalSignatureConfirmationState,
- });
- fireEvent.press(getByText('Reject'));
- expect(mockRejectSpy).toHaveBeenCalledTimes(1);
- });
-});
diff --git a/app/components/Views/confirmations/components/Confirm/Footer/Footer.tsx b/app/components/Views/confirmations/components/Confirm/Footer/Footer.tsx
deleted file mode 100644
index 6f776b669e0..00000000000
--- a/app/components/Views/confirmations/components/Confirm/Footer/Footer.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react';
-import { View } from 'react-native';
-
-import { strings } from '../../../../../../../locales/i18n';
-import StyledButton from '../../../../../../components/UI/StyledButton';
-import { useTheme } from '../../../../../../util/theme';
-import useApprovalRequest from '../../../hooks/useApprovalRequest';
-import createStyles from './style';
-
-const Footer = () => {
- const { onConfirm, onReject } = useApprovalRequest();
- const { colors } = useTheme();
-
- const styles = createStyles(colors);
-
- return (
-
-
- {strings('confirm.reject')}
-
-
-
- {strings('confirm.confirm')}
-
-
- );
-};
-
-export default Footer;
diff --git a/app/components/Views/confirmations/components/Confirm/Footer/__snapshots__/Footer.test.tsx.snap b/app/components/Views/confirmations/components/Confirm/Footer/__snapshots__/Footer.test.tsx.snap
deleted file mode 100644
index a289fd77735..00000000000
--- a/app/components/Views/confirmations/components/Confirm/Footer/__snapshots__/Footer.test.tsx.snap
+++ /dev/null
@@ -1,130 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Footer should match snapshot for personal sign 1`] = `
-
-
-
- Reject
-
-
-
-
-
- Confirm
-
-
-
-`;
diff --git a/app/components/Views/confirmations/components/Confirm/Footer/index.ts b/app/components/Views/confirmations/components/Confirm/Footer/index.ts
deleted file mode 100644
index be92134c1ba..00000000000
--- a/app/components/Views/confirmations/components/Confirm/Footer/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './Footer';
diff --git a/app/components/Views/confirmations/components/Confirm/Footer/style.ts b/app/components/Views/confirmations/components/Confirm/Footer/style.ts
deleted file mode 100644
index 3054785ba21..00000000000
--- a/app/components/Views/confirmations/components/Confirm/Footer/style.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-import { Colors } from '../../../../../../util/theme/models';
-
-const createStyles = (colors: Colors) =>
- StyleSheet.create({
- confirmButton: {
- flex: 1,
- },
- rejectButton: {
- flex: 1,
- backgroundColor: colors.background.alternative,
- },
- divider: {
- height: 1,
- backgroundColor: colors.border.muted,
- },
- buttonsContainer: {
- flexDirection: 'row',
- padding: 16,
- },
- buttonDivider: {
- width: 8,
- },
- });
-
-export default createStyles;
diff --git a/app/components/Views/confirmations/components/Confirm/Title/Title.test.tsx b/app/components/Views/confirmations/components/Confirm/Title/Title.test.tsx
deleted file mode 100644
index 4c20a4ed814..00000000000
--- a/app/components/Views/confirmations/components/Confirm/Title/Title.test.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react';
-
-import renderWithProvider from '../../../../../../util/test/renderWithProvider';
-import { personalSignatureConfirmationState } from '../../../../../../util/test/confirm-data-helpers';
-import Title from './index';
-
-describe('Title', () => {
- it('should match snapshot for personal sign', async () => {
- const container = renderWithProvider(, {
- state: personalSignatureConfirmationState,
- });
- expect(container).toMatchSnapshot();
- });
-});
diff --git a/app/components/Views/confirmations/components/Confirm/Title/Title.tsx b/app/components/Views/confirmations/components/Confirm/Title/Title.tsx
deleted file mode 100644
index c674171f14b..00000000000
--- a/app/components/Views/confirmations/components/Confirm/Title/Title.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import React, { useMemo } from 'react';
-import { Text, View } from 'react-native';
-import { TransactionType } from '@metamask/transaction-controller';
-
-import { strings } from '../../../../../../../locales/i18n';
-import { useTheme } from '../../../../../../util/theme';
-import useApprovalRequest from '../../../hooks/useApprovalRequest';
-import createStyles from './style';
-
-const getTitle = (confirmationType?: string) => {
- switch (confirmationType) {
- case TransactionType.personalSign:
- return strings('confirm.title.signature');
- default:
- return '';
- }
-};
-
-const Title = () => {
- const { approvalRequest } = useApprovalRequest();
- const { colors } = useTheme();
-
- const styles = createStyles(colors);
- const title = useMemo(
- () => getTitle(approvalRequest?.type),
- [approvalRequest?.type],
- );
-
- return (
-
- {title}
-
- );
-};
-
-export default Title;
diff --git a/app/components/Views/confirmations/components/Confirm/Title/__snapshots__/Title.test.tsx.snap b/app/components/Views/confirmations/components/Confirm/Title/__snapshots__/Title.test.tsx.snap
deleted file mode 100644
index d68aff5d51e..00000000000
--- a/app/components/Views/confirmations/components/Confirm/Title/__snapshots__/Title.test.tsx.snap
+++ /dev/null
@@ -1,24 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Title should match snapshot for personal sign 1`] = `
-
-
- Signature request
-
-
-`;
diff --git a/app/components/Views/confirmations/components/Confirm/Title/index.ts b/app/components/Views/confirmations/components/Confirm/Title/index.ts
deleted file mode 100644
index eaa93a3c584..00000000000
--- a/app/components/Views/confirmations/components/Confirm/Title/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from './Title';
diff --git a/app/components/Views/confirmations/components/Confirm/Title/style.ts b/app/components/Views/confirmations/components/Confirm/Title/style.ts
deleted file mode 100644
index 22003c846a7..00000000000
--- a/app/components/Views/confirmations/components/Confirm/Title/style.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { StyleSheet } from 'react-native';
-
-import { Colors } from '../../../../../../util/theme/models';
-import { fontStyles } from '../../../../../../styles/common';
-
-const createStyles = (colors: Colors) =>
- StyleSheet.create({
- titleContainer: {
- marginBottom: 10,
- },
- title: {
- color: colors.text.default,
- ...fontStyles.bold,
- fontSize: 18,
- fontWeight: '700'
- }
- });
-
-export default createStyles;
diff --git a/app/components/Views/confirmations/components/SignatureRequest/Root/Root.tsx b/app/components/Views/confirmations/components/SignatureRequest/Root/Root.tsx
index 0dfb8e11683..793d5fb8253 100644
--- a/app/components/Views/confirmations/components/SignatureRequest/Root/Root.tsx
+++ b/app/components/Views/confirmations/components/SignatureRequest/Root/Root.tsx
@@ -5,7 +5,6 @@ import { useNavigation } from '@react-navigation/native';
import setSignatureRequestSecurityAlertResponse from '../../../../../../actions/signatureRequest';
import { store } from '../../../../../../store';
import { useTheme } from '../../../../../../util/theme';
-import useConfirmationRedesignEnabled from '../../../hooks/useConfirmationRedesignEnabled';
import PersonalSign from '../../PersonalSign';
import TypedSign from '../../TypedSign';
import { MessageParams } from '../types';
@@ -40,7 +39,6 @@ const Root = ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(reduxState: any) => reduxState.modals.signMessageModalVisible,
);
- const { isRedesignedEnabled } = useConfirmationRedesignEnabled();
const toggleExpandedMessage = () =>
setShowExpandedMessage(!showExpandedMessage);
@@ -54,13 +52,7 @@ const Root = ({
};
}, []);
- if (
- !messageParams ||
- !currentPageMeta ||
- !approvalType ||
- !visibility ||
- isRedesignedEnabled
- ) {
+ if (!messageParams || !currentPageMeta || !approvalType || !visibility) {
return null;
}
diff --git a/app/components/Views/confirmations/hooks/useConfirmationRedesignEnabled.ts b/app/components/Views/confirmations/hooks/useConfirmationRedesignEnabled.ts
deleted file mode 100644
index f9e6a531a31..00000000000
--- a/app/components/Views/confirmations/hooks/useConfirmationRedesignEnabled.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { useMemo } from 'react';
-
-import { ApprovalTypes } from '../../../../core/RPCMethods/RPCMethodMiddleware';
-import useApprovalRequest from './useApprovalRequest';
-
-const useConfirmationRedesignEnabled = () => {
- const { approvalRequest } = useApprovalRequest();
- const approvalRequestType = approvalRequest?.type;
-
- const isRedesignedEnabled = useMemo(
- () =>
- approvalRequestType &&
- process.env.REDESIGNED_SIGNATURE_REQUEST === 'true' &&
- approvalRequestType === ApprovalTypes.PERSONAL_SIGN,
- [approvalRequestType],
- );
-
- return { isRedesignedEnabled };
-};
-
-export default useConfirmationRedesignEnabled;
diff --git a/app/constants/navigation/Routes.ts b/app/constants/navigation/Routes.ts
index 46462a29797..773bbc7dc47 100644
--- a/app/constants/navigation/Routes.ts
+++ b/app/constants/navigation/Routes.ts
@@ -137,13 +137,9 @@ const Routes = {
OPT_IN_STACK: 'OptInStack',
DETAILS: 'NotificationsDetails',
},
- STAKING: {
+ STAKE: {
STAKE: 'Stake',
UNSTAKE: 'Unstake',
- CLAIM: 'Claim',
- MODALS: {
- LEARN_MORE: 'LearnMore',
- },
},
///: BEGIN:ONLY_INCLUDE_IF(external-snaps)
SNAPS: {
diff --git a/app/constants/time.ts b/app/constants/time.ts
deleted file mode 100644
index 8b594570b04..00000000000
--- a/app/constants/time.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * All times are in milliseconds.
- */
-export const SECOND = 1000;
-
-export const MINUTE = SECOND * 60;
-
-export const HOUR = MINUTE * 60;
-
-export const DAY = HOUR * 24;
diff --git a/app/core/Analytics/MetaMetrics.events.ts b/app/core/Analytics/MetaMetrics.events.ts
index 5b972ea1130..0a61dbc6f16 100644
--- a/app/core/Analytics/MetaMetrics.events.ts
+++ b/app/core/Analytics/MetaMetrics.events.ts
@@ -80,8 +80,6 @@ enum EVENT_NAME {
// Network
NETWORK_SWITCHED = 'Network Switched',
- NETWORK_SWITCH_REQUESTED_AND_MODAL_SHOWN = 'Network Switch Requested and Modal Shown',
- NETWORK_SWITCH_CONFIRM_PRESSED = 'Network Switch Confirm Pressed',
NETWORK_ADDED = 'Network Added',
NETWORK_REQUESTED = 'Network Requested',
NETWORK_REQUEST_REJECTED = 'Network Request Rejected',
@@ -484,12 +482,6 @@ const events = {
COLLECTIBLE_REMOVED: generateOpt(EVENT_NAME.COLLECTIBLE_REMOVED),
CURRENCY_CHANGED: generateOpt(EVENT_NAME.CURRENCY_CHANGED),
NETWORK_SWITCHED: generateOpt(EVENT_NAME.NETWORK_SWITCHED),
- NETWORK_SWITCH_REQUESTED_AND_MODAL_SHOWN: generateOpt(
- EVENT_NAME.NETWORK_SWITCH_REQUESTED_AND_MODAL_SHOWN,
- ),
- NETWORK_SWITCH_CONFIRM_PRESSED: generateOpt(
- EVENT_NAME.NETWORK_SWITCH_CONFIRM_PRESSED,
- ),
NETWORK_ADDED: generateOpt(EVENT_NAME.NETWORK_ADDED),
NETWORK_REQUESTED: generateOpt(EVENT_NAME.NETWORK_REQUESTED),
NETWORK_REQUEST_REJECTED: generateOpt(EVENT_NAME.NETWORK_REQUEST_REJECTED),
diff --git a/app/core/BackgroundBridge/BackgroundBridge.js b/app/core/BackgroundBridge/BackgroundBridge.js
index 0202794e01a..9b7b4f63554 100644
--- a/app/core/BackgroundBridge/BackgroundBridge.js
+++ b/app/core/BackgroundBridge/BackgroundBridge.js
@@ -451,7 +451,7 @@ export class BackgroundBridge extends EventEmitter {
snapMethodMiddlewareBuilder(
Engine.context,
Engine.controllerMessenger,
- this.url,
+ origin,
// We assume that origins connecting through the BackgroundBridge are websites
SubjectType.Website,
),
diff --git a/app/core/DeeplinkManager/ParseManager/parseDeeplink.ts b/app/core/DeeplinkManager/ParseManager/parseDeeplink.ts
index 7bca677e35e..4c799701503 100644
--- a/app/core/DeeplinkManager/ParseManager/parseDeeplink.ts
+++ b/app/core/DeeplinkManager/ParseManager/parseDeeplink.ts
@@ -10,7 +10,6 @@ import handleUniversalLink from './handleUniversalLink';
import connectWithWC from './connectWithWC';
import { Alert } from 'react-native';
import { strings } from '../../../../locales/i18n';
-import AppConstants from '../../../core/AppConstants';
function parseDeeplink({
deeplinkManager: instance,
@@ -99,14 +98,8 @@ function parseDeeplink({
error as Error,
'DeepLinkManager:parse error parsing deeplink',
);
- if (origin === AppConstants.DEEPLINKS.ORIGIN_QR_CODE) {
- Alert.alert(
- strings('qr_scanner.unrecognized_address_qr_code_title'),
- strings('qr_scanner.unrecognized_address_qr_code_desc'),
- );
- } else {
- Alert.alert(strings('deeplink.invalid'), `Invalid URL: ${url}`);
- }
+
+ Alert.alert(strings('deeplink.invalid'), `Invalid URL: ${url}`);
}
return false;
diff --git a/app/core/Engine.test.js b/app/core/Engine.test.js
index 3bbe199447f..7ba32d42bde 100644
--- a/app/core/Engine.test.js
+++ b/app/core/Engine.test.js
@@ -52,7 +52,39 @@ describe('Engine', () => {
const engine = Engine.init({});
const initialBackgroundState = engine.datamodel.state;
- expect(initialBackgroundState).toStrictEqual(backgroundState);
+ expect(initialBackgroundState).toStrictEqual({
+ ...backgroundState,
+
+ // JSON cannot store the value undefined, so we append it here
+ SmartTransactionsController: {
+ smartTransactionsState: {
+ fees: {
+ approvalTxFees: undefined,
+ tradeTxFees: undefined,
+ },
+ feesByChainId: {
+ '0x1': {
+ approvalTxFees: undefined,
+ tradeTxFees: undefined,
+ },
+ '0xaa36a7': {
+ approvalTxFees: undefined,
+ tradeTxFees: undefined,
+ },
+ },
+ liveness: true,
+ livenessByChainId: {
+ '0x1': true,
+ '0xaa36a7': true,
+ },
+ smartTransactions: {
+ '0x1': [],
+ },
+ userOptIn: undefined,
+ userOptInV2: undefined,
+ },
+ },
+ });
});
it('setSelectedAccount throws an error if no account exists for the given address', () => {
diff --git a/app/core/Engine.ts b/app/core/Engine.ts
index 0c9901b42a5..4218e4e19fa 100644
--- a/app/core/Engine.ts
+++ b/app/core/Engine.ts
@@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-shadow */
import Crypto from 'react-native-quick-crypto';
-import { scrypt } from 'react-native-fast-crypto';
import {
AccountTrackerController,
AccountTrackerState,
@@ -35,9 +34,7 @@ import PREINSTALLED_SNAPS from '../lib/snaps/preinstalled-snaps';
///: END:ONLY_INCLUDE_IF
import {
AddressBookController,
- AddressBookControllerActions,
- AddressBookControllerEvents,
- AddressBookControllerState,
+ AddressBookState,
} from '@metamask/address-book-controller';
import { BaseState } from '@metamask/base-controller';
import { ComposableController } from '@metamask/composable-controller';
@@ -141,7 +138,6 @@ import {
LoggingController,
LoggingControllerState,
LoggingControllerActions,
- LoggingControllerEvents,
} from '@metamask/logging-controller';
import {
LedgerKeyring,
@@ -219,37 +215,19 @@ import {
networkIdUpdated,
networkIdWillUpdate,
} from '../core/redux/slices/inpageProvider';
-import SmartTransactionsController, {
- type SmartTransactionsControllerActions,
- type SmartTransactionsControllerEvents,
- type SmartTransactionsControllerState,
-} from '@metamask/smart-transactions-controller';
+import SmartTransactionsController from '@metamask/smart-transactions-controller';
import { getAllowedSmartTransactionsChainIds } from '../../app/constants/smartTransactions';
import { selectShouldUseSmartTransaction } from '../selectors/smartTransactionsController';
import { selectSwapsChainFeatureFlags } from '../reducers/swaps';
import { SmartTransactionStatuses } from '@metamask/smart-transactions-controller/dist/types';
import { submitSmartTransactionHook } from '../util/smart-transactions/smart-publish-hook';
+import { SmartTransactionsControllerState } from '@metamask/smart-transactions-controller/dist/SmartTransactionsController';
import { zeroAddress } from 'ethereumjs-util';
import { toChecksumHexAddress } from '@metamask/controller-utils';
import { ExtendedControllerMessenger } from './ExtendedControllerMessenger';
import EthQuery from '@metamask/eth-query';
import { TransactionControllerOptions } from '@metamask/transaction-controller/dist/types/TransactionController';
import DomainProxyMap from '../lib/DomainProxyMap/DomainProxyMap';
-import {
- MetaMetricsEventCategory,
- MetaMetricsEventName,
-} from '@metamask/smart-transactions-controller/dist/constants';
-import {
- getSmartTransactionMetricsProperties,
- getSmartTransactionMetricsSensitiveProperties,
-} from '@metamask/smart-transactions-controller/dist/utils';
-///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
-import { snapKeyringBuilder } from './SnapKeyring';
-import { removeAccountsFromPermissions } from './Permissions';
-import { keyringSnapPermissionsBuilder } from './SnapKeyring/keyringSnapsPermissions';
-import { HandleSnapRequestArgs } from './Snaps/types';
-import { handleSnapRequest } from './Snaps/utils';
-///: END:ONLY_INCLUDE_IF
const NON_EMPTY = 'NON_EMPTY';
@@ -280,7 +258,6 @@ type SnapsGlobalEvents =
///: END:ONLY_INCLUDE_IF
type GlobalActions =
- | AddressBookControllerActions
| ApprovalControllerActions
| GetCurrencyRateState
| GetGasFeeState
@@ -301,11 +278,9 @@ type GlobalActions =
| PreferencesControllerActions
| TokensControllerActions
| TokenListControllerActions
- | SelectedNetworkControllerActions
- | SmartTransactionsControllerActions;
+ | SelectedNetworkControllerActions;
type GlobalEvents =
- | AddressBookControllerEvents
| ApprovalControllerEvents
| CurrencyRateStateChange
| GasFeeStateChange
@@ -317,7 +292,6 @@ type GlobalEvents =
| SnapsGlobalEvents
///: END:ONLY_INCLUDE_IF
| SignatureControllerEvents
- | LoggingControllerEvents
| KeyringControllerEvents
| PPOMControllerEvents
| AccountsControllerEvents
@@ -325,15 +299,14 @@ type GlobalEvents =
| TokensControllerEvents
| TokenListControllerEvents
| TransactionControllerEvents
- | SelectedNetworkControllerEvents
- | SmartTransactionsControllerEvents;
+ | SelectedNetworkControllerEvents;
type PermissionsByRpcMethod = ReturnType;
type Permissions = PermissionsByRpcMethod[keyof PermissionsByRpcMethod];
export interface EngineState {
AccountTrackerController: AccountTrackerState;
- AddressBookController: AddressBookControllerState;
+ AddressBookController: AddressBookState;
AssetsContractController: BaseState;
NftController: NftState;
TokenListController: TokenListState;
@@ -419,20 +392,6 @@ type RequiredControllers = Omit;
*/
type OptionalControllers = Pick;
-/**
- * Combines required and optional controllers for the Engine context type.
- */
-export type EngineContext = RequiredControllers & Partial;
-
-/**
- * Type definition for the controller messenger used in the Engine.
- * It extends the base ControllerMessenger with global actions and events.
- */
-export type ControllerMessenger = ExtendedControllerMessenger<
- GlobalActions,
- GlobalEvents
->;
-
/**
* Core controller responsible for composing other metamask controllers together
* and exposing convenience methods for common wallet operations.
@@ -445,11 +404,11 @@ class Engine {
/**
* A collection of all controller instances
*/
- context: EngineContext;
+ context: RequiredControllers & Partial;
/**
* The global controller messenger.
*/
- controllerMessenger: ControllerMessenger;
+ controllerMessenger: ExtendedControllerMessenger;
/**
* ComposableController reference containing all child controllers
*/
@@ -470,16 +429,12 @@ class Engine {
* Object that runs and manages the execution of Snaps
*/
snapExecutionService: WebViewExecutionService;
- snapController: SnapController;
- subjectMetadataController: SubjectMetadataController;
///: END:ONLY_INCLUDE_IF
transactionController: TransactionController;
smartTransactionsController: SmartTransactionsController;
- keyringController: KeyringController;
-
/**
* Creates a CoreController instance
*/
@@ -624,6 +579,7 @@ class Engine {
);
const loggingController = new LoggingController({
+ // @ts-expect-error TODO: Resolve mismatch between base-controller versions.
messenger: this.controllerMessenger.getRestricted<
'LoggingController',
never,
@@ -635,6 +591,7 @@ class Engine {
}),
state: initialState.LoggingController,
});
+ // @ts-expect-error TODO: Resolve mismatch between base-controller versions.
const accountsControllerMessenger: AccountsControllerMessenger =
this.controllerMessenger.getRestricted({
name: 'AccountsController',
@@ -758,8 +715,6 @@ class Engine {
});
phishingController.maybeUpdateState();
- const additionalKeyrings = [];
-
const qrKeyringBuilder = () => {
const keyring = new QRHardwareKeyring();
// to fix the bug in #9560, forgetDevice will reset all keyring properties to default.
@@ -768,73 +723,16 @@ class Engine {
};
qrKeyringBuilder.type = QRHardwareKeyring.type;
- additionalKeyrings.push(qrKeyringBuilder);
-
const bridge = new LedgerMobileBridge(new LedgerTransportMiddleware());
const ledgerKeyringBuilder = () => new LedgerKeyring({ bridge });
ledgerKeyringBuilder.type = LedgerKeyring.type;
- additionalKeyrings.push(ledgerKeyringBuilder);
-
- ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
-
- /**
- * Removes an account from state / storage.
- *
- * @param {string} address - A hex address
- */
- const removeAccount = async (address: string) => {
- // Remove all associated permissions
- await removeAccountsFromPermissions([address]);
- // Remove account from the keyring
- await this.keyringController.removeAccount(address as Hex);
- return address;
- };
-
- const snapKeyringBuildMessenger = this.controllerMessenger.getRestricted({
- name: 'SnapKeyringBuilder',
- allowedActions: [
- 'ApprovalController:addRequest',
- 'ApprovalController:acceptRequest',
- 'ApprovalController:rejectRequest',
- 'ApprovalController:startFlow',
- 'ApprovalController:endFlow',
- 'ApprovalController:showSuccess',
- 'ApprovalController:showError',
- 'PhishingController:testOrigin',
- 'PhishingController:maybeUpdateState',
- 'KeyringController:getAccounts',
- 'AccountsController:setSelectedAccount',
- 'AccountsController:getAccountByAddress',
- 'AccountsController:setAccountName',
- ],
- allowedEvents: [],
- });
-
- const getSnapController = () => this.snapController;
-
- // Necessary to persist the keyrings and update the accounts both within the keyring controller and accounts controller
- const persistAndUpdateAccounts = async () => {
- await this.keyringController.persistAllKeyrings();
- await accountsController.updateAccounts();
- };
-
- additionalKeyrings.push(
- snapKeyringBuilder(
- snapKeyringBuildMessenger,
- getSnapController,
- persistAndUpdateAccounts,
- (address) => removeAccount(address),
- ),
- );
-
- ///: END:ONLY_INCLUDE_IF
-
- this.keyringController = new KeyringController({
+ const keyringController = new KeyringController({
removeIdentity: preferencesController.removeIdentity.bind(
preferencesController,
),
encryptor,
+ // @ts-expect-error TODO: Resolve mismatch between base-controller versions.
messenger: this.controllerMessenger.getRestricted({
name: 'KeyringController',
allowedActions: [],
@@ -842,7 +740,7 @@ class Engine {
}),
state: initialKeyringState || initialState.KeyringController,
// @ts-expect-error To Do: Update the type of QRHardwareKeyring to Keyring
- keyringBuilders: additionalKeyrings,
+ keyringBuilders: [qrKeyringBuilder, ledgerKeyringBuilder],
});
///: BEGIN:ONLY_INCLUDE_IF(preinstalled-snaps,external-snaps)
@@ -852,7 +750,7 @@ class Engine {
const getPrimaryKeyringMnemonic = () => {
// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- const [keyring]: any = this.keyringController.getKeyringsByType(
+ const [keyring]: any = keyringController.getKeyringsByType(
KeyringTypes.hd,
);
if (!keyring.mnemonic) {
@@ -867,97 +765,83 @@ class Engine {
return state === 'active';
};
- const snapRestrictedMethods = {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- clearSnapState: this.controllerMessenger.call.bind(
- this.controllerMessenger,
- 'SnapController:clearSnapState',
- ),
- getMnemonic: getPrimaryKeyringMnemonic.bind(this),
- getUnlockPromise: getAppState.bind(this),
- getSnap: this.controllerMessenger.call.bind(
- this.controllerMessenger,
- 'SnapController:get',
- ),
- handleSnapRpcRequest: async (args: HandleSnapRequestArgs) =>
- await handleSnapRequest(this.controllerMessenger, args),
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- getSnapState: this.controllerMessenger.call.bind(
- this.controllerMessenger,
- 'SnapController:getSnapState',
- ),
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- updateSnapState: this.controllerMessenger.call.bind(
- this.controllerMessenger,
- 'SnapController:updateSnapState',
- ),
- maybeUpdatePhishingList: this.controllerMessenger.call.bind(
- this.controllerMessenger,
- 'PhishingController:maybeUpdateState',
- ),
- isOnPhishingList: (origin: string) =>
- this.controllerMessenger.call<'PhishingController:testOrigin'>(
- 'PhishingController:testOrigin',
- origin,
- ).result,
- showDialog: (
- origin: string,
- type: EnumToUnion,
- // TODO: Replace "any" with type
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- content: any, // should be Component from '@metamask/snaps-ui';
- // TODO: Replace "any" with type
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- placeholder?: any,
- ) =>
- approvalController.addAndShowApprovalRequest({
- origin,
- type,
- requestData: { content, placeholder },
- }),
- showInAppNotification: (origin: string, args: NotificationArgs) => {
- Logger.log(
- 'Snaps/ showInAppNotification called with args: ',
- args,
- ' and origin: ',
- origin,
- );
- },
- hasPermission: (origin: string, target: string) =>
- this.controllerMessenger.call<'PermissionController:hasPermission'>(
- 'PermissionController:hasPermission',
- origin,
- target,
- ),
- };
- ///: END:ONLY_INCLUDE_IF
-
- ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
- const keyringSnapMethods = {
- getAllowedKeyringMethods: (origin: string) =>
- keyringSnapPermissionsBuilder(origin),
- getSnapKeyring: this.getSnapKeyring.bind(this),
- };
- ///: END:ONLY_INCLUDE_IF
-
const getSnapPermissionSpecifications = () => ({
...buildSnapEndowmentSpecifications(Object.keys(ExcludedSnapEndowments)),
...buildSnapRestrictedMethodSpecifications(
Object.keys(ExcludedSnapPermissions),
{
- ///: BEGIN:ONLY_INCLUDE_IF(preinstalled-snaps,external-snaps)
- ...snapRestrictedMethods,
- ///: END:ONLY_INCLUDE_IF
- ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
- ...keyringSnapMethods,
- ///: END:ONLY_INCLUDE_IF
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ clearSnapState: this.controllerMessenger.call.bind(
+ this.controllerMessenger,
+ 'SnapController:clearSnapState',
+ ),
+ getMnemonic: getPrimaryKeyringMnemonic.bind(this),
+ getUnlockPromise: getAppState.bind(this),
+ getSnap: this.controllerMessenger.call.bind(
+ this.controllerMessenger,
+ 'SnapController:get',
+ ),
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ handleSnapRpcRequest: this.controllerMessenger.call.bind(
+ this.controllerMessenger,
+ 'SnapController:handleRequest',
+ ),
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ getSnapState: this.controllerMessenger.call.bind(
+ this.controllerMessenger,
+ 'SnapController:getSnapState',
+ ),
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ updateSnapState: this.controllerMessenger.call.bind(
+ this.controllerMessenger,
+ 'SnapController:updateSnapState',
+ ),
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ maybeUpdatePhishingList: this.controllerMessenger.call.bind(
+ this.controllerMessenger,
+ 'PhishingController:maybeUpdateState',
+ ),
+ isOnPhishingList: (origin: string) =>
+ this.controllerMessenger.call<'PhishingController:testOrigin'>(
+ 'PhishingController:testOrigin',
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ origin,
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ ).result,
+ showDialog: (
+ origin: string,
+ type: EnumToUnion,
+ // TODO: Replace "any" with type
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ content: any, // should be Component from '@metamask/snaps-ui';
+ // TODO: Replace "any" with type
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ placeholder?: any,
+ ) =>
+ approvalController.addAndShowApprovalRequest({
+ origin,
+ type,
+ requestData: { content, placeholder },
+ }),
+ showInAppNotification: (origin: string, args: NotificationArgs) => {
+ Logger.log(
+ 'Snaps/ showInAppNotification called with args: ',
+ args,
+ ' and origin: ',
+ origin,
+ );
+ },
},
),
});
-
+ ///: END:ONLY_INCLUDE_IF
const accountTrackerController = new AccountTrackerController({
onPreferencesStateChange,
getIdentities: () => preferencesController.state.identities,
@@ -999,7 +883,7 @@ class Engine {
// @ts-expect-error Typecast permissionType from getPermissionSpecifications to be of type PermissionType.RestrictedMethod
permissionSpecifications: {
...getPermissionSpecifications({
- getAllAccounts: () => this.keyringController.getAccounts(),
+ getAllAccounts: () => keyringController.getAccounts(),
getInternalAccounts:
accountsController.listAccounts.bind(accountsController),
captureKeyringTypesWithMissingIdentities: (
@@ -1014,7 +898,7 @@ class Engine {
});
const keyringTypesWithMissingIdentities =
accountsMissingIdentities.map((address) =>
- this.keyringController.getAccountKeyringType(address),
+ keyringController.getAccountKeyringType(address),
);
const internalAccountCount = internalAccounts.length;
@@ -1063,7 +947,7 @@ class Engine {
});
///: BEGIN:ONLY_INCLUDE_IF(preinstalled-snaps,external-snaps)
- this.subjectMetadataController = new SubjectMetadataController({
+ const subjectMetadataController = new SubjectMetadataController({
// @ts-expect-error TODO: Resolve mismatch between base-controller versions.
messenger: this.controllerMessenger.getRestricted({
name: 'SubjectMetadataController',
@@ -1166,8 +1050,8 @@ class Engine {
`${approvalController.name}:addRequest`,
`${approvalController.name}:updateRequestState`,
`${permissionController.name}:grantPermissions`,
- `${this.subjectMetadataController.name}:getSubjectMetadata`,
- `${this.subjectMetadataController.name}:addSubjectMetadata`,
+ `${subjectMetadataController.name}:getSubjectMetadata`,
+ `${subjectMetadataController.name}:addSubjectMetadata`,
`${phishingController.name}:maybeUpdateState`,
`${phishingController.name}:testOrigin`,
`${snapsRegistry.name}:get`,
@@ -1184,7 +1068,7 @@ class Engine {
],
});
- this.snapController = new SnapController({
+ const snapController = new SnapController({
environmentEndowmentPermissions: Object.values(EndowmentPermissions),
featureFlags: {
requireAllowlist,
@@ -1216,6 +1100,7 @@ class Engine {
const authenticationController = new AuthenticationController.Controller({
state: initialState.AuthenticationController,
+ // @ts-expect-error TODO: Resolve mismatch between base-controller versions.
messenger: this.controllerMessenger.getRestricted({
name: 'AuthenticationController',
allowedActions: [
@@ -1237,6 +1122,7 @@ class Engine {
const userStorageController = new UserStorageController.Controller({
getMetaMetricsState: () => MetaMetrics.getInstance().isEnabled(),
state: initialState.UserStorageController,
+ // @ts-expect-error TODO: Resolve mismatch between base-controller versions.
messenger: this.controllerMessenger.getRestricted({
name: 'UserStorageController',
allowedActions: [
@@ -1249,22 +1135,14 @@ class Engine {
'AuthenticationController:performSignIn',
'NotificationServicesController:disableNotificationServices',
'NotificationServicesController:selectIsNotificationServicesEnabled',
- 'KeyringController:addNewAccount',
- 'AccountsController:listAccounts',
- 'AccountsController:updateAccountMetadata',
- ],
- allowedEvents: [
- 'KeyringController:lock',
- 'KeyringController:unlock',
- 'AccountsController:accountAdded',
- 'AccountsController:accountRenamed',
],
+ allowedEvents: ['KeyringController:unlock', 'KeyringController:lock'],
}),
- nativeScryptCrypto: scrypt,
});
const notificationServicesController =
new NotificationServicesController.Controller({
+ // @ts-expect-error TODO: Resolve mismatch between base-controller versions.
messenger: this.controllerMessenger.getRestricted({
name: 'NotificationServicesController',
allowedActions: [
@@ -1330,8 +1208,6 @@ class Engine {
smartTransactionsController: this.smartTransactionsController,
shouldUseSmartTransaction,
approvalController,
- // @ts-expect-error TODO: Resolve mismatch between base-controller versions.
- controllerMessenger: this.controllerMessenger,
featureFlags: selectSwapsChainFeatureFlags(store.getState()),
}) as Promise<{ transactionHash: string }>;
},
@@ -1375,28 +1251,21 @@ class Engine {
},
// @ts-expect-error at this point in time the provider will be defined by the `networkController.initializeProvider`
provider: networkController.getProviderAndBlockTracker().provider,
- sign: this.keyringController.signTransaction.bind(
- this.keyringController,
+ sign: keyringController.signTransaction.bind(
+ keyringController,
) as unknown as TransactionControllerOptions['sign'],
state: initialState.TransactionController,
});
const codefiTokenApiV2 = new CodefiTokenPricesServiceV2();
- const smartTransactionsControllerTrackMetaMetricsEvent = (
- params: {
- event: MetaMetricsEventName;
- category: MetaMetricsEventCategory;
- properties?: ReturnType;
- sensitiveProperties?: ReturnType<
- typeof getSmartTransactionMetricsSensitiveProperties
- >;
- },
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- options?: {
- metaMetricsId?: string;
- },
- ) => {
+ const smartTransactionsControllerTrackMetaMetricsEvent = (params: {
+ event: string;
+ category: string;
+ // TODO: Replace "any" with type
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ sensitiveProperties: any;
+ }) => {
const { event, category, ...restParams } = params;
MetaMetrics.getInstance().trackEvent(
@@ -1409,40 +1278,46 @@ class Engine {
restParams,
);
};
- this.smartTransactionsController = new SmartTransactionsController({
- // @ts-expect-error TODO: resolve types
- supportedChainIds: getAllowedSmartTransactionsChainIds(),
- getNonceLock: this.transactionController.getNonceLock.bind(
- this.transactionController,
- ),
- confirmExternalTransaction:
- this.transactionController.confirmExternalTransaction.bind(
+ this.smartTransactionsController = new SmartTransactionsController(
+ {
+ confirmExternalTransaction:
+ this.transactionController.confirmExternalTransaction.bind(
+ this.transactionController,
+ ),
+
+ getNetworkClientById:
+ networkController.getNetworkClientById.bind(networkController),
+ getNonceLock: this.transactionController.getNonceLock.bind(
this.transactionController,
),
- trackMetaMetricsEvent: smartTransactionsControllerTrackMetaMetricsEvent,
- state: initialState.SmartTransactionsController,
- // @ts-expect-error TODO: Resolve mismatch between base-controller versions.
- messenger: this.controllerMessenger.getRestricted({
- name: 'SmartTransactionsController',
- allowedActions: ['NetworkController:getNetworkClientById'],
- allowedEvents: ['NetworkController:stateChange'],
- }),
- getTransactions: this.transactionController.getTransactions.bind(
- this.transactionController,
- ),
- getMetaMetricsProps: () => Promise.resolve({}), // Return MetaMetrics props once we enable HW wallets for smart transactions.
- });
+ getTransactions: this.transactionController.getTransactions.bind(
+ this.transactionController,
+ ),
+ onNetworkStateChange: (listener) =>
+ this.controllerMessenger.subscribe(
+ AppConstants.NETWORK_STATE_CHANGE_EVENT,
+ listener,
+ ),
+
+ // TODO: Replace "any" with type
+ provider:
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ networkController.getProviderAndBlockTracker().provider as any,
+
+ trackMetaMetricsEvent: smartTransactionsControllerTrackMetaMetricsEvent,
+ getMetaMetricsProps: () => Promise.resolve({}), // Return MetaMetrics props once we enable HW wallets for smart transactions.
+ },
+ {
+ // @ts-expect-error TODO: resolve types
+ supportedChainIds: getAllowedSmartTransactionsChainIds(),
+ },
+ initialState.SmartTransactionsController,
+ );
const controllers: Controllers[keyof Controllers][] = [
- this.keyringController,
+ keyringController,
accountTrackerController,
- new AddressBookController({
- messenger: this.controllerMessenger.getRestricted({
- name: 'AddressBookController',
- allowedActions: [],
- allowedEvents: [],
- }),
- }),
+ new AddressBookController(),
assetsContractController,
nftController,
tokensController,
@@ -1586,13 +1461,14 @@ class Engine {
permissionController,
selectedNetworkController,
new SignatureController({
+ // @ts-expect-error TODO: Resolve mismatch between base-controller versions.
messenger: this.controllerMessenger.getRestricted({
name: 'SignatureController',
allowedActions: [
`${approvalController.name}:addRequest`,
- `${this.keyringController.name}:signPersonalMessage`,
- `${this.keyringController.name}:signMessage`,
- `${this.keyringController.name}:signTypedMessage`,
+ `${keyringController.name}:signPersonalMessage`,
+ `${keyringController.name}:signMessage`,
+ `${keyringController.name}:signTypedMessage`,
`${loggingController.name}:add`,
],
allowedEvents: [],
@@ -1605,8 +1481,8 @@ class Engine {
}),
loggingController,
///: BEGIN:ONLY_INCLUDE_IF(preinstalled-snaps,external-snaps)
- this.snapController,
- this.subjectMetadataController,
+ snapController,
+ subjectMetadataController,
authenticationController,
userStorageController,
notificationServicesController,
@@ -1935,20 +1811,6 @@ class Engine {
};
};
- ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
- getSnapKeyring = async () => {
- let [snapKeyring] = this.keyringController.getKeyringsByType(
- KeyringTypes.snap,
- );
- if (!snapKeyring) {
- snapKeyring = await this.keyringController.addNewKeyring(
- KeyringTypes.snap,
- );
- }
- return snapKeyring;
- };
- ///: END:ONLY_INCLUDE_IF
-
/**
* Returns true or false whether the user has funds or not
*/
diff --git a/app/core/EngineService/EngineService.ts b/app/core/EngineService/EngineService.ts
index 7f5334a80f0..d71a7d4d9ae 100644
--- a/app/core/EngineService/EngineService.ts
+++ b/app/core/EngineService/EngineService.ts
@@ -49,10 +49,7 @@ class EngineService {
}
const controllers = [
- {
- name: 'AddressBookController',
- key: `${engine.context.AddressBookController.name}:stateChange`,
- },
+ { name: 'AddressBookController' },
{ name: 'AssetsContractController' },
{ name: 'NftController' },
{
@@ -93,10 +90,7 @@ class EngineService {
name: 'TransactionController',
key: `${engine.context.TransactionController.name}:stateChange`,
},
- {
- name: 'SmartTransactionsController',
- key: `${engine.context.SmartTransactionsController.name}:stateChange`,
- },
+ { name: 'SmartTransactionsController' },
{ name: 'SwapsController' },
{
name: 'TokenListController',
diff --git a/app/core/NotificationManager.js b/app/core/NotificationManager.js
index 3bcf35ee01b..c01a4e1dbfb 100644
--- a/app/core/NotificationManager.js
+++ b/app/core/NotificationManager.js
@@ -1,9 +1,9 @@
'use strict';
+import notifee from '@notifee/react-native';
import Engine from './Engine';
import { hexToBN, renderFromWei } from '../util/number';
import Device from '../util/device';
-import notifee from '@notifee/react-native';
import { STORAGE_IDS } from '../util/notifications/settings/storage/constants';
import { strings } from '../../locales/i18n';
import { AppState } from 'react-native';
@@ -11,9 +11,9 @@ import { AppState } from 'react-native';
import {
NotificationTransactionTypes,
isNotificationsFeatureEnabled,
-
+ requestPushNotificationsPermission,
+ asyncAlert,
} from '../util/notifications';
-
import { safeToChecksumAddress } from '../util/address';
import ReviewManager from './ReviewManager';
import { selectChainId } from '../selectors/networkController';
@@ -152,7 +152,7 @@ class NotificationManager {
title,
body: message,
android: {
- lightUpScreen: true,
+ lightUpScreen: false,
channelId,
smallIcon: 'ic_notification_small',
largeIcon: 'ic_notification',
@@ -179,6 +179,7 @@ class NotificationManager {
} else {
pushData.userInfo = extraData; // check if is still needed
}
+
isNotificationsFeatureEnabled() && notifee.displayNotification(pushData);
} else {
this._showTransactionNotification({
@@ -255,6 +256,11 @@ class NotificationManager {
}
Promise.all(pollPromises);
+ Device.isIos() &&
+ setTimeout(() => {
+ requestPushNotificationsPermission(asyncAlert);
+ }, 5000);
+
// Prompt review
ReviewManager.promptReview();
@@ -485,6 +491,9 @@ export default {
gotIncomingTransaction(lastBlock) {
return instance?.gotIncomingTransaction(lastBlock);
},
+ requestPushNotificationsPermission() {
+ return instance?.requestPushNotificationsPermission(asyncAlert);
+ },
showSimpleNotification(data) {
return instance?.showSimpleNotification(data);
},
diff --git a/app/core/NotificationsManager.test.ts b/app/core/NotificationsManager.test.ts
index 2af79c085b5..bf4e31eab4a 100644
--- a/app/core/NotificationsManager.test.ts
+++ b/app/core/NotificationsManager.test.ts
@@ -1,5 +1,4 @@
import { NotificationTransactionTypes } from '../util/notifications';
-
import NotificationManager, {
constructTitleAndMessage,
} from './NotificationManager';
@@ -45,26 +44,6 @@ describe('NotificationManager', () => {
expect(notificationManager._backgroundMode).toBe(true);
});
- it('calling NotificationManager in _failedCallback mode should call _showNotification', () => {
- notificationManager._failedCallback({
- id: 1,
- txParams: {
- nonce: 1,
- },
- });
- expect(notificationManager._showNotification).toBeInstanceOf(Function);
- });
-
- it('calling NotificationManager onMessageReceived', () => {
- notificationManager.onMessageReceived({
- data: {
- title: 'title',
- shortDescription: 'shortDescription',
- },
- });
- expect(notificationManager.onMessageReceived).toBeInstanceOf(Function);
- });
-
it('calling NotificationManager in background mode OFF should be falsy', () => {
notificationManager._handleAppStateChange('active');
expect(notificationManager._backgroundMode).toBe(false);
@@ -90,16 +69,15 @@ describe('NotificationManager', () => {
expect(NotificationManager.getTransactionToView()).toBeTruthy();
});
- const selectedNotificationTypes: (keyof typeof NotificationTransactionTypes)[] =
- [
- 'pending',
- 'pending_deposit',
- 'pending_withdrawal',
- 'success_withdrawal',
- 'success_deposit',
- 'error',
- 'cancelled',
- ];
+ const selectedNotificationTypes: (keyof typeof NotificationTransactionTypes)[] = [
+ 'pending',
+ 'pending_deposit',
+ 'pending_withdrawal',
+ 'success_withdrawal',
+ 'success_deposit',
+ 'error',
+ 'cancelled',
+ ];
selectedNotificationTypes.forEach((type) => {
it(`should construct title and message for ${type}`, () => {
const { title, message } = constructTitleAndMessage({
diff --git a/app/core/Performance/Performance.test.ts b/app/core/Performance/Performance.test.ts
index a74ec1d6bee..c205b8aded8 100644
--- a/app/core/Performance/Performance.test.ts
+++ b/app/core/Performance/Performance.test.ts
@@ -2,6 +2,10 @@
import Performance from './Performance';
import performance from 'react-native-performance';
+// Extend the global object for __DEV__
+// eslint-disable-next-line @typescript-eslint/no-shadow
+declare const global: { __DEV__: boolean };
+
// Mock react-native-performance
jest.mock('react-native-performance', () => {
const originalModule = jest.requireActual('react-native-performance');
@@ -45,10 +49,9 @@ describe('Performance', () => {
jest.resetAllMocks();
});
- it('should not log performance numbers in production', () => {
- jest.mock('../../util/test/utils', () => ({
- isTest: true, // or false, depending on what you want to test
- }));
+ it('should log performance numbers in development mode', () => {
+ // Mock __DEV__ to be true to simulate development mode
+ global.__DEV__ = true;
// Mock console.info to verify its calls
console.info = jest.fn();
@@ -80,5 +83,22 @@ describe('Performance', () => {
`APP START TIME = MAX(NATIVE LAUNCH TIME, JS BUNDLE LOAD TIME) - 1500ms`,
),
);
+
+ // Clean up
+ global.__DEV__ = false; // Reset __DEV__ to its original state
+ });
+
+ it('should not log performance numbers in non-development mode', () => {
+ // Mock __DEV__ to be true to simulate development mode
+ global.__DEV__ = false;
+
+ // Set up performance service
+ Performance.setupPerformanceObservers();
+
+ // Verify that measure wasn't called
+ expect(performance.measure).not.toHaveBeenCalled();
+
+ // Verify that console.info wasn't called
+ expect(console.info).not.toHaveBeenCalled();
});
});
diff --git a/app/core/Performance/Performance.ts b/app/core/Performance/Performance.ts
index d9d71d76340..545438f1f93 100644
--- a/app/core/Performance/Performance.ts
+++ b/app/core/Performance/Performance.ts
@@ -1,23 +1,16 @@
/* eslint-disable no-console */
import performance, { PerformanceObserver } from 'react-native-performance';
-import StorageWrapper from '../../store/storage-wrapper';
-import { isTest } from '../../util/test/utils';
/**
* Service for measuring app performance
*/
-
-async function setPerformanceValues(appStartTime: number) {
- await StorageWrapper.setItem('appStartTime', appStartTime.toString());
-}
-
class Performance {
/**
* Measures app start and JS bundle loading times
*/
static setupPerformanceObservers = () => {
// Don't run in release mode
- if (!isTest) return;
+ if (!__DEV__) return;
new PerformanceObserver((list) => {
// Get measurement entries
@@ -58,9 +51,6 @@ class Performance {
);
console.info(`-------------------------------------------------------`);
console.info(`-------------------------------------------------------`);
-
- setPerformanceValues(appStartTime);
-
}
}).observe({ type: 'react-native-mark', buffered: true });
};
diff --git a/app/core/SnapKeyring/KeyringSnapPermissions.test.ts b/app/core/SnapKeyring/KeyringSnapPermissions.test.ts
deleted file mode 100644
index 473cc037125..00000000000
--- a/app/core/SnapKeyring/KeyringSnapPermissions.test.ts
+++ /dev/null
@@ -1,153 +0,0 @@
-import {
- SubjectMetadataController,
- SubjectType,
-} from '@metamask/permission-controller';
-import { KeyringRpcMethod } from '@metamask/keyring-api';
-import {
- isProtocolAllowed,
- keyringSnapPermissionsBuilder,
-} from './keyringSnapsPermissions';
-
-const PORTFOLIO_ORIGINS: string[] = [
- 'https://portfolio.metamask.io',
- 'https://dev.portfolio.metamask.io',
- 'https://ramps-dev.portfolio.metamask.io',
-];
-
-describe('keyringSnapPermissionsBuilder', () => {
- const mockController = new SubjectMetadataController({
- subjectCacheLimit: 100,
- messenger: {
- registerActionHandler: jest.fn(),
- registerInitialEventPayload: jest.fn(),
- publish: jest.fn(),
- // TODO: Replace `any` with type
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- } as any,
- state: {},
- });
- mockController.addSubjectMetadata({
- origin: 'https://some-dapp.com',
- subjectType: SubjectType.Website,
- });
-
- describe('Portfolio origin', () => {
- it.each(PORTFOLIO_ORIGINS)(
- 'returns the methods that can be called by %s',
- (origin: string) => {
- const permissions = keyringSnapPermissionsBuilder(origin);
- expect(permissions()).toStrictEqual([
- KeyringRpcMethod.ListAccounts,
- KeyringRpcMethod.GetAccount,
- KeyringRpcMethod.GetAccountBalances,
- KeyringRpcMethod.SubmitRequest,
- ]);
- },
- );
- it.each(PORTFOLIO_ORIGINS)(
- '%s cannot create an account',
- (origin: string) => {
- const permissions = keyringSnapPermissionsBuilder(origin);
- expect(permissions()).not.toContain(KeyringRpcMethod.CreateAccount);
- },
- );
- it.each(PORTFOLIO_ORIGINS)('%s can submit a request', (origin: string) => {
- const permissions = keyringSnapPermissionsBuilder(origin);
- expect(permissions()).toContain(KeyringRpcMethod.SubmitRequest);
- });
- });
-
- it('returns the methods metamask can call', () => {
- const permissions = keyringSnapPermissionsBuilder('metamask');
- expect(permissions()).toStrictEqual([
- KeyringRpcMethod.ListAccounts,
- KeyringRpcMethod.GetAccount,
- KeyringRpcMethod.FilterAccountChains,
- KeyringRpcMethod.DeleteAccount,
- KeyringRpcMethod.ListRequests,
- KeyringRpcMethod.GetRequest,
- KeyringRpcMethod.SubmitRequest,
- KeyringRpcMethod.RejectRequest,
- ]);
- });
-
- it('returns the methods a known origin can call', () => {
- const permissions = keyringSnapPermissionsBuilder('https://some-dapp.com');
- expect(permissions()).toStrictEqual([
- KeyringRpcMethod.ListAccounts,
- KeyringRpcMethod.GetAccount,
- KeyringRpcMethod.CreateAccount,
- KeyringRpcMethod.FilterAccountChains,
- KeyringRpcMethod.UpdateAccount,
- KeyringRpcMethod.DeleteAccount,
- KeyringRpcMethod.ExportAccount,
- KeyringRpcMethod.ListRequests,
- KeyringRpcMethod.GetRequest,
- KeyringRpcMethod.ApproveRequest,
- KeyringRpcMethod.RejectRequest,
- ]);
- });
-
- it('returns the methods an unknown origin can call', () => {
- const permissions = keyringSnapPermissionsBuilder(
- 'https://some-other-dapp.com',
- );
- expect(permissions()).toStrictEqual([
- KeyringRpcMethod.ListAccounts,
- KeyringRpcMethod.GetAccount,
- KeyringRpcMethod.CreateAccount,
- KeyringRpcMethod.FilterAccountChains,
- KeyringRpcMethod.UpdateAccount,
- KeyringRpcMethod.DeleteAccount,
- KeyringRpcMethod.ExportAccount,
- KeyringRpcMethod.ListRequests,
- KeyringRpcMethod.GetRequest,
- KeyringRpcMethod.ApproveRequest,
- KeyringRpcMethod.RejectRequest,
- ]);
- });
-
- it.each([
- '',
- 'null',
- 'sftp://some-dapp.com',
- 'http://some-dapp.com',
- '0',
- undefined,
- null,
- true,
- false,
- 1,
- 0,
- -1,
- ])('"%s" cannot call any methods', (origin: unknown) => {
- const permissions = keyringSnapPermissionsBuilder(
- // TODO: Replace `any` with type
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- origin as any,
- );
- expect(permissions()).toStrictEqual([]);
- });
-});
-
-describe('isProtocolAllowed', () => {
- it.each([
- ['http://some-dapp.com', false],
- ['https://some-dapp.com', true],
- ['sftp://some-dapp.com', false],
- ['', false],
- ['null', false],
- ['0', false],
- [undefined, false],
- [null, false],
- [true, false],
- [false, false],
- [1, false],
- [0, false],
- [-1, false],
- // TODO: Replace `any` with type
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- ])('"%s" cannot call any methods', (origin: any, expected: boolean) => {
- expect(isProtocolAllowed(origin)).toBe(expected);
- });
-});
diff --git a/app/core/SnapKeyring/SnapKeyring.test.ts b/app/core/SnapKeyring/SnapKeyring.test.ts
deleted file mode 100644
index fad726e0294..00000000000
--- a/app/core/SnapKeyring/SnapKeyring.test.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-import { ControllerMessenger } from '@metamask/base-controller';
-import {
- EthAccountType,
- InternalAccount,
- KeyringEvent,
-} from '@metamask/keyring-api';
-import { snapKeyringBuilder } from './SnapKeyring';
-import {
- SnapKeyringBuilderAllowActions,
- SnapKeyringBuilderMessenger,
-} from './types';
-import { SnapId } from '@metamask/snaps-sdk';
-
-const mockGetAccounts = jest.fn();
-const mockSnapId: SnapId = 'snapId' as SnapId;
-const mockSnapName = 'mock-snap';
-const mockSnapController = jest.fn();
-const mockPersisKeyringHelper = jest.fn();
-const mockSetSelectedAccount = jest.fn();
-const mockRemoveAccountHelper = jest.fn();
-const mockGetAccountByAddress = jest.fn();
-
-const address = '0x2a4d4b667D5f12C3F9Bf8F14a7B9f8D8d9b8c8fA';
-const accountNameSuggestion = 'Suggested Account Name';
-const mockAccount = {
- type: EthAccountType.Eoa,
- id: '3afa663e-0600-4d93-868a-61c2e553013b',
- address,
- methods: [],
- options: {},
-};
-const mockInternalAccount = {
- ...mockAccount,
- metadata: {
- snap: {
- enabled: true,
- id: mockSnapId,
- name: mockSnapName,
- },
- name: accountNameSuggestion,
- keyring: {
- type: '',
- },
- importTime: 0,
- },
-};
-
-const createControllerMessenger = ({
- account = mockInternalAccount,
-}: {
- account?: InternalAccount;
-} = {}): SnapKeyringBuilderMessenger => {
- const messenger = new ControllerMessenger<
- SnapKeyringBuilderAllowActions,
- never
- >().getRestricted({
- name: 'SnapKeyringBuilder',
- allowedActions: [
- 'ApprovalController:addRequest',
- 'ApprovalController:acceptRequest',
- 'ApprovalController:rejectRequest',
- 'ApprovalController:startFlow',
- 'ApprovalController:endFlow',
- 'ApprovalController:showSuccess',
- 'ApprovalController:showError',
- 'PhishingController:maybeUpdateState',
- 'KeyringController:getAccounts',
- 'AccountsController:setSelectedAccount',
- 'AccountsController:getAccountByAddress',
- ],
- allowedEvents: [],
- });
-
- jest.spyOn(messenger, 'call').mockImplementation((...args) => {
- // This mock implementation does not have a nice discriminate union where types/parameters can be correctly inferred
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const [actionType, ...params]: any[] = args;
-
- switch (actionType) {
- case 'KeyringController:getAccounts':
- return mockGetAccounts.mockResolvedValue([])();
-
- case 'AccountsController:getAccountByAddress':
- return mockGetAccountByAddress.mockReturnValue(account)(params);
-
- case 'AccountsController:setSelectedAccount':
- return mockSetSelectedAccount(params);
-
- default:
- throw new Error(
- `MOCK_FAIL - unsupported messenger call: ${actionType}`,
- );
- }
- });
-
- return messenger;
-};
-
-const createSnapKeyringBuilder = () =>
- snapKeyringBuilder(
- createControllerMessenger(),
- mockSnapController,
- mockPersisKeyringHelper,
- mockRemoveAccountHelper,
- );
-
-describe('Snap Keyring Methods', () => {
- afterEach(() => {
- jest.resetAllMocks();
- });
-
- describe('addAccount', () => {
- afterEach(() => {
- jest.resetAllMocks();
- });
-
- it('handles account creation with without a user defined name', async () => {
- const builder = createSnapKeyringBuilder();
- await builder().handleKeyringSnapMessage(mockSnapId, {
- method: KeyringEvent.AccountCreated,
- params: {
- account: mockAccount,
- displayConfirmation: true,
- },
- });
- expect(mockPersisKeyringHelper).toHaveBeenCalledTimes(2);
- expect(mockGetAccountByAddress).toHaveBeenCalledTimes(1);
- expect(mockGetAccountByAddress).toHaveBeenCalledWith([
- mockAccount.address.toLowerCase(),
- ]);
- });
- });
-});
diff --git a/app/core/SnapKeyring/SnapKeyring.ts b/app/core/SnapKeyring/SnapKeyring.ts
deleted file mode 100644
index ec5e9e5659f..00000000000
--- a/app/core/SnapKeyring/SnapKeyring.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-import { SnapKeyring } from '@metamask/eth-snap-keyring';
-import type { SnapController } from '@metamask/snaps-controllers';
-import { SnapKeyringBuilderMessenger } from './types';
-import Logger from '../../util/Logger';
-
-/**
- * Constructs a SnapKeyring builder with specified handlers for managing snap accounts.
- * - Here is the equivalent function on the extension: https://github.com/MetaMask/metamask-extension/blob/develop/app/scripts/lib/snap-keyring/snap-keyring.ts#L111
- *
- * @param controllerMessenger - The controller messenger instance.
- * @param getSnapController - A function that retrieves the Snap Controller instance.
- * @param persistKeyringHelper - A function that persists all keyrings in the vault.
- * @param removeAccountHelper - A function to help remove an account based on its address.
- * @returns The constructed SnapKeyring builder instance with the following methods:
- * - `saveState`: Persists all keyrings in the keyring controller.
- * - `addAccount`: Initiates the process of adding an account with user confirmation and handling the user input.
- * - `removeAccount`: Initiates the process of removing an account with user confirmation and handling the user input.
- * - `redirectUser`: Redirects the user to a specified URL with a message to complete signing. This method is used to handle asynchronous signing requests.
- * - `addressExists`: Returns a boolean indicating if an address exists in the keyring.
- */
-export const snapKeyringBuilder = (
- controllerMessenger: SnapKeyringBuilderMessenger,
- getSnapController: () => SnapController,
- persistKeyringHelper: () => Promise,
- removeAccountHelper: (address: string) => Promise,
-): { (): SnapKeyring; type: string } => {
- const builder = () =>
- new SnapKeyring(getSnapController(), {
- addressExists: async (address) =>
- (
- await controllerMessenger.call('KeyringController:getAccounts')
- ).includes(address.toLowerCase()),
-
- redirectUser: async (snapId: string, url: string, message: string) => {
- Logger.log(
- `SnapKeyring: redirectUser called with \n
- - snapId: ${snapId} \n
- - url: ${url} \n
- - message: ${message} \n`,
- );
- },
-
- saveState: async () => {
- await persistKeyringHelper();
- },
-
- addAccount: async (
- address: string,
- snapId: string,
- handleUserInput: (accepted: boolean) => Promise,
- accountNameSuggestion = '',
- displayConfirmation = false,
- ) => {
- // TODO: Implement proper snap account confirmations. Currently, we are approving everything for testing purposes.
- Logger.log(
- `SnapKeyring: addAccount called with \n
- - address: ${address} \n
- - handleUserInput: ${handleUserInput} \n
- - snapId: ${snapId} \n
- - accountNameSuggestion: ${accountNameSuggestion} \n
- - displayConfirmation: ${displayConfirmation}`,
- );
-
- // Approve everything for now because we have not implemented snap account confirmations yet
- await handleUserInput(true);
- await persistKeyringHelper();
- const account = controllerMessenger.call(
- 'AccountsController:getAccountByAddress',
- address,
- );
- if (!account) {
- throw new Error(`Internal account not found for address: ${address}`);
- }
-
- // Set the selected account to the new account
- controllerMessenger.call(
- 'AccountsController:setSelectedAccount',
- account.id,
- );
- },
-
- removeAccount: async (
- address: string,
- snapId: string,
- handleUserInput: (accepted: boolean) => Promise,
- ) => {
- // TODO: Implement proper snap account confirmations. Currently, we are approving everything for testing purposes.
- Logger.log(
- `SnapKeyring: removeAccount called with \n
- - address: ${address} \n
- - handleUserInput: ${handleUserInput} \n
- - snapId: ${snapId} \n`,
- );
- // Approve everything for now because we have not implemented snap account confirmations yet
- await handleUserInput(true);
- await removeAccountHelper(address);
- await persistKeyringHelper();
- },
- });
- builder.type = SnapKeyring.type;
- return builder;
-};
diff --git a/app/core/SnapKeyring/index.ts b/app/core/SnapKeyring/index.ts
deleted file mode 100644
index 5ad5c16f3aa..00000000000
--- a/app/core/SnapKeyring/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { snapKeyringBuilder } from './SnapKeyring';
diff --git a/app/core/SnapKeyring/keyringSnapsPermissions.ts b/app/core/SnapKeyring/keyringSnapsPermissions.ts
deleted file mode 100644
index cde232c37de..00000000000
--- a/app/core/SnapKeyring/keyringSnapsPermissions.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-/* eslint-disable no-console */
-import { KeyringRpcMethod } from '@metamask/keyring-api';
-
-/**
- * The origins of the Portfolio dapp.
- */
-const PORTFOLIO_ORIGINS: string[] = [
- 'https://portfolio.metamask.io',
- ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
- 'https://dev.portfolio.metamask.io',
- 'https://stage.portfolio.metamask.io',
- 'https://ramps-dev.portfolio.metamask.io',
- 'https://portfolio-builds.metafi-dev.codefi.network',
- ///: END:ONLY_INCLUDE_IF
-];
-
-/**
- * List of keyring methods MetaMask can call.
- */
-const METAMASK_ALLOWED_METHODS: string[] = [
- KeyringRpcMethod.ListAccounts,
- KeyringRpcMethod.GetAccount,
- KeyringRpcMethod.FilterAccountChains,
- KeyringRpcMethod.DeleteAccount,
- KeyringRpcMethod.ListRequests,
- KeyringRpcMethod.GetRequest,
- KeyringRpcMethod.SubmitRequest,
- KeyringRpcMethod.RejectRequest,
-];
-
-/**
- * List of keyring methods a dapp can call.
- * !NOTE: DO NOT INCLUDE `KeyringRpcMethod.SubmitRequest` IN THIS LIST.
- */
-const WEBSITE_ALLOWED_METHODS: string[] = [
- KeyringRpcMethod.ListAccounts,
- KeyringRpcMethod.GetAccount,
- KeyringRpcMethod.CreateAccount,
- KeyringRpcMethod.FilterAccountChains,
- KeyringRpcMethod.UpdateAccount,
- KeyringRpcMethod.DeleteAccount,
- KeyringRpcMethod.ExportAccount,
- KeyringRpcMethod.ListRequests,
- KeyringRpcMethod.GetRequest,
- KeyringRpcMethod.ApproveRequest,
- KeyringRpcMethod.RejectRequest,
-];
-
-/**
- * List of keyring methods that Portfolio can call.
- */
-const PORTFOLIO_ALLOWED_METHODS: string[] = [
- KeyringRpcMethod.ListAccounts,
- KeyringRpcMethod.GetAccount,
- KeyringRpcMethod.GetAccountBalances,
- KeyringRpcMethod.SubmitRequest,
-];
-
-/**
- * List of allowed protocols. On Flask, HTTP is also allowed for testing.
- */
-const ALLOWED_PROTOCOLS: string[] = [
- 'https:',
-];
-
-/**
- * Checks if the protocol of the origin is allowed.
- *
- * @param origin - The origin to check.
- * @returns `true` if the protocol of the origin is allowed, `false` otherwise.
- */
-export function isProtocolAllowed(origin: string): boolean {
- try {
- const url = new URL(origin);
- return ALLOWED_PROTOCOLS.includes(url.protocol);
- } catch (error) {
- return false;
- }
-}
-
-/**
- * Builds a function that returns the list of keyring methods an origin can
- * call.
- * - Here is the equivalent function on the extension: https://github.com/MetaMask/metamask-extension/blob/develop/app/scripts/lib/snap-keyring/keyring-snaps-permissions.ts#L96
- * @param controller - Reference to the `SubjectMetadataController`.
- * @param origin - The origin itself.
- * @returns A function that returns the list of keyring methods an origin can
- * call.
- */
-export function keyringSnapPermissionsBuilder(
- origin: string,
-): () => string[] {
- return () => {
- if (origin === 'metamask') {
- return METAMASK_ALLOWED_METHODS;
- }
-
- if (PORTFOLIO_ORIGINS.includes(origin)) {
- return PORTFOLIO_ALLOWED_METHODS;
- }
- return isProtocolAllowed(origin) ? WEBSITE_ALLOWED_METHODS : [];
- };
-}
diff --git a/app/core/SnapKeyring/types.ts b/app/core/SnapKeyring/types.ts
deleted file mode 100644
index 701198d1d50..00000000000
--- a/app/core/SnapKeyring/types.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { RestrictedControllerMessenger } from '@metamask/base-controller';
-import { MaybeUpdateState, TestOrigin } from '@metamask/phishing-controller';
-import type { KeyringControllerGetAccountsAction } from '@metamask/keyring-controller';
-import { GetSubjectMetadata } from '@metamask/permission-controller';
-import {
- AccountsControllerGetAccountByAddressAction,
- AccountsControllerSetAccountNameAction,
- AccountsControllerSetSelectedAccountAction,
-} from '@metamask/accounts-controller';
-import type {
- AcceptRequest,
- AddApprovalRequest,
- EndFlow,
- RejectRequest,
- ShowError,
- ShowSuccess,
- StartFlow,
-} from '@metamask/approval-controller';
-
-export type SnapKeyringBuilderAllowActions =
- | StartFlow
- | EndFlow
- | ShowSuccess
- | ShowError
- | AddApprovalRequest
- | AcceptRequest
- | RejectRequest
- | MaybeUpdateState
- | TestOrigin
- | KeyringControllerGetAccountsAction
- | GetSubjectMetadata
- | AccountsControllerSetSelectedAccountAction
- | AccountsControllerGetAccountByAddressAction
- | AccountsControllerSetAccountNameAction;
-
-export type SnapKeyringBuilderMessenger = RestrictedControllerMessenger<
- 'SnapKeyringBuilder',
- SnapKeyringBuilderAllowActions,
- never,
- SnapKeyringBuilderAllowActions['type'],
- never
->;
diff --git a/app/core/Snaps/SnapBridge.ts b/app/core/Snaps/SnapBridge.ts
index 5a9c468dc65..858bcdcd9fc 100644
--- a/app/core/Snaps/SnapBridge.ts
+++ b/app/core/Snaps/SnapBridge.ts
@@ -162,7 +162,9 @@ export default class SnapBridge {
engine.push(filterMiddleware);
engine.push(subscriptionManager.middleware);
- const { context, controllerMessenger } = Engine;
+ // TODO: Replace "any" with type
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const { context, controllerMessenger } = Engine as any;
const { PermissionController } = context;
engine.push(
diff --git a/app/core/Snaps/SnapsMethodMiddleware.ts b/app/core/Snaps/SnapsMethodMiddleware.ts
index c2ad0d4f3ae..47bc9ae7372 100644
--- a/app/core/Snaps/SnapsMethodMiddleware.ts
+++ b/app/core/Snaps/SnapsMethodMiddleware.ts
@@ -4,25 +4,16 @@ import {
RequestedPermissions,
SubjectType,
} from '@metamask/permission-controller';
-import { SnapRpcHookArgs } from '@metamask/snaps-utils';
import { RestrictedMethods } from '../Permissions/constants';
-import { keyringSnapPermissionsBuilder } from '../SnapKeyring/keyringSnapsPermissions';
-import { SnapId } from '@metamask/snaps-sdk';
-import { EngineContext } from '../Engine';
-import { handleSnapRequest } from './utils';
-export function getSnapIdFromRequest(
- request: Record,
-): SnapId | null {
- const { snapId } = request;
- return typeof snapId === 'string' ? snapId as SnapId : null;
-}
// Snaps middleware
/*
from extension https://github.dev/MetaMask/metamask-extension/blob/1d5e8a78400d7aaaf2b3cbdb30cff9399061df34/app/scripts/metamask-controller.js#L3830-L3861
*/
const snapMethodMiddlewareBuilder = (
- engineContext: EngineContext,
+ // TODO: Replace "any" with type
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ engineContext: any,
// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
controllerMessenger: any,
@@ -45,11 +36,6 @@ const snapMethodMiddlewareBuilder = (
engineContext.PermissionController,
origin,
),
- hasPermission: engineContext.PermissionController.hasPermission.bind(
- engineContext.PermissionController,
- origin,
- ),
- getAllowedKeyringMethods: keyringSnapPermissionsBuilder(origin),
getSnapFile: controllerMessenger.call.bind(
controllerMessenger,
'SnapController:getFile',
@@ -65,26 +51,6 @@ const snapMethodMiddlewareBuilder = (
origin,
RestrictedMethods.wallet_snap,
),
- getSnap: controllerMessenger.call.bind(
- controllerMessenger,
- 'SnapController:get',
- ),
- handleSnapRpcRequest: async (request: Omit) => {
- const snapId = getSnapIdFromRequest(request);
-
- if (!snapId) {
- throw new Error(
- 'snapMethodMiddlewareBuilder handleSnapRpcRequest: Invalid snap request: snapId not found',
- );
- }
-
- return await handleSnapRequest(controllerMessenger, {
- snapId,
- origin,
- handler: request.handler,
- request: request.request,
- });
- },
});
export default snapMethodMiddlewareBuilder;
diff --git a/app/core/Snaps/permissions/permissions.ts b/app/core/Snaps/permissions/permissions.ts
index c0eb81bd2f1..acf658902d2 100644
--- a/app/core/Snaps/permissions/permissions.ts
+++ b/app/core/Snaps/permissions/permissions.ts
@@ -9,6 +9,5 @@ export const EndowmentPermissions = Object.freeze({
'endowment:cronjob': 'endowment:cronjob',
'endowment:ethereum-provider': 'endowment:ethereum-provider',
'endowment:rpc': 'endowment:rpc',
- 'endowment:keyring': 'endowment:keyring',
} as const);
///: END:ONLY_INCLUDE_IF
diff --git a/app/core/Snaps/types.ts b/app/core/Snaps/types.ts
deleted file mode 100644
index 025b64f4fd0..00000000000
--- a/app/core/Snaps/types.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { SnapId } from '@metamask/snaps-sdk';
-import { SnapRpcHookArgs } from '@metamask/snaps-utils';
-
-export type HandleSnapRequestArgs = SnapRpcHookArgs & { snapId: SnapId };
diff --git a/app/core/Snaps/utils.ts b/app/core/Snaps/utils.ts
deleted file mode 100644
index ac2157dc785..00000000000
--- a/app/core/Snaps/utils.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { ControllerMessenger } from '../Engine';
-import { HandleSnapRequestArgs } from './types';
-
-/**
- * Passes a JSON-RPC request object to the SnapController for execution.
- *
- * @param {object} args - A bag of options.
- * @param {string} args.snapId - The ID of the recipient snap.
- * @param {string} args.origin - The origin of the RPC request.
- * @param {string} args.handler - The handler to trigger on the snap for the request.
- * @param {object} args.request - The JSON-RPC request object.
- * @returns The result of the JSON-RPC request.
- */
-export async function handleSnapRequest(
- controllerMessenger: ControllerMessenger,
- args: HandleSnapRequestArgs,
-) {
- return await controllerMessenger.call('SnapController:handleRequest', args);
-}
diff --git a/app/core/__mocks__/MockedEngine.ts b/app/core/__mocks__/MockedEngine.ts
index dcdde0ec33c..104f346e2be 100644
--- a/app/core/__mocks__/MockedEngine.ts
+++ b/app/core/__mocks__/MockedEngine.ts
@@ -1,13 +1,38 @@
+import { KeyringTypes } from '@metamask/keyring-controller';
import { CHAIN_IDS } from '@metamask/transaction-controller';
import { mockNetworkState } from '../../util/test/network';
import { NetworkClientId } from '@metamask/network-controller';
import Engine from '../../core/Engine';
-import { MOCK_KEYRING_CONTROLLER_STATE } from '../../util/test/keyringControllerTestUtils';
export const mockedEngine = {
init: () => Engine.init({}),
context: {
- KeyringController: MOCK_KEYRING_CONTROLLER_STATE,
+ KeyringController: {
+ keyring: {
+ keyrings: [
+ {
+ mnemonic:
+ 'one two three four five six seven eight nine ten eleven twelve',
+ },
+ ],
+ },
+ state: {
+ keyrings: [
+ {
+ accounts: ['0xd018538C87232FF95acbCe4870629b75640a78E7'],
+ type: KeyringTypes.simple,
+ },
+ {
+ accounts: ['0xB374Ca013934e498e5baD3409147F34E6c462389'],
+ type: KeyringTypes.qr,
+ },
+ {
+ accounts: ['0x71C7656EC7ab88b098defB751B7401B5f6d8976F'],
+ type: KeyringTypes.hd,
+ },
+ ],
+ },
+ },
NetworkController: {
getNetworkClientById: (networkClientId: NetworkClientId) => {
if (networkClientId === 'linea_goerli') {
diff --git a/app/declarations/index.d.ts b/app/declarations/index.d.ts
index 094baa70c44..04d7a1238f9 100644
--- a/app/declarations/index.d.ts
+++ b/app/declarations/index.d.ts
@@ -6,8 +6,6 @@ declare module '@metamask/react-native-payments/lib/js/__mocks__';
declare module 'react-native-fade-in-image';
-declare module 'react-native-fast-crypto';
-
declare module 'react-native-minimizer';
declare module 'xhr2';
diff --git a/app/lib/snaps/SnapsExecutionWebView.tsx b/app/lib/snaps/SnapsExecutionWebView.tsx
index 24b8e99a087..157ef74df69 100644
--- a/app/lib/snaps/SnapsExecutionWebView.tsx
+++ b/app/lib/snaps/SnapsExecutionWebView.tsx
@@ -1,10 +1,13 @@
+/* eslint-disable @typescript-eslint/no-var-requires */
+/* eslint-disable @typescript-eslint/no-require-imports */
+/* eslint-disable import/no-commonjs */
///: BEGIN:ONLY_INCLUDE_IF(preinstalled-snaps,external-snaps)
import React, { Component, RefObject } from 'react';
import { View, ScrollView, NativeSyntheticEvent } from 'react-native';
-import { WebViewMessageEvent, WebView } from '@metamask/react-native-webview';
+import WebView, { WebViewMessageEvent } from '@metamask/react-native-webview';
import { createStyles } from './styles';
+import { WebViewInterface } from '@metamask/snaps-controllers/dist/types/services/webview/WebViewMessageStream';
import { WebViewError } from '@metamask/react-native-webview/lib/WebViewTypes';
-import { WebViewInterface } from '@metamask/snaps-controllers/react-native';
import { PostMessageEvent } from '@metamask/post-message-stream';
const SNAPS_EE_URL = 'https://execution.metamask.io/webview/6.7.1/index.html';
diff --git a/app/reducers/index.ts b/app/reducers/index.ts
index 51f397e1128..df18eae36c8 100644
--- a/app/reducers/index.ts
+++ b/app/reducers/index.ts
@@ -33,6 +33,7 @@ import smartTransactionsReducer from '../core/redux/slices/smartTransactions';
import transactionMetricsReducer from '../core/redux/slices/transactionMetrics';
import originThrottlingReducer from '../core/redux/slices/originThrottling';
import notificationsAccountsProvider from '../core/redux/slices/notifications';
+
/**
* Infer state from a reducer
*
diff --git a/app/selectors/addressBookController.test.ts b/app/selectors/addressBookController.test.ts
index e63d2e954fa..5f80f6e1c1c 100644
--- a/app/selectors/addressBookController.test.ts
+++ b/app/selectors/addressBookController.test.ts
@@ -1,10 +1,10 @@
import { RootState } from '../reducers';
import { selectAddressBook } from './addressBookController';
-import { AddressBookControllerState } from '@metamask/address-book-controller';
+import { AddressBookState } from '@metamask/address-book-controller';
describe('selectAddressBook', () => {
it('returns addressBook from state', () => {
- const mockAddressBookControllerState: AddressBookControllerState = {
+ const mockAddressBookState: AddressBookState = {
addressBook: {
'0x1': {
'0x123': {
@@ -21,13 +21,13 @@ describe('selectAddressBook', () => {
const mockState = {
engine: {
backgroundState: {
- AddressBookController: mockAddressBookControllerState,
+ AddressBookController: mockAddressBookState,
},
},
};
expect(selectAddressBook(mockState as RootState)).toEqual(
- mockAddressBookControllerState.addressBook,
+ mockAddressBookState.addressBook,
);
});
});
diff --git a/app/selectors/addressBookController.ts b/app/selectors/addressBookController.ts
index eff948309b4..c734a075f2c 100644
--- a/app/selectors/addressBookController.ts
+++ b/app/selectors/addressBookController.ts
@@ -1,12 +1,12 @@
import { RootState } from '../reducers';
import { createSelector } from 'reselect';
-import { AddressBookControllerState } from '@metamask/address-book-controller';
+import { AddressBookState } from '@metamask/address-book-controller';
export const selectAddressBookControllerState = (state: RootState) =>
state.engine.backgroundState.AddressBookController;
export const selectAddressBook = createSelector(
selectAddressBookControllerState,
- (addressBookControllerState: AddressBookControllerState) =>
+ (addressBookControllerState: AddressBookState) =>
addressBookControllerState.addressBook,
);
diff --git a/app/selectors/types.ts b/app/selectors/types.ts
index 5b0c2e6281b..5bf4921a87c 100644
--- a/app/selectors/types.ts
+++ b/app/selectors/types.ts
@@ -11,7 +11,7 @@ import {
} from '@metamask/assets-controllers';
import SwapsController from '@metamask/swaps-controller';
import { NetworkState } from '@metamask/network-controller';
-import { AddressBookControllerState } from '@metamask/address-book-controller';
+import { AddressBookState } from '@metamask/address-book-controller';
import { BaseState } from '@metamask/base-controller';
import { KeyringControllerMemState } from '@metamask/keyring-controller';
import { PreferencesState } from '@metamask/preferences-controller';
@@ -28,7 +28,7 @@ export interface EngineState {
engine: {
backgroundState: {
AccountTrackerController: AccountTrackerState;
- AddressBookController: AddressBookControllerState;
+ AddressBookController: AddressBookState;
AssetsContractController: BaseState;
NftController: NftState;
TokenListController: TokenListState;
diff --git a/app/store/migrations/029.ts b/app/store/migrations/029.ts
index f98e171d70b..0431c101b0d 100644
--- a/app/store/migrations/029.ts
+++ b/app/store/migrations/029.ts
@@ -9,7 +9,7 @@ import { TransactionParams } from '@metamask/transaction-controller';
import { captureException } from '@sentry/react-native';
import {
AddressBookEntry,
- AddressBookControllerState,
+ AddressBookState,
} from '@metamask/address-book-controller';
import {
Nft,
@@ -211,7 +211,7 @@ export default async function migrate(stateAsync: unknown) {
state?.engine?.backgroundState?.AddressBookController;
const newAddressBookControllerState = state?.engine?.backgroundState
- ?.AddressBookController as AddressBookControllerState;
+ ?.AddressBookController as AddressBookState;
if (!isObject(addressBookControllerState)) {
captureException(
diff --git a/app/util/address/index.test.ts b/app/util/address/index.test.ts
index 763059d0ba5..e19035c34d2 100644
--- a/app/util/address/index.test.ts
+++ b/app/util/address/index.test.ts
@@ -15,37 +15,6 @@ import {
getKeyringByAddress,
getLabelTextByAddress,
} from '.';
-import {
- mockHDKeyringAddress,
- mockQrKeyringAddress,
- mockSimpleKeyringAddress,
-} from '../test/keyringControllerTestUtils';
-
-const snapAddress = '0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272';
-
-jest.mock('../../core/Engine', () => {
- const { KeyringTypes } = jest.requireActual('@metamask/keyring-controller');
- const { MOCK_KEYRING_CONTROLLER_STATE } = jest.requireActual(
- '../test/keyringControllerTestUtils',
- );
- return {
- context: {
- KeyringController: {
- ...MOCK_KEYRING_CONTROLLER_STATE,
- state: {
- keyrings: [
- ...MOCK_KEYRING_CONTROLLER_STATE.state.keyrings,
- {
- accounts: [snapAddress],
- index: 0,
- type: KeyringTypes.snap,
- },
- ],
- },
- },
- },
- };
-});
describe('isENS', () => {
it('should return false by default', () => {
@@ -263,13 +232,19 @@ describe('isQRHardwareAccount', () => {
});
it('should return false if address is from keyring type simple', () => {
- expect(isQRHardwareAccount(mockSimpleKeyringAddress)).toBeFalsy();
+ expect(
+ isQRHardwareAccount('0xd018538C87232FF95acbCe4870629b75640a78E7'),
+ ).toBeFalsy();
});
it('should return false if address is from keyring type hd', () => {
- expect(isQRHardwareAccount(mockHDKeyringAddress)).toBeFalsy();
+ expect(
+ isQRHardwareAccount('0x71C7656EC7ab88b098defB751B7401B5f6d8976F'),
+ ).toBeFalsy();
});
it('should return true if address is from keyring type qr', () => {
- expect(isQRHardwareAccount(mockQrKeyringAddress)).toBeTruthy();
+ expect(
+ isQRHardwareAccount('0xB374Ca013934e498e5baD3409147F34E6c462389'),
+ ).toBeTruthy();
});
});
describe('getKeyringByAddress', () => {
@@ -282,7 +257,9 @@ describe('getKeyringByAddress', () => {
expect(getKeyringByAddress('ens.eth')).toBeUndefined();
});
it('should return address if found', () => {
- expect(getKeyringByAddress(mockQrKeyringAddress)).not.toBe(undefined);
+ expect(
+ getKeyringByAddress('0xB374Ca013934e498e5baD3409147F34E6c462389'),
+ ).not.toBe(undefined);
});
it('should return null if address not found', () => {
expect(
@@ -292,7 +269,9 @@ describe('getKeyringByAddress', () => {
});
describe('isHardwareAccount,', () => {
it('should return true if account is a QR keyring', () => {
- expect(isHardwareAccount(mockQrKeyringAddress)).toBeTruthy();
+ expect(
+ isHardwareAccount('0xB374Ca013934e498e5baD3409147F34E6c462389'),
+ ).toBeTruthy();
});
it('should return false if account is not a hardware keyring', () => {
@@ -302,26 +281,16 @@ describe('isHardwareAccount,', () => {
});
});
describe('getLabelTextByAddress,', () => {
- beforeEach(() => {
- jest.resetAllMocks();
- });
-
it('should return accounts.qr_hardware if account is a QR keyring', () => {
- expect(getLabelTextByAddress(mockQrKeyringAddress)).toBe(
- 'accounts.qr_hardware',
- );
+ expect(
+ getLabelTextByAddress('0xB374Ca013934e498e5baD3409147F34E6c462389'),
+ ).toBe('accounts.qr_hardware');
});
it('should return KeyringTypes.simple if address is a imported account', () => {
- expect(getLabelTextByAddress(mockSimpleKeyringAddress)).toBe(
- 'accounts.imported',
- );
- });
-
- it('returns "Snaps (beta)" if account is a Snap keyring', () => {
- expect(getLabelTextByAddress(snapAddress)).toBe(
- 'accounts.snap_account_tag',
- );
+ expect(
+ getLabelTextByAddress('0xd018538C87232FF95acbCe4870629b75640a78E7'),
+ ).toBe('accounts.imported');
});
it('should return null if address is empty', () => {
@@ -343,13 +312,19 @@ describe('getAddressAccountType', () => {
);
});
it('should return QR if address is from a keyring type qr', () => {
- expect(getAddressAccountType(mockQrKeyringAddress)).toBe('QR');
+ expect(
+ getAddressAccountType('0xB374Ca013934e498e5baD3409147F34E6c462389'),
+ ).toBe('QR');
});
it('should return imported if address is from a keyring type simple', () => {
- expect(getAddressAccountType(mockSimpleKeyringAddress)).toBe('Imported');
+ expect(
+ getAddressAccountType('0xd018538C87232FF95acbCe4870629b75640a78E7'),
+ ).toBe('Imported');
});
it('should return MetaMask if address is not qr or simple', () => {
- expect(getAddressAccountType(mockHDKeyringAddress)).toBe('MetaMask');
+ expect(
+ getAddressAccountType('0x71C7656EC7ab88b098defB751B7401B5f6d8976F'),
+ ).toBe('MetaMask');
});
});
describe('resemblesAddress', () => {
@@ -362,6 +337,8 @@ describe('resemblesAddress', () => {
expect(resemblesAddress('address-stub-1')).toBeFalsy();
});
it('should return true if address resemble an eth address', () => {
- expect(resemblesAddress(mockHDKeyringAddress)).toBeTruthy();
+ expect(
+ resemblesAddress('0x71C7656EC7ab88b098defB751B7401B5f6d8976F'),
+ ).toBeTruthy();
});
});
diff --git a/app/util/address/index.ts b/app/util/address/index.ts
index 6d333d76f9e..5561b99d43d 100644
--- a/app/util/address/index.ts
+++ b/app/util/address/index.ts
@@ -34,15 +34,10 @@ import { store } from '../../store';
import { regex } from '../../../app/util/regex';
import Logger from '../../../app/util/Logger';
import { InternalAccount } from '@metamask/keyring-api';
-import { AddressBookControllerState } from '@metamask/address-book-controller';
+import { AddressBookState } from '@metamask/address-book-controller';
import { NetworkType, toChecksumHexAddress } from '@metamask/controller-utils';
import { NetworkClientId, NetworkState } from '@metamask/network-controller';
-import {
- AccountImportStrategy,
- ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
- KeyringTypes,
- ///: END:ONLY_INCLUDE_IF
-} from '@metamask/keyring-controller';
+import { AccountImportStrategy } from '@metamask/keyring-controller';
import { Hex, isHexString } from '@metamask/utils';
const {
@@ -249,10 +244,6 @@ export function getLabelTextByAddress(address: string) {
return 'accounts.qr_hardware';
case ExtendedKeyringTypes.simple:
return 'accounts.imported';
- ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
- case KeyringTypes.snap:
- return 'accounts.snap_account_tag';
- ///: END:ONLY_INCLUDE_IF
}
}
return null;
@@ -384,7 +375,7 @@ export function isValidHexAddress(
*/
function checkIfAddressAlreadySaved(
address: string,
- addressBook: AddressBookControllerState['addressBook'],
+ addressBook: AddressBookState['addressBook'],
chainId: Hex,
internalAccounts: InternalAccount[],
) {
@@ -429,7 +420,7 @@ function checkIfAddressAlreadySaved(
*/
export async function validateAddressOrENS(
toAccount: string,
- addressBook: AddressBookControllerState['addressBook'],
+ addressBook: AddressBookState['addressBook'],
internalAccounts: InternalAccount[],
chainId: Hex,
) {
diff --git a/app/util/browserScripts.ts b/app/util/browserScripts.js
similarity index 91%
rename from app/util/browserScripts.ts
rename to app/util/browserScripts.js
index ecd9d419915..c213a48f2d0 100644
--- a/app/util/browserScripts.ts
+++ b/app/util/browserScripts.js
@@ -76,10 +76,7 @@ export const JS_WINDOW_INFORMATION = `
export const JS_DESELECT_TEXT = `if (window.getSelection) {window.getSelection().removeAllRanges();}
else if (document.selection) {document.selection.empty();}`;
-export const JS_POST_MESSAGE_TO_PROVIDER = (
- message: object,
- origin: string
-) => `(function () {
+export const JS_POST_MESSAGE_TO_PROVIDER = (message, origin) => `(function () {
try {
window.postMessage(${JSON.stringify(message)}, '${origin}');
} catch (e) {
@@ -87,10 +84,7 @@ export const JS_POST_MESSAGE_TO_PROVIDER = (
}
})()`;
-export const JS_IFRAME_POST_MESSAGE_TO_PROVIDER = (
- _message: object,
- _origin: string
-) =>
+export const JS_IFRAME_POST_MESSAGE_TO_PROVIDER = (message, origin) =>
`(function () {})()`;
/** Disable sending messages to iframes for now
*
@@ -99,7 +93,7 @@ export const JS_IFRAME_POST_MESSAGE_TO_PROVIDER = (
for (let frame of iframes){
try {
- frame.contentWindow.postMessage(${JSON.stringify(_message)}, '${_origin}');
+ frame.contentWindow.postMessage(${JSON.stringify(message)}, '${origin}');
} catch (e) {
//Nothing to do
}
diff --git a/app/util/bytes.ts b/app/util/bytes.js
similarity index 72%
rename from app/util/bytes.ts
rename to app/util/bytes.js
index 3d046a30889..ac04d050c20 100644
--- a/app/util/bytes.ts
+++ b/app/util/bytes.js
@@ -5,10 +5,11 @@
*
* @returns - Hex string
*/
-export default function byteArrayToHex(value: Uint8Array): string {
+export default function byteArrayToHex(value) {
const HexCharacters = '0123456789abcdef';
const result = [];
- for (const v of value) {
+ for (let i = 0; i < value.length; i++) {
+ const v = value[i];
result.push(HexCharacters[(v & 0xf0) >> 4] + HexCharacters[v & 0x0f]);
}
return '0x' + result.join('');
diff --git a/app/util/checkAddress.test.ts b/app/util/checkAddress.test.ts
index 32a07f0dbba..4a253bf80c8 100644
--- a/app/util/checkAddress.test.ts
+++ b/app/util/checkAddress.test.ts
@@ -1,10 +1,10 @@
-import { AddressBookControllerState } from '@metamask/address-book-controller';
+import { AddressBookState } from '@metamask/address-book-controller';
import checkIfAddressIsSaved from './checkAddress';
describe('checkIfAddressIsSaved', () => {
it(`returns an empty array if the transaction recipient is unset`, () => {
const mockAddress = '0x0000000000000000000000000000000000000001';
- const addressBook: AddressBookControllerState['addressBook'] = {
+ const addressBook: AddressBookState['addressBook'] = {
'0x1': {
[mockAddress]: {
address: mockAddress,
@@ -26,7 +26,7 @@ describe('checkIfAddressIsSaved', () => {
// TODO: Update this case to return undefined to improve consistency
it('returns undefined if the address book is empty', () => {
const mockAddress = '0x0000000000000000000000000000000000000001';
- const addressBook: AddressBookControllerState['addressBook'] = {};
+ const addressBook: AddressBookState['addressBook'] = {};
const chainId = '0x1';
const transaction = {
to: mockAddress,
@@ -40,7 +40,7 @@ describe('checkIfAddressIsSaved', () => {
it('returns an empty array if transaction recipient is not in the address book', () => {
const mockAddress1 = '0x0000000000000000000000000000000000000001';
const mockAddress2 = '0x0000000000000000000000000000000000000002';
- const addressBook: AddressBookControllerState['addressBook'] = {
+ const addressBook: AddressBookState['addressBook'] = {
'0x1': {
[mockAddress2]: {
address: mockAddress2,
@@ -63,7 +63,7 @@ describe('checkIfAddressIsSaved', () => {
it('returns an empty array if transaction recipient is not in the address book for the given network', () => {
const mockAddress = '0x0000000000000000000000000000000000000001';
- const addressBook: AddressBookControllerState['addressBook'] = {
+ const addressBook: AddressBookState['addressBook'] = {
'0x2': {
[mockAddress]: {
address: mockAddress,
@@ -86,7 +86,7 @@ describe('checkIfAddressIsSaved', () => {
it('returns an address book entry', () => {
const mockAddress = '0x0000000000000000000000000000000000000001';
- const addressBook: AddressBookControllerState['addressBook'] = {
+ const addressBook: AddressBookState['addressBook'] = {
'0x1': {
[mockAddress]: {
address: mockAddress,
@@ -115,7 +115,7 @@ describe('checkIfAddressIsSaved', () => {
it('returns an address book entry with a checksummed address', () => {
const mockAddress = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
const mockAddressChecksummed = '0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa';
- const addressBook: AddressBookControllerState['addressBook'] = {
+ const addressBook: AddressBookState['addressBook'] = {
'0x1': {
[mockAddress]: {
address: mockAddress,
@@ -145,7 +145,7 @@ describe('checkIfAddressIsSaved', () => {
it('returns multiple address book entries', () => {
const mockAddress = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
const mockAddressChecksummed = '0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa';
- const addressBook: AddressBookControllerState['addressBook'] = {
+ const addressBook: AddressBookState['addressBook'] = {
'0x1': {
[mockAddress]: {
address: mockAddress,
diff --git a/app/util/checkAddress.ts b/app/util/checkAddress.ts
index 74c1d994a58..097d49a5041 100644
--- a/app/util/checkAddress.ts
+++ b/app/util/checkAddress.ts
@@ -1,4 +1,4 @@
-import type { AddressBookControllerState } from '@metamask/address-book-controller';
+import type { AddressBookState } from '@metamask/address-book-controller';
import { toChecksumAddress } from 'ethereumjs-util';
/**
@@ -12,7 +12,7 @@ import { toChecksumAddress } from 'ethereumjs-util';
* transaction recipient.
*/
const checkIfAddressIsSaved = (
- addressBook: AddressBookControllerState['addressBook'],
+ addressBook: AddressBookState['addressBook'],
chainId: string,
// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
diff --git a/app/util/date/index.js b/app/util/date/index.js
index d8292ebf03b..0035c6a2a9b 100644
--- a/app/util/date/index.js
+++ b/app/util/date/index.js
@@ -1,5 +1,4 @@
import { strings } from '../../../locales/i18n';
-import { MINUTE, HOUR, DAY } from '../../constants/time';
export function toLocaleDateTime(timestamp) {
const dateObj = new Date(timestamp);
@@ -62,27 +61,3 @@ export const formatTimestampToYYYYMMDD = (timestamp) => {
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
-
-/**
- * Returns an object containing the difference in days, hours, and minutes between a now and a future timestamp.
- *
- * @param {number} timestamp - The timestamp in the future to compare to now.
- *
- * @returns object with difference in amount of days, hours, and minutes. If timestamp is in the past, a default value of { days: 0, hours: 0, minutes: 0 } is returned.
- */
-export const getTimeDifferenceFromNow = (timestamp) => {
- const currentTime = Date.now();
-
- // Default when timestamp is in the past.
- if (timestamp < currentTime) {
- return { days: 0, hours: 0, minutes: 0 };
- }
-
- const differenceInMilliseconds = timestamp - currentTime;
-
- const days = Math.floor(differenceInMilliseconds / DAY);
- const hours = Math.floor((differenceInMilliseconds % DAY) / HOUR);
- const minutes = Math.floor((differenceInMilliseconds % HOUR) / MINUTE);
-
- return { days, hours, minutes };
-};
diff --git a/app/util/date/index.test.ts b/app/util/date/index.test.ts
index a71879af94b..80e8cf1f27b 100644
--- a/app/util/date/index.test.ts
+++ b/app/util/date/index.test.ts
@@ -3,7 +3,6 @@ import {
msToHours,
toDateFormat,
formatTimestampToYYYYMMDD,
- getTimeDifferenceFromNow,
} from '.';
const TZ = 'America/Toronto';
@@ -75,58 +74,3 @@ describe('Date util :: formatTimestampToYYYYMMDD', () => {
expect(formatTimestampToYYYYMMDD(date)).toEqual('2024-07-31');
});
});
-
-describe('Date util :: getDaysAndHoursRemaining', () => {
- // 2024-09-24 19:19:41
- const MOCK_NOW = 1727205581107;
-
- const mockDateNow = jest.spyOn(Date, 'now');
-
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- afterAll(() => {
- jest.spyOn(Date, 'now').mockRestore();
- });
-
- it('returns time difference between timestamp and now', () => {
- mockDateNow.mockImplementation(() => MOCK_NOW);
-
- // 2024-09-29 23:54:41 (5 days, 4 hours, and 35 minutes after MOCK_NOW)
- const TIMESTAMP = 1727654081107;
-
- const { days, hours, minutes } = getTimeDifferenceFromNow(
- Number(TIMESTAMP),
- );
-
- expect(days).toBe(5);
- expect(hours).toBe(4);
- expect(minutes).toBe(35);
- });
-
- it('returns correct value when timestamp and current time are identical', () => {
- mockDateNow.mockImplementation(() => MOCK_NOW);
-
- const { days, hours, minutes } = getTimeDifferenceFromNow(Number(MOCK_NOW));
-
- expect(days).toBe(0);
- expect(hours).toBe(0);
- expect(minutes).toBe(0);
- });
-
- it('returns default value when timestamp passed in is in the past', () => {
- // 2024-09-21 19:19:41 (3 days before MOCK_NOW)
- const TIMESTAMP = 1726946381107;
-
- mockDateNow.mockImplementation(() => MOCK_NOW);
-
- const { days, hours, minutes } = getTimeDifferenceFromNow(
- Number(TIMESTAMP),
- );
-
- expect(days).toBe(0);
- expect(hours).toBe(0);
- expect(minutes).toBe(0);
- });
-});
diff --git a/app/util/metrics/TrackOnboarding/trackOnboarding.ts b/app/util/metrics/TrackOnboarding/trackOnboarding.ts
index 03df7ec95de..38b2d2920ed 100644
--- a/app/util/metrics/TrackOnboarding/trackOnboarding.ts
+++ b/app/util/metrics/TrackOnboarding/trackOnboarding.ts
@@ -11,7 +11,10 @@ import { JsonMap } from '@segment/analytics-react-native';
const trackOnboarding = (
event: IMetaMetricsEvent,
properties: JsonMap = {},
- saveOnboardingEvent?: (...args: [IMetaMetricsEvent]) => void
+ saveOnboardingEvent?: (event: IMetaMetricsEvent) => {
+ event: IMetaMetricsEvent;
+ type: string;
+ },
): void => {
InteractionManager.runAfterInteractions(async () => {
const metrics = MetaMetrics.getInstance();
diff --git a/app/util/networks/index.js b/app/util/networks/index.js
index 0339830ef04..6838b0bf2cb 100644
--- a/app/util/networks/index.js
+++ b/app/util/networks/index.js
@@ -579,5 +579,5 @@ export const deprecatedGetNetworkId = async () => {
});
};
-export const isMultichainVersion1Enabled =
+export const isMutichainVersion1Enabled =
process.env.MM_MULTICHAIN_V1_ENABLED === '1';
diff --git a/app/util/notifications/androidChannels.test.ts b/app/util/notifications/androidChannels.test.ts
deleted file mode 100644
index c0cb6a45412..00000000000
--- a/app/util/notifications/androidChannels.test.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { AndroidImportance } from '@notifee/react-native';
-import {
- ChannelId,
- MetaMaskAndroidChannel,
- notificationChannels,
-} from './androidChannels';
-
-describe('notificationChannels', () => {
- it('should have two channels', () => {
- expect(notificationChannels).toHaveLength(2);
- });
-
- it('should have the correct properties for the first channel', () => {
- const firstChannel: MetaMaskAndroidChannel = notificationChannels[0];
- expect(firstChannel).toEqual({
- id: ChannelId.DEFAULT_NOTIFICATION_CHANNEL_ID,
- name: 'Transaction Complete',
- lights: false,
- vibration: false,
- importance: AndroidImportance.DEFAULT,
- title: 'Transaction',
- subtitle: 'Transaction Complete',
- });
- });
-
- it('should have the correct properties for the second channel', () => {
- const secondChannel: MetaMaskAndroidChannel = notificationChannels[1];
- expect(secondChannel).toEqual({
- id: ChannelId.ANNOUNCEMENT_NOTIFICATION_CHANNEL_ID,
- name: 'MetaMask Announcement',
- lights: false,
- vibration: false,
- importance: AndroidImportance.DEFAULT,
- title: 'Announcement',
- subtitle: 'MetaMask Announcement',
- });
- });
-
- it('should have unique titles for each channel', () => {
- const titles = notificationChannels.map((channel) => channel.title);
- const uniqueTitles = new Set(titles);
- expect(uniqueTitles.size).toBe(titles.length);
- });
-
- it('should have unique subtitles for each channel', () => {
- const subtitles = notificationChannels.map((channel) => channel.subtitle);
- const uniqueSubtitles = new Set(subtitles);
- expect(uniqueSubtitles.size).toBe(subtitles.length);
- });
-});
diff --git a/app/util/notifications/androidChannels.ts b/app/util/notifications/androidChannels.ts
deleted file mode 100644
index 4841cb7e1a5..00000000000
--- a/app/util/notifications/androidChannels.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { AndroidChannel, AndroidImportance } from '@notifee/react-native';
-
-export enum ChannelId {
- DEFAULT_NOTIFICATION_CHANNEL_ID = 'DEFAULT_NOTIFICATION_CHANNEL_ID',
- ANNOUNCEMENT_NOTIFICATION_CHANNEL_ID = 'ANNOUNCEMENT_NOTIFICATION_CHANNEL_ID',
-}
-
-export interface MetaMaskAndroidChannel extends AndroidChannel {
- id: ChannelId;
- title: string;
- subtitle: string;
-}
-
-export const notificationChannels = [
- {
- id: ChannelId.DEFAULT_NOTIFICATION_CHANNEL_ID,
- name: 'Transaction Complete',
- lights: false,
- vibration: false,
- importance: AndroidImportance.DEFAULT,
- title: 'Transaction',
- subtitle: 'Transaction Complete',
- } as MetaMaskAndroidChannel,
- {
- id: ChannelId.ANNOUNCEMENT_NOTIFICATION_CHANNEL_ID,
- name: 'MetaMask Announcement',
- lights: false,
- vibration: false,
- importance: AndroidImportance.DEFAULT,
- title: 'Announcement',
- subtitle: 'MetaMask Announcement',
- } as MetaMaskAndroidChannel,
-];
diff --git a/app/util/notifications/hooks/index.test.ts b/app/util/notifications/hooks/index.test.ts
index bbe94a97dc3..c826067a63a 100644
--- a/app/util/notifications/hooks/index.test.ts
+++ b/app/util/notifications/hooks/index.test.ts
@@ -6,9 +6,6 @@ import notifee, {
import useNotificationHandler from './index';
import { NavigationProp, ParamListBase } from '@react-navigation/native';
-import Routes from '../../../constants/navigation/Routes';
-import { Notification } from '../../../util/notifications/types';
-import { TRIGGER_TYPES } from '../constants';
jest.mock('../../../util/device');
jest.mock('../../../core/NotificationManager', () => ({
@@ -29,67 +26,51 @@ jest.mock('@notifee/react-native', () => ({
},
}));
-jest.mock('../../../core/NotificationManager', () => ({
- setTransactionToView: jest.fn(),
-}));
-
-jest.mock('../../../util/device', () => ({
- isAndroid: jest.fn(),
-}));
-
const mockNavigate = jest.fn();
const mockNavigation = {
navigate: mockNavigate,
} as unknown as NavigationProp;
-const notification = {
- id: 1,
- type: TRIGGER_TYPES.ERC1155_RECEIVED,
- data: {
- id: 1,
- trigger_id: '1',
- chain_id: 1,
- block_number: 1,
- block_timestamp: '',
- tx_hash: '',
- unread: false,
- created_at: '',
- address: '',
- type: TRIGGER_TYPES.ERC1155_RECEIVED,
- data: {},
- createdAt: '',
- isRead: false,
- },
-} as unknown as Notification;
+const bootstrapAndroidInitialNotification = jest
+ .fn()
+ .mockResolvedValue(undefined);
const mockNotificationEvent = (event: NotifeeEvent) => ({
type: event.type,
detail: {
- notification,
+ notification: {
+ body: 'notificationTest',
+ data: {
+ action: 'tx',
+ id: '123',
+ },
+ },
},
});
-
describe('useNotificationHandler', () => {
beforeEach(() => {
jest.clearAllMocks();
});
- it('should navigate to NOTIFICATIONS.DETAILS if notification is pressed', async () => {
- const { result } = renderHook(() => useNotificationHandler(mockNavigation));
+ it('sets initial badge count and initializes Android notifications on mount', async () => {
+ renderHook(() =>
+ useNotificationHandler(
+ bootstrapAndroidInitialNotification,
+ mockNavigation,
+ ),
+ );
- await result.current.handlePressedNotification(notification);
+ expect(bootstrapAndroidInitialNotification).toHaveBeenCalled();
- expect(mockNavigation.navigate).toHaveBeenCalledWith(
- Routes.NOTIFICATIONS.DETAILS,
- {
- notificationId: notification.id,
- },
- );
+ jest.runAllTimers();
});
it('should handle notifications correctly', async () => {
const { waitFor } = renderHook(() =>
- useNotificationHandler(mockNavigation),
+ useNotificationHandler(
+ bootstrapAndroidInitialNotification,
+ mockNavigation,
+ ),
);
await act(async () => {
@@ -115,7 +96,10 @@ describe('useNotificationHandler', () => {
it('should do nothing if the EventType is DISMISSED', async () => {
const { waitFor } = renderHook(() =>
- useNotificationHandler(mockNavigation),
+ useNotificationHandler(
+ bootstrapAndroidInitialNotification,
+ mockNavigation,
+ ),
);
await act(async () => {
@@ -142,7 +126,10 @@ describe('useNotificationHandler', () => {
it('should do nothing if data.action is not tx', async () => {
const { waitFor } = renderHook(() =>
- useNotificationHandler(mockNavigation),
+ useNotificationHandler(
+ bootstrapAndroidInitialNotification,
+ mockNavigation,
+ ),
);
await act(async () => {
@@ -171,7 +158,10 @@ describe('useNotificationHandler', () => {
it('handleOpenedNotification should do nothing if notification is null', async () => {
const { waitFor } = renderHook(() =>
- useNotificationHandler(mockNavigation),
+ useNotificationHandler(
+ bootstrapAndroidInitialNotification,
+ mockNavigation,
+ ),
);
await act(async () => {
@@ -193,7 +183,10 @@ describe('useNotificationHandler', () => {
it('should navigate to the transaction view when the notification action is "tx"', async () => {
const { waitFor } = renderHook(() =>
- useNotificationHandler(mockNavigation),
+ useNotificationHandler(
+ bootstrapAndroidInitialNotification,
+ mockNavigation,
+ ),
);
await act(async () => {
@@ -223,7 +216,10 @@ describe('useNotificationHandler', () => {
}));
const { waitFor } = renderHook(() =>
- useNotificationHandler(mockNavigation),
+ useNotificationHandler(
+ bootstrapAndroidInitialNotification,
+ mockNavigation,
+ ),
);
await act(async () => {
diff --git a/app/util/notifications/hooks/index.ts b/app/util/notifications/hooks/index.ts
index e91fe593eaf..796d8c3b19f 100644
--- a/app/util/notifications/hooks/index.ts
+++ b/app/util/notifications/hooks/index.ts
@@ -1,33 +1,39 @@
import { useCallback, useEffect } from 'react';
-import { NavigationProp, ParamListBase } from '@react-navigation/native';
-import NotificationsService from '../../../util/notifications/services/NotificationService';
+import notifee, {
+ Event as NotifeeEvent,
+ EventType,
+} from '@notifee/react-native';
+import NotificationManager from '../../../core/NotificationManager';
import Routes from '../../../constants/navigation/Routes';
-import {
- isNotificationsFeatureEnabled,
- TRIGGER_TYPES,
-} from '../../../util/notifications';
-import { Notification } from '../../../util/notifications/types';
-import { Linking } from 'react-native';
+import { setupAndroidChannel } from '../setupAndroidChannels';
+import { SimpleNotification } from '../types';
+import Device from '../../../util/device';
+import { NavigationProp, ParamListBase } from '@react-navigation/native';
-const useNotificationHandler = (navigation: NavigationProp) => {
+const useNotificationHandler = (
+ bootstrapAndroidInitialNotification: () => Promise,
+ navigation: NavigationProp,
+) => {
const performActionBasedOnOpenedNotificationType = useCallback(
- async (notification: Notification) => {
- if (
- notification.type === TRIGGER_TYPES.FEATURES_ANNOUNCEMENT &&
- notification.data.externalLink
- ) {
- Linking.openURL(notification.data.externalLink.externalLinkUrl);
+ async (notification: SimpleNotification) => {
+ const { data } = notification;
+
+ if (data && data.action === 'tx') {
+ if (data.id) {
+ NotificationManager.setTransactionToView(data.id);
+ }
+ if (navigation) {
+ navigation.navigate(Routes.TRANSACTIONS_VIEW);
+ }
} else {
- navigation.navigate(Routes.NOTIFICATIONS.DETAILS, {
- notificationId: notification.id,
- });
+ navigation.navigate(Routes.NOTIFICATIONS.VIEW);
}
},
[navigation],
);
- const handlePressedNotification = useCallback(
- (notification?: Notification) => {
+ const handleOpenedNotification = useCallback(
+ (notification?: SimpleNotification) => {
if (!notification) {
return;
}
@@ -36,36 +42,28 @@ const useNotificationHandler = (navigation: NavigationProp) => {
[performActionBasedOnOpenedNotificationType],
);
- useEffect(() => {
- if (!isNotificationsFeatureEnabled()) return;
-
- const unsubscribeForegroundEvent = NotificationsService.onForegroundEvent(
- async ({ type, detail }) =>
- await NotificationsService.handleNotificationEvent({
- type,
- detail,
- callback: handlePressedNotification,
- }),
- );
-
- NotificationsService.onBackgroundEvent(
- async ({ type, detail }) =>
- await NotificationsService.handleNotificationEvent({
- type,
- detail,
- callback: handlePressedNotification,
- }),
- );
-
- return () => {
- unsubscribeForegroundEvent();
- };
- }, [handlePressedNotification]);
+ const handleNotificationPressed = useCallback(
+ (event: NotifeeEvent) => {
+ if (event.type === EventType.PRESS) {
+ handleOpenedNotification(event.detail.notification);
+ }
+ },
+ [handleOpenedNotification],
+ );
- return {
- performActionBasedOnOpenedNotificationType,
- handlePressedNotification,
- };
+ useEffect(() => {
+ bootstrapAndroidInitialNotification();
+ setTimeout(() => {
+ if (Device.isAndroid()) {
+ setupAndroidChannel();
+ }
+ notifee.onForegroundEvent(handleNotificationPressed);
+ }, 1000);
+ }, [
+ bootstrapAndroidInitialNotification,
+ navigation,
+ handleNotificationPressed,
+ ]);
};
export default useNotificationHandler;
diff --git a/app/util/notifications/index.ts b/app/util/notifications/index.ts
index d1e6201e1e9..ec78eb1f67a 100644
--- a/app/util/notifications/index.ts
+++ b/app/util/notifications/index.ts
@@ -1,7 +1,7 @@
export * from './types';
export * from './constants';
-export * from './androidChannels';
+export * from './setupAndroidChannels';
export * from './settings';
export * from './hooks';
export * from './methods';
-export * from './services';
+export * from './pushPermissions';
diff --git a/app/util/notifications/methods/common.ts b/app/util/notifications/methods/common.ts
index 171d150f517..c80bc3fdb80 100644
--- a/app/util/notifications/methods/common.ts
+++ b/app/util/notifications/methods/common.ts
@@ -2,7 +2,6 @@ import dayjs, { Dayjs } from 'dayjs';
import isYesterday from 'dayjs/plugin/isYesterday';
import relativeTime from 'dayjs/plugin/relativeTime';
-import notifee from '@notifee/react-native';
import localeData from 'dayjs/plugin/localeData';
import { Web3Provider } from '@ethersproject/providers';
import { toHex } from '@metamask/controller-utils';
@@ -471,13 +470,3 @@ export const getUsdAmount = (amount: string, decimals: string, usd: string) => {
return formatAmount(numericAmount);
};
-
-export const hasInitialNotification = async () =>
- Boolean(await notifee.getInitialNotification());
-
-export function withTimeout(promise: Promise, ms: number): Promise {
- const timeout = new Promise((_, reject) =>
- setTimeout(() => reject(new Error(strings('notifications.timeout'))), ms),
- );
- return Promise.race([promise, timeout]);
-}
diff --git a/app/util/notifications/pushPermissions.test.ts b/app/util/notifications/pushPermissions.test.ts
new file mode 100644
index 00000000000..b9f80882f2f
--- /dev/null
+++ b/app/util/notifications/pushPermissions.test.ts
@@ -0,0 +1,113 @@
+import notifee, {
+ NotificationSettings,
+ AuthorizationStatus,
+ IOSNotificationSetting,
+ IOSNotificationSettings,
+ AndroidNotificationSettings,
+ AndroidNotificationSetting,
+} from '@notifee/react-native';
+
+import { strings } from '../../../locales/i18n';
+import { requestPushNotificationsPermission } from './pushPermissions';
+
+jest.mock('@notifee/react-native', () => ({
+ getNotificationSettings: jest.fn(),
+ AuthorizationStatus: {
+ AUTHORIZED: 1,
+ DENIED: 2,
+ },
+ IOSNotificationSetting: {
+ ENABLED: 1,
+ DISABLED: 2,
+ },
+ AndroidNotificationSetting: {
+ ENABLED: 1,
+ DISABLED: 2,
+ },
+}));
+
+jest.mock('../Logger', () => ({
+ error: jest.fn(),
+}));
+
+jest.mock('../../../locales/i18n', () => ({
+ strings: jest.fn(),
+}));
+
+jest.mock('./pushPermissions', () => ({
+ ...jest.requireActual('./pushPermissions'),
+ AsyncAlert: jest.fn(),
+}));
+
+describe('requestPushNotificationsPermission', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ const mockIOSSettings: IOSNotificationSettings = {
+ authorizationStatus: AuthorizationStatus.AUTHORIZED,
+ alert: IOSNotificationSetting.ENABLED,
+ badge: IOSNotificationSetting.ENABLED,
+ sound: IOSNotificationSetting.ENABLED,
+ carPlay: IOSNotificationSetting.DISABLED,
+ criticalAlert: IOSNotificationSetting.DISABLED,
+ inAppNotificationSettings: IOSNotificationSetting.DISABLED,
+ lockScreen: IOSNotificationSetting.ENABLED,
+ notificationCenter: IOSNotificationSetting.ENABLED,
+ showPreviews: 1,
+ announcement: 1,
+ };
+
+ const mockAndroidSettings: AndroidNotificationSettings = {
+ alarm: AndroidNotificationSetting.ENABLED,
+ };
+
+ it('should return notification settings if already authorized', async () => {
+ const mockSettings: NotificationSettings = {
+ authorizationStatus: AuthorizationStatus.AUTHORIZED,
+ ios: mockIOSSettings,
+ android: mockAndroidSettings,
+ web: {},
+ };
+
+ (notifee.getNotificationSettings as jest.Mock).mockResolvedValue(
+ mockSettings,
+ );
+
+ const result = await requestPushNotificationsPermission();
+
+ expect(notifee.getNotificationSettings).toHaveBeenCalledTimes(1);
+ expect(result).toBe(mockSettings);
+ });
+
+ it('should prompt the user with AsyncAlert and simulate a click', async () => {
+ const mockSettings: NotificationSettings = {
+ authorizationStatus: AuthorizationStatus.DENIED,
+ ios: mockIOSSettings,
+ android: mockAndroidSettings,
+ web: {},
+ };
+
+ const updatedSettings: NotificationSettings = {
+ ...mockSettings,
+ authorizationStatus: AuthorizationStatus.AUTHORIZED,
+ };
+
+ (notifee.getNotificationSettings as jest.Mock)
+ .mockResolvedValueOnce(mockSettings)
+ .mockResolvedValueOnce(updatedSettings);
+ (strings as jest.Mock).mockImplementation((key: string) => key);
+
+ const mockAsyncAlert = jest.fn().mockResolvedValue(true);
+
+ await requestPushNotificationsPermission(mockAsyncAlert);
+
+ expect(mockAsyncAlert).toHaveBeenCalledWith(
+ 'notifications.prompt_title',
+ 'notifications.prompt_desc',
+ );
+ expect(notifee.getNotificationSettings).toHaveBeenCalledTimes(1);
+ expect(strings).toHaveBeenCalledWith('notifications.prompt_title');
+ expect(strings).toHaveBeenCalledWith('notifications.prompt_desc');
+ });
+});
diff --git a/app/util/notifications/pushPermissions.ts b/app/util/notifications/pushPermissions.ts
new file mode 100644
index 00000000000..6610803e1b5
--- /dev/null
+++ b/app/util/notifications/pushPermissions.ts
@@ -0,0 +1,123 @@
+import notifee, {
+ AuthorizationStatus,
+ NotificationSettings,
+} from '@notifee/react-native';
+import { Linking, Alert as NativeAlert, Platform } from 'react-native';
+import { strings } from '../../../locales/i18n';
+import { mmStorage } from './settings';
+import { STORAGE_IDS } from './settings/storage/constants';
+import Logger from '../Logger';
+import Device from '../device';
+import { store } from '../../store';
+
+interface AlertButton {
+ text: string;
+ onPress: () => void | Promise;
+}
+
+export const openSystemSettings = () => {
+ if (Platform.OS === 'ios') {
+ Linking.openSettings();
+ } else {
+ notifee.openNotificationSettings();
+ }
+};
+
+export const isDeviceNotificationEnabled = async () => {
+ const settings = await notifee.getNotificationSettings();
+ switch (settings.authorizationStatus) {
+ case AuthorizationStatus.AUTHORIZED:
+ case AuthorizationStatus.PROVISIONAL:
+ store.dispatch({
+ type: 'TOGGLE_DEVICE_NOTIFICATIONS',
+ deviceNotificationEnabled: true,
+ });
+ return true;
+ default:
+ store.dispatch({
+ type: 'TOGGLE_DEVICE_NOTIFICATIONS',
+ deviceNotificationEnabled: false,
+ });
+ return false;
+ }
+};
+
+const defaultButtons = (resolve: (value: boolean) => void): AlertButton[] => [
+ {
+ text: strings('notifications.prompt_cancel'),
+ onPress: () => {
+ const promptCount = mmStorage.getLocal(
+ STORAGE_IDS.PUSH_NOTIFICATIONS_PROMPT_COUNT,
+ );
+ mmStorage.saveLocal(
+ STORAGE_IDS.PUSH_NOTIFICATIONS_PROMPT_COUNT,
+ promptCount + 1,
+ );
+ mmStorage.saveLocal(
+ STORAGE_IDS.PUSH_NOTIFICATIONS_PROMPT_TIME,
+ Date.now().toString(),
+ );
+
+ resolve(false);
+ },
+ },
+ {
+ text: strings('notifications.prompt_ok'),
+ onPress: async () => {
+ openSystemSettings();
+ resolve(true);
+ },
+ },
+];
+
+export const asyncAlert = (
+ title: string,
+ msg: string,
+ getButtons: (
+ resolve: (value: boolean) => void,
+ ) => AlertButton[] = defaultButtons,
+): Promise =>
+ new Promise((resolve) => {
+ NativeAlert.alert(title, msg, getButtons(resolve), {
+ cancelable: false,
+ });
+ });
+
+export const requestPushNotificationsPermission = async (
+ alertFunction: typeof asyncAlert = asyncAlert,
+): Promise => {
+ let permissionStatus: NotificationSettings | undefined;
+ permissionStatus = await notifee.getNotificationSettings();
+ if (
+ permissionStatus.authorizationStatus === AuthorizationStatus.AUTHORIZED ||
+ permissionStatus.authorizationStatus === AuthorizationStatus.PROVISIONAL
+ ) {
+ return permissionStatus;
+ }
+ try {
+ await alertFunction(
+ strings('notifications.prompt_title'),
+ strings('notifications.prompt_desc'),
+ );
+
+ if (Device.isIos()) {
+ permissionStatus = await notifee.requestPermission({
+ provisional: true,
+ });
+ } else {
+ permissionStatus = await notifee.requestPermission();
+ }
+
+ return permissionStatus;
+ } catch (e) {
+ if (e instanceof Error) {
+ Logger.error(e, strings('notifications.error_checking_permission'));
+ } else {
+ Logger.error(
+ new Error(strings('notifications.error_checking_permission')),
+ );
+ }
+ }
+};
+
+export default requestPushNotificationsPermission;
diff --git a/app/util/notifications/services/NotificationService.test.ts b/app/util/notifications/services/NotificationService.test.ts
deleted file mode 100644
index e40e78dd47e..00000000000
--- a/app/util/notifications/services/NotificationService.test.ts
+++ /dev/null
@@ -1,138 +0,0 @@
-import notifee, {
- AuthorizationStatus,
- AndroidChannel,
- AndroidImportance,
- EventType,
-} from '@notifee/react-native';
-import { Linking } from 'react-native';
-import { ChannelId } from '../../../util/notifications/androidChannels';
-import NotificationsService from './NotificationService';
-
-jest.mock('@notifee/react-native');
-jest.mock('react-native', () => ({
- Linking: { openSettings: jest.fn() },
- Platform: { OS: 'ios' },
- Alert: { alert: jest.fn() },
-}));
-jest.mock('../settings', () => ({
- mmStorage: {
- getLocal: jest.fn(),
- saveLocal: jest.fn(),
- },
-}));
-jest.mock('../../../store', () => ({
- store: {
- dispatch: jest.fn(),
- },
-}));
-jest.mock('../../../util/Logger', () => ({
- error: jest.fn(),
-}));
-
-describe('NotificationsService', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it('should get blocked notifications', async () => {
- (notifee.getNotificationSettings as jest.Mock).mockResolvedValue({
- authorizationStatus: AuthorizationStatus.AUTHORIZED,
- });
- (notifee.getChannels as jest.Mock).mockResolvedValue([
- { id: ChannelId.DEFAULT_NOTIFICATION_CHANNEL_ID, blocked: true },
- ]);
-
- const blockedNotifications =
- await NotificationsService.getBlockedNotifications();
-
- expect(
- blockedNotifications.get(ChannelId.DEFAULT_NOTIFICATION_CHANNEL_ID),
- ).toBe(true);
- });
-
- it('should handle notification press', async () => {
- const detail = {
- notification: {
- id: 'test-id',
- data: { url: 'https://example.com' },
- },
- };
- const callback = jest.fn();
-
- await NotificationsService.handleNotificationPress({ detail, callback });
-
- expect(notifee.cancelTriggerNotification).toHaveBeenCalledWith('test-id');
- expect(callback).toHaveBeenCalledWith(detail.notification);
- });
-
- it('should open system settings on iOS', () => {
- NotificationsService.openSystemSettings();
-
- expect(Linking.openSettings).toHaveBeenCalled();
- });
-
- it('should create notification channels', async () => {
- const channel: AndroidChannel = {
- id: ChannelId.DEFAULT_NOTIFICATION_CHANNEL_ID,
- name: 'Test Channel',
- importance: AndroidImportance.HIGH,
- };
-
- await NotificationsService.createChannel(channel);
-
- expect(notifee.createChannel).toHaveBeenCalledWith(channel);
- });
-
- it.concurrent(
- 'should return authorized from getAllPermissions',
- async () => {
- const result = await NotificationsService.getAllPermissions();
- expect(result.permission).toBe('authorized');
- },
- 10000,
- );
-
- it('should return authorized from requestPermission ', async () => {
- const result = await NotificationsService.requestPermission();
- expect(result).toBe('authorized');
- });
-
- it('should return denied from requestPermission', async () => {
- (notifee.requestPermission as jest.Mock).mockResolvedValue({
- authorizationStatus: AuthorizationStatus.DENIED,
- });
- const result = await NotificationsService.requestPermission();
- expect(result).toBe('denied');
- });
-
- it('should handle notification event', async () => {
- const callback = jest.fn();
-
- await NotificationsService.handleNotificationEvent({
- type: EventType.DELIVERED,
- detail: {
- notification: {
- id: '123',
- },
- },
- callback,
- });
-
- expect(NotificationsService.incrementBadgeCount).toBeInstanceOf(Function);
-
- await NotificationsService.handleNotificationEvent({
- type: EventType.PRESS,
- detail: {
- notification: {
- id: '123',
- },
- },
- callback,
- });
-
- expect(NotificationsService.decrementBadgeCount).toBeInstanceOf(Function);
- expect(NotificationsService.cancelTriggerNotification).toBeInstanceOf(
- Function,
- );
- });
-});
diff --git a/app/util/notifications/services/NotificationService.ts b/app/util/notifications/services/NotificationService.ts
deleted file mode 100644
index 6c6f104404f..00000000000
--- a/app/util/notifications/services/NotificationService.ts
+++ /dev/null
@@ -1,234 +0,0 @@
-import notifee, {
- AuthorizationStatus,
- Event as NotifeeEvent,
- EventType,
- EventDetail,
- AndroidChannel,
-} from '@notifee/react-native';
-
-import { Notification } from '../types';
-
-import { Linking, Platform, Alert as NativeAlert } from 'react-native';
-import {
- ChannelId,
- notificationChannels,
-} from '../../../util/notifications/androidChannels';
-
-import { strings } from '../../../../locales/i18n';
-import { mmStorage } from '../settings';
-import { STORAGE_IDS } from '../settings/storage/constants';
-import { store } from '../../../store';
-import Logger from '../../../util/Logger';
-import { withTimeout } from '../methods';
-
-interface AlertButton {
- text: string;
- onPress: () => void | Promise;
-}
-
-class NotificationsService {
- async getBlockedNotifications(): Promise