From 60ef96880319d165cea6027cc88bb70ebbb8a221 Mon Sep 17 00:00:00 2001 From: Piyal Basu Date: Thu, 15 Aug 2024 17:36:44 -0400 Subject: [PATCH] release/5.23.0 (#1371) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feature/standard wallet interface (#1357) * Feature/soroswap (#1347) * Feature/hash signing 2 (#1303) * flips the session store flag, updates listeners to not be intialized … (#1169) * capture amplitude errors to better understand why they're triggering sentry (#1167) * flips the session store flag, updates listeners to not be intialized async for v3 * updates e2e tests for manifest v3 --------- Co-authored-by: Piyal Basu * add scripts tag for Firefox (#1294) * uses chrome storage in migrations instead of local storage * checks for migrated account in migration logic to set migrated network * don't babel-polyfill contentScript (#1297) * don't clear all of localStore on recoverAccount (#1301) * add hash signing option (ui and data storage) * fix redux state update; show custom errors from hardware wallet * Added translations * when experimental mode is enabled, switch network * PR comments * rm logs --------- Co-authored-by: aristides * Feature/trustline sac (#1289) * Bump axios, @docusaurus/core and @docusaurus/preset-classic in /docs (#1244) Removes [axios](https://github.com/axios/axios). It's no longer used after updating ancestor dependencies [axios](https://github.com/axios/axios), [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) and [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic). These dependencies need to be updated together. Removes `axios` Updates `@docusaurus/core` from 2.4.1 to 3.2.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.2.1/packages/docusaurus) Updates `@docusaurus/preset-classic` from 2.4.1 to 3.2.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.2.1/packages/docusaurus-preset-classic) --- updated-dependencies: - dependency-name: axios dependency-type: indirect - dependency-name: "@docusaurus/core" dependency-type: direct:production - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump express from 4.18.2 to 4.19.2 in /docs (#1216) Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump tar from 6.1.15 to 6.2.1 (#1231) Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.15 to 6.2.1. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v6.1.15...v6.2.1) --- updated-dependencies: - dependency-name: tar dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump express from 4.18.2 to 4.19.2 (#1214) Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /docs (#1208) Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4. - [Release notes](https://github.com/webpack/webpack-dev-middleware/releases) - [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4) --- updated-dependencies: - dependency-name: webpack-dev-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump webpack-dev-middleware from 5.3.3 to 5.3.4 (#1207) Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4. - [Release notes](https://github.com/webpack/webpack-dev-middleware/releases) - [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4) --- updated-dependencies: - dependency-name: webpack-dev-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump follow-redirects from 1.15.4 to 1.15.6 in /docs (#1198) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump follow-redirects from 1.15.2 to 1.15.6 (#1197) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump ip from 2.0.0 to 2.0.1 (#1128) Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1. - [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1) --- updated-dependencies: - dependency-name: ip dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump axios, @docusaurus/core and @docusaurus/preset-classic in /docs (#1055) Removes [axios](https://github.com/axios/axios). It's no longer used after updating ancestor dependencies [axios](https://github.com/axios/axios), [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) and [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic). These dependencies need to be updated together. Removes `axios` Updates `@docusaurus/core` from 2.4.1 to 3.0.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.0.1/packages/docusaurus) Updates `@docusaurus/preset-classic` from 2.4.1 to 3.0.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.0.1/packages/docusaurus-preset-classic) --- updated-dependencies: - dependency-name: axios dependency-type: indirect - dependency-name: "@docusaurus/core" dependency-type: direct:production - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * first pass at adding remove dropdown * replaces all remaining instances of wallet-sdk * tweaks tx detail row layouts to avoid overflows * adds getContractSpec, uses it to display parameter names in tx sign detail view * migrates KeyManager to ts-wallet-sdk * upgrades react and react dom types to 18 * adds react types to extension workspace, fixes type errors * set styles for dropdown * Added translations * adds standalone version of getContractSpec and related helpers * Added translations * Added translations * undo husky pre push comments * Feature/p21 futurenet release (#1278) * switch between stellar-sdk and stellar-sdk-next based on network * increase max diff pixel ratio for playwright * rm console * adds tx timeout in send and swap settings * Added translations * tweaks tooltip text * Added translations * happy path for new add asset flow * fix trustline error warning and fix naming * Added translations * rollback dep upgrades * rollback changes * rollback package-lock upgrade * add snapshots for manage assets and send payment (#1309) --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Aristides Staffieri * guard against dispatching incorrect parameter (#1305) * adds Hardware sign usage in ReviewAuth for use during invokeHostFn signing, tweaks soroban icon to not overflow in signing details (#1282) * adds loader for hardware signing during signing (#1284) Co-authored-by: Piyal Basu * adds sign blob error for encoding mismatch (#1300) * fix bad merge * [BUG] replaces all instances of pickTransfer with getTokenSpec workflow (#1312) * replaces all instances of pickTransfer with getTokenSpec workflow * check for transfer once token spec is confirmed * rm unused dep * fixes bad merge from upstream in ManageAssets * Added translations * remove accidental mobile4 dir addition --------- Co-authored-by: Piyal Basu * Test/data storage (#1304) * adds test for data storage get item * adds tests for network migrations * adds test for remaining migrations * reverts bad merge from upstream --------- Co-authored-by: Piyal Basu * [CHORE] upgrades all webpack related deps (#1307) * upgrades all webpack related deps * updates lock file * updates migrations test for recent changes * [CHORE] upgrade to docusaurus v3 (#1306) * upgrade to docusaurus v3 * edit manifest for chrome before uploading (#1315) * use single quotes (#1316) --------- Co-authored-by: Piyal Basu * Fix manage assets tests (#1317) * fix tests for new manage assets flow * fix broken manage-assets tests * rm unused dep * fix e2e tests * fix query for visible loader * fix assets test * selector for individual asset balance * add better error messaging for liabilities when removing an asset (#1319) * add better error messaging for liabilities when removing an asset * add tests and rm sellingLiabilities * rm more selling liabilities * fix diff * fix diff again * verify params before fetching issuer info (#1320) * add better error messaging for liabilities when removing an asset * add tests and rm sellingLiabilities * rm more selling liabilities * fix diff * fix diff again * verify params before fetching issuer info * test soroswap api * pin docusaurus core to match versions * adding soroswap tokens to swap dropdown * split dataStorageAccess into a different file to allow for jest mocks with circular deps * updates all references for new dataStorage path * fix add asset padding; fix ledger trustline error; fix token-spec check (#1324) * fix add asset padding; fix ledger trustline error; fix token-spec check * use dynamic verified token in e2e test * undo the hw wallet trustline error fix while I figure out how to do it properly * revert isTokenSpec change * better trustline error handling; fix remove token (#1326) * starting work on bestpath * upgrade redux toolkit (#1333) * upgrade redux toolkit * allow a longer timeout to account for network congestion * styling and copy fixes * Added translations * upgrade soroswap sdk and break out simulation component * Bugfix/5.20.0 qa (#1334) * styling and copy fixes * Added translations * Bugfix/5.20.0 legal copy (#1337) * legal copy changes * Added translations * fix go back button * use history.goBack for go back button * show icons for soroswap tokens and final cleanup * remove axios and auto add the token after successful token swap * adding comments * enable linting * simplify asset dropdown logic --------- Signed-off-by: dependabot[bot] Co-authored-by: aristides Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * uses key val for public keys for inflation dest and trustor fields * adding documentation to the Soroswap methods (#1350) * removes sentry exceptions from expected failures to fetch contract specs * check if either asset is a soroswap token (#1353) * making freighter-api conform to wallet standard interface * Added translations * Revert "Added translations" This reverts commit 48d82d5dc96ade559f929cb353f4626f656939c2. * update remaining api methods to match * rm unused dep * pr comments * fix tests * increase timeout * use jest timeout * fix test name --------- Signed-off-by: dependabot[bot] Co-authored-by: aristides Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Feature/standard wallet interface docs (#1370) * Feature/soroswap (#1347) * Feature/hash signing 2 (#1303) * flips the session store flag, updates listeners to not be intialized … (#1169) * capture amplitude errors to better understand why they're triggering sentry (#1167) * flips the session store flag, updates listeners to not be intialized async for v3 * updates e2e tests for manifest v3 --------- Co-authored-by: Piyal Basu * add scripts tag for Firefox (#1294) * uses chrome storage in migrations instead of local storage * checks for migrated account in migration logic to set migrated network * don't babel-polyfill contentScript (#1297) * don't clear all of localStore on recoverAccount (#1301) * add hash signing option (ui and data storage) * fix redux state update; show custom errors from hardware wallet * Added translations * when experimental mode is enabled, switch network * PR comments * rm logs --------- Co-authored-by: aristides * Feature/trustline sac (#1289) * Bump axios, @docusaurus/core and @docusaurus/preset-classic in /docs (#1244) Removes [axios](https://github.com/axios/axios). It's no longer used after updating ancestor dependencies [axios](https://github.com/axios/axios), [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) and [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic). These dependencies need to be updated together. Removes `axios` Updates `@docusaurus/core` from 2.4.1 to 3.2.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.2.1/packages/docusaurus) Updates `@docusaurus/preset-classic` from 2.4.1 to 3.2.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.2.1/packages/docusaurus-preset-classic) --- updated-dependencies: - dependency-name: axios dependency-type: indirect - dependency-name: "@docusaurus/core" dependency-type: direct:production - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump express from 4.18.2 to 4.19.2 in /docs (#1216) Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump tar from 6.1.15 to 6.2.1 (#1231) Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.15 to 6.2.1. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v6.1.15...v6.2.1) --- updated-dependencies: - dependency-name: tar dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump express from 4.18.2 to 4.19.2 (#1214) Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /docs (#1208) Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4. - [Release notes](https://github.com/webpack/webpack-dev-middleware/releases) - [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4) --- updated-dependencies: - dependency-name: webpack-dev-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump webpack-dev-middleware from 5.3.3 to 5.3.4 (#1207) Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4. - [Release notes](https://github.com/webpack/webpack-dev-middleware/releases) - [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4) --- updated-dependencies: - dependency-name: webpack-dev-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump follow-redirects from 1.15.4 to 1.15.6 in /docs (#1198) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump follow-redirects from 1.15.2 to 1.15.6 (#1197) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump ip from 2.0.0 to 2.0.1 (#1128) Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1. - [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1) --- updated-dependencies: - dependency-name: ip dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump axios, @docusaurus/core and @docusaurus/preset-classic in /docs (#1055) Removes [axios](https://github.com/axios/axios). It's no longer used after updating ancestor dependencies [axios](https://github.com/axios/axios), [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) and [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic). These dependencies need to be updated together. Removes `axios` Updates `@docusaurus/core` from 2.4.1 to 3.0.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.0.1/packages/docusaurus) Updates `@docusaurus/preset-classic` from 2.4.1 to 3.0.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.0.1/packages/docusaurus-preset-classic) --- updated-dependencies: - dependency-name: axios dependency-type: indirect - dependency-name: "@docusaurus/core" dependency-type: direct:production - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * first pass at adding remove dropdown * replaces all remaining instances of wallet-sdk * tweaks tx detail row layouts to avoid overflows * adds getContractSpec, uses it to display parameter names in tx sign detail view * migrates KeyManager to ts-wallet-sdk * upgrades react and react dom types to 18 * adds react types to extension workspace, fixes type errors * set styles for dropdown * Added translations * adds standalone version of getContractSpec and related helpers * Added translations * Added translations * undo husky pre push comments * Feature/p21 futurenet release (#1278) * switch between stellar-sdk and stellar-sdk-next based on network * increase max diff pixel ratio for playwright * rm console * adds tx timeout in send and swap settings * Added translations * tweaks tooltip text * Added translations * happy path for new add asset flow * fix trustline error warning and fix naming * Added translations * rollback dep upgrades * rollback changes * rollback package-lock upgrade * add snapshots for manage assets and send payment (#1309) --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Aristides Staffieri * guard against dispatching incorrect parameter (#1305) * adds Hardware sign usage in ReviewAuth for use during invokeHostFn signing, tweaks soroban icon to not overflow in signing details (#1282) * adds loader for hardware signing during signing (#1284) Co-authored-by: Piyal Basu * adds sign blob error for encoding mismatch (#1300) * fix bad merge * [BUG] replaces all instances of pickTransfer with getTokenSpec workflow (#1312) * replaces all instances of pickTransfer with getTokenSpec workflow * check for transfer once token spec is confirmed * rm unused dep * fixes bad merge from upstream in ManageAssets * Added translations * remove accidental mobile4 dir addition --------- Co-authored-by: Piyal Basu * Test/data storage (#1304) * adds test for data storage get item * adds tests for network migrations * adds test for remaining migrations * reverts bad merge from upstream --------- Co-authored-by: Piyal Basu * [CHORE] upgrades all webpack related deps (#1307) * upgrades all webpack related deps * updates lock file * updates migrations test for recent changes * [CHORE] upgrade to docusaurus v3 (#1306) * upgrade to docusaurus v3 * edit manifest for chrome before uploading (#1315) * use single quotes (#1316) --------- Co-authored-by: Piyal Basu * Fix manage assets tests (#1317) * fix tests for new manage assets flow * fix broken manage-assets tests * rm unused dep * fix e2e tests * fix query for visible loader * fix assets test * selector for individual asset balance * add better error messaging for liabilities when removing an asset (#1319) * add better error messaging for liabilities when removing an asset * add tests and rm sellingLiabilities * rm more selling liabilities * fix diff * fix diff again * verify params before fetching issuer info (#1320) * add better error messaging for liabilities when removing an asset * add tests and rm sellingLiabilities * rm more selling liabilities * fix diff * fix diff again * verify params before fetching issuer info * test soroswap api * pin docusaurus core to match versions * adding soroswap tokens to swap dropdown * split dataStorageAccess into a different file to allow for jest mocks with circular deps * updates all references for new dataStorage path * fix add asset padding; fix ledger trustline error; fix token-spec check (#1324) * fix add asset padding; fix ledger trustline error; fix token-spec check * use dynamic verified token in e2e test * undo the hw wallet trustline error fix while I figure out how to do it properly * revert isTokenSpec change * better trustline error handling; fix remove token (#1326) * starting work on bestpath * upgrade redux toolkit (#1333) * upgrade redux toolkit * allow a longer timeout to account for network congestion * styling and copy fixes * Added translations * upgrade soroswap sdk and break out simulation component * Bugfix/5.20.0 qa (#1334) * styling and copy fixes * Added translations * Bugfix/5.20.0 legal copy (#1337) * legal copy changes * Added translations * fix go back button * use history.goBack for go back button * show icons for soroswap tokens and final cleanup * remove axios and auto add the token after successful token swap * adding comments * enable linting * simplify asset dropdown logic --------- Signed-off-by: dependabot[bot] Co-authored-by: aristides Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * uses key val for public keys for inflation dest and trustor fields * adding documentation to the Soroswap methods (#1350) * removes sentry exceptions from expected failures to fetch contract specs * check if either asset is a soroswap token (#1353) * making freighter-api conform to wallet standard interface * Added translations * Revert "Added translations" This reverts commit 48d82d5dc96ade559f929cb353f4626f656939c2. * update remaining api methods to match * rm unused dep * pr comments * fix tests * increase timeout * use jest timeout * fix test name * adding docs for standard wallet interface * updating docs and playground * don't use Partial type * simplify types * update types in guide * rollback version * rollback yarn.lock * fix tests --------- Signed-off-by: dependabot[bot] Co-authored-by: aristides Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * add wallet change watcher + docs (#1373) * Feature/soroswap (#1347) * Feature/hash signing 2 (#1303) * flips the session store flag, updates listeners to not be intialized … (#1169) * capture amplitude errors to better understand why they're triggering sentry (#1167) * flips the session store flag, updates listeners to not be intialized async for v3 * updates e2e tests for manifest v3 --------- Co-authored-by: Piyal Basu * add scripts tag for Firefox (#1294) * uses chrome storage in migrations instead of local storage * checks for migrated account in migration logic to set migrated network * don't babel-polyfill contentScript (#1297) * don't clear all of localStore on recoverAccount (#1301) * add hash signing option (ui and data storage) * fix redux state update; show custom errors from hardware wallet * Added translations * when experimental mode is enabled, switch network * PR comments * rm logs --------- Co-authored-by: aristides * Feature/trustline sac (#1289) * Bump axios, @docusaurus/core and @docusaurus/preset-classic in /docs (#1244) Removes [axios](https://github.com/axios/axios). It's no longer used after updating ancestor dependencies [axios](https://github.com/axios/axios), [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) and [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic). These dependencies need to be updated together. Removes `axios` Updates `@docusaurus/core` from 2.4.1 to 3.2.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.2.1/packages/docusaurus) Updates `@docusaurus/preset-classic` from 2.4.1 to 3.2.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.2.1/packages/docusaurus-preset-classic) --- updated-dependencies: - dependency-name: axios dependency-type: indirect - dependency-name: "@docusaurus/core" dependency-type: direct:production - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump express from 4.18.2 to 4.19.2 in /docs (#1216) Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump tar from 6.1.15 to 6.2.1 (#1231) Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.15 to 6.2.1. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v6.1.15...v6.2.1) --- updated-dependencies: - dependency-name: tar dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump express from 4.18.2 to 4.19.2 (#1214) Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /docs (#1208) Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4. - [Release notes](https://github.com/webpack/webpack-dev-middleware/releases) - [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4) --- updated-dependencies: - dependency-name: webpack-dev-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump webpack-dev-middleware from 5.3.3 to 5.3.4 (#1207) Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4. - [Release notes](https://github.com/webpack/webpack-dev-middleware/releases) - [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4) --- updated-dependencies: - dependency-name: webpack-dev-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump follow-redirects from 1.15.4 to 1.15.6 in /docs (#1198) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump follow-redirects from 1.15.2 to 1.15.6 (#1197) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump ip from 2.0.0 to 2.0.1 (#1128) Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1. - [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1) --- updated-dependencies: - dependency-name: ip dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump axios, @docusaurus/core and @docusaurus/preset-classic in /docs (#1055) Removes [axios](https://github.com/axios/axios). It's no longer used after updating ancestor dependencies [axios](https://github.com/axios/axios), [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) and [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic). These dependencies need to be updated together. Removes `axios` Updates `@docusaurus/core` from 2.4.1 to 3.0.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.0.1/packages/docusaurus) Updates `@docusaurus/preset-classic` from 2.4.1 to 3.0.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.0.1/packages/docusaurus-preset-classic) --- updated-dependencies: - dependency-name: axios dependency-type: indirect - dependency-name: "@docusaurus/core" dependency-type: direct:production - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * first pass at adding remove dropdown * replaces all remaining instances of wallet-sdk * tweaks tx detail row layouts to avoid overflows * adds getContractSpec, uses it to display parameter names in tx sign detail view * migrates KeyManager to ts-wallet-sdk * upgrades react and react dom types to 18 * adds react types to extension workspace, fixes type errors * set styles for dropdown * Added translations * adds standalone version of getContractSpec and related helpers * Added translations * Added translations * undo husky pre push comments * Feature/p21 futurenet release (#1278) * switch between stellar-sdk and stellar-sdk-next based on network * increase max diff pixel ratio for playwright * rm console * adds tx timeout in send and swap settings * Added translations * tweaks tooltip text * Added translations * happy path for new add asset flow * fix trustline error warning and fix naming * Added translations * rollback dep upgrades * rollback changes * rollback package-lock upgrade * add snapshots for manage assets and send payment (#1309) --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Aristides Staffieri * guard against dispatching incorrect parameter (#1305) * adds Hardware sign usage in ReviewAuth for use during invokeHostFn signing, tweaks soroban icon to not overflow in signing details (#1282) * adds loader for hardware signing during signing (#1284) Co-authored-by: Piyal Basu * adds sign blob error for encoding mismatch (#1300) * fix bad merge * [BUG] replaces all instances of pickTransfer with getTokenSpec workflow (#1312) * replaces all instances of pickTransfer with getTokenSpec workflow * check for transfer once token spec is confirmed * rm unused dep * fixes bad merge from upstream in ManageAssets * Added translations * remove accidental mobile4 dir addition --------- Co-authored-by: Piyal Basu * Test/data storage (#1304) * adds test for data storage get item * adds tests for network migrations * adds test for remaining migrations * reverts bad merge from upstream --------- Co-authored-by: Piyal Basu * [CHORE] upgrades all webpack related deps (#1307) * upgrades all webpack related deps * updates lock file * updates migrations test for recent changes * [CHORE] upgrade to docusaurus v3 (#1306) * upgrade to docusaurus v3 * edit manifest for chrome before uploading (#1315) * use single quotes (#1316) --------- Co-authored-by: Piyal Basu * Fix manage assets tests (#1317) * fix tests for new manage assets flow * fix broken manage-assets tests * rm unused dep * fix e2e tests * fix query for visible loader * fix assets test * selector for individual asset balance * add better error messaging for liabilities when removing an asset (#1319) * add better error messaging for liabilities when removing an asset * add tests and rm sellingLiabilities * rm more selling liabilities * fix diff * fix diff again * verify params before fetching issuer info (#1320) * add better error messaging for liabilities when removing an asset * add tests and rm sellingLiabilities * rm more selling liabilities * fix diff * fix diff again * verify params before fetching issuer info * test soroswap api * pin docusaurus core to match versions * adding soroswap tokens to swap dropdown * split dataStorageAccess into a different file to allow for jest mocks with circular deps * updates all references for new dataStorage path * fix add asset padding; fix ledger trustline error; fix token-spec check (#1324) * fix add asset padding; fix ledger trustline error; fix token-spec check * use dynamic verified token in e2e test * undo the hw wallet trustline error fix while I figure out how to do it properly * revert isTokenSpec change * better trustline error handling; fix remove token (#1326) * starting work on bestpath * upgrade redux toolkit (#1333) * upgrade redux toolkit * allow a longer timeout to account for network congestion * styling and copy fixes * Added translations * upgrade soroswap sdk and break out simulation component * Bugfix/5.20.0 qa (#1334) * styling and copy fixes * Added translations * Bugfix/5.20.0 legal copy (#1337) * legal copy changes * Added translations * fix go back button * use history.goBack for go back button * show icons for soroswap tokens and final cleanup * remove axios and auto add the token after successful token swap * adding comments * enable linting * simplify asset dropdown logic --------- Signed-off-by: dependabot[bot] Co-authored-by: aristides Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * uses key val for public keys for inflation dest and trustor fields * adding documentation to the Soroswap methods (#1350) * removes sentry exceptions from expected failures to fetch contract specs * check if either asset is a soroswap token (#1353) * making freighter-api conform to wallet standard interface * Added translations * Revert "Added translations" This reverts commit 48d82d5dc96ade559f929cb353f4626f656939c2. * update remaining api methods to match * rm unused dep * pr comments * fix tests * increase timeout * use jest timeout * fix test name * adding docs for standard wallet interface * updating docs and playground * don't use Partial type * simplify types * update types in guide * rollback version * rollback yarn.lock * fix tests * add wallet change watcher + docs * add clarifying line of copy * fix test --------- Signed-off-by: dependabot[bot] Co-authored-by: aristides Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * add isNonSSELEnabled flag and use that to block non-https tx's (#1374) * add isNonSSELEnabled flag and use that to block non-https tx's * rm comment * moving isNonSSLEnabled to Advanced Settings * moving isNonSSLEnabled to Advanced Settings (#1375) * Feature/connect dapp warning (#1361) * Feature/soroswap (#1347) * Feature/hash signing 2 (#1303) * flips the session store flag, updates listeners to not be intialized … (#1169) * capture amplitude errors to better understand why they're triggering sentry (#1167) * flips the session store flag, updates listeners to not be intialized async for v3 * updates e2e tests for manifest v3 --------- Co-authored-by: Piyal Basu * add scripts tag for Firefox (#1294) * uses chrome storage in migrations instead of local storage * checks for migrated account in migration logic to set migrated network * don't babel-polyfill contentScript (#1297) * don't clear all of localStore on recoverAccount (#1301) * add hash signing option (ui and data storage) * fix redux state update; show custom errors from hardware wallet * Added translations * when experimental mode is enabled, switch network * PR comments * rm logs --------- Co-authored-by: aristides * Feature/trustline sac (#1289) * Bump axios, @docusaurus/core and @docusaurus/preset-classic in /docs (#1244) Removes [axios](https://github.com/axios/axios). It's no longer used after updating ancestor dependencies [axios](https://github.com/axios/axios), [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) and [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic). These dependencies need to be updated together. Removes `axios` Updates `@docusaurus/core` from 2.4.1 to 3.2.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.2.1/packages/docusaurus) Updates `@docusaurus/preset-classic` from 2.4.1 to 3.2.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.2.1/packages/docusaurus-preset-classic) --- updated-dependencies: - dependency-name: axios dependency-type: indirect - dependency-name: "@docusaurus/core" dependency-type: direct:production - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump express from 4.18.2 to 4.19.2 in /docs (#1216) Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump tar from 6.1.15 to 6.2.1 (#1231) Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.15 to 6.2.1. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v6.1.15...v6.2.1) --- updated-dependencies: - dependency-name: tar dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump express from 4.18.2 to 4.19.2 (#1214) Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /docs (#1208) Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4. - [Release notes](https://github.com/webpack/webpack-dev-middleware/releases) - [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4) --- updated-dependencies: - dependency-name: webpack-dev-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump webpack-dev-middleware from 5.3.3 to 5.3.4 (#1207) Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4. - [Release notes](https://github.com/webpack/webpack-dev-middleware/releases) - [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4) --- updated-dependencies: - dependency-name: webpack-dev-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump follow-redirects from 1.15.4 to 1.15.6 in /docs (#1198) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.4 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.4...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump follow-redirects from 1.15.2 to 1.15.6 (#1197) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.6. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.6) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump ip from 2.0.0 to 2.0.1 (#1128) Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1. - [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1) --- updated-dependencies: - dependency-name: ip dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * Bump axios, @docusaurus/core and @docusaurus/preset-classic in /docs (#1055) Removes [axios](https://github.com/axios/axios). It's no longer used after updating ancestor dependencies [axios](https://github.com/axios/axios), [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) and [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic). These dependencies need to be updated together. Removes `axios` Updates `@docusaurus/core` from 2.4.1 to 3.0.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.0.1/packages/docusaurus) Updates `@docusaurus/preset-classic` from 2.4.1 to 3.0.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.0.1/packages/docusaurus-preset-classic) --- updated-dependencies: - dependency-name: axios dependency-type: indirect - dependency-name: "@docusaurus/core" dependency-type: direct:production - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * first pass at adding remove dropdown * replaces all remaining instances of wallet-sdk * tweaks tx detail row layouts to avoid overflows * adds getContractSpec, uses it to display parameter names in tx sign detail view * migrates KeyManager to ts-wallet-sdk * upgrades react and react dom types to 18 * adds react types to extension workspace, fixes type errors * set styles for dropdown * Added translations * adds standalone version of getContractSpec and related helpers * Added translations * Added translations * undo husky pre push comments * Feature/p21 futurenet release (#1278) * switch between stellar-sdk and stellar-sdk-next based on network * increase max diff pixel ratio for playwright * rm console * adds tx timeout in send and swap settings * Added translations * tweaks tooltip text * Added translations * happy path for new add asset flow * fix trustline error warning and fix naming * Added translations * rollback dep upgrades * rollback changes * rollback package-lock upgrade * add snapshots for manage assets and send payment (#1309) --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Aristides Staffieri * guard against dispatching incorrect parameter (#1305) * adds Hardware sign usage in ReviewAuth for use during invokeHostFn signing, tweaks soroban icon to not overflow in signing details (#1282) * adds loader for hardware signing during signing (#1284) Co-authored-by: Piyal Basu * adds sign blob error for encoding mismatch (#1300) * fix bad merge * [BUG] replaces all instances of pickTransfer with getTokenSpec workflow (#1312) * replaces all instances of pickTransfer with getTokenSpec workflow * check for transfer once token spec is confirmed * rm unused dep * fixes bad merge from upstream in ManageAssets * Added translations * remove accidental mobile4 dir addition --------- Co-authored-by: Piyal Basu * Test/data storage (#1304) * adds test for data storage get item * adds tests for network migrations * adds test for remaining migrations * reverts bad merge from upstream --------- Co-authored-by: Piyal Basu * [CHORE] upgrades all webpack related deps (#1307) * upgrades all webpack related deps * updates lock file * updates migrations test for recent changes * [CHORE] upgrade to docusaurus v3 (#1306) * upgrade to docusaurus v3 * edit manifest for chrome before uploading (#1315) * use single quotes (#1316) --------- Co-authored-by: Piyal Basu * Fix manage assets tests (#1317) * fix tests for new manage assets flow * fix broken manage-assets tests * rm unused dep * fix e2e tests * fix query for visible loader * fix assets test * selector for individual asset balance * add better error messaging for liabilities when removing an asset (#1319) * add better error messaging for liabilities when removing an asset * add tests and rm sellingLiabilities * rm more selling liabilities * fix diff * fix diff again * verify params before fetching issuer info (#1320) * add better error messaging for liabilities when removing an asset * add tests and rm sellingLiabilities * rm more selling liabilities * fix diff * fix diff again * verify params before fetching issuer info * test soroswap api * pin docusaurus core to match versions * adding soroswap tokens to swap dropdown * split dataStorageAccess into a different file to allow for jest mocks with circular deps * updates all references for new dataStorage path * fix add asset padding; fix ledger trustline error; fix token-spec check (#1324) * fix add asset padding; fix ledger trustline error; fix token-spec check * use dynamic verified token in e2e test * undo the hw wallet trustline error fix while I figure out how to do it properly * revert isTokenSpec change * better trustline error handling; fix remove token (#1326) * starting work on bestpath * upgrade redux toolkit (#1333) * upgrade redux toolkit * allow a longer timeout to account for network congestion * styling and copy fixes * Added translations * upgrade soroswap sdk and break out simulation component * Bugfix/5.20.0 qa (#1334) * styling and copy fixes * Added translations * Bugfix/5.20.0 legal copy (#1337) * legal copy changes * Added translations * fix go back button * use history.goBack for go back button * show icons for soroswap tokens and final cleanup * remove axios and auto add the token after successful token swap * adding comments * enable linting * simplify asset dropdown logic --------- Signed-off-by: dependabot[bot] Co-authored-by: aristides Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * uses key val for public keys for inflation dest and trustor fields * adding documentation to the Soroswap methods (#1350) * removes sentry exceptions from expected failures to fetch contract specs * check if either asset is a soroswap token (#1353) * adds warning for current account xlm insufficient for fee * adds loading state for getting balances before check * uses correct logic to check for enough xlm to cover fee * use correct denomination for fee in xlm comparison * make warning dismissable * adds malicious variant to connection request views * adds blockaid duck * moves blockaid scanSite to helper from duck workflow * tweaks loader for grant access * adds warning for grant access on blockaid scan miss --------- Signed-off-by: dependabot[bot] Co-authored-by: Piyal Basu Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [BUG] SAC balance shows up twice in asset list (#1376) * removes tokens with no balance, tweaks add asset tests timeout * Added translations * restore pre-push debug comment * revert new snapshots * adds default for recommended fee in hook * Feature/doc preview build master (#1379) * use single quotes * add different cmds for netlify prod vs preview * Bugfix/move netlify toml 2 (#1381) * moving isNonSSLEnabled to Advanced Settings * move the netlify.toml * bump freighter-api version to trigger docs build * fix toml * try different netlify context * update yarn.lock and defensive check for blockaid "miss" * dont show dapp scan warning in grant access (#1390) * Bugfix/sign msg auth entry buffer (#1392) * copy fix * Added translations * return a buffer from sign message and sign auth entry * rm eslint-disables * fix tests * Bugfix/re enable tests (#1395) * copy fix * Added translations * re-enable flakey e2e tests * rm eslint-disables * adds tx scan calls during tx sign, no UI warnings (#1393) * scan asset during Add Asset flow and Sign classic tx flow (#1405) * upgrading ledger SDK (#1407) * rm ssl toggle from Preferences (#1408) * Bump fast-xml-parser in the npm_and_yarn group across 1 directory (#1391) Bumps the npm_and_yarn group with 1 update in the / directory: [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser). Updates `fast-xml-parser` from 4.4.0 to 4.4.1 - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v4.4.0...v4.4.1) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the minor-and-patch group across 3 directories with 26 updates (#1399) Bumps the minor-and-patch group with 24 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) | `7.24.7` | `7.25.2` | | [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) | `7.24.7` | `7.25.1` | | [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) | `7.24.7` | `7.25.3` | | [@vercel/ncc](https://github.com/vercel/ncc) | `0.36.1` | `0.38.1` | | [eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y) | `6.8.0` | `6.9.0` | | [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) | `7.34.2` | `7.35.0` | | [webpack](https://github.com/webpack/webpack) | `5.91.0` | `5.93.0` | | [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) | `48.2.9` | `48.11.0` | | [@stellar/js-xdr](https://github.com/stellar/js-xdr) | `3.1.1` | `3.1.2` | | [stellar-sdk](https://github.com/stellar/js-stellar-sdk) | `12.1.0` | `12.2.0` | | [webextension-polyfill](https://github.com/mozilla/webextension-polyfill) | `0.10.0` | `0.12.0` | | [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) | `3.3.2` | `3.4.0` | | [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) | `3.3.2` | `3.4.0` | | [@ledgerhq/hw-transport-webusb](https://github.com/LedgerHQ/ledger-live) | `6.28.6` | `6.29.2` | | @stellar/typescript-wallet-sdk-km | `1.5.0` | `1.7.0` | | [@types/chrome](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/chrome) | `0.0.104` | `0.0.269` | | [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) | `4.17.5` | `4.17.7` | | [@types/webextension-polyfill](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/webextension-polyfill) | `0.9.2` | `0.10.7` | | [html-loader](https://github.com/webpack-contrib/html-loader) | `5.0.0` | `5.1.0` | | [sass](https://github.com/sass/dart-sass) | `1.77.5` | `1.77.8` | | [soroswap-router-sdk](https://github.com/soroswap/soroswap-router-sdk) | `1.2.8` | `1.2.10` | | [tslib](https://github.com/Microsoft/tslib) | `2.0.0` | `2.6.3` | | [@playwright/test](https://github.com/microsoft/playwright) | `1.41.0` | `1.45.3` | | [url](https://github.com/defunctzombie/node-url) | `0.11.3` | `0.11.4` | Bumps the minor-and-patch group with 1 update in the /@shared/api directory: [webextension-polyfill](https://github.com/mozilla/webextension-polyfill). Bumps the minor-and-patch group with 6 updates in the /extension directory: | Package | From | To | | --- | --- | --- | | [webextension-polyfill](https://github.com/mozilla/webextension-polyfill) | `0.10.0` | `0.12.0` | | [@types/chrome](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/chrome) | `0.0.104` | `0.0.269` | | [@types/webextension-polyfill](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/webextension-polyfill) | `0.9.2` | `0.10.7` | | [@types/yup](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/yup) | `0.29.14` | `0.32.0` | | [tslib](https://github.com/Microsoft/tslib) | `2.0.0` | `2.6.3` | | [@playwright/test](https://github.com/microsoft/playwright) | `1.41.0` | `1.45.3` | Updates `@babel/core` from 7.24.7 to 7.25.2 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.25.2/packages/babel-core) Updates `@babel/eslint-parser` from 7.24.7 to 7.25.1 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.25.1/eslint/babel-eslint-parser) Updates `@babel/preset-env` from 7.24.7 to 7.25.3 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.25.3/packages/babel-preset-env) Updates `@vercel/ncc` from 0.36.1 to 0.38.1 - [Release notes](https://github.com/vercel/ncc/releases) - [Commits](https://github.com/vercel/ncc/compare/0.36.1...0.38.1) Updates `eslint-plugin-jsx-a11y` from 6.8.0 to 6.9.0 - [Release notes](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/releases) - [Changelog](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/CHANGELOG.md) - [Commits](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/compare/v6.8.0...v6.9.0) Updates `eslint-plugin-react` from 7.34.2 to 7.35.0 - [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases) - [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md) - [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.34.2...v7.35.0) Updates `webpack` from 5.91.0 to 5.93.0 - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.91.0...v5.93.0) Updates `eslint-plugin-jsdoc` from 48.2.9 to 48.11.0 - [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases) - [Changelog](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/.releaserc) - [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v48.2.9...v48.11.0) Updates `@stellar/js-xdr` from 3.1.1 to 3.1.2 - [Release notes](https://github.com/stellar/js-xdr/releases) - [Changelog](https://github.com/stellar/js-xdr/blob/master/CHANGELOG.md) - [Commits](https://github.com/stellar/js-xdr/compare/v3.1.1...v3.1.2) Updates `stellar-sdk` from 12.1.0 to 12.2.0 - [Release notes](https://github.com/stellar/js-stellar-sdk/releases) - [Changelog](https://github.com/stellar/js-stellar-sdk/blob/master/CHANGELOG.md) - [Commits](https://github.com/stellar/js-stellar-sdk/compare/v12.1.0...v12.2.0) Updates `webextension-polyfill` from 0.10.0 to 0.12.0 - [Release notes](https://github.com/mozilla/webextension-polyfill/releases) - [Commits](https://github.com/mozilla/webextension-polyfill/compare/0.10.0...0.12.0) Updates `@docusaurus/core` from 3.3.2 to 3.4.0 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.4.0/packages/docusaurus) Updates `@docusaurus/preset-classic` from 3.3.2 to 3.4.0 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.4.0/packages/docusaurus-preset-classic) Updates `@ledgerhq/hw-transport-webusb` from 6.28.6 to 6.29.2 - [Release notes](https://github.com/LedgerHQ/ledger-live/releases) - [Commits](https://github.com/LedgerHQ/ledger-live/compare/@ledgerhq/hw-transport-webusb@6.28.6...@ledgerhq/hw-transport-webusb@6.29.2) Updates `@stellar/typescript-wallet-sdk-km` from 1.5.0 to 1.7.0 Updates `@types/chrome` from 0.0.104 to 0.0.269 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/chrome) Updates `@types/lodash` from 4.17.5 to 4.17.7 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash) Updates `@types/webextension-polyfill` from 0.9.2 to 0.10.7 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/webextension-polyfill) Updates `html-loader` from 5.0.0 to 5.1.0 - [Release notes](https://github.com/webpack-contrib/html-loader/releases) - [Changelog](https://github.com/webpack-contrib/html-loader/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/html-loader/compare/v5.0.0...v5.1.0) Updates `sass` from 1.77.5 to 1.77.8 - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.77.5...1.77.8) Updates `semver` from 7.6.2 to 7.6.3 - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v7.6.2...v7.6.3) Updates `soroswap-router-sdk` from 1.2.8 to 1.2.10 - [Commits](https://github.com/soroswap/soroswap-router-sdk/commits) Updates `tslib` from 2.0.0 to 2.6.3 - [Release notes](https://github.com/Microsoft/tslib/releases) - [Commits](https://github.com/Microsoft/tslib/compare/2.0.0...v2.6.3) Updates `@playwright/test` from 1.41.0 to 1.45.3 - [Release notes](https://github.com/microsoft/playwright/releases) - [Commits](https://github.com/microsoft/playwright/compare/v1.41.0...v1.45.3) Updates `url` from 0.11.3 to 0.11.4 - [Commits](https://github.com/defunctzombie/node-url/compare/v0.11.3...v0.11.4) Updates `webextension-polyfill` from 0.10.0 to 0.12.0 - [Release notes](https://github.com/mozilla/webextension-polyfill/releases) - [Commits](https://github.com/mozilla/webextension-polyfill/compare/0.10.0...0.12.0) Updates `webextension-polyfill` from 0.10.0 to 0.12.0 - [Release notes](https://github.com/mozilla/webextension-polyfill/releases) - [Commits](https://github.com/mozilla/webextension-polyfill/compare/0.10.0...0.12.0) Updates `@types/chrome` from 0.0.104 to 0.0.269 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/chrome) Updates `@types/webextension-polyfill` from 0.9.2 to 0.10.7 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/webextension-polyfill) Updates `@types/yup` from 0.29.14 to 0.32.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/yup) Updates `tslib` from 2.0.0 to 2.6.3 - [Release notes](https://github.com/Microsoft/tslib/releases) - [Commits](https://github.com/Microsoft/tslib/compare/2.0.0...v2.6.3) Updates `@playwright/test` from 1.41.0 to 1.45.3 - [Release notes](https://github.com/microsoft/playwright/releases) - [Commits](https://github.com/microsoft/playwright/compare/v1.41.0...v1.45.3) --- updated-dependencies: - dependency-name: "@babel/core" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@babel/eslint-parser" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@babel/preset-env" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@vercel/ncc" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: eslint-plugin-jsx-a11y dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: eslint-plugin-react dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: webpack dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: eslint-plugin-jsdoc dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@stellar/js-xdr" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: stellar-sdk dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: webextension-polyfill dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@docusaurus/core" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@docusaurus/preset-classic" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@ledgerhq/hw-transport-webusb" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@stellar/typescript-wallet-sdk-km" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@types/chrome" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: "@types/lodash" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: "@types/webextension-polyfill" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: html-loader dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: sass dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: semver dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: soroswap-router-sdk dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: tslib dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@playwright/test" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: url dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: webextension-polyfill dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: webextension-polyfill dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@types/chrome" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: "@types/webextension-polyfill" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@types/yup" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: tslib dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: "@playwright/test" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-and-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Piyal Basu * minor updates after merging automated dep updates (#1409) * minor updates after merging automated dep updates * update yup lib * update e2e test docker image * show soroswap simulation reject error (#1413) * fix bug with soroswapping back to xlm (#1415) * fix bug with soroswapping back to xlm * disable Review Send button if no fee is selected * wait for continue button to become enabled * align advanced settings warning buttons to bottom of screen (#1418) * disable swap loader check --------- Signed-off-by: dependabot[bot] Co-authored-by: aristides Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/runTests.yml | 2 +- @shared/api/external.ts | 238 +-- @shared/api/helpers/extensionMessaging.ts | 16 + @shared/api/internal.ts | 10 +- @shared/api/package.json | 4 +- @shared/api/types.ts | 19 +- @shared/constants/package.json | 2 +- @shared/helpers/package.json | 2 +- @stellar/freighter-api/package.json | 2 +- .../src/__tests__/getAddress.test.js | 22 + .../src/__tests__/getNetwork.test.js | 26 +- .../src/__tests__/getPublicKey.test.js | 18 - .../freighter-api/src/__tests__/index.test.js | 4 +- .../src/__tests__/signAuthEntry.test.js | 25 + .../src/__tests__/signBlob.test.js | 19 - .../src/__tests__/signMessage.test.js | 27 + .../src/__tests__/signTransaction.test.js | 28 +- @stellar/freighter-api/src/getAddress.ts | 22 + @stellar/freighter-api/src/getNetwork.ts | 21 +- .../freighter-api/src/getNetworkDetails.ts | 45 +- @stellar/freighter-api/src/getPublicKey.ts | 5 - @stellar/freighter-api/src/getUserInfo.ts | 5 - @stellar/freighter-api/src/index.ts | 18 +- @stellar/freighter-api/src/isAllowed.ts | 21 +- @stellar/freighter-api/src/isConnected.ts | 16 +- @stellar/freighter-api/src/requestAccess.ts | 21 +- @stellar/freighter-api/src/setAllowed.ts | 21 +- @stellar/freighter-api/src/signAuthEntry.ts | 33 +- @stellar/freighter-api/src/signBlob.ts | 10 - @stellar/freighter-api/src/signMessage.ts | 35 + @stellar/freighter-api/src/signTransaction.ts | 29 +- .../freighter-api/src/watchWalletChanges.ts | 77 + docs/docs/guide/usingFreighterWebApp.md | 283 ++-- ...etPublicKeyDemo.tsx => GetAddressDemo.tsx} | 21 +- .../playground/components/GetNetworkDemo.tsx | 19 +- .../components/GetNetworkDetailsDemo.tsx | 13 +- .../playground/components/GetUserInfoDemo.tsx | 32 - .../playground/components/IsAllowedDemo.tsx | 7 +- .../playground/components/IsConnectedDemo.tsx | 7 +- .../components/RequestAccessDemo.tsx | 13 +- .../playground/components/SetAllowedDemo.tsx | 7 +- .../components/SignAuthEntryDemo.tsx | 39 +- .../playground/components/SignBlobDemo.tsx | 39 - .../playground/components/SignMessageDemo.tsx | 63 + .../components/SignTransactionDemo.tsx | 31 +- .../components/WatchWalletChangesDemo.tsx | 47 + docs/docs/playground/getAddress.mdx | 11 + docs/docs/playground/getPublicKey.mdx | 11 - docs/docs/playground/getUserInfo.mdx | 11 - docs/docs/playground/signBlob.mdx | 11 - docs/docs/playground/signMessage.mdx | 11 + docs/docs/playground/watchWalletChanges.mdx | 11 + docs/package.json | 6 +- docs/sidebars.js | 6 +- extension/e2e-tests/addAsset.test.ts | 156 +- extension/e2e-tests/helpers/login.ts | 2 +- extension/e2e-tests/sendPayment.test.ts | 128 +- extension/package.json | 20 +- extension/src/background/helpers/account.ts | 11 + .../src/background/helpers/dataStorage.ts | 12 + .../freighterApiMessageListener.ts | 561 ++++--- .../messageListener/popupMessageListener.ts | 32 +- extension/src/constants/localStorageTypes.ts | 1 + extension/src/helpers/stellar.ts | 6 +- extension/src/helpers/urls.ts | 8 +- extension/src/popup/App.tsx | 4 +- extension/src/popup/Router.tsx | 12 +- extension/src/popup/__testHelpers__/index.tsx | 1 - .../src/popup/components/ModalInfo/index.tsx | 28 +- .../popup/components/ModalInfo/styles.scss | 16 +- .../components/PunycodedDomain/index.tsx | 3 - .../components/PunycodedDomain/styles.scss | 6 - .../components/WarningMessages/index.tsx | 53 +- .../components/WarningMessages/styles.scss | 22 + .../manageAssets/ChooseAsset/index.tsx | 2 +- .../ManageAssetRowButton/index.tsx | 10 +- .../manageAssets/ManageAssetRows/index.tsx | 46 +- .../SendConfirm/TransactionDetails/index.tsx | 2 +- .../SendSettings/Settings/index.tsx | 52 +- .../src/popup/components/signBlob/index.tsx | 30 - .../{signBlob => signMessage}/index.scss | 2 +- .../popup/components/signMessage/index.tsx | 20 + .../signTransaction/Operations/index.tsx | 49 +- extension/src/popup/constants/metricsNames.ts | 8 +- extension/src/popup/constants/routes.ts | 4 +- extension/src/popup/ducks/settings.ts | 21 +- extension/src/popup/ducks/token-payment.ts | 71 + .../src/popup/ducks/transactionSubmission.ts | 4 - extension/src/popup/helpers/blockaid.ts | 144 ++ .../src/popup/helpers/hardwareConnect.ts | 5 +- extension/src/popup/helpers/sorobanSwap.ts | 12 +- extension/src/popup/helpers/useNetworkFees.ts | 1 + .../src/popup/locales/en/translation.json | 48 +- .../src/popup/locales/pt/translation.json | 48 +- extension/src/popup/metrics/views.ts | 6 +- .../popup/views/AdvancedSettings/index.tsx | 187 +++ .../styles.scss | 9 +- .../views/ExperimentalFeatures/index.tsx | 171 -- .../src/popup/views/GrantAccess/index.tsx | 117 +- .../src/popup/views/GrantAccess/styles.scss | 9 + extension/src/popup/views/IntegrationTest.tsx | 2 + .../src/popup/views/Preferences/styles.scss | 2 +- .../src/popup/views/ReviewAuth/index.tsx | 2 +- extension/src/popup/views/Security/index.tsx | 4 +- .../src/popup/views/SignAuthEntry/index.tsx | 33 +- .../views/{SignBlob => SignMessage}/index.tsx | 71 +- .../{SignBlob => SignMessage}/styles.scss | 2 +- .../src/popup/views/SignTransaction/index.tsx | 41 +- .../views/__tests__/ManageAssets.test.tsx | 3 - .../views/__tests__/SendTokenPayment.test.tsx | 1 + .../views/__tests__/SignTransaction.test.tsx | 49 +- .../src/popup/views/__tests__/Swap.test.tsx | 4 +- .../views/__tests__/SwapUnfunded.test.tsx | 1 - netlify.toml | 4 + package.json | 17 +- testNodeCompat.js | 8 +- yarn.lock | 1438 +++++++++-------- 117 files changed, 3213 insertions(+), 2171 deletions(-) create mode 100644 @stellar/freighter-api/src/__tests__/getAddress.test.js delete mode 100644 @stellar/freighter-api/src/__tests__/getPublicKey.test.js create mode 100644 @stellar/freighter-api/src/__tests__/signAuthEntry.test.js delete mode 100644 @stellar/freighter-api/src/__tests__/signBlob.test.js create mode 100644 @stellar/freighter-api/src/__tests__/signMessage.test.js create mode 100644 @stellar/freighter-api/src/getAddress.ts delete mode 100644 @stellar/freighter-api/src/getPublicKey.ts delete mode 100644 @stellar/freighter-api/src/getUserInfo.ts delete mode 100644 @stellar/freighter-api/src/signBlob.ts create mode 100644 @stellar/freighter-api/src/signMessage.ts create mode 100644 @stellar/freighter-api/src/watchWalletChanges.ts rename docs/docs/playground/components/{GetPublicKeyDemo.tsx => GetAddressDemo.tsx} (55%) delete mode 100644 docs/docs/playground/components/GetUserInfoDemo.tsx delete mode 100644 docs/docs/playground/components/SignBlobDemo.tsx create mode 100644 docs/docs/playground/components/SignMessageDemo.tsx create mode 100644 docs/docs/playground/components/WatchWalletChangesDemo.tsx create mode 100644 docs/docs/playground/getAddress.mdx delete mode 100644 docs/docs/playground/getPublicKey.mdx delete mode 100644 docs/docs/playground/getUserInfo.mdx delete mode 100644 docs/docs/playground/signBlob.mdx create mode 100644 docs/docs/playground/signMessage.mdx create mode 100644 docs/docs/playground/watchWalletChanges.mdx delete mode 100644 extension/src/popup/components/signBlob/index.tsx rename extension/src/popup/components/{signBlob => signMessage}/index.scss (88%) create mode 100644 extension/src/popup/components/signMessage/index.tsx create mode 100644 extension/src/popup/helpers/blockaid.ts create mode 100644 extension/src/popup/views/AdvancedSettings/index.tsx rename extension/src/popup/views/{ExperimentalFeatures => AdvancedSettings}/styles.scss (86%) delete mode 100644 extension/src/popup/views/ExperimentalFeatures/index.tsx rename extension/src/popup/views/{SignBlob => SignMessage}/index.tsx (74%) rename extension/src/popup/views/{SignBlob => SignMessage}/styles.scss (98%) create mode 100644 netlify.toml diff --git a/.github/workflows/runTests.yml b/.github/workflows/runTests.yml index ae2bda772..80c1fda4a 100644 --- a/.github/workflows/runTests.yml +++ b/.github/workflows/runTests.yml @@ -8,7 +8,7 @@ jobs: timeout-minutes: 20 runs-on: ubuntu-latest container: - image: mcr.microsoft.com/playwright:v1.41.0-jammy + image: mcr.microsoft.com/playwright:v1.45.3-jammy steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 diff --git a/@shared/api/external.ts b/@shared/api/external.ts index 54aa2eaae..c0f674a88 100644 --- a/@shared/api/external.ts +++ b/@shared/api/external.ts @@ -1,10 +1,16 @@ import { EXTERNAL_SERVICE_TYPES } from "../constants/services"; import { NetworkDetails } from "../constants/stellar"; -import { sendMessageToContentScript } from "./helpers/extensionMessaging"; -import { UserInfo } from "./types"; - -export const requestAccess = async (): Promise => { - let response = { publicKey: "", error: "" }; +import { + sendMessageToContentScript, + FreighterApiInternalError, +} from "./helpers/extensionMessaging"; +import { FreighterApiError } from "./types"; + +export const requestAccess = async (): Promise<{ + publicKey: string; + error?: FreighterApiError; +}> => { + let response; try { response = await sendMessageToContentScript({ type: EXTERNAL_SERVICE_TYPES.REQUEST_ACCESS, @@ -13,16 +19,16 @@ export const requestAccess = async (): Promise => { console.error(e); } - const { publicKey, error } = response; + const { publicKey } = response || { publicKey: "" }; - if (error) { - throw error; - } - return publicKey; + return { publicKey, error: response?.apiError }; }; -export const requestPublicKey = async (): Promise => { - let response = { publicKey: "", error: "" }; +export const requestPublicKey = async (): Promise<{ + publicKey: string; + error?: FreighterApiError; +}> => { + let response; try { response = await sendMessageToContentScript({ type: EXTERNAL_SERVICE_TYPES.REQUEST_PUBLIC_KEY, @@ -31,12 +37,7 @@ export const requestPublicKey = async (): Promise => { console.error(e); } - const { publicKey, error } = response; - - if (error) { - throw error; - } - return publicKey; + return { publicKey: response?.publicKey || "", error: response?.apiError }; }; export const submitTransaction = async ( @@ -44,12 +45,15 @@ export const submitTransaction = async ( opts?: | string | { - network?: string; accountToSign?: string; networkPassphrase?: string; }, accountToSign?: string, -): Promise => { +): Promise<{ + signedTransaction: string; + signerAddress: string; + error?: FreighterApiError; +}> => { let network = ""; let _accountToSign = ""; let networkPassphrase = ""; @@ -60,7 +64,6 @@ export const submitTransaction = async ( This logic maintains backwards compatibility for older versions */ if (typeof opts === "object") { - network = opts.network || ""; _accountToSign = opts.accountToSign || ""; networkPassphrase = opts.networkPassphrase || ""; } else { @@ -68,7 +71,7 @@ export const submitTransaction = async ( _accountToSign = accountToSign || ""; } - let response = { signedTransaction: "", error: "" }; + let response; try { response = await sendMessageToContentScript({ transactionXdr, @@ -78,26 +81,31 @@ export const submitTransaction = async ( type: EXTERNAL_SERVICE_TYPES.SUBMIT_TRANSACTION, }); } catch (e) { - console.error(e); - throw e; + return { + signedTransaction: "", + signerAddress: "", + error: FreighterApiInternalError, + }; } - const { signedTransaction, error } = response; + const { signedTransaction, signerAddress } = response; - if (error) { - throw error; - } - return signedTransaction; + return { signedTransaction, signerAddress, error: response?.apiError }; }; -export const submitBlob = async ( +export const submitMessage = async ( blob: string, opts?: { - accountToSign?: string; + address?: string; + networkPassphrase?: string; }, -): Promise => { - let response = { signedBlob: "", error: "" }; +): Promise<{ + signedMessage: Buffer | null; + signerAddress: string; + error?: FreighterApiError; +}> => { + let response; const _opts = opts || {}; - const accountToSign = _opts.accountToSign || ""; + const accountToSign = _opts.address || ""; try { response = await sendMessageToContentScript({ blob, @@ -105,59 +113,82 @@ export const submitBlob = async ( type: EXTERNAL_SERVICE_TYPES.SUBMIT_BLOB, }); } catch (e) { - console.error(e); - throw e; + return { + signedMessage: null, + signerAddress: "", + error: FreighterApiInternalError, + }; } - const { signedBlob, error } = response; + const { signedBlob, signerAddress } = response; - if (error) { - throw error; - } - return signedBlob; + return { + signedMessage: signedBlob || null, + signerAddress, + error: response?.apiError, + }; }; export const submitAuthEntry = async ( entryXdr: string, opts?: { - accountToSign?: string; + address?: string; + networkPassphrase?: string; }, -): Promise => { - let response = { signedAuthEntry: "", error: "" }; +): Promise<{ + signedAuthEntry: Buffer | null; + signerAddress: string; + error?: FreighterApiError; +}> => { const _opts = opts || {}; - const accountToSign = _opts.accountToSign || ""; + const accountToSign = _opts.address || ""; + let response; try { response = await sendMessageToContentScript({ entryXdr, accountToSign, + networkPassphrase: opts?.networkPassphrase, type: EXTERNAL_SERVICE_TYPES.SUBMIT_AUTH_ENTRY, }); } catch (e) { console.error(e); + return { + signedAuthEntry: null, + signerAddress: "", + error: FreighterApiInternalError, + }; } - const { signedAuthEntry, error } = response; + const { signedAuthEntry, signerAddress } = response; - if (error) { - throw error; - } - return signedAuthEntry; + return { + signedAuthEntry: signedAuthEntry || null, + signerAddress, + error: response?.apiError, + }; }; -export const requestNetwork = async (): Promise => { - let response = { network: "", error: "" }; +export const requestNetwork = async (): Promise<{ + network: string; + networkPassphrase: string; + error?: FreighterApiError; +}> => { + let response; try { response = await sendMessageToContentScript({ - type: EXTERNAL_SERVICE_TYPES.REQUEST_NETWORK, + type: EXTERNAL_SERVICE_TYPES.REQUEST_NETWORK_DETAILS, }); } catch (e) { console.error(e); } - const { network, error } = response; + const { networkDetails } = response || { + networkDetails: { network: "", networkPassphrase: "" }, + }; - if (error) { - throw error; - } - return network; + return { + network: networkDetails?.network, + networkPassphrase: networkDetails?.networkPassphrase, + error: response?.apiError, + }; }; export const requestNetworkDetails = async (): Promise<{ @@ -165,40 +196,43 @@ export const requestNetworkDetails = async (): Promise<{ networkUrl: string; networkPassphrase: string; sorobanRpcUrl?: string; + error?: FreighterApiError; }> => { - let response = { + let response; + try { + response = await sendMessageToContentScript({ + type: EXTERNAL_SERVICE_TYPES.REQUEST_NETWORK_DETAILS, + }); + } catch (e) { + console.error(e); + } + + const { networkDetails, apiError } = response || { networkDetails: { network: "", networkName: "", networkUrl: "", networkPassphrase: "", sorobanRpcUrl: undefined, + apiError: "", } as NetworkDetails, - error: "", }; - try { - response = await sendMessageToContentScript({ - type: EXTERNAL_SERVICE_TYPES.REQUEST_NETWORK_DETAILS, - }); - } catch (e) { - console.error(e); - } - const { networkDetails, error } = response; - const { + const { network, networkUrl, networkPassphrase, sorobanRpcUrl } = + networkDetails; + + return { network, networkUrl, networkPassphrase, sorobanRpcUrl, - } = networkDetails; - - if (error) { - throw error; - } - return { network, networkUrl, networkPassphrase, sorobanRpcUrl }; + error: apiError, + }; }; -export const requestConnectionStatus = async (): Promise => { +export const requestConnectionStatus = async (): Promise<{ + isConnected: boolean; +}> => { let response = { isConnected: false, }; @@ -211,13 +245,14 @@ export const requestConnectionStatus = async (): Promise => { console.error(e); } - return response.isConnected; + return { isConnected: response.isConnected }; }; -export const requestAllowedStatus = async (): Promise => { - let response = { - isAllowed: false, - }; +export const requestAllowedStatus = async (): Promise<{ + isAllowed: boolean; + error?: FreighterApiError; +}> => { + let response; try { response = await sendMessageToContentScript({ @@ -227,14 +262,16 @@ export const requestAllowedStatus = async (): Promise => { console.error(e); } - return response.isAllowed; + const { isAllowed } = response || { isAllowed: false }; + + return { isAllowed, error: response?.apiError }; }; -export const setAllowedStatus = async (): Promise => { - let response = { - isAllowed: false, - error: "", - }; +export const setAllowedStatus = async (): Promise<{ + isAllowed: boolean; + error?: FreighterApiError; +}> => { + let response; try { response = await sendMessageToContentScript({ @@ -244,28 +281,9 @@ export const setAllowedStatus = async (): Promise => { console.error(e); } - const { isAllowed, error } = response; - - if (error) { - throw error; - } - return isAllowed; -}; - -export const requestUserInfo = async (): Promise => { - let response = { userInfo: { publicKey: "" }, error: "" }; - try { - response = await sendMessageToContentScript({ - type: EXTERNAL_SERVICE_TYPES.REQUEST_USER_INFO, - }); - } catch (e) { - console.error(e); - } - - const { userInfo, error } = response; + const { isAllowed } = response || { + isAllowed: false, + }; - if (error) { - throw error; - } - return userInfo; + return { isAllowed, error: response?.apiError }; }; diff --git a/@shared/api/helpers/extensionMessaging.ts b/@shared/api/helpers/extensionMessaging.ts index e809a6e3a..7cc85f8be 100644 --- a/@shared/api/helpers/extensionMessaging.ts +++ b/@shared/api/helpers/extensionMessaging.ts @@ -76,3 +76,19 @@ export const sendMessageToBackground = async (msg: Msg): Promise => { return res; }; + +export const FreighterApiNodeError = { + code: -1, + message: "Node environment is not supported", +}; + +export const FreighterApiInternalError = { + code: -1, + message: + "The wallet encountered an internal error. Please try again or contact the wallet if the problem persists.", +}; + +export const FreighterApiDeclinedError = { + code: -4, + message: "The user rejected this request.", +}; diff --git a/@shared/api/internal.ts b/@shared/api/internal.ts index 37a95e367..acda59008 100644 --- a/@shared/api/internal.ts +++ b/@shared/api/internal.ts @@ -476,7 +476,6 @@ export const getAccountIndexerBalances = async ( return { ...data, balances: formattedBalances, - tokensWithNoBalance: contractIds.filter((id) => !balanceIds.includes(id)), }; }; @@ -577,7 +576,6 @@ export const getAccountBalancesStandalone = async ({ balances, isFunded: false, subentryCount, - tokensWithNoBalance: [], }; } @@ -585,7 +583,6 @@ export const getAccountBalancesStandalone = async ({ const tokenIdList = await getTokenIds(network as NETWORKS); const tokenBalances = {} as any; - const tokensWithNoBalance = []; if (tokenIdList.length) { if (!networkDetails.sorobanRpcUrl) { @@ -633,7 +630,6 @@ export const getAccountBalancesStandalone = async ({ }; } catch (e) { console.error(`Token "${tokenId}" missing data on RPC server`); - tokensWithNoBalance.push(tokenId); } } } @@ -642,7 +638,6 @@ export const getAccountBalancesStandalone = async ({ balances: { ...balances, ...tokenBalances }, isFunded, subentryCount, - tokensWithNoBalance: [], }; }; @@ -1194,6 +1189,7 @@ export const saveSettings = async ({ userNotification: { enabled: false, message: "" }, settingsState: SettingsState.IDLE, isSorobanPublicEnabled: false, + isNonSSLEnabled: false, error: "", }; @@ -1215,13 +1211,16 @@ export const saveSettings = async ({ export const saveExperimentalFeatures = async ({ isExperimentalModeEnabled, isHashSigningEnabled, + isNonSSLEnabled, }: { isExperimentalModeEnabled: boolean; isHashSigningEnabled: boolean; + isNonSSLEnabled: boolean; }): Promise => { let response = { isExperimentalModeEnabled: false, isHashSigningEnabled: false, + isNonSSLEnabled: false, networkDetails: MAINNET_NETWORK_DETAILS, networksList: DEFAULT_NETWORKS, experimentalFeaturesState: SettingsState.IDLE, @@ -1232,6 +1231,7 @@ export const saveExperimentalFeatures = async ({ response = await sendMessageToBackground({ isExperimentalModeEnabled, isHashSigningEnabled, + isNonSSLEnabled, type: SERVICE_TYPES.SAVE_EXPERIMENTAL_FEATURES, }); } catch (e) { diff --git a/@shared/api/package.json b/@shared/api/package.json index 31a111375..811a34b31 100644 --- a/@shared/api/package.json +++ b/@shared/api/package.json @@ -8,9 +8,9 @@ "bignumber.js": "^9.1.1", "prettier": "^2.0.5", "stellar-sdk": "yarn:stellar-sdk@^12.1.0", - "stellar-sdk-next": "yarn:stellar-sdk@12.1.0", + "stellar-sdk-next": "yarn:stellar-sdk@^12.1.0", "typescript": "~3.7.2", - "webextension-polyfill": "^0.10.0" + "webextension-polyfill": "^0.12.0" }, "devDependencies": { "@lavamoat/allow-scripts": "^2.3.1", diff --git a/@shared/api/types.ts b/@shared/api/types.ts index 38e030b91..ec4b219b8 100644 --- a/@shared/api/types.ts +++ b/@shared/api/types.ts @@ -22,6 +22,7 @@ export type MigratableAccount = Account & { keyIdIndex: number }; export interface Response { error: string; + apiError: FreighterApiError; messagedId: number; applicationState: APPLICATION_STATE; publicKey: string; @@ -37,9 +38,10 @@ export interface Response { sign: (sourceKeys: {}) => void; }; transactionXDR: string; + signerAddress: string; signedTransaction: string; - signedBlob: string; - signedAuthEntry: string; + signedBlob: Buffer | null; + signedAuthEntry: Buffer | null; source: string; type: SERVICE_TYPES; url: string; @@ -87,6 +89,7 @@ export interface Response { balancesToMigrate: BalanceToMigrate[]; isMergeSelected: boolean; recommendedFee: string; + isNonSSLEnabled: boolean; } export interface BlockedDomains { @@ -103,7 +106,8 @@ export interface BlockedAccount { export interface ExternalRequestBase { network: string; networkPassphrase: string; - accountToSign: string; + accountToSign?: string; + address?: string; type: EXTERNAL_SERVICE_TYPES; } @@ -143,12 +147,14 @@ export interface Preferences { isSafetyValidationEnabled: boolean; isValidatingSafeAssetsEnabled: boolean; networksList: NetworkDetails[]; + isNonSSLEnabled: boolean; error: string; } export interface ExperimentalFeatures { isExperimentalModeEnabled: boolean; isHashSigningEnabled: boolean; + isNonSSLEnabled: boolean; networkDetails: NetworkDetails; networksList: NetworkDetails[]; experimentalFeaturesState: SettingsState; @@ -274,7 +280,6 @@ export type HorizonOperation = Horizon.ServerApi.OperationRecord; export interface AccountBalancesInterface { balances: Balances; - tokensWithNoBalance: string[]; isFunded: boolean | null; subentryCount: number; error?: { horizon: any; soroban: any }; @@ -309,3 +314,9 @@ declare global { freighterApi: { [key: string]: any }; } } + +export interface FreighterApiError { + code: number; + message: string; + ext?: string[]; +} diff --git a/@shared/constants/package.json b/@shared/constants/package.json index 479775da3..5be86f2ac 100644 --- a/@shared/constants/package.json +++ b/@shared/constants/package.json @@ -4,7 +4,7 @@ "version": "1.0.0", "dependencies": { "stellar-sdk": "yarn:stellar-sdk@^12.1.0", - "stellar-sdk-next": "yarn:stellar-sdk@12.1.0", + "stellar-sdk-next": "yarn:stellar-sdk@^12.1.0", "typescript": "~3.7.2" }, "devDependencies": { diff --git a/@shared/helpers/package.json b/@shared/helpers/package.json index 5ae7d864d..5b0f2d3ce 100644 --- a/@shared/helpers/package.json +++ b/@shared/helpers/package.json @@ -5,7 +5,7 @@ "dependencies": { "bignumber.js": "^9.1.1", "stellar-sdk": "yarn:stellar-sdk@^12.1.0", - "stellar-sdk-next": "yarn:stellar-sdk@12.1.0", + "stellar-sdk-next": "yarn:stellar-sdk@^12.1.0", "typescript": "~3.7.2" }, "devDependencies": { diff --git a/@stellar/freighter-api/package.json b/@stellar/freighter-api/package.json index 26eb5da0f..088169c0a 100644 --- a/@stellar/freighter-api/package.json +++ b/@stellar/freighter-api/package.json @@ -1,6 +1,6 @@ { "name": "@stellar/freighter-api", - "version": "2.0.0", + "version": "3.0.0", "license": "Apache-2.0", "author": "Stellar Development Foundation ", "description": "Utility functions to interact with Freighter extension", diff --git a/@stellar/freighter-api/src/__tests__/getAddress.test.js b/@stellar/freighter-api/src/__tests__/getAddress.test.js new file mode 100644 index 000000000..4ae4dcdd6 --- /dev/null +++ b/@stellar/freighter-api/src/__tests__/getAddress.test.js @@ -0,0 +1,22 @@ +import * as extensionMessaging from "@shared/api/helpers/extensionMessaging"; +import { getAddress } from "../getAddress"; + +describe("getPublicKey", () => { + it("returns a publicKey", async () => { + const TEST_KEY = "100"; + extensionMessaging.sendMessageToContentScript = jest + .fn() + .mockReturnValue({ publicKey: TEST_KEY }); + + const publicKey = await getAddress(); + expect(publicKey).toEqual({ address: TEST_KEY }); + }); + it("returns an error", async () => { + const TEST_ERROR = "Error!"; + extensionMessaging.sendMessageToContentScript = jest + .fn() + .mockReturnValue({ apiError: TEST_ERROR }); + const publicKey = await getAddress(); + expect(publicKey).toEqual({ address: "", error: TEST_ERROR }); + }); +}); diff --git a/@stellar/freighter-api/src/__tests__/getNetwork.test.js b/@stellar/freighter-api/src/__tests__/getNetwork.test.js index f483fb818..d43e500e7 100644 --- a/@stellar/freighter-api/src/__tests__/getNetwork.test.js +++ b/@stellar/freighter-api/src/__tests__/getNetwork.test.js @@ -1,20 +1,24 @@ -import * as apiExternal from "@shared/api/external"; +import * as extensionMessaging from "@shared/api/helpers/extensionMessaging"; import { getNetwork } from "../getNetwork"; -jest.mock("@shared/api/external"); - describe("getNetwork", () => { it("returns a network", async () => { - const TEST_NETWORK = "PUBLIC"; - apiExternal.requestNetwork = jest.fn().mockReturnValue(TEST_NETWORK); + extensionMessaging.sendMessageToContentScript = jest.fn().mockReturnValue({ + networkDetails: { network: "foo", networkPassphrase: "baz" }, + }); const network = await getNetwork(); - expect(network).toBe(TEST_NETWORK); + expect(network).toEqual({ network: "foo", networkPassphrase: "baz" }); }); - it("throws an error", () => { - const TEST_ERROR = "Error!"; - apiExternal.requestNetwork = jest.fn().mockImplementation(() => { - throw TEST_ERROR; + it("returns an error", async () => { + extensionMessaging.sendMessageToContentScript = jest + .fn() + .mockReturnValue({ apiError: "error" }); + const network = await getNetwork(); + + expect(network).toEqual({ + network: "", + networkPassphrase: "", + error: "error", }); - expect(getNetwork).toThrowError(TEST_ERROR); }); }); diff --git a/@stellar/freighter-api/src/__tests__/getPublicKey.test.js b/@stellar/freighter-api/src/__tests__/getPublicKey.test.js deleted file mode 100644 index eaaf826c3..000000000 --- a/@stellar/freighter-api/src/__tests__/getPublicKey.test.js +++ /dev/null @@ -1,18 +0,0 @@ -import * as apiExternal from "@shared/api/external"; -import { getPublicKey } from "../getPublicKey"; - -describe("getPublicKey", () => { - it("returns a publicKey", async () => { - const TEST_KEY = "100"; - apiExternal.requestPublicKey = jest.fn().mockReturnValue(TEST_KEY); - const publicKey = await getPublicKey(); - expect(publicKey).toBe(TEST_KEY); - }); - it("throws an error", () => { - const TEST_ERROR = "Error!"; - apiExternal.requestPublicKey = jest.fn().mockImplementation(() => { - throw TEST_ERROR; - }); - expect(getPublicKey).toThrowError(TEST_ERROR); - }); -}); diff --git a/@stellar/freighter-api/src/__tests__/index.test.js b/@stellar/freighter-api/src/__tests__/index.test.js index 9986bc681..13de21a07 100644 --- a/@stellar/freighter-api/src/__tests__/index.test.js +++ b/@stellar/freighter-api/src/__tests__/index.test.js @@ -3,9 +3,9 @@ import FreighterAPI from "../index"; describe("freighter API", () => { it("has keys", () => { expect(typeof FreighterAPI.isConnected).toBe("function"); - expect(typeof FreighterAPI.getPublicKey).toBe("function"); + expect(typeof FreighterAPI.getAddress).toBe("function"); expect(typeof FreighterAPI.signTransaction).toBe("function"); - expect(typeof FreighterAPI.signBlob).toBe("function"); + expect(typeof FreighterAPI.signMessage).toBe("function"); expect(typeof FreighterAPI.signAuthEntry).toBe("function"); }); }); diff --git a/@stellar/freighter-api/src/__tests__/signAuthEntry.test.js b/@stellar/freighter-api/src/__tests__/signAuthEntry.test.js new file mode 100644 index 000000000..a89a28351 --- /dev/null +++ b/@stellar/freighter-api/src/__tests__/signAuthEntry.test.js @@ -0,0 +1,25 @@ +import * as extensionMessaging from "@shared/api/helpers/extensionMessaging"; + +import { signAuthEntry } from "../signAuthEntry"; + +describe("signAuthEntry", () => { + it("returns a signed auth entry", async () => { + extensionMessaging.sendMessageToContentScript = jest + .fn() + .mockReturnValue({ signedAuthEntry: "foo", signerAddress: "baz" }); + const authEntry = await signAuthEntry(); + expect(authEntry).toEqual({ signedAuthEntry: "foo", signerAddress: "baz" }); + }); + it("returns a generic error", async () => { + extensionMessaging.sendMessageToContentScript = jest + .fn() + .mockReturnValue({ apiError: "baz" }); + const authEntry = await signAuthEntry(); + + expect(authEntry).toEqual({ + signedAuthEntry: null, + signerAddress: "", + error: "baz", + }); + }); +}); diff --git a/@stellar/freighter-api/src/__tests__/signBlob.test.js b/@stellar/freighter-api/src/__tests__/signBlob.test.js deleted file mode 100644 index 272b08c10..000000000 --- a/@stellar/freighter-api/src/__tests__/signBlob.test.js +++ /dev/null @@ -1,19 +0,0 @@ -import * as apiExternal from "@shared/api/external"; -import { signBlob } from "../signBlob"; - -describe("signBlob", () => { - it("returns a signed blob", async () => { - const TEST_BLOB = atob("AAA"); - apiExternal.submitBlob = jest.fn().mockReturnValue(TEST_BLOB); - const blob = await signBlob(); - expect(blob).toBe(TEST_BLOB); - }); - - it("throws a generic error", () => { - const TEST_ERROR = "Error!"; - apiExternal.submitBlob = jest.fn().mockImplementation(() => { - throw TEST_ERROR; - }); - expect(signBlob).toThrowError(TEST_ERROR); - }); -}); diff --git a/@stellar/freighter-api/src/__tests__/signMessage.test.js b/@stellar/freighter-api/src/__tests__/signMessage.test.js new file mode 100644 index 000000000..5adfbbf3b --- /dev/null +++ b/@stellar/freighter-api/src/__tests__/signMessage.test.js @@ -0,0 +1,27 @@ +import * as extensionMessaging from "@shared/api/helpers/extensionMessaging"; +import { signMessage } from "../signMessage"; + +describe("signMessage", () => { + it("returns a signed message", async () => { + const TEST_BLOB = { signedBlob: "foo", signerAddress: "bar" }; + extensionMessaging.sendMessageToContentScript = jest + .fn() + .mockImplementationOnce(() => TEST_BLOB); + const blob = await signMessage(); + expect(blob).toEqual({ signedMessage: "foo", signerAddress: "bar" }); + }); + + it("returns a generic error", async () => { + extensionMessaging.sendMessageToContentScript = jest + .fn() + .mockImplementationOnce(() => ({ + apiError: "error", + })); + const msg = await signMessage(); + expect(msg).toEqual({ + signedMessage: null, + signerAddress: "", + error: "error", + }); + }); +}); diff --git a/@stellar/freighter-api/src/__tests__/signTransaction.test.js b/@stellar/freighter-api/src/__tests__/signTransaction.test.js index 708e19858..13e8cbde3 100644 --- a/@stellar/freighter-api/src/__tests__/signTransaction.test.js +++ b/@stellar/freighter-api/src/__tests__/signTransaction.test.js @@ -1,18 +1,30 @@ -import * as apiExternal from "@shared/api/external"; +import * as extensionMessaging from "@shared/api/helpers/extensionMessaging"; + import { signTransaction } from "../signTransaction"; describe("signTransaction", () => { it("returns a transaction", async () => { const TEST_TRANSACTION = "AAA"; - apiExternal.submitTransaction = jest.fn().mockReturnValue(TEST_TRANSACTION); + extensionMessaging.sendMessageToContentScript = jest.fn().mockReturnValue({ + signedTransaction: TEST_TRANSACTION, + signerAddress: "baz", + }); const transaction = await signTransaction(); - expect(transaction).toBe(TEST_TRANSACTION); + expect(transaction).toEqual({ + signedTxXdr: TEST_TRANSACTION, + signerAddress: "baz", + }); }); - it("throws a generic error", () => { - const TEST_ERROR = "Error!"; - apiExternal.submitTransaction = jest.fn().mockImplementation(() => { - throw TEST_ERROR; + it("returns a generic error", async () => { + extensionMessaging.sendMessageToContentScript = jest + .fn() + .mockReturnValue({ apiError: "baz" }); + const transaction = await signTransaction(); + + expect(transaction).toEqual({ + signedTxXdr: "", + signerAddress: "", + error: "baz", }); - expect(signTransaction).toThrowError(TEST_ERROR); }); }); diff --git a/@stellar/freighter-api/src/getAddress.ts b/@stellar/freighter-api/src/getAddress.ts new file mode 100644 index 000000000..2d44c6b85 --- /dev/null +++ b/@stellar/freighter-api/src/getAddress.ts @@ -0,0 +1,22 @@ +import { requestPublicKey } from "@shared/api/external"; +import { FreighterApiError } from "@shared/api/types"; +import { FreighterApiNodeError } from "@shared/api/helpers/extensionMessaging"; +import { isBrowser } from "."; + +export const getAddress = async (): Promise< + { address: string } & { error?: FreighterApiError } +> => { + let address = ""; + if (isBrowser) { + const req = await requestPublicKey(); + address = req.publicKey; + + if (req.error) { + return { address, error: req.error }; + } + + return { address }; + } + + return { address, error: FreighterApiNodeError }; +}; diff --git a/@stellar/freighter-api/src/getNetwork.ts b/@stellar/freighter-api/src/getNetwork.ts index 5ca49543e..e3c9c6711 100644 --- a/@stellar/freighter-api/src/getNetwork.ts +++ b/@stellar/freighter-api/src/getNetwork.ts @@ -1,5 +1,22 @@ import { requestNetwork } from "@shared/api/external"; +import { FreighterApiError } from "@shared/api/types"; +import { FreighterApiNodeError } from "@shared/api/helpers/extensionMessaging"; import { isBrowser } from "."; -export const getNetwork = (): Promise => - isBrowser ? requestNetwork() : Promise.resolve(""); +export const getNetwork = async (): Promise< + { network: string; networkPassphrase: string } & { + error?: FreighterApiError; + } +> => { + if (isBrowser) { + const req = await requestNetwork(); + + if (req.error) { + return { network: "", networkPassphrase: "", error: req.error }; + } + + return { network: req.network, networkPassphrase: req.networkPassphrase }; + } + + return { network: "", networkPassphrase: "", error: FreighterApiNodeError }; +}; diff --git a/@stellar/freighter-api/src/getNetworkDetails.ts b/@stellar/freighter-api/src/getNetworkDetails.ts index 43f2ac65e..aafe3d90b 100644 --- a/@stellar/freighter-api/src/getNetworkDetails.ts +++ b/@stellar/freighter-api/src/getNetworkDetails.ts @@ -1,17 +1,40 @@ import { requestNetworkDetails } from "@shared/api/external"; +import { FreighterApiError } from "@shared/api/types"; +import { FreighterApiNodeError } from "@shared/api/helpers/extensionMessaging"; import { isBrowser } from "."; -export const getNetworkDetails = (): Promise<{ - network: string; - networkUrl: string; - networkPassphrase: string; - sorobanRpcUrl?: string; -}> => - isBrowser - ? requestNetworkDetails() - : Promise.resolve({ +export const getNetworkDetails = async (): Promise< + { + network: string; + networkUrl: string; + networkPassphrase: string; + sorobanRpcUrl?: string; + } & { error?: FreighterApiError } +> => { + if (isBrowser) { + const req = await requestNetworkDetails(); + + if (req.error) { + return { network: "", networkUrl: "", networkPassphrase: "", - sorobanRpcUrl: "", - }); + error: req.error, + }; + } + + return { + network: req.network, + networkUrl: req.networkUrl, + networkPassphrase: req.networkPassphrase, + sorobanRpcUrl: req.sorobanRpcUrl, + }; + } + + return { + network: "", + networkUrl: "", + networkPassphrase: "", + error: FreighterApiNodeError, + }; +}; diff --git a/@stellar/freighter-api/src/getPublicKey.ts b/@stellar/freighter-api/src/getPublicKey.ts deleted file mode 100644 index 57c133260..000000000 --- a/@stellar/freighter-api/src/getPublicKey.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { requestPublicKey } from "@shared/api/external"; -import { isBrowser } from "."; - -export const getPublicKey = (): Promise => - isBrowser ? requestPublicKey() : Promise.resolve(""); diff --git a/@stellar/freighter-api/src/getUserInfo.ts b/@stellar/freighter-api/src/getUserInfo.ts deleted file mode 100644 index 52e779749..000000000 --- a/@stellar/freighter-api/src/getUserInfo.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { requestUserInfo } from "@shared/api/external"; -import { isBrowser } from "."; - -export const getUserInfo = (): Promise<{ publicKey: string }> => - isBrowser ? requestUserInfo() : Promise.resolve({ publicKey: "" }); diff --git a/@stellar/freighter-api/src/index.ts b/@stellar/freighter-api/src/index.ts index 8b7f8363c..e9aebeda3 100644 --- a/@stellar/freighter-api/src/index.ts +++ b/@stellar/freighter-api/src/index.ts @@ -1,40 +1,40 @@ -import { getPublicKey } from "./getPublicKey"; +import { getAddress } from "./getAddress"; import { signTransaction } from "./signTransaction"; -import { signBlob } from "./signBlob"; +import { signMessage } from "./signMessage"; import { signAuthEntry } from "./signAuthEntry"; import { isConnected } from "./isConnected"; import { getNetwork } from "./getNetwork"; import { getNetworkDetails } from "./getNetworkDetails"; import { isAllowed } from "./isAllowed"; import { setAllowed } from "./setAllowed"; -import { getUserInfo } from "./getUserInfo"; import { requestAccess } from "./requestAccess"; +import { WatchWalletChanges } from "./watchWalletChanges"; export const isBrowser = typeof window !== "undefined"; export { - getPublicKey, + getAddress, signTransaction, - signBlob, + signMessage, signAuthEntry, isConnected, getNetwork, getNetworkDetails, isAllowed, setAllowed, - getUserInfo, requestAccess, + WatchWalletChanges, }; export default { - getPublicKey, + getAddress, signTransaction, - signBlob, + signMessage, signAuthEntry, isConnected, getNetwork, getNetworkDetails, isAllowed, setAllowed, - getUserInfo, requestAccess, + WatchWalletChanges, }; diff --git a/@stellar/freighter-api/src/isAllowed.ts b/@stellar/freighter-api/src/isAllowed.ts index 68578431c..2cd7631f8 100644 --- a/@stellar/freighter-api/src/isAllowed.ts +++ b/@stellar/freighter-api/src/isAllowed.ts @@ -1,5 +1,22 @@ import { requestAllowedStatus } from "@shared/api/external"; +import { FreighterApiError } from "@shared/api/types"; +import { FreighterApiNodeError } from "@shared/api/helpers/extensionMessaging"; import { isBrowser } from "."; -export const isAllowed = (): Promise => - isBrowser ? requestAllowedStatus() : Promise.resolve(false); +export const isAllowed = async (): Promise< + { isAllowed: boolean } & { error?: FreighterApiError } +> => { + let isAllowed = false; + if (isBrowser) { + const req = await requestAllowedStatus(); + isAllowed = req.isAllowed; + + if (req.error) { + return { isAllowed, error: req.error }; + } + + return { isAllowed }; + } + + return { isAllowed, error: FreighterApiNodeError }; +}; diff --git a/@stellar/freighter-api/src/isConnected.ts b/@stellar/freighter-api/src/isConnected.ts index d62f633d3..99e388a54 100644 --- a/@stellar/freighter-api/src/isConnected.ts +++ b/@stellar/freighter-api/src/isConnected.ts @@ -1,12 +1,18 @@ import { requestConnectionStatus } from "@shared/api/external"; +import { FreighterApiError } from "@shared/api/types"; +import { FreighterApiNodeError } from "@shared/api/helpers/extensionMessaging"; import { isBrowser } from "."; -export const isConnected = (): Promise => { - if (!isBrowser) return Promise.resolve(false); +export const isConnected = async (): Promise< + { isConnected: boolean } & { error?: FreighterApiError } +> => { + if (isBrowser) { + if (window.freighter) { + return Promise.resolve({ isConnected: window.freighter }); + } - if (window.freighter) { - return Promise.resolve(window.freighter); + return requestConnectionStatus(); } - return requestConnectionStatus(); + return { isConnected: false, error: FreighterApiNodeError }; }; diff --git a/@stellar/freighter-api/src/requestAccess.ts b/@stellar/freighter-api/src/requestAccess.ts index d94c3a74d..2dc85e22b 100644 --- a/@stellar/freighter-api/src/requestAccess.ts +++ b/@stellar/freighter-api/src/requestAccess.ts @@ -1,5 +1,22 @@ import { requestAccess as requestAccessApi } from "@shared/api/external"; +import { FreighterApiError } from "@shared/api/types"; +import { FreighterApiNodeError } from "@shared/api/helpers/extensionMessaging"; import { isBrowser } from "."; -export const requestAccess = (): Promise => - isBrowser ? requestAccessApi() : Promise.resolve(""); +export const requestAccess = async (): Promise< + { address: string } & { error?: FreighterApiError } +> => { + let address = ""; + if (isBrowser) { + const req = await requestAccessApi(); + address = req.publicKey; + + if (req.error) { + return { address, error: req.error }; + } + + return { address }; + } + + return { address, error: FreighterApiNodeError }; +}; diff --git a/@stellar/freighter-api/src/setAllowed.ts b/@stellar/freighter-api/src/setAllowed.ts index ce5e6bf88..e668876e6 100644 --- a/@stellar/freighter-api/src/setAllowed.ts +++ b/@stellar/freighter-api/src/setAllowed.ts @@ -1,5 +1,22 @@ import { setAllowedStatus } from "@shared/api/external"; +import { FreighterApiError } from "@shared/api/types"; +import { FreighterApiNodeError } from "@shared/api/helpers/extensionMessaging"; import { isBrowser } from "."; -export const setAllowed = (): Promise => - isBrowser ? setAllowedStatus() : Promise.resolve(false); +export const setAllowed = async (): Promise< + { isAllowed: boolean } & { error?: FreighterApiError } +> => { + let isAllowed = false; + if (isBrowser) { + const req = await setAllowedStatus(); + isAllowed = req.isAllowed; + + if (req.error) { + return { isAllowed, error: req.error }; + } + + return { isAllowed }; + } + + return { isAllowed, error: FreighterApiNodeError }; +}; diff --git a/@stellar/freighter-api/src/signAuthEntry.ts b/@stellar/freighter-api/src/signAuthEntry.ts index dd14cdf8f..ecc0e5d38 100644 --- a/@stellar/freighter-api/src/signAuthEntry.ts +++ b/@stellar/freighter-api/src/signAuthEntry.ts @@ -1,10 +1,35 @@ import { submitAuthEntry } from "@shared/api/external"; +import { FreighterApiError } from "@shared/api/types"; +import { FreighterApiNodeError } from "@shared/api/helpers/extensionMessaging"; import { isBrowser } from "."; -export const signAuthEntry = ( +export const signAuthEntry = async ( entryXdr: string, opts?: { - accountToSign?: string; + networkPassphrase?: string; + address?: string; } -): Promise => - isBrowser ? submitAuthEntry(entryXdr, opts) : Promise.resolve(""); +): Promise< + { signedAuthEntry: Buffer | null; signerAddress: string } & { + error?: FreighterApiError; + } +> => { + if (isBrowser) { + const req = await submitAuthEntry(entryXdr, opts); + + if (req.error) { + return { signedAuthEntry: null, signerAddress: "", error: req.error }; + } + + return { + signedAuthEntry: req.signedAuthEntry, + signerAddress: req.signerAddress, + }; + } + + return { + signedAuthEntry: null, + signerAddress: "", + error: FreighterApiNodeError, + }; +}; diff --git a/@stellar/freighter-api/src/signBlob.ts b/@stellar/freighter-api/src/signBlob.ts deleted file mode 100644 index bd3984918..000000000 --- a/@stellar/freighter-api/src/signBlob.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { submitBlob } from "@shared/api/external"; -import { isBrowser } from "."; - -export const signBlob = ( - blob: string, - opts?: { - accountToSign?: string; - } -): Promise => - isBrowser ? submitBlob(blob, opts) : Promise.resolve(""); diff --git a/@stellar/freighter-api/src/signMessage.ts b/@stellar/freighter-api/src/signMessage.ts new file mode 100644 index 000000000..34b77bde3 --- /dev/null +++ b/@stellar/freighter-api/src/signMessage.ts @@ -0,0 +1,35 @@ +import { submitMessage } from "@shared/api/external"; +import { FreighterApiError } from "@shared/api/types"; +import { FreighterApiNodeError } from "@shared/api/helpers/extensionMessaging"; +import { isBrowser } from "."; + +export const signMessage = async ( + message: string, + opts?: { + networkPassphrase?: string; + address?: string; + } +): Promise< + { signedMessage: Buffer | null; signerAddress: string } & { + error?: FreighterApiError; + } +> => { + if (isBrowser) { + const req = await submitMessage(message, opts); + + if (req.error) { + return { signedMessage: null, signerAddress: "", error: req.error }; + } + + return { + signedMessage: req.signedMessage, + signerAddress: req.signerAddress, + }; + } + + return { + signedMessage: null, + signerAddress: "", + error: FreighterApiNodeError, + }; +}; diff --git a/@stellar/freighter-api/src/signTransaction.ts b/@stellar/freighter-api/src/signTransaction.ts index 96ae11e95..1a0182fba 100644 --- a/@stellar/freighter-api/src/signTransaction.ts +++ b/@stellar/freighter-api/src/signTransaction.ts @@ -1,12 +1,31 @@ import { submitTransaction } from "@shared/api/external"; +import { FreighterApiError } from "@shared/api/types"; +import { FreighterApiNodeError } from "@shared/api/helpers/extensionMessaging"; import { isBrowser } from "."; -export const signTransaction = ( +export const signTransaction = async ( transactionXdr: string, opts?: { - network?: string; networkPassphrase?: string; - accountToSign?: string; + address?: string; } -): Promise => - isBrowser ? submitTransaction(transactionXdr, opts) : Promise.resolve(""); +): Promise< + { signedTxXdr: string; signerAddress: string } & { + error?: FreighterApiError; + } +> => { + if (isBrowser) { + const req = await submitTransaction(transactionXdr, opts); + + if (req.error) { + return { signedTxXdr: "", signerAddress: "", error: req.error }; + } + + return { + signedTxXdr: req.signedTransaction, + signerAddress: req.signerAddress, + }; + } + + return { signedTxXdr: "", signerAddress: "", error: FreighterApiNodeError }; +}; diff --git a/@stellar/freighter-api/src/watchWalletChanges.ts b/@stellar/freighter-api/src/watchWalletChanges.ts new file mode 100644 index 000000000..68e52899b --- /dev/null +++ b/@stellar/freighter-api/src/watchWalletChanges.ts @@ -0,0 +1,77 @@ +import { requestPublicKey } from "@shared/api/external"; +import { requestNetworkDetails } from "@shared/api/external"; +import { FreighterApiNodeError } from "@shared/api/helpers/extensionMessaging"; +import { FreighterApiError } from "@shared/api/types"; +import { isBrowser } from "."; + +interface CallbackParams { + address: string; + network: string; + networkPassphrase: string; + error?: FreighterApiError; +} + +export class WatchWalletChanges { + timeout: number; + currentAddress: string; + currentNetwork: string; + currentNetworkPassphrase: string; + isRunning: boolean; + + constructor(timeout = 3000) { + this.timeout = timeout; + this.currentAddress = ""; + this.currentNetwork = ""; + this.currentNetworkPassphrase = ""; + this.isRunning = false; + } + + watch(cb: (params: CallbackParams) => void): { error?: FreighterApiError } { + if (!isBrowser) { + return { error: FreighterApiNodeError }; + } + this.isRunning = true; + this.fetchInfo(cb); + + return {}; + } + + fetchInfo = async (cb: (params: CallbackParams) => void) => { + if (!this.isRunning) { + return; + } + const publicKeyReq = await requestPublicKey(); + const networkDetailsReq = await requestNetworkDetails(); + + if (publicKeyReq.error || networkDetailsReq.error) { + cb({ + address: "", + network: "", + networkPassphrase: "", + error: publicKeyReq.error || networkDetailsReq.error, + }); + } + + if ( + this.currentAddress !== publicKeyReq.publicKey || + this.currentNetwork !== networkDetailsReq.network || + this.currentNetworkPassphrase !== networkDetailsReq.networkPassphrase + ) { + this.currentAddress = publicKeyReq.publicKey; + this.currentNetwork = networkDetailsReq.network; + this.currentNetworkPassphrase = networkDetailsReq.networkPassphrase; + + cb({ + address: publicKeyReq.publicKey, + network: networkDetailsReq.network, + networkPassphrase: networkDetailsReq.networkPassphrase, + }); + } + + setTimeout(() => this.fetchInfo(cb), this.timeout); + }; + + stop() { + this.isRunning = false; + } +} diff --git a/docs/docs/guide/usingFreighterWebApp.md b/docs/docs/guide/usingFreighterWebApp.md index 5cdf0741e..0ee7db0a2 100644 --- a/docs/docs/guide/usingFreighterWebApp.md +++ b/docs/docs/guide/usingFreighterWebApp.md @@ -18,7 +18,7 @@ or import just the modules you require: ```javascript import { isConnected, - getPublicKey, + getAddress, signAuthEntry, signTransaction, signBlob, @@ -29,57 +29,61 @@ Now let's dig into what functionality is available to you: ### isConnected -#### `isConnected() -> >` +#### `isConnected() -> >` This function is useful for determining if a user in your application has Freighter installed. -```javascript +```typescript import { isConnected } from "@stellar/freighter-api"; -if (await isConnected()) { +const isAppConnected = await isConnected(); + +if (isAppConnected.isConnected) { alert("User has Freighter!"); } ``` ### isAllowed -#### `isAllowed() -> >` +#### `isAllowed() -> >` This function is useful for determining if a user has previously authorized your app to receive data from Freighter. -```javascript +```typescript import { isAllowed } from "@stellar/freighter-api"; -if (await isAllowed()) { +const isAppAllowed = await isAllowed(); + +if (isAppAllowed.isAllowed) { alert("User has allowed your app!"); } ``` ### setAllowed -#### `setAllowed() -> >` +#### `setAllowed() -> >` If a user has never interacted with your app before, this function will prompt the user to provide your app privileges to receive user data. If and when the user accepts, this function will resolve with a boolean of `true` indicating the app is now on the extension's "Allow list". This means the extension can immediately provide user data without any user action. -```javascript +```typescript import { setAllowed } from "@stellar/freighter-api"; -const isAllowed = await setAllowed(); +const isAppAllowed = await setAllowed(); -if (isAllowed) { +if (isAppAllowed.isAllowed) { alert("Successfully added the app to Freighter's Allow List"); } ``` ### requestAccess -#### `requestAccess() -> >` +#### `requestAccess() -> >` If a user has never interacted with your app before, this function will prompt the user to provide your app privileges to receive the user's public key. If and when the user accepts, this function will resolve with an object containing the public key. Otherwise, it will provide an error. If the user has authorized your application previously, it will be on the extension's "Allow list", meaning the extension can immediately provide the public key without any user action. -```javascript +```typescript import { isConnected, requestAccess, @@ -88,126 +92,56 @@ import { signBlob, } from "@stellar/freighter-api"; -if (await isConnected()) { +const isAppConnected = await isConnected(); + +if ("isConnected" in isAppConnected && isAppConnected.isConnected) { alert("User has Freighter!"); } const retrievePublicKey = async () => { - let publicKey = ""; - let error = ""; - - try { - publicKey = await requestAccess(); - } catch (e) { - error = e; - } + const accessObj = await requestAccess(); - if (error) { - return error; + if (accessObj.error) { + return accessObj.error; + } else { + return accessObj.address; } - - return publicKey; }; const result = retrievePublicKey(); ``` -### getPublicKey +### getAddress -#### `getPublicKey() -> >` +#### `getAddress() -> >` This is a more lightweight version of `requestAccess` above. If the user has authorized your application previously and Freighter is connected, Freighter will simply return the public key. If either one of the above is not true, it will return an empty string. -```javascript -import { getPublicKey } from "@stellar/freighter-api"; +```typescript +import { getAddress } from "@stellar/freighter-api"; const retrievePublicKey = async () => { - let publicKey = ""; - let error = ""; + const addressObj = await getAddress(); - try { - publicKey = await getPublicKey(); - } catch (e) { - error = e; + if (addressObj.error) { + return addressObj.error; + } else { + return addressObj.address; } - - if (error) { - return error; - } - - return publicKey; }; const result = retrievePublicKey(); ``` -### getUserInfo - -#### `getUserInfo() -> >` - -Similar to `getPublicKey` above, this will transmit user data from Freighter to an authorized app. - -_NOTE:_ An important difference between `getUserInfo` and `getPublicKey` is that `getPublicKey` will prompt a user to allow authorization if they had not previously done so. `getUserInfo` will _not_ prompt the user. If your app has not been authorized, or if a user needs to authenticate inside of Freighter, you will simply receive no data. Use with caution as you may need to use other checks to ensure a good UX. See below for an example - -```javascript -import { - isConnected, - isAllowed, - setAllowed, - getUserInfo, - signAuthEntry, - signTransaction, - signBlob, -} from "@stellar/freighter-api"; - -if (await isConnected()) { - alert("User has Freighter!"); -} - -const retrieveUserInfo = async () => { - let userInfo = { publicKey: "" }; - let error = ""; - - try { - userInfo = await getUserInfo(); - } catch (e) { - error = e; - } - - if (error) { - return error; - } - - if (!userInfo.publicKey) { - // we didn't get anything back. Maybe the app hasn't been authorixed? - - const isAllowed = await isAllowed(); - - if (!isAllowed) { - // oh, we forgot to make sure the app is allowed. Let's do that now - await setAllowed(); - - // now, let's try getting that user info again - // it should work now that this app is "allowed" - userInfo = await getUserInfo(); - } - } - - return userInfo.publicKey; -}; - -const result = retrieveUserInfo(); -``` - ### getNetwork -#### `getNetwork() -> >` +#### `getNetwork() -> >` -This function is useful for determining what network the user has configured Freighter to use. Freighter will be configured to either `PUBLIC` or `TESTNET`. +This function is useful for determining what network the user has configured Freighter to use. Freighter will be configured to either `PUBLIC`, `TESTNET`, `FUTURENET`, or `STANDALONE` (for custom networks). -```javascript +```typescript import { isConnected, getNetwork, @@ -216,25 +150,23 @@ import { signBlob, } from "@stellar/freighter-api"; -if (await isConnected()) { +const isAppConnected = await isConnected(); + +if (isAppConnected.isConnected) { alert("User has Freighter!"); } const retrieveNetwork = async () => { - let network = ""; - let error = ""; - - try { - network = await getNetwork(); - } catch (e) { - error = e; + const networkObj = await getNetwork(); + + if (networkObj.error) { + return networkObj.error; + } else { + return { + network: networkObj.network, + networkPassphrase: networkObj.networkPassphrase, + }; } - - if (error) { - return error; - } - - return network; }; const result = retrieveNetwork(); @@ -242,7 +174,7 @@ const result = retrieveNetwork(); ### signTransaction -#### `signTransaction(xdr: string, opts?: { network?: string, networkPassphrase?: string, accountToSign?: string }) -> >` +#### `signTransaction(xdr: string, opts?: { network?: string, networkPassphrase?: string, address?: string }) -> >` This function accepts a transaction XDR string as the first parameter, which it will decode, sign as the user, and then return the signed transaction to your application. @@ -260,21 +192,21 @@ You can also use this `opts` to specify which account's signature you’re reque ### signAuthEntry -#### `signAuthEntry(authEntryXdr: string, opts: { accountToSign: string }) -> >` +#### `signAuthEntry(authEntryXdr: string, opts: { address: string }) -> >` This function accepts an [authorization entry preimage](https://github.com/stellar/js-stellar-base/blob/a9567e5843760bfb6a8b786592046aee4c9d38b2/types/next.d.ts#L6895) as the first parameter and it returns a signed hash of the same authorization entry, which can be added to the [address credentials](https://github.com/stellar/js-stellar-base/blob/a9567e5843760bfb6a8b786592046aee4c9d38b2/types/next.d.ts#L6614) of the same entry. The [`authorizeEntry` helper](https://github.com/stellar/js-stellar-base/blob/e3d6fc3351e7d242b374c7c6057668366364a279/src/auth.js#L97) in stellar base is a good example of how this works. The second parameter is an optional `opts` object where you can specify which account's signature you’re requesting. If Freighter has the public key requested, it will switch to that account. If not, it will alert the user that they do not have the requested account. -### signBlob +### signMessage -#### `signBlob(b64blob: string, opts: { accountToSign: string }) -> >` +#### `signMessage(message: string, opts: { address: string }) -> >` -This function accepts a base64 encoded blob of arbitrary data as the first parameter, which it will decode, sign as the user, and return a Buffer of the signed contents. +This function accepts a string as the first parameter, which it will decode, sign as the user, and return a Buffer of the signed contents. The second parameter is an optional `opts` object where you can specify which account's signature you’re requesting. If Freighter has the public key requested, it will switch to that account. If not, it will alert the user that they do not have the requested account. -```javascript +```typescript import { isConnected, getPublicKey, @@ -282,25 +214,20 @@ import { signBlob, } from "@stellar/freighter-api"; -if (await isConnected()) { +const isAppConnected = await isConnected(); + +if (isAppConnected.isConnected) { alert("User has Freighter!"); } const retrievePublicKey = async () => { - let publicKey = ""; - let error = ""; + const accessObj = await requestAccess(); - try { - publicKey = await getPublicKey(); - } catch (e) { - error = e; + if (accessObj.error) { + throw new Error(accessObj.error.message); + } else { + return accessObj.address; } - - if (error) { - return error; - } - - return publicKey; }; const retrievedPublicKey = retrievePublicKey(); @@ -310,23 +237,16 @@ const userSignTransaction = async ( network: string, signWith: string ) => { - let signedTransaction = ""; - let error = ""; - - try { - signedTransaction = await signTransaction(xdr, { - network, - accountToSign: signWith, - }); - } catch (e) { - error = e; + const signedTransactionRes = await signTransaction(xdr, { + network, + address: signWith, + }); + + if (signedTransactionRes.error) { + throw new Error(signedTransactionRes.error.message); + } else { + return signedTransactionRes.signedTxXdr; } - - if (error) { - return error; - } - - return signedTransaction; }; const xdr = ""; // replace this with an xdr string of the transaction you want to sign @@ -335,7 +255,7 @@ const userSignedTransaction = userSignTransaction(xdr, "TESTNET"); freighter-api will return a signed transaction xdr. Below is an example of how you might submit this signed transaction to Horizon using `stellar-sdk` (https://github.com/stellar/js-stellar-sdk): -```javascript +```typescript import { Server, TransactionBuilder } from "stellar-sdk"; const userSignTransaction = async ( @@ -343,23 +263,16 @@ const userSignTransaction = async ( network: string, signWith: string ) => { - let signedTransaction = ""; - let error = ""; - - try { - signedTransaction = await signTransaction(xdr, { - network, - accountToSign: signWith, - }); - } catch (e) { - error = e; - } - - if (error) { - return error; + const signedTransactionRes = await signTransaction(xdr, { + network, + address: signWith, + }); + + if (signedTransactionRes.error) { + throw new Error(signedTransactionRes.error.message); + } else { + return signedTransactionRes.signedTxXdr; } - - return signedTransaction; }; const xdr = ""; // replace this with an xdr string of the transaction you want to sign @@ -377,3 +290,35 @@ const transactionToSubmit = TransactionBuilder.fromXDR( const response = await server.submitTransaction(transactionToSubmit); ``` + +### WatchWalletChanges + +#### `WatchWalletChanges -> new WatchWalletChanges(timeout?: number)` + +The class `WatchWalletChanges` provides methods to watch changes from Freighter. To use this class, first instantiate with with an optional `timeout` param to determine how often you want to check for changes in the wallet. The default is `3000` ms. + +##### `WatchWalletChanges.watch(callback: ({ address: string; network: string; networkPassphrase; string }) => void)` + +The `watch()` method starts polling the extension for updates. By passing a callback into the method, you can access Freighter's `address`, `network`, and `networkPassphrase`. This method will only emit results when something has changed. + +##### `WatchWalletChanges.stop()` + +The `stop()` method will stop polling Freighter for changes: + +```typescript +import { WatchWalletChanges } from "@stellar/freighter-api"; + +const Watcher = new WatchWalletChanges(1000); + +Watcher.watch((watcherResults) => { + document.querySelector("#address").innerHTML = watcherResults.address; + document.querySelector("#network").innerHTML = watcherResults.network; + document.querySelector("#networkPassphrase").innerHTML = + watcherResults.networkPassphrase; +}); + +setTimeout(() => { + // after 30 seconds, stop watching + Watcher.stop(); +}, 30000); +``` diff --git a/docs/docs/playground/components/GetPublicKeyDemo.tsx b/docs/docs/playground/components/GetAddressDemo.tsx similarity index 55% rename from docs/docs/playground/components/GetPublicKeyDemo.tsx rename to docs/docs/playground/components/GetAddressDemo.tsx index 78cd168e3..371f790c5 100644 --- a/docs/docs/playground/components/GetPublicKeyDemo.tsx +++ b/docs/docs/playground/components/GetAddressDemo.tsx @@ -1,31 +1,28 @@ import React, { useState } from "react"; -import { getPublicKey } from "@stellar/freighter-api"; +import { getAddress } from "@stellar/freighter-api"; import { PlaygroundInput } from "./basics/inputs"; -export const GetPublicKeyDemo = () => { +export const GetAddressDemo = () => { const [publicKeyResult, setPublicKeyResult] = useState(""); const btnHandler = async () => { - let publicKey; - let error = ""; + const address = await getAddress(); - try { - publicKey = await getPublicKey(); - } catch (e) { - error = e; + if (address.error) { + setPublicKeyResult(JSON.stringify(address.error)); + } else { + setPublicKeyResult(address.address); } - - setPublicKeyResult(publicKey || error); }; return (
- What is your public key? + What is your wallet address?
); diff --git a/docs/docs/playground/components/GetNetworkDemo.tsx b/docs/docs/playground/components/GetNetworkDemo.tsx index cff8ed277..5009ed900 100644 --- a/docs/docs/playground/components/GetNetworkDemo.tsx +++ b/docs/docs/playground/components/GetNetworkDemo.tsx @@ -4,18 +4,17 @@ import { PlaygroundInput } from "./basics/inputs"; export const GetNetworkDemo = () => { const [networkResult, setNetworkResult] = useState(""); + const [networkPassphraseResult, setNetworkPhraseResult] = useState(""); const btnHandler = async () => { - let network; - let error = ""; + const network = await getNetwork(); - try { - network = await getNetwork(); - } catch (e) { - error = e; + if (network.error) { + setNetworkResult(JSON.stringify(network.error)); + } else { + setNetworkResult(network.network); + setNetworkPhraseResult(network.networkPassphrase); } - - setNetworkResult(network || error); }; return ( @@ -24,6 +23,10 @@ export const GetNetworkDemo = () => { What network is Freighter using? +
+ What network passphrase is Freighter using? + +
diff --git a/docs/docs/playground/components/GetNetworkDetailsDemo.tsx b/docs/docs/playground/components/GetNetworkDetailsDemo.tsx index 1bed7e2c2..b4fefd87a 100644 --- a/docs/docs/playground/components/GetNetworkDetailsDemo.tsx +++ b/docs/docs/playground/components/GetNetworkDetailsDemo.tsx @@ -6,16 +6,13 @@ export const GetNetworkDetailsDemo = () => { const [networkDetailsResult, setNetworkDetailsResult] = useState(""); const btnHandler = async () => { - let networkDetails; - let error = ""; + const networkDetailsObj = await getNetworkDetails(); - try { - networkDetails = await getNetworkDetails(); - } catch (e) { - error = e; + if (networkDetailsObj.error) { + setNetworkDetailsResult(JSON.stringify(networkDetailsObj.error)); + } else { + setNetworkDetailsResult(JSON.stringify(networkDetailsObj)); } - - setNetworkDetailsResult(JSON.stringify(networkDetails) || error); }; return ( diff --git a/docs/docs/playground/components/GetUserInfoDemo.tsx b/docs/docs/playground/components/GetUserInfoDemo.tsx deleted file mode 100644 index 6c321cca1..000000000 --- a/docs/docs/playground/components/GetUserInfoDemo.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { useState } from "react"; -import { getUserInfo } from "@stellar/freighter-api"; -import { PlaygroundInput } from "./basics/inputs"; - -export const GetUserInfoDemo = () => { - const [userInfoResult, setUserInfoResult] = useState({}); - - const btnHandler = async () => { - let userInfo; - let error = ""; - - try { - userInfo = await getUserInfo(); - } catch (e) { - error = e; - } - - setUserInfoResult(userInfo || error); - }; - - return ( -
-
- What is Freighter's user info? - -
- -
- ); -}; diff --git a/docs/docs/playground/components/IsAllowedDemo.tsx b/docs/docs/playground/components/IsAllowedDemo.tsx index 1cca0d7ed..86841ba37 100644 --- a/docs/docs/playground/components/IsAllowedDemo.tsx +++ b/docs/docs/playground/components/IsAllowedDemo.tsx @@ -5,7 +5,12 @@ import { PlaygroundInput } from "./basics/inputs"; export const IsAllowedDemo = () => { const [isAllowedState, setIsAllowedState] = useState(" "); const btnHandler = async () => { - setIsAllowedState((await isAllowed()).toString()); + const isAllowedRes = await isAllowed(); + if (isAllowedRes.error) { + setIsAllowedState(JSON.stringify(isAllowedRes.error)); + } else { + setIsAllowedState(isAllowedRes.isAllowed.toString()); + } }; return (
diff --git a/docs/docs/playground/components/IsConnectedDemo.tsx b/docs/docs/playground/components/IsConnectedDemo.tsx index 58ae1ff47..01f27215d 100644 --- a/docs/docs/playground/components/IsConnectedDemo.tsx +++ b/docs/docs/playground/components/IsConnectedDemo.tsx @@ -5,7 +5,12 @@ import { PlaygroundInput } from "./basics/inputs"; export const IsConnectedDemo = () => { const [isConnectedState, setIsConnectedState] = useState(" "); const btnHandler = async () => { - setIsConnectedState((await isConnected()).toString()); + const isConnectedRes = await isConnected(); + if (isConnectedRes.error) { + setIsConnectedState(JSON.stringify(isConnectedRes.error)); + } else { + setIsConnectedState(isConnectedRes.isConnected.toString()); + } }; return (
diff --git a/docs/docs/playground/components/RequestAccessDemo.tsx b/docs/docs/playground/components/RequestAccessDemo.tsx index 4c4bf9a0f..380565b7a 100644 --- a/docs/docs/playground/components/RequestAccessDemo.tsx +++ b/docs/docs/playground/components/RequestAccessDemo.tsx @@ -6,16 +6,13 @@ export const RequestAccessDemo = () => { const [publicKeyResult, setPublicKeyResult] = useState(""); const btnHandler = async () => { - let publicKey; - let error = ""; + const access = await requestAccess(); - try { - publicKey = await requestAccess(); - } catch (e) { - error = e; + if (access.error) { + setPublicKeyResult(JSON.stringify(access.error)); + } else { + setPublicKeyResult(access.address); } - - setPublicKeyResult(publicKey || error); }; return ( diff --git a/docs/docs/playground/components/SetAllowedDemo.tsx b/docs/docs/playground/components/SetAllowedDemo.tsx index afd832969..39755c4d1 100644 --- a/docs/docs/playground/components/SetAllowedDemo.tsx +++ b/docs/docs/playground/components/SetAllowedDemo.tsx @@ -5,7 +5,12 @@ import { PlaygroundInput } from "./basics/inputs"; export const SetAllowedDemo = () => { const [allowedState, setAllowedState] = useState(" "); const btnHandler = async () => { - setAllowedState((await setAllowed()).toString()); + const setAllowedRes = await setAllowed(); + if (setAllowedRes.error) { + setAllowedState(JSON.stringify(setAllowedRes.error)); + } else { + setAllowedState(setAllowedRes.isAllowed.toString()); + } }; return (
diff --git a/docs/docs/playground/components/SignAuthEntryDemo.tsx b/docs/docs/playground/components/SignAuthEntryDemo.tsx index e9c630cdf..db1bbdaf4 100644 --- a/docs/docs/playground/components/SignAuthEntryDemo.tsx +++ b/docs/docs/playground/components/SignAuthEntryDemo.tsx @@ -1,25 +1,38 @@ import React, { useState } from "react"; import { signAuthEntry } from "@stellar/freighter-api"; -import { PlaygroundTextarea } from "./basics/inputs"; +import { PlaygroundTextarea, PlaygroundInput } from "./basics/inputs"; export const SignAuthEntryDemo = () => { const [entryXdr, setEntryXdr] = useState(""); + const [networkPassphrase, setNetworkPassphrase] = useState(""); + const [publicKey, setPublicKey] = useState(""); const [result, setResult] = useState(""); + const [signerAddressResult, setSignerAddressResult] = useState(""); const xdrOnChangeHandler = (e: React.ChangeEvent) => { setEntryXdr(e.currentTarget.value); }; + const networkPassphraseOnChangeHandler = ( + e: React.ChangeEvent + ) => { + setNetworkPassphrase(e.currentTarget.value); + }; + const publicKeyOnChangeHandler = (e: React.ChangeEvent) => { + setPublicKey(e.currentTarget.value); + }; const btnHandler = async () => { - let signedAuthEntry; - let error = ""; + const signedAuthEntryObj = await signAuthEntry(entryXdr, { + address: publicKey, + networkPassphrase, + }); - try { - signedAuthEntry = await signAuthEntry(entryXdr); - } catch (e) { - error = e; + if (signedAuthEntryObj.error) { + setResult(JSON.stringify(signedAuthEntryObj.error)); + } else { + setResult(JSON.stringify(signedAuthEntryObj.signedAuthEntry)); + setSignerAddressResult(signedAuthEntryObj.signerAddress); } - setResult(JSON.stringify(signedAuthEntry) || error); }; return ( @@ -28,9 +41,19 @@ export const SignAuthEntryDemo = () => { Enter entry preimage XDR: +
+ Enter network passphrase (optional): + +
+
+ Request signature from specific public key (optional): + +
Result: + Signer address: +
-
- ); -}; diff --git a/docs/docs/playground/components/SignMessageDemo.tsx b/docs/docs/playground/components/SignMessageDemo.tsx new file mode 100644 index 000000000..d4806e6f1 --- /dev/null +++ b/docs/docs/playground/components/SignMessageDemo.tsx @@ -0,0 +1,63 @@ +import React, { useState } from "react"; +import { signMessage } from "@stellar/freighter-api"; +import { PlaygroundTextarea, PlaygroundInput } from "./basics/inputs"; + +export const SignMessageDemo = () => { + const [message, setMessage] = useState(""); + const [networkPassphrase, setNetworkPassphrase] = useState(""); + const [publicKey, setPublicKey] = useState(""); + const [result, setResult] = useState(""); + const [signerAddressResult, setSignerAddressResult] = useState(""); + const networkPassphraseOnChangeHandler = ( + e: React.ChangeEvent + ) => { + setNetworkPassphrase(e.currentTarget.value); + }; + const publicKeyOnChangeHandler = (e: React.ChangeEvent) => { + setPublicKey(e.currentTarget.value); + }; + + const blobOnChangeHandler = (e: React.ChangeEvent) => { + setMessage(e.currentTarget.value); + }; + + const btnHandler = async () => { + const signedMessageObj = await signMessage(message, { + address: publicKey, + networkPassphrase, + }); + + if (signedMessageObj.error) { + setResult(JSON.stringify(signedMessageObj.error)); + } else { + console.log(signedMessageObj); + setResult(JSON.stringify(signedMessageObj.signedMessage)); + setSignerAddressResult(signedMessageObj.signerAddress); + } + }; + return ( +
+
+ Enter message to sign: + +
+
+ Enter network passphrase (optional): + +
+
+ Request signature from specific public key (optional): + +
+
+ Result: + + Signer address: + +
+ +
+ ); +}; diff --git a/docs/docs/playground/components/SignTransactionDemo.tsx b/docs/docs/playground/components/SignTransactionDemo.tsx index f153950b6..a43344656 100644 --- a/docs/docs/playground/components/SignTransactionDemo.tsx +++ b/docs/docs/playground/components/SignTransactionDemo.tsx @@ -4,17 +4,14 @@ import { PlaygroundInput, PlaygroundTextarea } from "./basics/inputs"; export const SignTransactionDemo = () => { const [transactionXdr, setTransactionXdr] = useState(""); - const [network, setNetwork] = useState(""); const [networkPassphrase, setNetworkPassphrase] = useState(""); const [publicKey, setPublicKey] = useState(""); const [transactionResult, setTransactionResult] = useState(""); + const [signerAddressResult, setSignerAddressResult] = useState(""); const xdrOnChangeHandler = (e: React.ChangeEvent) => { setTransactionXdr(e.currentTarget.value); }; - const networkOnChangeHandler = (e: React.ChangeEvent) => { - setNetwork(e.currentTarget.value); - }; const networkPassphraseOnChangeHandler = ( e: React.ChangeEvent ) => { @@ -26,18 +23,18 @@ export const SignTransactionDemo = () => { const btnHandler = async () => { let signedTransaction; - let error = ""; - try { - signedTransaction = await signTransaction(transactionXdr, { - network, - accountToSign: publicKey, - networkPassphrase, - }); - } catch (e) { - error = e; + signedTransaction = await signTransaction(transactionXdr, { + address: publicKey, + networkPassphrase, + }); + + if (signedTransaction.error) { + setTransactionResult(JSON.stringify(signedTransaction.error)); + } else { + setTransactionResult(signedTransaction.signedTxXdr); + setSignerAddressResult(signedTransaction.signerAddress); } - setTransactionResult(signedTransaction || error); }; return (
@@ -45,10 +42,6 @@ export const SignTransactionDemo = () => { Enter transaction XDR: -
- Enter network - "TESTNET"|"PUBLIC"|"FUTURENET" (optional): - -
Enter network passphrase (optional): @@ -60,6 +53,8 @@ export const SignTransactionDemo = () => {
Result: + Signer address: +
+
+ ); +}; diff --git a/docs/docs/playground/getAddress.mdx b/docs/docs/playground/getAddress.mdx new file mode 100644 index 000000000..597303c19 --- /dev/null +++ b/docs/docs/playground/getAddress.mdx @@ -0,0 +1,11 @@ +--- +id: getAddress +title: getAddress +--- + +#### `getAddress()` + +import { GetAddressDemo } from "./components/GetAddressDemo"; + +Test Freighter's `getAddress` method: + diff --git a/docs/docs/playground/getPublicKey.mdx b/docs/docs/playground/getPublicKey.mdx deleted file mode 100644 index c8dfacacc..000000000 --- a/docs/docs/playground/getPublicKey.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -id: getPublicKey -title: getPublicKey ---- - -#### `getPublicKey()` - -import { GetPublicKeyDemo } from "./components/GetPublicKeyDemo"; - -Test Freighter's `getPublicKey` method: - diff --git a/docs/docs/playground/getUserInfo.mdx b/docs/docs/playground/getUserInfo.mdx deleted file mode 100644 index 84f78ce00..000000000 --- a/docs/docs/playground/getUserInfo.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -id: getUserInfo -title: getUserInfo ---- - -#### `getUserInfo()` - -import { GetUserInfoDemo } from "./components/GetUserInfoDemo"; - -Test Freighter's `getUserInfo` method: - diff --git a/docs/docs/playground/signBlob.mdx b/docs/docs/playground/signBlob.mdx deleted file mode 100644 index 6c38b6c29..000000000 --- a/docs/docs/playground/signBlob.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -id: signBlob -title: signBlob ---- - -#### `signBlob(b64blob: string)` - -import { SignBlobDemo } from "./components/SignBlobDemo"; - -Test Freighter's `signBlob` method: - diff --git a/docs/docs/playground/signMessage.mdx b/docs/docs/playground/signMessage.mdx new file mode 100644 index 000000000..ad18e4add --- /dev/null +++ b/docs/docs/playground/signMessage.mdx @@ -0,0 +1,11 @@ +--- +id: signMessage +title: signMessage +--- + +#### `signMessage(message: string)` + +import { SignMessageDemo } from "./components/SignMessageDemo"; + +Test Freighter's `signBlob` method: + diff --git a/docs/docs/playground/watchWalletChanges.mdx b/docs/docs/playground/watchWalletChanges.mdx new file mode 100644 index 000000000..0008989ba --- /dev/null +++ b/docs/docs/playground/watchWalletChanges.mdx @@ -0,0 +1,11 @@ +--- +id: watchWalletChanges +title: WatchWalletChanges +--- + +#### `WatchWalletChangesDemo()` + +import { WatchWalletChangesDemo } from "./components/WatchWalletChangesDemo"; + +Test Freighter's `WatchWalletChangesDemo` method: + diff --git a/docs/package.json b/docs/package.json index 11f45e7bf..3fafd73c5 100644 --- a/docs/package.json +++ b/docs/package.json @@ -23,9 +23,9 @@ ] }, "dependencies": { - "@docusaurus/core": "3.3.2", - "@docusaurus/preset-classic": "3.3.2", - "@stellar/freighter-api": "latest", + "@docusaurus/core": "3.4.0", + "@docusaurus/preset-classic": "3.4.0", + "@stellar/freighter-api": "3.0.0", "clsx": "^1.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/docs/sidebars.js b/docs/sidebars.js index 27697ae7d..eaf81369f 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -4,13 +4,13 @@ const playgroundPaths = [ "isAllowed", "setAllowed", "requestAccess", - "getPublicKey", - "getUserInfo", + "getAddress", "getNetwork", "getNetworkDetails", "signTransaction", "signAuthEntry", - "signBlob", + "signMessage", + "watchWalletChanges", ]; const GUIDE_BASE_PATH = "guide"; diff --git a/extension/e2e-tests/addAsset.test.ts b/extension/e2e-tests/addAsset.test.ts index f8783b79d..53f5ac342 100644 --- a/extension/e2e-tests/addAsset.test.ts +++ b/extension/e2e-tests/addAsset.test.ts @@ -1,87 +1,87 @@ -// import { test, expect, expectPageToHaveScreenshot } from "./test-fixtures"; -// import { loginToTestAccount, PASSWORD } from "./helpers/login"; +import { test, expect, expectPageToHaveScreenshot } from "./test-fixtures"; +import { loginToTestAccount, PASSWORD } from "./helpers/login"; -// test("Adding unverified Soroban token", async ({ page, extensionId }) => { -// test.slow(); -// await loginToTestAccount({ page, extensionId }); +test("Adding unverified Soroban token", async ({ page, extensionId }) => { + test.slow(); + await loginToTestAccount({ page, extensionId }); -// await page.getByText("Manage Assets").click({ force: true }); -// await page.getByPlaceholder("Enter password").fill(PASSWORD); -// await page.getByText("Log In").click({ force: true }); -// await expectPageToHaveScreenshot({ -// page, -// screenshot: "manage-assets-page.png", -// }); -// await expect(page.getByText("Your assets")).toBeVisible(); -// await page.getByText("Add an asset").click({ force: true }); -// await page.getByText("Add manually").click({ force: true }); -// await page -// .getByTestId("search-token-input") -// .fill("CAHX2LUNQ4YKNJTDEFW2LSFOXDAL4QI4736RV52ZUGCIRJK5U7MWQWW6"); -// await expect(page.getByTestId("asset-notification")).toHaveText( -// "Not on your listsFreighter uses asset lists to check assets you interact with. You can define your own assets lists in Settings.", -// ); -// await expect(page.getByTestId("ManageAssetCode")).toHaveText("E2E Token"); -// await expect(page.getByTestId("ManageAssetRowButton")).toHaveText("Add"); -// await page.getByTestId("ManageAssetRowButton").click({ force: true }); + await page.getByText("Manage Assets").click({ force: true }); + await page.getByPlaceholder("Enter password").fill(PASSWORD); + await page.getByText("Log In").click({ force: true }); + await expectPageToHaveScreenshot({ + page, + screenshot: "manage-assets-page.png", + }); + await expect(page.getByText("Your assets")).toBeVisible(); + await page.getByText("Add an asset").click({ force: true }); + await page.getByText("Add manually").click({ force: true }); + await page + .getByTestId("search-token-input") + .fill("CAHX2LUNQ4YKNJTDEFW2LSFOXDAL4QI4736RV52ZUGCIRJK5U7MWQWW6"); + await expect(page.getByTestId("asset-notification")).toHaveText( + "Not on your listsFreighter uses asset lists to check assets you interact with. You can define your own assets lists in Settings.", + ); + await expect(page.getByTestId("ManageAssetCode")).toHaveText("E2E Token"); + await expect(page.getByTestId("ManageAssetRowButton")).toHaveText("Add"); + await page.getByTestId("ManageAssetRowButton").click({ force: true }); -// await expect(page.getByTestId("token-warning-notification")).toHaveText( -// "This asset is not part of an asset list. Please, double-check the asset you’re interacting with and proceed with care. Freighter uses asset lists to check assets you interact with. You can define your own assets lists in Settings.", -// ); -// await expectPageToHaveScreenshot({ -// page, -// screenshot: "manage-assets-unverified-token.png", -// }); -// await page.getByTestId("add-asset").dispatchEvent("click"); -// await expect(page.getByTestId("account-view")).toContainText("100 E2E"); -// }); -// test("Adding Soroban verified token", async ({ page, extensionId }) => { -// const assetsList = await fetch( -// "https://api.stellar.expert/explorer/testnet/asset-list/top50", -// ); -// const assetsListData = await assetsList.json(); -// const verifiedToken = -// assetsListData?.assets[0]?.contract || -// "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA"; + await expect(page.getByTestId("token-warning-notification")).toHaveText( + "This asset is not part of an asset list. Please, double-check the asset you’re interacting with and proceed with care. Freighter uses asset lists to check assets you interact with. You can define your own assets lists in Settings.", + ); + await expectPageToHaveScreenshot({ + page, + screenshot: "manage-assets-unverified-token.png", + }); + await page.getByTestId("add-asset").dispatchEvent("click"); + await expect(page.getByTestId("account-view")).toContainText("100 E2E"); +}); +test("Adding Soroban verified token", async ({ page, extensionId }) => { + const assetsList = await fetch( + "https://api.stellar.expert/explorer/testnet/asset-list/top50", + ); + const assetsListData = await assetsList.json(); + const verifiedToken = + assetsListData?.assets[0]?.contract || + "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA"; -// test.slow(); -// await loginToTestAccount({ page, extensionId }); + test.slow(); + await loginToTestAccount({ page, extensionId }); -// await page.getByText("Manage Assets").click({ force: true }); -// await page.getByPlaceholder("Enter password").fill(PASSWORD); -// await page.getByText("Log In").click({ force: true }); + await page.getByText("Manage Assets").click({ force: true }); + await page.getByPlaceholder("Enter password").fill(PASSWORD); + await page.getByText("Log In").click({ force: true }); -// await expect(page.getByText("Your assets")).toBeVisible(); -// await page.getByText("Add an asset").click({ force: true }); -// await page.getByText("Add manually").click({ force: true }); -// await page.getByTestId("search-token-input").fill(verifiedToken); -// await expect(page.getByTestId("asset-notification")).toHaveText( -// "On your listsFreighter uses asset lists to check assets you interact with. You can define your own assets lists in Settings.", -// ); -// await expect(page.getByTestId("ManageAssetCode")).toHaveText("USDC"); -// await expect(page.getByTestId("ManageAssetRowButton")).toHaveText("Add"); -// await page.getByTestId("ManageAssetRowButton").click({ force: true }); + await expect(page.getByText("Your assets")).toBeVisible(); + await page.getByText("Add an asset").click({ force: true }); + await page.getByText("Add manually").click({ force: true }); + await page.getByTestId("search-token-input").fill(verifiedToken); + await expect(page.getByTestId("asset-notification")).toHaveText( + "On your listsFreighter uses asset lists to check assets you interact with. You can define your own assets lists in Settings.", + ); + await expect(page.getByTestId("ManageAssetCode")).toHaveText("USDC"); + await expect(page.getByTestId("ManageAssetRowButton")).toHaveText("Add"); + await page.getByTestId("ManageAssetRowButton").click({ force: true }); -// await expect(page.getByTestId("token-warning-notification")).toHaveText( -// `This asset is part of the asset lists "StellarExpert Top 50."Freighter uses asset lists to check assets you interact with. You can define your own assets lists in Settings. -// `, -// ); -// await expectPageToHaveScreenshot({ -// page, -// screenshot: "manage-assets-verified-token.png", -// }); -// await page.getByTestId("add-asset").dispatchEvent("click"); -// await expect(page.getByTestId("account-view")).toBeVisible({ -// timeout: 30000, -// }); + await expect(page.getByTestId("token-warning-notification")).toHaveText( + `This asset is part of the asset lists "StellarExpert Top 50."Freighter uses asset lists to check assets you interact with. You can define your own assets lists in Settings. + `, + ); + await expectPageToHaveScreenshot({ + page, + screenshot: "manage-assets-verified-token.png", + }); + await page.getByTestId("add-asset").dispatchEvent("click"); + await expect(page.getByTestId("account-view")).toBeVisible({ + timeout: 30000, + }); -// await page.getByText("Manage Assets").click({ force: true }); -// await page -// .getByTestId("ManageAssetRowButton__ellipsis") -// .click({ force: true }); -// await page.getByText("Remove asset").click({ force: true }); + await page.getByText("Manage Assets").click({ force: true }); + await page + .getByTestId("ManageAssetRowButton__ellipsis") + .click({ force: true }); + await page.getByText("Remove asset").click({ force: true }); -// await expect(page.getByTestId("account-view")).toBeVisible({ -// timeout: 30000, -// }); -// }); + await expect(page.getByTestId("account-view")).toBeVisible({ + timeout: 30000, + }); +}); diff --git a/extension/e2e-tests/helpers/login.ts b/extension/e2e-tests/helpers/login.ts index bbc00fb49..214b61384 100644 --- a/extension/e2e-tests/helpers/login.ts +++ b/extension/e2e-tests/helpers/login.ts @@ -76,7 +76,7 @@ export const loginToTestAccount = async ({ page, extensionId }) => { await page.goto(`chrome-extension://${extensionId}/index.html#/account`); await expect(page.getByTestId("network-selector-open")).toBeVisible({ - timeout: 10000, + timeout: 50000, }); await page.getByTestId("network-selector-open").click(); await page.getByText("Test Net").click(); diff --git a/extension/e2e-tests/sendPayment.test.ts b/extension/e2e-tests/sendPayment.test.ts index 64bee64ed..fee5c8918 100644 --- a/extension/e2e-tests/sendPayment.test.ts +++ b/extension/e2e-tests/sendPayment.test.ts @@ -1,72 +1,72 @@ -// import { test, expect, expectPageToHaveScreenshot } from "./test-fixtures"; -// import { loginAndFund, PASSWORD } from "./helpers/login"; +import { test, expect, expectPageToHaveScreenshot } from "./test-fixtures"; +import { loginAndFund, PASSWORD } from "./helpers/login"; -// test("Send XLM payment", async ({ page, extensionId }) => { -// test.slow(); -// await loginAndFund({ page, extensionId }); -// await page.getByTitle("Send Payment").click({ force: true }); +test("Send XLM payment", async ({ page, extensionId }) => { + test.slow(); + await loginAndFund({ page, extensionId }); + await page.getByTitle("Send Payment").click({ force: true }); -// await expect(page.getByText("Send To")).toBeVisible(); -// await expectPageToHaveScreenshot({ -// page, -// screenshot: "send-payment-to.png", -// }); -// await page -// .getByTestId("send-to-input") -// .fill("GBTYAFHGNZSTE4VBWZYAGB3SRGJEPTI5I4Y22KZ4JTVAN56LESB6JZOF"); -// await page.getByText("Continue").click({ force: true }); + await expect(page.getByText("Send To")).toBeVisible(); + await expectPageToHaveScreenshot({ + page, + screenshot: "send-payment-to.png", + }); + await page + .getByTestId("send-to-input") + .fill("GBTYAFHGNZSTE4VBWZYAGB3SRGJEPTI5I4Y22KZ4JTVAN56LESB6JZOF"); + await page.getByText("Continue").click({ force: true }); -// await expect(page.getByText("Send XLM")).toBeVisible(); -// await expectPageToHaveScreenshot({ -// page, -// screenshot: "send-payment-amount.png", -// }); -// await page.getByTestId("send-amount-amount-input").fill("1"); -// await page.getByText("Continue").click({ force: true }); + await expect(page.getByText("Send XLM")).toBeVisible(); + await expectPageToHaveScreenshot({ + page, + screenshot: "send-payment-amount.png", + }); + await page.getByTestId("send-amount-amount-input").fill("1"); + await page.getByText("Continue").click({ force: true }); -// await expect(page.getByText("Send Settings")).toBeVisible(); -// await expect(page.getByTestId("SendSettingsTransactionFee")).toHaveText( -// /[0-9]/, -// ); -// await expectPageToHaveScreenshot( -// { -// page, -// screenshot: "send-payment-settings.png", -// }, -// { -// mask: [page.locator("[data-testid='SendSettingsTransactionFee']")], -// }, -// ); -// await page.getByText("Review Send").click({ force: true }); + await expect(page.getByText("Send Settings")).toBeVisible(); + await expect(page.getByTestId("SendSettingsTransactionFee")).toHaveText( + /[0-9]/, + ); + await expectPageToHaveScreenshot( + { + page, + screenshot: "send-payment-settings.png", + }, + { + mask: [page.locator("[data-testid='SendSettingsTransactionFee']")], + }, + ); + await page.getByText("Review Send").click({ force: true }); -// await expect(page.getByText("Verification")).toBeVisible(); -// await page.getByPlaceholder("Enter password").fill(PASSWORD); -// await expectPageToHaveScreenshot({ -// page, -// screenshot: "send-payment-password.png", -// }); -// await page.getByText("Submit").click({ force: true }); + await expect(page.getByText("Verification")).toBeVisible(); + await page.getByPlaceholder("Enter password").fill(PASSWORD); + await expectPageToHaveScreenshot({ + page, + screenshot: "send-payment-password.png", + }); + await page.getByText("Submit").click({ force: true }); -// await expect(page.getByText("Confirm Send")).toBeVisible(); -// await expectPageToHaveScreenshot({ -// page, -// screenshot: "send-payment-confirm.png", -// }); -// await page.getByTestId("transaction-details-btn-send").click({ force: true }); + await expect(page.getByText("Confirm Send")).toBeVisible(); + await expectPageToHaveScreenshot({ + page, + screenshot: "send-payment-confirm.png", + }); + await page.getByTestId("transaction-details-btn-send").click({ force: true }); -// await expect(page.getByText("Successfully sent")).toBeVisible({ -// timeout: 20000, -// }); -// await expectPageToHaveScreenshot({ -// page, -// screenshot: "send-payment-sent.png", -// }); + await expect(page.getByText("Successfully sent")).toBeVisible({ + timeout: 20000, + }); + await expectPageToHaveScreenshot({ + page, + screenshot: "send-payment-sent.png", + }); -// await page.getByText("Details").click({ force: true }); -// await expectPageToHaveScreenshot({ -// page, -// screenshot: "send-payment-details.png", -// }); -// await expect(page.getByText("Sent XLM")).toBeVisible(); -// await expect(page.getByTestId("asset-amount")).toContainText("1 XLM"); -// }); + await page.getByText("Details").click({ force: true }); + await expectPageToHaveScreenshot({ + page, + screenshot: "send-payment-details.png", + }); + await expect(page.getByText("Sent XLM")).toBeVisible(); + await expect(page.getByTestId("asset-amount")).toContainText("1 XLM"); +}); diff --git a/extension/package.json b/extension/package.json index d8d98148c..efe6eb5a2 100644 --- a/extension/package.json +++ b/extension/package.json @@ -15,8 +15,8 @@ "test:e2e": "playwright test" }, "dependencies": { - "@ledgerhq/hw-app-str": "^6.27.1", - "@ledgerhq/hw-transport-webusb": "^6.27.1", + "@ledgerhq/hw-app-str": "^7.0.2", + "@ledgerhq/hw-transport-webusb": "^6.29.2", "@reduxjs/toolkit": "1.6.0", "@sentry/browser": "^6.0.0", "@sentry/tracing": "^6.0.0", @@ -27,7 +27,7 @@ "@stellar/typescript-wallet-sdk-km": "^1.0.1", "@testing-library/react": "^14.2.1", "@testing-library/user-event": "^7.1.2", - "@types/chrome": "^0.0.104", + "@types/chrome": "^0.0.269", "@types/history": "^4.7.7", "@types/jsdom": "^16.2.3", "@types/lodash": "^4.14.149", @@ -40,8 +40,8 @@ "@types/react-router-dom": "^5.1.3", "@types/redux": "^3.6.0", "@types/testing-library__jest-dom": "^5.14.5", - "@types/webextension-polyfill": "^0.9.2", - "@types/yup": "^0.29.2", + "@types/webextension-polyfill": "^0.10.7", + "@types/yup": "^0.32.0", "bignumber.js": "^9.1.1", "buffer": "^6.0.3", "classnames": "^2.5.1", @@ -83,16 +83,16 @@ "stellar-hd-wallet": "^0.0.10", "stellar-identicon-js": "^1.0.0", "stellar-sdk": "yarn:stellar-sdk@^12.1.0", - "stellar-sdk-next": "yarn:stellar-sdk@12.1.0", + "stellar-sdk-next": "yarn:stellar-sdk@^12.1.0", "svg-url-loader": "^8.0.0", "tsconfig-paths-webpack-plugin": "^4.1.0", - "tslib": "2.0.0", - "webextension-polyfill": "^0.10.0", - "yup": "^0.29.1" + "tslib": "2.6.3", + "webextension-polyfill": "^0.12.0", + "yup": "^1.4.0" }, "devDependencies": { "@lavamoat/allow-scripts": "^2.3.1", - "@playwright/test": "1.41.0", + "@playwright/test": "1.45.3", "@svgr/webpack": "^8.1.0", "@types/semver": "^7.5.2", "https-browserify": "^1.0.0", diff --git a/extension/src/background/helpers/account.ts b/extension/src/background/helpers/account.ts index bb5ee4d5c..383d33e8e 100644 --- a/extension/src/background/helpers/account.ts +++ b/extension/src/background/helpers/account.ts @@ -12,6 +12,7 @@ import { HAS_ACCOUNT_SUBSCRIPTION, ASSETS_LISTS_ID, IS_HASH_SIGNING_ENABLED_ID, + IS_NON_SSL_ENABLED_ID, } from "constants/localStorageTypes"; import { DEFAULT_NETWORKS, NetworkDetails } from "@shared/constants/stellar"; import { DEFAULT_ASSETS_LISTS } from "@shared/constants/soroban/token"; @@ -150,6 +151,16 @@ export const getAssetsLists = async () => { return assetLists; }; +export const getIsNonSSLEnabled = async () => { + if (!(await localStore.getItem(IS_NON_SSL_ENABLED_ID))) { + await localStore.setItem(IS_NON_SSL_ENABLED_ID, false); + } + const isNonSSLEnabled = + (await localStore.getItem(IS_NON_SSL_ENABLED_ID)) ?? false; + + return isNonSSLEnabled; +}; + export const getIsRpcHealthy = async (networkDetails: NetworkDetails) => { let rpcHealth = { status: "" }; if (isCustomNetwork(networkDetails)) { diff --git a/extension/src/background/helpers/dataStorage.ts b/extension/src/background/helpers/dataStorage.ts index d5bbbc027..1047496f5 100644 --- a/extension/src/background/helpers/dataStorage.ts +++ b/extension/src/background/helpers/dataStorage.ts @@ -9,6 +9,7 @@ import { TOKEN_ID_LIST, ASSETS_LISTS_ID, IS_HASH_SIGNING_ENABLED_ID, + IS_NON_SSL_ENABLED_ID, } from "constants/localStorageTypes"; import { DEFAULT_NETWORKS, @@ -238,6 +239,16 @@ export const addIsHashSigningEnabled = async () => { } }; +export const addIsNonSSLEnabled = async () => { + const localStore = dataStorageAccess(browserLocalStorage); + const storageVersion = (await localStore.getItem(STORAGE_VERSION)) as string; + + if (!storageVersion || semver.lt(storageVersion, "4.2.0")) { + await localStore.setItem(IS_NON_SSL_ENABLED_ID, false); + await migrateDataStorageVersion("4.2.0)"); + } +}; + export const versionedMigration = async () => { // sequentially call migrations in order to enforce smooth schema upgrades @@ -249,6 +260,7 @@ export const versionedMigration = async () => { await resetAccountSubscriptions(); await addAssetsLists(); await addIsHashSigningEnabled(); + await addIsNonSSLEnabled(); }; // Updates storage version diff --git a/extension/src/background/messageListener/freighterApiMessageListener.ts b/extension/src/background/messageListener/freighterApiMessageListener.ts index 64f6e2058..44a281982 100644 --- a/extension/src/background/messageListener/freighterApiMessageListener.ts +++ b/extension/src/background/messageListener/freighterApiMessageListener.ts @@ -11,6 +11,10 @@ import { ExternalRequest as Request, } from "@shared/api/types"; import { stellarSdkServer } from "@shared/api/helpers/stellarSdkServer"; +import { + FreighterApiInternalError, + FreighterApiDeclinedError, +} from "@shared/api/helpers/extensionMessaging"; import { MessageResponder } from "background/types"; import { FlaggedKeys, TransactionInfo } from "types/transactions"; @@ -85,6 +89,7 @@ export const freighterApiMessageListener = ( browser.windows.create({ url: chrome.runtime.getURL(`/index.html#/grant-access?${encodeOrigin}`), ...WINDOW_SETTINGS, + width: 400, }); return new Promise((resolve) => { @@ -95,7 +100,11 @@ export const freighterApiMessageListener = ( resolve({ publicKey: publicKeySelector(sessionStore.getState()) }); } - resolve({ error: "User declined access" }); + resolve({ + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiDeclinedError, + error: FreighterApiDeclinedError.message, + }); }; responseQueue.push(response); @@ -103,274 +112,336 @@ export const freighterApiMessageListener = ( }; const requestPublicKey = async () => { - const publicKey = publicKeySelector(sessionStore.getState()); + try { + const publicKey = publicKeySelector(sessionStore.getState()); - if ((await isSenderAllowed({ sender })) && publicKey) { - // okay, the requester checks out and we have public key, send it - return { publicKey }; - } + if ((await isSenderAllowed({ sender })) && publicKey) { + // okay, the requester checks out and we have public key, send it + return { publicKey }; + } - return { publicKey: "" }; + return { publicKey: "" }; + } catch (e) { + return { + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiInternalError, + error: FreighterApiInternalError.message, + }; + } }; const submitTransaction = async () => { - const { - transactionXdr, - network: _network, - networkPassphrase, - accountToSign, - } = request as ExternalRequestTx; - - const network = - _network === null || !_network - ? MAINNET_NETWORK_DETAILS.network - : _network; - - const isMainnet = await getIsMainnet(); - const { networkUrl, networkPassphrase: currentNetworkPassphrase } = - await getNetworkDetails(); - const Sdk = getSdk(currentNetworkPassphrase); - - const { tab, url: tabUrl = "" } = sender; - const domain = getUrlHostname(tabUrl); - const punycodedDomain = getPunycodedDomain(domain); - - const allowListStr = (await localStore.getItem(ALLOWLIST_ID)) || ""; - const allowList = allowListStr.split(","); - const isDomainListedAllowed = await isSenderAllowed({ sender }); - - const transaction = Sdk.TransactionBuilder.fromXDR( - transactionXdr, - networkPassphrase || Sdk.Networks[network as keyof typeof Sdk.Networks], - ); - - const directoryLookupJson = await cachedFetch( - STELLAR_EXPERT_BLOCKED_ACCOUNTS_URL, - CACHED_BLOCKED_ACCOUNTS_ID, - ); - const accountData = directoryLookupJson?._embedded?.records || []; - - let _operations = [{}] as StellarSdk.Operation[]; - - if ("operations" in transaction) { - _operations = transaction.operations; - } + try { + const { + transactionXdr, + network: _network, + networkPassphrase, + accountToSign, + address: addressToSign, + } = request as ExternalRequestTx; + + const network = + _network === null || !_network + ? MAINNET_NETWORK_DETAILS.network + : _network; + + const isMainnet = await getIsMainnet(); + const { networkUrl, networkPassphrase: currentNetworkPassphrase } = + await getNetworkDetails(); + const Sdk = getSdk(currentNetworkPassphrase); + + const { tab, url: tabUrl = "" } = sender; + const domain = getUrlHostname(tabUrl); + const punycodedDomain = getPunycodedDomain(domain); + + const allowListStr = (await localStore.getItem(ALLOWLIST_ID)) || ""; + const allowList = allowListStr.split(","); + const isDomainListedAllowed = await isSenderAllowed({ sender }); + + const transaction = Sdk.TransactionBuilder.fromXDR( + transactionXdr, + networkPassphrase || Sdk.Networks[network as keyof typeof Sdk.Networks], + ); + + const directoryLookupJson = await cachedFetch( + STELLAR_EXPERT_BLOCKED_ACCOUNTS_URL, + CACHED_BLOCKED_ACCOUNTS_ID, + ); + const accountData = directoryLookupJson?._embedded?.records || []; + + let _operations = [{}] as StellarSdk.Operation[]; + + if ("operations" in transaction) { + _operations = transaction.operations; + } - if ("innerTransaction" in transaction) { - _operations = transaction.innerTransaction.operations; - } + if ("innerTransaction" in transaction) { + _operations = transaction.innerTransaction.operations; + } - const flaggedKeys: FlaggedKeys = {}; - - const isValidatingMemo = (await getIsMemoValidationEnabled()) && isMainnet; - const isValidatingSafety = - (await getIsSafetyValidationEnabled()) && isMainnet; - - if (isValidatingMemo || isValidatingSafety) { - _operations.forEach((operation: StellarSdk.Operation) => { - accountData.forEach( - ({ address, tags }: { address: string; tags: string[] }) => { - if ( - "destination" in operation && - address === operation.destination - ) { - let collectedTags = [...tags]; - - /* if the user has opted out of validation, remove applicable tags */ - if (!isValidatingMemo) { - collectedTags.filter( - (tag) => tag !== TRANSACTION_WARNING.memoRequired, - ); - } - if (!isValidatingSafety) { - collectedTags = collectedTags.filter( - (tag) => tag !== TRANSACTION_WARNING.unsafe, - ); - collectedTags = collectedTags.filter( - (tag) => tag !== TRANSACTION_WARNING.malicious, - ); + const flaggedKeys: FlaggedKeys = {}; + + const isValidatingMemo = + (await getIsMemoValidationEnabled()) && isMainnet; + const isValidatingSafety = + (await getIsSafetyValidationEnabled()) && isMainnet; + + if (isValidatingMemo || isValidatingSafety) { + _operations.forEach((operation: StellarSdk.Operation) => { + accountData.forEach( + ({ address, tags }: { address: string; tags: string[] }) => { + if ( + "destination" in operation && + address === operation.destination + ) { + let collectedTags = [...tags]; + + /* if the user has opted out of validation, remove applicable tags */ + if (!isValidatingMemo) { + collectedTags.filter( + (tag) => tag !== TRANSACTION_WARNING.memoRequired, + ); + } + if (!isValidatingSafety) { + collectedTags = collectedTags.filter( + (tag) => tag !== TRANSACTION_WARNING.unsafe, + ); + collectedTags = collectedTags.filter( + (tag) => tag !== TRANSACTION_WARNING.malicious, + ); + } + flaggedKeys[operation.destination] = { + ...flaggedKeys[operation.destination], + tags: collectedTags, + }; } - flaggedKeys[operation.destination] = { - ...flaggedKeys[operation.destination], - tags: collectedTags, - }; - } - }, - ); - }); - } + }, + ); + }); + } - const server = stellarSdkServer(networkUrl, networkPassphrase); + const server = stellarSdkServer(networkUrl, networkPassphrase); - try { - await server.checkMemoRequired(transaction as StellarSdk.Transaction); - } catch (e: any) { - if ("accountId" in e) { - flaggedKeys[e.accountId] = { - ...flaggedKeys[e.accountId], - tags: [TRANSACTION_WARNING.memoRequired], - }; + try { + await server.checkMemoRequired(transaction as StellarSdk.Transaction); + } catch (e: any) { + if ("accountId" in e) { + flaggedKeys[e.accountId] = { + ...flaggedKeys[e.accountId], + tags: [TRANSACTION_WARNING.memoRequired], + }; + } } - } - const transactionInfo = { - transaction, - transactionXdr, - tab, - isDomainListedAllowed, - url: tabUrl, - flaggedKeys, - accountToSign, - } as TransactionInfo; - - transactionQueue.push(transaction as StellarSdk.Transaction); - const encodedBlob = encodeObject(transactionInfo); - - const popup = browser.windows.create({ - url: chrome.runtime.getURL( - `/index.html#/sign-transaction?${encodedBlob}`, - ), - ...WINDOW_SETTINGS, - }); + const transactionInfo = { + transaction, + transactionXdr, + tab, + isDomainListedAllowed, + url: tabUrl, + flaggedKeys, + accountToSign: accountToSign || addressToSign, + } as TransactionInfo; + + transactionQueue.push(transaction as StellarSdk.Transaction); + const encodedBlob = encodeObject(transactionInfo); + + const popup = browser.windows.create({ + url: chrome.runtime.getURL( + `/index.html#/sign-transaction?${encodedBlob}`, + ), + ...WINDOW_SETTINGS, + }); - return new Promise((resolve) => { - if (!popup) { - resolve({ error: "Couldn't open access prompt" }); - } else { - browser.windows.onRemoved.addListener(() => + return new Promise((resolve) => { + if (!popup) { resolve({ - error: "User declined access", - }), - ); - } - const response = (signedTransaction: string) => { - if (signedTransaction) { - if (!isDomainListedAllowed) { - allowList.push(punycodedDomain); - localStore.setItem(ALLOWLIST_ID, allowList.join()); - } - resolve({ signedTransaction }); + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiInternalError, + error: FreighterApiInternalError.message, + }); + } else { + browser.windows.onRemoved.addListener(() => + resolve({ + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiDeclinedError, + error: FreighterApiDeclinedError.message, + }), + ); } + const response = (signedTransaction: string, signerAddress: string) => { + if (signedTransaction) { + if (!isDomainListedAllowed) { + allowList.push(punycodedDomain); + localStore.setItem(ALLOWLIST_ID, allowList.join()); + } + resolve({ signedTransaction, signerAddress }); + } - resolve({ error: "User declined access" }); - }; + resolve({ + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiDeclinedError, + error: FreighterApiDeclinedError.message, + }); + }; - responseQueue.push(response); - }); + responseQueue.push(response); + }); + } catch (e) { + return { + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + + apiError: FreighterApiInternalError, + error: FreighterApiInternalError.message, + }; + } }; const submitBlob = async () => { - const { blob, accountToSign } = request as ExternalRequestBlob; - - const { tab, url: tabUrl = "" } = sender; - const domain = getUrlHostname(tabUrl); - const punycodedDomain = getPunycodedDomain(domain); - - const allowListStr = (await localStore.getItem(ALLOWLIST_ID)) || ""; - const allowList = allowListStr.split(","); - const isDomainListedAllowed = await isSenderAllowed({ sender }); - - const blobData = { - isDomainListedAllowed, - domain, - tab, - blob, - url: tabUrl, - accountToSign, - }; + try { + const { blob, accountToSign, address, networkPassphrase } = + request as ExternalRequestBlob; + + const { tab, url: tabUrl = "" } = sender; + const domain = getUrlHostname(tabUrl); + const punycodedDomain = getPunycodedDomain(domain); + + const allowListStr = (await localStore.getItem(ALLOWLIST_ID)) || ""; + const allowList = allowListStr.split(","); + const isDomainListedAllowed = await isSenderAllowed({ sender }); + + const blobData = { + isDomainListedAllowed, + domain, + tab, + message: blob, + url: tabUrl, + accountToSign: accountToSign || address, + networkPassphrase, + }; - blobQueue.push(blobData); - const encodedBlob = encodeObject(blobData); - const popup = browser.windows.create({ - url: chrome.runtime.getURL(`/index.html#/sign-blob?${encodedBlob}`), - ...WINDOW_SETTINGS, - }); + blobQueue.push(blobData); + const encodedBlob = encodeObject(blobData); + const popup = browser.windows.create({ + url: chrome.runtime.getURL(`/index.html#/sign-message?${encodedBlob}`), + ...WINDOW_SETTINGS, + }); - return new Promise((resolve) => { - if (!popup) { - resolve({ error: "Couldn't open access prompt" }); - } else { - browser.windows.onRemoved.addListener(() => + return new Promise((resolve) => { + if (!popup) { resolve({ - error: "User declined access", - }), - ); - } - - // reject if not b64 encoded - try { - atob(blob); - } catch (error) { - resolve({ error: "encoding error: blob should be base64 encoded" }); - } + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiInternalError, + error: FreighterApiInternalError.message, + }); + } else { + browser.windows.onRemoved.addListener(() => + resolve({ + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiDeclinedError, + error: FreighterApiDeclinedError.message, + }), + ); + } - const response = (signedBlob: string) => { - if (signedBlob) { - if (!isDomainListedAllowed) { - allowList.push(punycodedDomain); - localStore.setItem(ALLOWLIST_ID, allowList.join()); + const response = (signedBlob: string, signerAddress: string) => { + if (signedBlob) { + if (!isDomainListedAllowed) { + allowList.push(punycodedDomain); + localStore.setItem(ALLOWLIST_ID, allowList.join()); + } + resolve({ signedBlob, signerAddress }); } - resolve({ signedBlob }); - } - resolve({ error: "User declined access" }); - }; + resolve({ + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiDeclinedError, + error: FreighterApiDeclinedError.message, + }); + }; - responseQueue.push(response); - }); + responseQueue.push(response); + }); + } catch (e) { + return { + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiInternalError, + error: FreighterApiInternalError.message, + }; + } }; const submitAuthEntry = async () => { - const { entryXdr, accountToSign } = request as ExternalRequestAuthEntry; - - const { tab, url: tabUrl = "" } = sender; - const domain = getUrlHostname(tabUrl); - const punycodedDomain = getPunycodedDomain(domain); - - const allowListStr = (await localStore.getItem(ALLOWLIST_ID)) || ""; - const allowList = allowListStr.split(","); - const isDomainListedAllowed = await isSenderAllowed({ sender }); - - const authEntry = { - entry: entryXdr, - accountToSign, - tab, - url: tabUrl, - }; + try { + const { entryXdr, accountToSign, address, networkPassphrase } = + request as ExternalRequestAuthEntry; + + const { tab, url: tabUrl = "" } = sender; + const domain = getUrlHostname(tabUrl); + const punycodedDomain = getPunycodedDomain(domain); + + const allowListStr = (await localStore.getItem(ALLOWLIST_ID)) || ""; + const allowList = allowListStr.split(","); + const isDomainListedAllowed = await isSenderAllowed({ sender }); + + const authEntry = { + entry: entryXdr, + accountToSign: accountToSign || address, + tab, + url: tabUrl, + networkPassphrase, + }; - authEntryQueue.push(authEntry); - const encodedAuthEntry = encodeObject(authEntry); - const popup = browser.windows.create({ - url: chrome.runtime.getURL( - `/index.html#/sign-auth-entry?${encodedAuthEntry}`, - ), - ...WINDOW_SETTINGS, - }); + authEntryQueue.push(authEntry); + const encodedAuthEntry = encodeObject(authEntry); + const popup = browser.windows.create({ + url: chrome.runtime.getURL( + `/index.html#/sign-auth-entry?${encodedAuthEntry}`, + ), + ...WINDOW_SETTINGS, + }); - return new Promise((resolve) => { - if (!popup) { - resolve({ error: "Couldn't open access prompt" }); - } else { - browser.windows.onRemoved.addListener(() => + return new Promise((resolve) => { + if (!popup) { resolve({ - error: "User declined access", - }), - ); - } - const response = (signedAuthEntry: string) => { - if (signedAuthEntry) { - if (!isDomainListedAllowed) { - allowList.push(punycodedDomain); - localStore.setItem(ALLOWLIST_ID, allowList.join()); - } - resolve({ signedAuthEntry }); + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiInternalError, + error: FreighterApiInternalError.message, + }); + } else { + browser.windows.onRemoved.addListener(() => + resolve({ + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiDeclinedError, + error: FreighterApiDeclinedError.message, + }), + ); } + const response = (signedAuthEntry: string) => { + if (signedAuthEntry) { + if (!isDomainListedAllowed) { + allowList.push(punycodedDomain); + localStore.setItem(ALLOWLIST_ID, allowList.join()); + } + resolve({ signedAuthEntry }); + } - resolve({ error: "User declined access" }); - }; + resolve({ + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiDeclinedError, + error: FreighterApiDeclinedError.message, + }); + }; - responseQueue.push(response); - }); + responseQueue.push(response); + }); + } catch (e) { + return { + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiInternalError, + error: FreighterApiInternalError.message, + }; + } }; const requestNetwork = async () => { @@ -382,6 +453,7 @@ export const freighterApiMessageListener = ( console.error(error); return { error }; } + return { network }; }; @@ -398,7 +470,11 @@ export const freighterApiMessageListener = ( networkDetails = await getNetworkDetails(); } catch (error) { console.error(error); - return { error }; + return { + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiInternalError, + error: FreighterApiInternalError.message, + }; } return { networkDetails }; }; @@ -406,9 +482,15 @@ export const freighterApiMessageListener = ( const requestConnectionStatus = () => ({ isConnected: true }); const requestAllowedStatus = async () => { - const isAllowed = await isSenderAllowed({ sender }); + try { + const isAllowed = await isSenderAllowed({ sender }); - return { isAllowed }; + return { isAllowed }; + } catch (e) { + return { + apiError: FreighterApiInternalError, + }; + } }; const setAllowedStatus = async () => { @@ -427,6 +509,7 @@ export const freighterApiMessageListener = ( browser.windows.create({ url: chrome.runtime.getURL(`/index.html#/grant-access?${encodeOrigin}`), ...WINDOW_SETTINGS, + width: 400, }); return new Promise((resolve) => { @@ -438,7 +521,11 @@ export const freighterApiMessageListener = ( resolve({ isAllowed: isAllowedResponse }); } - resolve({ error: "User declined access" }); + resolve({ + // return 2 error formats: one for clients running older versions of freighter-api, and one to adhere to the standard wallet interface + apiError: FreighterApiDeclinedError, + error: FreighterApiDeclinedError.message, + }); }; responseQueue.push(response); diff --git a/extension/src/background/messageListener/popupMessageListener.ts b/extension/src/background/messageListener/popupMessageListener.ts index 829ed7d2d..22c07f033 100644 --- a/extension/src/background/messageListener/popupMessageListener.ts +++ b/extension/src/background/messageListener/popupMessageListener.ts @@ -54,6 +54,7 @@ import { NETWORKS_LIST_ID, TOKEN_ID_LIST, IS_HASH_SIGNING_ENABLED_ID, + IS_NON_SSL_ENABLED_ID, } from "constants/localStorageTypes"; import { FUTURENET_NETWORK_DETAILS, @@ -81,6 +82,7 @@ import { getNetworkDetails, getNetworksList, getAssetsLists, + getIsNonSSLEnabled, HW_PREFIX, getBipPath, subscribeTokenBalance, @@ -130,19 +132,23 @@ const numOfPublicKeysToCheck = 5; const sessionTimer = new SessionTimer(); // eslint-disable-next-line -export const responseQueue: Array<(message?: any) => void> = []; +export const responseQueue: Array< + (message?: any, messageAddress?: any) => void +> = []; export const transactionQueue: StellarSdk.Transaction[] = []; export const blobQueue: { isDomainListedAllowed: boolean; domain: string; tab: browser.Tabs.Tab | undefined; - blob: string; + message: string; url: string; - accountToSign: string; + accountToSign?: string; + address?: string; }[] = []; export const authEntryQueue: { - accountToSign: string; + accountToSign?: string; + address?: string; tab: browser.Tabs.Tab | undefined; entry: string; // xdr.SorobanAuthorizationEntry url: string; @@ -1068,7 +1074,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { const transactionResponse = responseQueue.pop(); if (typeof transactionResponse === "function") { - transactionResponse(response); + transactionResponse(response, sourceKeys.publicKey()); return {}; } } @@ -1087,13 +1093,13 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { const blob = blobQueue.pop(); const response = blob - ? sourceKeys.sign(Buffer.from(blob.blob, "base64")) + ? sourceKeys.sign(Buffer.from(blob.message, "base64")) : null; const blobResponse = responseQueue.pop(); if (typeof blobResponse === "function") { - blobResponse(response); + blobResponse(response, sourceKeys.publicKey()); return {}; } } @@ -1118,7 +1124,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { const entryResponse = responseQueue.pop(); if (typeof entryResponse === "function") { - entryResponse(response); + entryResponse(response, sourceKeys.publicKey()); return {}; } } @@ -1211,6 +1217,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { isMemoValidationEnabled, isSafetyValidationEnabled, isValidatingSafeAssetsEnabled, + isNonSSLEnabled, } = request; await localStore.setItem(DATA_SHARING_ID, isDataSharingAllowed); @@ -1223,6 +1230,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { IS_VALIDATING_SAFE_ASSETS_ID, isValidatingSafeAssetsEnabled, ); + await localStore.setItem(IS_NON_SSL_ENABLED_ID, isNonSSLEnabled); const networkDetails = await getNetworkDetails(); const isRpcHealthy = await getIsRpcHealthy(networkDetails); @@ -1238,13 +1246,16 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { networksList: await getNetworksList(), isRpcHealthy, isSorobanPublicEnabled: featureFlags.useSorobanPublic, + isNonSSLEnabled: await getIsNonSSLEnabled(), }; }; const saveExperimentalFeatures = async () => { - const { isExperimentalModeEnabled, isHashSigningEnabled } = request; + const { isExperimentalModeEnabled, isHashSigningEnabled, isNonSSLEnabled } = + request; await localStore.setItem(IS_HASH_SIGNING_ENABLED_ID, isHashSigningEnabled); + await localStore.setItem(IS_NON_SSL_ENABLED_ID, isNonSSLEnabled); const currentIsExperimentalModeEnabled = await getIsExperimentalModeEnabled(); @@ -1272,6 +1283,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { return { isExperimentalModeEnabled: await getIsExperimentalModeEnabled(), isHashSigningEnabled: await getIsHashSigningEnabled(), + isNonSSLEnabled: await getIsNonSSLEnabled(), networkDetails: await getNetworkDetails(), networksList: await getNetworksList(), }; @@ -1288,6 +1300,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { const userNotification = await getUserNotification(); const isHashSigningEnabled = await getIsHashSigningEnabled(); const assetsLists = await getAssetsLists(); + const isNonSSLEnabled = await getIsNonSSLEnabled(); return { allowList: await getAllowList(), @@ -1303,6 +1316,7 @@ export const popupMessageListener = (request: Request, sessionStore: Store) => { isRpcHealthy, userNotification, assetsLists, + isNonSSLEnabled, }; }; diff --git a/extension/src/constants/localStorageTypes.ts b/extension/src/constants/localStorageTypes.ts index 4aea23e32..757fe3f53 100644 --- a/extension/src/constants/localStorageTypes.ts +++ b/extension/src/constants/localStorageTypes.ts @@ -22,3 +22,4 @@ export const STORAGE_VERSION = "storageVersion"; export const HAS_ACCOUNT_SUBSCRIPTION = "hasAccountSubscription"; export const ASSETS_LISTS_ID = "assetsLists"; export const IS_HASH_SIGNING_ENABLED_ID = "isHashSigningEnabled"; +export const IS_NON_SSL_ENABLED_ID = "isNonSSLEnabled"; diff --git a/extension/src/helpers/stellar.ts b/extension/src/helpers/stellar.ts index 698ae2575..1103868f2 100644 --- a/extension/src/helpers/stellar.ts +++ b/extension/src/helpers/stellar.ts @@ -128,11 +128,9 @@ export const isMuxedAccount = (publicKey: string) => publicKey.startsWith("M"); export const isFederationAddress = (address: string) => address.includes("*"); export const isMainnet = (networkDetails: NetworkDetails) => { - const { networkPassphrase, networkUrl } = networkDetails; + const { networkPassphrase } = networkDetails; - return ( - networkPassphrase === Networks.PUBLIC && networkUrl === NETWORK_URLS.PUBLIC - ); + return networkPassphrase === Networks.PUBLIC; }; export const isTestnet = (networkDetails: NetworkDetails) => { diff --git a/extension/src/helpers/urls.ts b/extension/src/helpers/urls.ts index 14b9ad322..01fc1de80 100644 --- a/extension/src/helpers/urls.ts +++ b/extension/src/helpers/urls.ts @@ -2,13 +2,14 @@ import punycode from "punycode"; import browser from "webextension-polyfill"; import { TransactionInfo } from "../types/transactions"; -export interface BlobToSign { +export interface MessageToSign { isDomainListedAllowed: boolean; domain: string; tab?: browser.Tabs.Tab; - blob: string; + message: string; url: string; accountToSign: string; + networkPassphrase?: string; } export interface EntryToSign { @@ -18,6 +19,7 @@ export interface EntryToSign { entry: string; url: string; accountToSign: string; + networkPassphrase?: string; } export const encodeObject = (obj: object) => @@ -33,7 +35,7 @@ export const removeQueryParam = (url = "") => url.replace(/\?(.*)/, ""); export const parsedSearchParam = ( param: string, -): TransactionInfo | BlobToSign | EntryToSign => { +): TransactionInfo | MessageToSign | EntryToSign => { const decodedSearchParam = decodeString(param.replace("?", "")); return decodedSearchParam ? JSON.parse(decodedSearchParam) : {}; }; diff --git a/extension/src/popup/App.tsx b/extension/src/popup/App.tsx index dbf46c11f..42ebf3636 100755 --- a/extension/src/popup/App.tsx +++ b/extension/src/popup/App.tsx @@ -9,14 +9,12 @@ import { reducer as auth } from "popup/ducks/accountServices"; import { reducer as settings } from "popup/ducks/settings"; import { reducer as transactionSubmission } from "popup/ducks/transactionSubmission"; import { reducer as tokenPaymentSimulation } from "popup/ducks/token-payment"; - import { Loading } from "popup/components/Loading"; import { ErrorTracking } from "popup/components/ErrorTracking"; - +import { ErrorBoundary } from "./components/ErrorBoundary"; import { Router } from "./Router"; import "./styles/global.scss"; -import { ErrorBoundary } from "./components/ErrorBoundary"; const rootReducer = combineReducers({ auth, diff --git a/extension/src/popup/Router.tsx b/extension/src/popup/Router.tsx index 367562886..fa6739435 100644 --- a/extension/src/popup/Router.tsx +++ b/extension/src/popup/Router.tsx @@ -58,7 +58,7 @@ import { ViewPublicKey } from "popup/views/ViewPublicKey"; import { Settings } from "popup/views/Settings"; import { Preferences } from "popup/views/Preferences"; import { Security } from "popup/views/Security"; -import { ExperimentalFeatures } from "popup/views/ExperimentalFeatures"; +import { AdvancedSettings } from "popup/views/AdvancedSettings"; import { About } from "popup/views/About"; import { SendPayment } from "popup/views/SendPayment"; import { ManageAssets } from "popup/views/ManageAssets"; @@ -73,7 +73,7 @@ import "popup/metrics/views"; import { DEV_SERVER } from "@shared/constants/services"; import { SettingsState } from "@shared/api/types"; -import { SignBlob } from "./views/SignBlob"; +import { SignMessage } from "./views/SignMessage"; import { ReviewAuth } from "./views/ReviewAuth"; import { View } from "./basics/layout/View"; @@ -330,8 +330,8 @@ const Outlet = () => { - - + + @@ -399,8 +399,8 @@ const Outlet = () => { - - + + {DEV_SERVER && ( diff --git a/extension/src/popup/__testHelpers__/index.tsx b/extension/src/popup/__testHelpers__/index.tsx index 7ae886308..593074759 100644 --- a/extension/src/popup/__testHelpers__/index.tsx +++ b/extension/src/popup/__testHelpers__/index.tsx @@ -71,7 +71,6 @@ export const Wrapper: React.FunctionComponent = ({ }; export const mockBalances = { - tokensWithNoBalance: [], balances: { ["DT:CCXVDIGMR6WTXZQX2OEVD6YM6AYCYPXPQ7YYH6OZMRS7U6VD3AVHNGBJ"]: { token: { diff --git a/extension/src/popup/components/ModalInfo/index.tsx b/extension/src/popup/components/ModalInfo/index.tsx index a220d2d7b..46230bf5d 100644 --- a/extension/src/popup/components/ModalInfo/index.tsx +++ b/extension/src/popup/components/ModalInfo/index.tsx @@ -1,38 +1,44 @@ import React from "react"; - +import classNames from "classnames"; import { Card, Icon } from "@stellar/design-system"; + import { PunycodedDomain } from "popup/components/PunycodedDomain"; -import { FirstTimeWarningMessage } from "popup/components/WarningMessages"; +import { MaliciousDomainWarning } from "../WarningMessages"; import "./styles.scss"; interface ModalInfoProps { children: React.ReactNode; domain: string; - domainTitle: string; subject: string; + variant?: "default" | "malicious"; } export const ModalInfo = ({ children, domain, - domainTitle, subject, -}: ModalInfoProps) => ( - <> -
+ variant = "default", +}: ModalInfoProps) => { + const cardClasses = classNames("ModalInfo--card", { + Malicious: variant === "malicious", + }); + return ( +
- +

Connection Request

- + {variant === "malicious" && ( + + )}
{subject}
{children}
- -); + ); +}; diff --git a/extension/src/popup/components/ModalInfo/styles.scss b/extension/src/popup/components/ModalInfo/styles.scss index abf613548..34ec65861 100644 --- a/extension/src/popup/components/ModalInfo/styles.scss +++ b/extension/src/popup/components/ModalInfo/styles.scss @@ -1,4 +1,14 @@ .ModalInfo { + &--card.Malicious { + .Card { + .PunycodedDomain { + .PunycodedDomain__domain { + color: var(--color-red-50); + } + } + } + } + &--connection-request { display: flex; justify-content: center; @@ -24,10 +34,10 @@ &--subject { font-size: 0.75rem; - color: var(--color-gray-70); - background-color: var(--color-gray-30); + color: #a0a0a0; + background-color: #1c1c1c; border-radius: 6px; - line-height: 1.5rem; + line-height: 1rem; margin-bottom: 1.5rem; padding-bottom: 1.5rem; padding: 0.5rem; diff --git a/extension/src/popup/components/PunycodedDomain/index.tsx b/extension/src/popup/components/PunycodedDomain/index.tsx index 3e13c2906..0aa1cb9bd 100644 --- a/extension/src/popup/components/PunycodedDomain/index.tsx +++ b/extension/src/popup/components/PunycodedDomain/index.tsx @@ -7,12 +7,10 @@ import "./styles.scss"; export const PunycodedDomain = ({ domain, - domainTitle, isRow, ...props }: { domain: string; - domainTitle?: string; isRow?: boolean; }) => { const punycodedDomain = getPunycodedDomain(domain); @@ -37,7 +35,6 @@ export const PunycodedDomain = ({ {isDomainValid ? punycodedDomain : `xn-${punycodedDomain}`}
-
{domainTitle}
); }; diff --git a/extension/src/popup/components/PunycodedDomain/styles.scss b/extension/src/popup/components/PunycodedDomain/styles.scss index d5bb2cfef..535d9889b 100644 --- a/extension/src/popup/components/PunycodedDomain/styles.scss +++ b/extension/src/popup/components/PunycodedDomain/styles.scss @@ -19,12 +19,6 @@ color: var(--color-gray-90); } - &__title { - color: var(--color-gray-60); - font-size: 0.875rem; - overflow-wrap: anywhere; - } - div { margin-bottom: 0.25rem; } diff --git a/extension/src/popup/components/WarningMessages/index.tsx b/extension/src/popup/components/WarningMessages/index.tsx index e323e50cc..5d2391ca8 100644 --- a/extension/src/popup/components/WarningMessages/index.tsx +++ b/extension/src/popup/components/WarningMessages/index.tsx @@ -2,7 +2,7 @@ import React, { useState, useRef, useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { createPortal } from "react-dom"; import { Button, Icon, Loader, Notification } from "@stellar/design-system"; -import { useTranslation } from "react-i18next"; +import { useTranslation, Trans } from "react-i18next"; import { POPUP_HEIGHT } from "constants/dimensions"; import { Account, @@ -1053,3 +1053,54 @@ const WarningMessageTokenDetails = ({ ); }; + +export const SSLWarningMessage = ({ url }: { url: string }) => { + const { t } = useTranslation(); + + return ( + window.close()} + isActive + variant={WarningMessageVariant.warning} + header={t("WEBSITE CONNECTION IS NOT SECURE")} + > +

+ + The website {url} does not use an SSL certificate. + For additional safety Freighter only works with websites that provide + an SSL certificate by default. You may enable connection to domains + that do not use an SSL certificate in Settings > Security > + Advanced Settings. + +

+
+ ); +}; + +export const MaliciousDomainWarning = ({ message }: { message: string }) => ( +
+
+ +
+

{message}

+
+); + +export const BlockAidMissWarning = () => { + const { t } = useTranslation(); + + return ( + +
+

+ {t( + "Proceed with caution. Blockaid is unable to provide a risk assesment for this domain at this time.", + )} +

+
+
+ ); +}; diff --git a/extension/src/popup/components/WarningMessages/styles.scss b/extension/src/popup/components/WarningMessages/styles.scss index 1529dd980..93681a2a0 100644 --- a/extension/src/popup/components/WarningMessages/styles.scss +++ b/extension/src/popup/components/WarningMessages/styles.scss @@ -416,3 +416,25 @@ .InvokerAuthWarning { overflow-wrap: break-word; } + +.MaliciousDomainWarning { + display: flex; + background-color: var(--color-red-20); + border: 1px solid #671e22; + border-radius: 6px; + margin-bottom: 10px; + padding: 0.5rem; + padding-left: 0.25rem; + + .Icon { + margin-right: 5px; + .WarningMessage__icon { + color: var(--color-red-60); + } + } + + .Message { + font-size: 0.75rem; + line-height: 1rem; + } +} diff --git a/extension/src/popup/components/manageAssets/ChooseAsset/index.tsx b/extension/src/popup/components/manageAssets/ChooseAsset/index.tsx index 57385de52..7e79e131d 100644 --- a/extension/src/popup/components/manageAssets/ChooseAsset/index.tsx +++ b/extension/src/popup/components/manageAssets/ChooseAsset/index.tsx @@ -173,7 +173,7 @@ export const ChooseAsset = ({ balances }: ChooseAssetProps) => { ref={ManageAssetRowsWrapperRef} > {managingAssets ? ( - + ) : ( )} diff --git a/extension/src/popup/components/manageAssets/ManageAssetRowButton/index.tsx b/extension/src/popup/components/manageAssets/ManageAssetRowButton/index.tsx index 889fb0acd..f451e1b65 100644 --- a/extension/src/popup/components/manageAssets/ManageAssetRowButton/index.tsx +++ b/extension/src/popup/components/manageAssets/ManageAssetRowButton/index.tsx @@ -9,10 +9,10 @@ import { AppDispatch } from "popup/App"; import { navigateTo } from "popup/helpers/navigate"; import { stellarSdkServer } from "@shared/api/helpers/stellarSdkServer"; import { emitMetric } from "helpers/metrics"; -import { useNetworkFees } from "popup/helpers/useNetworkFees"; import { getCanonicalFromAsset } from "helpers/stellar"; import { getManageAssetXDR } from "popup/helpers/getManageAssetXDR"; import { checkForSuspiciousAsset } from "popup/helpers/checkForSuspiciousAsset"; +import { scanAsset } from "popup/helpers/blockaid"; import { METRIC_NAMES } from "popup/constants/metricsNames"; import { publicKeySelector, @@ -61,6 +61,7 @@ interface ManageAssetRowButtonProps { setNewAssetFlags: (flags: any) => void; setShowUnverifiedWarning: (rowButtonShowing: boolean) => void; setHandleAddToken: (func: any) => void; + recommendedFee: string; } export const ManageAssetRowButton = ({ @@ -82,6 +83,7 @@ export const ManageAssetRowButton = ({ setNewAssetFlags, setShowUnverifiedWarning, setHandleAddToken, + recommendedFee, }: ManageAssetRowButtonProps) => { const dispatch: AppDispatch = useDispatch(); const { t } = useTranslation(); @@ -95,7 +97,6 @@ export const ManageAssetRowButton = ({ const walletType = useSelector(hardwareWalletTypeSelector); const networkDetails = useSelector(settingsNetworkDetailsSelector); const publicKey = useSelector(publicKeySelector); - const { recommendedFee } = useNetworkFees(); const isHardwareWallet = !!walletType; const ManageAssetRowDropdownRef = useRef(null); @@ -210,6 +211,11 @@ export const ManageAssetRowButton = ({ networkDetails, }); + await scanAsset( + `${assetRowData.code}-${assetRowData.issuer}`, + networkDetails, + ); + if (isBlockedDomain(assetRowData.domain) && !isTrustlineActive) { setShowBlockedDomainWarning(true); setSuspiciousAssetData(assetRowData); diff --git a/extension/src/popup/components/manageAssets/ManageAssetRows/index.tsx b/extension/src/popup/components/manageAssets/ManageAssetRows/index.tsx index 0477e5a12..145a2c49a 100644 --- a/extension/src/popup/components/manageAssets/ManageAssetRows/index.tsx +++ b/extension/src/popup/components/manageAssets/ManageAssetRows/index.tsx @@ -12,6 +12,7 @@ import { truncateString, } from "helpers/stellar"; import { isContractId } from "popup/helpers/soroban"; +import { useNetworkFees } from "popup/helpers/useNetworkFees"; import { LoadingBackground } from "popup/basics/LoadingBackground"; import { ROUTES } from "popup/constants/routes"; @@ -51,7 +52,6 @@ interface ManageAssetRowsProps { children?: React.ReactNode; header?: React.ReactNode; assetRows: ManageAssetCurrency[]; - chooseAsset?: boolean; isVerifiedToken?: boolean; isVerificationInfoShowing?: boolean; verifiedLists?: string[]; @@ -69,7 +69,6 @@ export const ManageAssetRows = ({ children, header, assetRows, - chooseAsset, isVerifiedToken, isVerificationInfoShowing, verifiedLists, @@ -83,6 +82,7 @@ export const ManageAssetRows = ({ const dispatch: AppDispatch = useDispatch(); const { accountBalanceStatus } = useSelector(tokensSelector); const walletType = useSelector(hardwareWalletTypeSelector); + const { recommendedFee } = useNetworkFees(); const [showBlockedDomainWarning, setShowBlockedDomainWarning] = useState(false); @@ -176,10 +176,9 @@ export const ManageAssetRows = ({ } const isContract = isContractId(contract); const canonicalAsset = getCanonicalFromAsset(code, issuer); - const isTrustlineActive = - Object.keys(accountBalances.balances).some( - (balance) => balance === canonicalAsset, - ) || accountBalances.tokensWithNoBalance.includes(issuer); + const isTrustlineActive = Object.keys( + accountBalances.balances, + ).some((balance) => balance === canonicalAsset); const isActionPending = submitStatus === ActionStatus.PENDING || accountBalanceStatus === ActionStatus.PENDING; @@ -215,45 +214,12 @@ export const ManageAssetRows = ({ setAssetSubmitting={setAssetSubmitting} setShowNewAssetWarning={setShowNewAssetWarning} setShowUnverifiedWarning={setShowUnverifiedWarning} + recommendedFee={recommendedFee} /> ); }, )} - - {chooseAsset && - accountBalances.tokensWithNoBalance.map((contract) => { - const isActionPending = - accountBalanceStatus === ActionStatus.PENDING; - - return ( -
- - -
- ); - })} {children} diff --git a/extension/src/popup/components/sendPayment/SendConfirm/TransactionDetails/index.tsx b/extension/src/popup/components/sendPayment/SendConfirm/TransactionDetails/index.tsx index fa2eac4ce..c17970730 100644 --- a/extension/src/popup/components/sendPayment/SendConfirm/TransactionDetails/index.tsx +++ b/extension/src/popup/components/sendPayment/SendConfirm/TransactionDetails/index.tsx @@ -297,7 +297,7 @@ export const TransactionDetails = ({ goBack }: { goBack: () => void }) => { sourceAsset: sourceAsset.code, }); - if (isSoroswap) { + if (isSoroswap && destAsset.issuer) { await dispatch( addTokenId({ publicKey, diff --git a/extension/src/popup/components/sendPayment/SendSettings/Settings/index.tsx b/extension/src/popup/components/sendPayment/SendSettings/Settings/index.tsx index 23baa6d49..20ed42dca 100644 --- a/extension/src/popup/components/sendPayment/SendSettings/Settings/index.tsx +++ b/extension/src/popup/components/sendPayment/SendSettings/Settings/index.tsx @@ -3,7 +3,6 @@ import { useSelector, useDispatch } from "react-redux"; import { Formik, Form, Field, FieldProps } from "formik"; import { Icon, Textarea, Link, Button } from "@stellar/design-system"; import { useTranslation } from "react-i18next"; -import { captureException } from "@sentry/browser"; import { navigateTo } from "popup/helpers/navigate"; import { useNetworkFees } from "popup/helpers/useNetworkFees"; @@ -21,8 +20,7 @@ import { saveSimulation, transactionSubmissionSelector, } from "popup/ducks/transactionSubmission"; -import { simulateTokenPayment } from "popup/ducks/token-payment"; -import { buildAndSimulateSoroswapTx } from "popup/helpers/sorobanSwap"; +import { simulateTokenPayment, simulateSwap } from "popup/ducks/token-payment"; import { InfoTooltip } from "popup/basics/InfoTooltip"; import { publicKeySelector } from "popup/ducks/accountServices"; @@ -89,6 +87,28 @@ export const Settings = ({ const showSlippage = (isPathPayment || isSwap) && !isSoroswap; async function goToReview() { + if (isSoroswap) { + const simulatedTx = await dispatch( + simulateSwap({ + networkDetails, + publicKey, + amountIn: amount, + amountInDecimals: decimals || 0, + amountOut: destinationAmount, + amountOutDecimals: destinationDecimals || 0, + memo, + transactionFee, + path, + }), + ); + + if (simulateSwap.fulfilled.match(simulatedTx)) { + dispatch(saveSimulation(simulatedTx.payload)); + navigateTo(next); + } + return; + } + if (isToken) { const assetAddress = asset.split(":")[1]; const balances = @@ -128,31 +148,6 @@ export const Settings = ({ return; } - if (isSoroswap) { - let simulatedTx; - try { - simulatedTx = await buildAndSimulateSoroswapTx({ - networkDetails, - publicKey, - amountIn: amount, - amountInDecimals: decimals, - amountOut: destinationAmount, - amountOutDecimals: destinationDecimals, - memo, - transactionFee, - path, - }); - } catch (e) { - console.error(e); - captureException( - `Unable to simulate soroswap tx ${JSON.stringify(path)}`, - ); - return; - } - - dispatch(saveSimulation(simulatedTx)); - navigateTo(next); - } navigateTo(next); } @@ -351,6 +346,7 @@ export const Settings = ({ + + + + + + ); +}; diff --git a/extension/src/popup/views/ExperimentalFeatures/styles.scss b/extension/src/popup/views/AdvancedSettings/styles.scss similarity index 86% rename from extension/src/popup/views/ExperimentalFeatures/styles.scss rename to extension/src/popup/views/AdvancedSettings/styles.scss index fb483f3e4..92ca8d710 100644 --- a/extension/src/popup/views/ExperimentalFeatures/styles.scss +++ b/extension/src/popup/views/AdvancedSettings/styles.scss @@ -1,4 +1,11 @@ -.ExperimentalFeatures { +.AdvancedSettings { + &__column { + display: flex; + height: 100%; + flex-direction: column; + justify-content: space-between; + } + &__understood-buttons { display: flex; flex-direction: column; diff --git a/extension/src/popup/views/ExperimentalFeatures/index.tsx b/extension/src/popup/views/ExperimentalFeatures/index.tsx deleted file mode 100644 index 459732d4f..000000000 --- a/extension/src/popup/views/ExperimentalFeatures/index.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import React, { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { useSelector, useDispatch } from "react-redux"; -import { useHistory } from "react-router-dom"; -import { Notification, Button, Toggle, Loader } from "@stellar/design-system"; -import { Field, Form, Formik } from "formik"; - -import { - saveExperimentalFeatures, - settingsSelector, -} from "popup/ducks/settings"; -import { SettingsState } from "@shared/api/types"; - -import { SubviewHeader } from "popup/components/SubviewHeader"; -import { AutoSaveFields } from "popup/components/AutoSave"; -import { View } from "popup/basics/layout/View"; -import IconExperimental from "popup/assets/icon-settings-experimental.svg"; - -import "./styles.scss"; - -export const ExperimentalFeatures = () => { - const { t } = useTranslation(); - const dispatch = useDispatch(); - const history = useHistory(); - const [isUnderstood, setIsUnderstood] = useState(false); - - const { - isExperimentalModeEnabled, - isHashSigningEnabled, - experimentalFeaturesState, - } = useSelector(settingsSelector); - - interface SettingValues { - isExperimentalModeEnabledValue: boolean; - isHashSigningEnabledValue: boolean; - } - - const initialValues: SettingValues = { - isExperimentalModeEnabledValue: isExperimentalModeEnabled, - isHashSigningEnabledValue: isHashSigningEnabled, - }; - - const handleSubmit = async (formValue: SettingValues) => { - const { isExperimentalModeEnabledValue, isHashSigningEnabledValue } = - formValue; - - // eslint-disable-next-line - await dispatch( - saveExperimentalFeatures({ - isExperimentalModeEnabled: isExperimentalModeEnabledValue, - isHashSigningEnabled: isHashSigningEnabledValue, - }), - ); - }; - - const isLoading = experimentalFeaturesState === SettingsState.LOADING; - - return isUnderstood ? ( - <> - - - -
- -
-
-
-
- icon experimental feature -
- {t("Use Futurenet")} - {isLoading ? ( -
- -
- ) : null} -
-
- } - id="isExperimentalModeEnabledValue" - /> -
-
-
- {t( - "Use experimental API’s and connect to the Futurenet, a test network. Please proceed at your own risk as you may be interacting with schemas that are untested and still changing.", - )} -
-
-
-
-
-
- icon experimental feature -
- {t("Enable Blind Signing on Ledger")} - {isLoading ? ( -
- -
- ) : null} -
-
- } - id="isHashSigningEnabledValue" - /> -
-
-
- {t( - "This can be used to sign arbitrary transaction hashes without having to decode them first. Ledger will not display the transaction details in the device display prior to signing so make sure you only interact with applications you know and trust.", - )}{" "} - - {t("Learn More")} - -
-
- -
-
- - ) : ( - <> - - - -
- - -
-
- - ); -}; diff --git a/extension/src/popup/views/GrantAccess/index.tsx b/extension/src/popup/views/GrantAccess/index.tsx index 143856338..b7ef68486 100644 --- a/extension/src/popup/views/GrantAccess/index.tsx +++ b/extension/src/popup/views/GrantAccess/index.tsx @@ -1,19 +1,17 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useLocation } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { Button } from "@stellar/design-system"; +import { Button, Loader } from "@stellar/design-system"; import { getUrlHostname, parsedSearchParam } from "helpers/urls"; - import { rejectAccess, grantAccess } from "popup/ducks/access"; import { publicKeySelector } from "popup/ducks/accountServices"; - import { ButtonsContainer, ModalWrapper } from "popup/basics/Modal"; - import { ModalInfo } from "popup/components/ModalInfo"; - import { KeyIdenticon } from "popup/components/identicons/KeyIdenticon"; +import { settingsNetworkDetailsSelector } from "popup/ducks/settings"; +import { useScanSite } from "popup/helpers/blockaid"; import "popup/metrics/access"; import "./styles.scss"; @@ -24,12 +22,19 @@ export const GrantAccess = () => { const dispatch = useDispatch(); const [isGranting, setIsGranting] = useState(false); - const { tab, url } = parsedSearchParam(location.search); - - const title = tab && tab.title ? tab.title : ""; - + const { url } = parsedSearchParam(location.search); const domain = getUrlHostname(url); const publicKey = useSelector(publicKeySelector); + const networkDetails = useSelector(settingsNetworkDetailsSelector); + const { scanSite, isLoading, data } = useScanSite(); + + useEffect(() => { + const fetchData = async () => { + await scanSite(url, networkDetails); + }; + fetchData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const rejectAndClose = () => { dispatch(rejectAccess()); @@ -46,39 +51,67 @@ export const GrantAccess = () => { return ( <> - -
-
Connecting with
-
- -
+ {isLoading ? ( +
+
- - - - - + ) : ( + +
+
Connecting with
+
+ +
+
+ {data?.is_malicious ? ( + + + + + ) : ( + + + + + )} +
+ )} ); diff --git a/extension/src/popup/views/GrantAccess/styles.scss b/extension/src/popup/views/GrantAccess/styles.scss index 0ad837b37..901d2a829 100644 --- a/extension/src/popup/views/GrantAccess/styles.scss +++ b/extension/src/popup/views/GrantAccess/styles.scss @@ -12,4 +12,13 @@ margin-top: 10px; border-color: var(--color-gray-40); } + + &__loader { + width: 100vw; + height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } } diff --git a/extension/src/popup/views/IntegrationTest.tsx b/extension/src/popup/views/IntegrationTest.tsx index 935b77642..08d04b1f6 100644 --- a/extension/src/popup/views/IntegrationTest.tsx +++ b/extension/src/popup/views/IntegrationTest.tsx @@ -308,6 +308,7 @@ export const IntegrationTest = () => { assertEq(res.isMemoValidationEnabled, true); assertEq(res.isSafetyValidationEnabled, true); assertEq(res.isValidatingSafeAssetsEnabled, true); + assertEq(res.isNonSSLEnabled, true); }); res = await loadSettings(); @@ -320,6 +321,7 @@ export const IntegrationTest = () => { assertEq(res.isSafetyValidationEnabled, true); assertEq(res.isValidatingSafeAssetsEnabled, true); assertEq(res.isExperimentalModeEnabled, true); + assertEq(res.isNonSSLEnabled, true); }); res = await showBackupPhrase(testPassword); diff --git a/extension/src/popup/views/Preferences/styles.scss b/extension/src/popup/views/Preferences/styles.scss index a8fdfc50e..d3faa1991 100644 --- a/extension/src/popup/views/Preferences/styles.scss +++ b/extension/src/popup/views/Preferences/styles.scss @@ -5,12 +5,12 @@ display: flex; flex-direction: column; gap: 1rem; + margin-bottom: 2rem; &--title { color: var(--color-gray-60); font-weight: var(--font-weight-medium); } - margin-bottom: 2rem; } &--label { cursor: pointer; diff --git a/extension/src/popup/views/ReviewAuth/index.tsx b/extension/src/popup/views/ReviewAuth/index.tsx index be4034519..628307548 100644 --- a/extension/src/popup/views/ReviewAuth/index.tsx +++ b/extension/src/popup/views/ReviewAuth/index.tsx @@ -128,7 +128,7 @@ export const ReviewAuth = () => {
- +
diff --git a/extension/src/popup/views/Security/index.tsx b/extension/src/popup/views/Security/index.tsx index 917bac777..6ec628002 100644 --- a/extension/src/popup/views/Security/index.tsx +++ b/extension/src/popup/views/Security/index.tsx @@ -47,10 +47,10 @@ export const Security = () => { {t("Show recovery phrase")} } > - {t("Experimental features")} + {t("Advanced settings")} {/* { diff --git a/extension/src/popup/views/SignAuthEntry/index.tsx b/extension/src/popup/views/SignAuthEntry/index.tsx index d5ec90945..faf3d0b73 100644 --- a/extension/src/popup/views/SignAuthEntry/index.tsx +++ b/extension/src/popup/views/SignAuthEntry/index.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; import { Button, Card, Icon, Notification } from "@stellar/design-system"; import { useLocation } from "react-router-dom"; import { useSelector } from "react-redux"; -import { useTranslation, Trans } from "react-i18next"; +import { useTranslation } from "react-i18next"; import { truncatedPublicKey } from "helpers/stellar"; import { HardwareSign } from "popup/components/hardwareConnect/HardwareSign"; @@ -14,11 +14,16 @@ import { FirstTimeWarningMessage, WarningMessageVariant, WarningMessage, + SSLWarningMessage, } from "popup/components/WarningMessages"; import { AuthEntry } from "popup/components/signAuthEntry/AuthEntry"; import { View } from "popup/basics/layout/View"; import { signEntry, rejectAuthEntry } from "popup/ducks/access"; -import { settingsExperimentalModeSelector } from "popup/ducks/settings"; +import { + isNonSSLEnabledSelector, + settingsExperimentalModeSelector, + settingsNetworkDetailsSelector, +} from "popup/ducks/settings"; import { ShowOverlayStatus } from "popup/ducks/transactionSubmission"; import { VerifyAccount } from "popup/views/VerifyAccount"; @@ -35,9 +40,13 @@ export const SignAuthEntry = () => { const isExperimentalModeEnabled = useSelector( settingsExperimentalModeSelector, ); + const isNonSSLEnabled = useSelector(isNonSSLEnabledSelector); + const { networkName, networkPassphrase } = useSelector( + settingsNetworkDetailsSelector, + ); const params = parsedSearchParam(location.search) as EntryToSign; - const { accountToSign } = params; + const { accountToSign, networkPassphrase: entryNetworkPassphrase } = params; const { allAccounts, @@ -77,25 +86,27 @@ export const SignAuthEntry = () => { ); } - if (!params.url.startsWith("https") && !isExperimentalModeEnabled) { + if (entryNetworkPassphrase && entryNetworkPassphrase !== networkPassphrase) { return ( window.close()} isActive - variant={WarningMessageVariant.warning} - header={t("WEBSITE CONNECTION IS NOT SECURE")} + header={`${t("Freighter is set to")} ${networkName}`} >

- - The website {params.url} does not use an SSL - certificate. For additional safety Freighter only works with - websites that provide an SSL certificate. - + {t("The requester expects you to sign this auth entry on")}{" "} + {entryNetworkPassphrase}.

+

{t("Signing this transaction is not possible at the moment.")}

); } + if (!params.url.startsWith("https") && !isNonSSLEnabled) { + return ; + } + return isPasswordRequired ? ( { +export const SignMessage = () => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const location = useLocation(); @@ -36,9 +41,19 @@ export const SignBlob = () => { const isExperimentalModeEnabled = useSelector( settingsExperimentalModeSelector, ); + const isNonSSLEnabled = useSelector(isNonSSLEnabledSelector); + const { networkName, networkPassphrase } = useSelector( + settingsNetworkDetailsSelector, + ); - const blob = parsedSearchParam(location.search) as BlobToSign; - const { accountToSign, domain, isDomainListedAllowed, url } = blob; + const message = parsedSearchParam(location.search) as MessageToSign; + const { + accountToSign, + domain, + isDomainListedAllowed, + url, + networkPassphrase: blobNetworkPassphrase, + } = message; const { allAccounts, @@ -54,7 +69,7 @@ export const SignBlob = () => { setIsPasswordRequired, verifyPasswordThenSign, hardwareWalletType, - } = useSetupSigningFlow(rejectBlob, signBlob, blob.blob, accountToSign); + } = useSetupSigningFlow(rejectBlob, signBlob, message.message, accountToSign); if (isHardwareWallet) { return ( @@ -73,25 +88,27 @@ export const SignBlob = () => { ); } - if (!url.startsWith("https") && !isExperimentalModeEnabled) { + if (blobNetworkPassphrase && blobNetworkPassphrase !== networkPassphrase) { return ( window.close()} isActive - variant={WarningMessageVariant.warning} - header={t("WEBSITE CONNECTION IS NOT SECURE")} + header={`${t("Freighter is set to")} ${networkName}`} >

- - The website {url} does not use an SSL certificate. - For additional safety Freighter only works with websites that - provide an SSL certificate. - + {t("The requester expects you to sign this message on")}{" "} + {blobNetworkPassphrase}.

+

{t("Signing this transaction is not possible at the moment.")}

); } + if (!url.startsWith("https") && !isNonSSLEnabled) { + return ; + } + return isPasswordRequired ? ( { >

{t( - "You are attempting to sign arbitrary data. Please use extreme caution and understand the implications of signing this data.", + "You are attempting to sign an arbitrary message. Please use extreme caution and understand the implications of signing this data.", )}

{!isDomainListedAllowed ? : null} -
+
-
- {t("is requesting approval to sign a blob of data")} +
+ {t("is requesting approval to sign a message")}
-
-
+
+
{t("Approve using")}:
setIsDropdownOpen(true)} > { imported={currentAccount.imported} /> -
+
{accountNotFound && accountToSign ? ( -
+
} @@ -177,7 +194,7 @@ export const SignBlob = () => {
) : null}
- +