diff --git a/e2e-tests/popup/transfers/casper-native-transfer.spec.ts b/e2e-tests/popup/transfers/casper-native-transfer.spec.ts index 9ace695ca..34d07429d 100644 --- a/e2e-tests/popup/transfers/casper-native-transfer.spec.ts +++ b/e2e-tests/popup/transfers/casper-native-transfer.spec.ts @@ -162,7 +162,7 @@ popup.describe('Popup UI: Casper Native Transfer', () => { await popupExpect( popupPage.getByRole('heading', { - name: 'failed to send http request, details: Code: 500, err: Internal Server Error' + name: 'Internal Server Error' }) ).toBeVisible(); await popupExpect( diff --git a/e2e-tests/popup/transfers/erc-20-transfer.spec.ts b/e2e-tests/popup/transfers/erc-20-transfer.spec.ts index b53e4969f..100c517dc 100644 --- a/e2e-tests/popup/transfers/erc-20-transfer.spec.ts +++ b/e2e-tests/popup/transfers/erc-20-transfer.spec.ts @@ -181,7 +181,7 @@ popup.describe('Popup UI: ERC-20 transfer', () => { await popupExpect( popupPage.getByRole('heading', { - name: 'failed to send http request, details: Code: 500, err: Internal Server Error' + name: 'Internal Server Error' }) ).toBeVisible(); await popupExpect( diff --git a/package-lock.json b/package-lock.json index 428afd10d..c49f3445d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,9 +26,9 @@ "@zondax/ledger-casper": "^2.6.3", "base64-loader": "1.0.0", "big.js": "^6.2.1", - "casper-js-sdk": "5.0.10", - "casper-wallet-core": "https://github.com/make-software/casper-wallet-core.git#v1.3.0", - "date-fns": "^2.30.0", + "casper-js-sdk": "5.0.12", + "casper-wallet-core": "git+ssh://git@github.com:make-software/casper-wallet-core.git#442723023778ee0bbfce3ca886015d1372a883e2", + "date-fns": "^4.1.0", "dotenv-webpack": "^8.1.0", "i18next": "^23.11.0", "i18next-browser-languagedetector": "^7.2.1", @@ -2532,6 +2532,16 @@ "uuid": "^11.0.3" } }, + "node_modules/@casper-ecosystem/casper-eip-712": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@casper-ecosystem/casper-eip-712/-/casper-eip-712-1.2.1.tgz", + "integrity": "sha512-qhrpaISByIoOnbELH7hhLMVBinIP1vOc4Z+6ZI5aItcc1O3SrDXNu0fdKm5qRSLLb5p94JxaS+jGegiIAwiKDA==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.8.0", + "@noble/hashes": "^1.7.0" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -5426,17 +5436,6 @@ "node": ">=10" } }, - "node_modules/@open-rpc/client-js": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@open-rpc/client-js/-/client-js-1.8.1.tgz", - "integrity": "sha512-vV+Hetl688nY/oWI9IFY0iKDrWuLdYhf7OIKI6U1DcnJV7r4gAgwRJjEr1QVYszUc0gjkHoQJzqevmXMGLyA0g==", - "dependencies": { - "isomorphic-fetch": "^3.0.0", - "isomorphic-ws": "^5.0.0", - "strict-event-emitter-types": "^2.0.0", - "ws": "^7.0.0" - } - }, "node_modules/@peculiar/asn1-cms": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz", @@ -5757,11 +5756,6 @@ "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", "dev": true }, - "node_modules/@react-native/typescript-config": { - "version": "0.74.83", - "resolved": "https://registry.npmjs.org/@react-native/typescript-config/-/typescript-config-0.74.83.tgz", - "integrity": "sha512-UTcZZYkSD+vv2O67bL/wu0GCGJP3BCbIxXd9ZewNkJmiWl5BbfoNl23+EjmDwM2V66gu24VB/RsSMn0TdmFs8Q==" - }, "node_modules/@redux-devtools/cli": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@redux-devtools/cli/-/cli-4.0.3.tgz", @@ -8124,6 +8118,7 @@ "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, "dependencies": { "@types/node": "*" } @@ -9330,6 +9325,28 @@ "dev": true, "license": "MIT" }, + "node_modules/addons-linter/node_modules/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/addons-linter/node_modules/semver": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", @@ -9377,6 +9394,34 @@ "node": ">=8" } }, + "node_modules/addons-linter/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/addons-linter/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/addons-linter/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/addons-moz-compare": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/addons-moz-compare/-/addons-moz-compare-1.3.0.tgz", @@ -11031,9 +11076,10 @@ "license": "CC-BY-4.0" }, "node_modules/casper-js-sdk": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/casper-js-sdk/-/casper-js-sdk-5.0.10.tgz", - "integrity": "sha512-dfJIB4N7UZdsT1sjqzHyL8QdBXZf0yzS1N8e4KTgs7eKI8kIpCPCeJ7cnyddWQQ9uvqYq29WY9gV9rsYj68T1Q==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/casper-js-sdk/-/casper-js-sdk-5.0.12.tgz", + "integrity": "sha512-2jzJ2joQRESSeQ1I7Za5w2fBfBSncTRl24roLtgX4zK1kzJj7YYJZEpVugtZaNa5ZgV3sJOp2qSSl6WYTkwniQ==", + "license": "Apache 2.0", "dependencies": { "@ethersproject/bignumber": "^5.0.8", "@ethersproject/bytes": "^5.0.5", @@ -11042,21 +11088,21 @@ "@noble/ed25519": "^1.7.3", "@noble/hashes": "^1.2.0", "@noble/secp256k1": "^1.7.1", - "@open-rpc/client-js": "^1.8.1", "@scure/bip32": "^1.1.5", "@scure/bip39": "^1.2.0", - "@types/ws": "^8.2.2", "asn1.js": "^5.4.1", - "axios": "^1.8.4", + "axios": "^1.15.0", "bn.js": "^5.2.1", "eventsource": "^2.0.2", "glob": "^7.1.6", "humanize-duration": "^3.24.0", - "lodash": "^4.17.21", "node-fetch": "2.6.13", "reflect-metadata": "^0.1.13", "ts-results": "npm:@casperlabs/ts-results@^3.3.4", "typedjson": "^1.6.0-rc2" + }, + "engines": { + "node": ">=18" } }, "node_modules/casper-js-sdk/node_modules/node-fetch": { @@ -11099,22 +11145,22 @@ }, "node_modules/casper-wallet-core": { "name": "CasperWalletCore", - "version": "1.2.0", - "resolved": "git+ssh://git@github.com/make-software/casper-wallet-core.git#ca6639b7b65f777bec21dde598dffe84d338e4b0", - "integrity": "sha512-+kP3ksjcgiOeSWtdoivhihp+/m3Gkyiq5S9e2zaFWVXyPDYgR2jv/QVSAGrVZ78D5m6G/w3ae3NRVhLrp1v93A==", + "version": "1.3.0", + "resolved": "git+ssh://git@github.com/make-software/casper-wallet-core.git#442723023778ee0bbfce3ca886015d1372a883e2", + "integrity": "sha512-Df4r1A1uFidPoEnUMVBXHD+ccKSiJstFq9OUkWQPKWrBz6vuBaIcpe2nyhnHIHKcTrBu2vteM/A0FG0p9OrelQ==", "dependencies": { + "@casper-ecosystem/casper-eip-712": "1.2.1", "@noble/hashes": "^1.8.0", - "@react-native/typescript-config": "0.74.83", "apisauce": "^3.2.2", - "casper-js-sdk": "5.0.10", - "date-fns": "^2.30.0", + "casper-js-sdk": "5.0.12", + "date-fns": "^4.4.0", "decimal.js": "^10.6.0", "deepmerge": "^4.3.1", - "lru-cache": "10.4.3", - "uuid": "^9.0.0" + "lru-cache": "11.5.1", + "uuid": "^14.0.0" }, "engines": { - "node": ">=18" + "node": "20 || >=22" } }, "node_modules/casper-wallet-core/node_modules/@noble/hashes": { @@ -11129,20 +11175,24 @@ } }, "node_modules/casper-wallet-core/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "engines": { + "node": "20 || >=22" + } }, "node_modules/casper-wallet-core/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", + "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist-node/bin/uuid" } }, "node_modules/chalk": { @@ -11878,6 +11928,23 @@ "node": ">=8" } }, + "node_modules/concurrently/node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", @@ -12849,18 +12916,13 @@ } }, "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz", + "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", + "license": "MIT", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" } }, "node_modules/dateformat": { @@ -17696,23 +17758,6 @@ "node": ">=0.10.0" } }, - "node_modules/isomorphic-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", - "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", - "dependencies": { - "node-fetch": "^2.6.1", - "whatwg-fetch": "^3.4.1" - } - }, - "node_modules/isomorphic-ws": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", - "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", - "peerDependencies": { - "ws": "*" - } - }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -26642,11 +26687,6 @@ "bare-events": "^2.2.0" } }, - "node_modules/strict-event-emitter-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz", - "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==" - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -29571,11 +29611,6 @@ "node": ">=0.10.0" } }, - "node_modules/whatwg-fetch": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" - }, "node_modules/whatwg-mimetype": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", @@ -29944,27 +29979,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/ws": { - "version": "7.5.11", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", - "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", - "license": "MIT", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/wsl-utils": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", diff --git a/package.json b/package.json index 91c4b4ad7..10ea78b0c 100644 --- a/package.json +++ b/package.json @@ -70,9 +70,9 @@ "@zondax/ledger-casper": "^2.6.3", "base64-loader": "1.0.0", "big.js": "^6.2.1", - "casper-js-sdk": "5.0.10", - "casper-wallet-core": "git+ssh://git@github.com:make-software/casper-wallet-core.git#v1.3.0", - "date-fns": "^2.30.0", + "casper-js-sdk": "5.0.12", + "casper-wallet-core": "git+ssh://git@github.com:make-software/casper-wallet-core.git#442723023778ee0bbfce3ca886015d1372a883e2", + "date-fns": "^4.1.0", "dotenv-webpack": "^8.1.0", "i18next": "^23.11.0", "i18next-browser-languagedetector": "^7.2.1", diff --git a/src/apps/signature-request/app-router.tsx b/src/apps/signature-request/app-router.tsx index 6dd6d305b..ce319513f 100644 --- a/src/apps/signature-request/app-router.tsx +++ b/src/apps/signature-request/app-router.tsx @@ -15,6 +15,7 @@ import { selectVaultIsLocked } from '@background/redux/session/selectors'; import { ErrorPath, LockedRouter, WindowErrorPage } from '@libs/layout'; import { DecryptMessagePage } from './pages/decrypt-message'; +import { SignEip712Page } from './pages/sign-eip712'; import { SignMessagePage } from './pages/sign-message'; import { SignTransactionPage } from './pages/sign-transaction'; @@ -31,6 +32,7 @@ export function AppRouter() { } /> } /> + } /> } diff --git a/src/apps/signature-request/pages/sign-eip712/eip712-display-row.tsx b/src/apps/signature-request/pages/sign-eip712/eip712-display-row.tsx new file mode 100644 index 000000000..e7e2ca588 --- /dev/null +++ b/src/apps/signature-request/pages/sign-eip712/eip712-display-row.tsx @@ -0,0 +1,118 @@ +import { + IEIP712DisplayRow, + getCasperNetworkByChainName +} from 'casper-wallet-core'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; + +import { DeployIcon } from '@src/constants'; + +import { ContractInfoRow } from '@popup/pages/deploy-details/components/common'; + +import { NetworkIconColor } from '@signature-request/pages/sign-transaction/signature-request-value'; + +import { AlignedFlexRow, SpaceBetweenFlexRow, SpacingSize } from '@libs/layout'; +import { Hash, HashVariant, SvgIcon, Typography } from '@libs/ui/components'; +import { AccountInfoRow } from '@libs/ui/components/account-info-row/account-info-row'; +import { capitalizeString } from '@libs/ui/utils'; + +const RowDataContainer = styled(SpaceBetweenFlexRow)` + margin: 16px; + width: unset; + gap: 8px; +`; + +interface Eip712DisplayRowProps { + row: IEIP712DisplayRow; +} + +export function Eip712DisplayRow({ row }: Eip712DisplayRowProps) { + if (row.contractPackage) { + return ( + + + {row.label} + + + + ); + } + + if (row.presentation === 'account') { + return ( + + + {row.label} + + + + ); + } + + return ( + + + {row.label} + + {row.presentation === 'hash' ? ( + + ) : ( + + {row.displayValue} + + )} + + ); +} + +interface Eip712NetworkRowProps { + chainName: string; +} + +export function Eip712NetworkRow({ chainName }: Eip712NetworkRowProps) { + const { t } = useTranslation(); + const network = getCasperNetworkByChainName(chainName); + + return ( + + + {t('Network')} + + + {network && ( + + )} + + {capitalizeString(network ?? chainName)} + + + + ); +} diff --git a/src/apps/signature-request/pages/sign-eip712/index.tsx b/src/apps/signature-request/pages/sign-eip712/index.tsx new file mode 100644 index 000000000..c8507a809 --- /dev/null +++ b/src/apps/signature-request/pages/sign-eip712/index.tsx @@ -0,0 +1,350 @@ +import { Conversions, PrivateKey, PublicKey } from 'casper-js-sdk'; +import { + IEIP712SignTypedDataOptions, + IEIP712TypedData +} from 'casper-wallet-core'; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState +} from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { shallowEqual, useSelector } from 'react-redux'; + +import { ErrorMessages } from '@src/constants'; +import { getPrivateKeyHexFromSecretKey, getSigningAccount } from '@src/utils'; + +import { useAccountManager } from '@popup/hooks/use-account-actions-with-events'; + +import { SignEip712Content } from '@signature-request/pages/sign-eip712/sign-eip712-content'; +import { SignatureRequestLoading } from '@signature-request/pages/sign-transaction/signature-request-loading'; +import { SignatureRequestRawJson } from '@signature-request/pages/sign-transaction/signature-request-raw-json'; + +import { closeCurrentWindow } from '@background/close-current-window'; +import { + selectConnectedAccountNamesByOrigin, + selectEip712JsonById, + selectVaultAccounts +} from '@background/redux/vault/selectors'; +import { + parseRequestTabId, + sendSdkResponseToSpecificTab +} from '@background/send-sdk-response-to-specific-tab'; +import { eip712Repository } from '@background/wallet-repositories'; + +import { sdkMethod } from '@content/sdk-method'; + +import { getAccountHashFromPublicKey } from '@libs/entities/Account'; +import { + FooterButtonsContainer, + HeaderPopup, + HeaderSubmenuBarNavLink, + LayoutWindow +} from '@libs/layout'; +import { useFetchAccountsInfo } from '@libs/services/account-info'; +import { useFetchAccountsBalances } from '@libs/services/balance-service'; +import { useFetchDataForEip712Request } from '@libs/services/signature-request-service'; +import { HardwareWalletType } from '@libs/types/account'; +import { + ApproveConnectionContent, + Button, + Typography +} from '@libs/ui/components'; +import { getFaviconUrlFromOrigin } from '@libs/ui/components/site-favicon-badge/site-favicon-badge'; + +export function SignEip712Page() { + const { t } = useTranslation(); + + const searchParams = new URLSearchParams(document.location.search); + const requestId = searchParams.get('requestId'); + const requestTabId = parseRequestTabId(searchParams); + const signingPublicKeyHex = searchParams.get('signingPublicKeyHex'); + const requestOrigin = searchParams.get('origin'); + + if ( + !requestId || + !signingPublicKeyHex || + !requestOrigin || + requestTabId == null + ) { + throw Error(ErrorMessages.signTransaction.MISSING_SEARCH_PARAM.description); + } + + const [showRawJson, setShowRawJson] = useState(false); + const responseSentRef = useRef(false); + + const accounts = useSelector(selectVaultAccounts, shallowEqual); + const eip712JsonById = useSelector(selectEip712JsonById, shallowEqual); + + const connectedAccountNames = useSelector( + selectConnectedAccountNamesByOrigin(requestOrigin), + shallowEqual + ); + + const { typedData, options, isInvalidPayload } = useMemo<{ + typedData?: IEIP712TypedData; + options?: IEIP712SignTypedDataOptions; + isInvalidPayload?: boolean; + }>(() => { + const raw = eip712JsonById[requestId]; + + if (!raw) { + return {}; + } + + try { + return JSON.parse(raw); + } catch { + return { isInvalidPayload: true }; + } + }, [eip712JsonById, requestId]); + + // Stored payload is present but not valid JSON — cannot proceed. + if (isInvalidPayload) { + const error = Error( + ErrorMessages.signTypedData.INVALID_TYPED_DATA.description + ); + sendSdkResponseToSpecificTab( + sdkMethod.signTypedDataError(error, { requestId }), + requestTabId + ); + throw error; + } + + const signingAccount = useMemo( + () => getSigningAccount(accounts, signingPublicKeyHex), + [accounts, signingPublicKeyHex] + ); + + // signing account should exist in wallet + if (!signingAccount) { + const error = Error( + ErrorMessages.signTransaction.SIGNING_ACCOUNT_MISSING.description + ); + sendSdkResponseToSpecificTab( + sdkMethod.signTypedDataError(error, { requestId }), + requestTabId + ); + throw error; + } + + // Ledger hardware wallets do not support EIP-712 typed data signing. + if (signingAccount.hardware === HardwareWalletType.Ledger) { + const error = Error( + ErrorMessages.signTypedData.LEDGER_NOT_SUPPORTED.description + ); + sendSdkResponseToSpecificTab( + sdkMethod.signTypedDataError(error, { requestId }), + requestTabId + ); + throw error; + } + + // Watch-only accounts have no secret key and cannot sign. + if (!signingAccount.secretKey) { + const error = Error( + ErrorMessages.signTransaction.SIGNING_ACCOUNT_MISSING.description + ); + sendSdkResponseToSpecificTab( + sdkMethod.signTypedDataError(error, { requestId }), + requestTabId + ); + throw error; + } + + const shouldTryToConnectAccount = + connectedAccountNames && + !connectedAccountNames.includes(signingAccount.name); + + const signingAccountHash = getAccountHashFromPublicKey( + signingAccount.publicKey + ); + + const { accountsBalances, isLoadingBalances } = useFetchAccountsBalances([ + signingAccountHash + ]); + + const accountsInfo = useFetchAccountsInfo([signingAccount.publicKey]); + + const { connectAnotherAccountWithEvent: connectAnotherAccount } = + useAccountManager(); + + const { signatureRequest, isLoadingSignatureRequest } = + useFetchDataForEip712Request({ + typedData, + options, + signingPublicKeyHex, + requestId, + requestTabId + }); + + const handleConnect = useCallback(async () => { + await connectAnotherAccount(signingAccount.name, requestOrigin); + }, [requestOrigin, connectAnotherAccount, signingAccount.name]); + + const handleCancel = useCallback(() => { + if (responseSentRef.current) return; + responseSentRef.current = true; + sendSdkResponseToSpecificTab( + sdkMethod.signTypedDataResponse( + { + cancelled: true, + signature: null, + digest: null, + publicKey: null, + error: null + }, + { requestId } + ), + requestTabId + ); + closeCurrentWindow(); + }, [requestId, requestTabId]); + + const handleSign = useCallback(() => { + if (!typedData || responseSentRef.current) { + return; + } + + try { + // EIP-712 supports software-key signing only; Ledger accounts are rejected earlier. + const publicKey = PublicKey.fromHex(signingAccount.publicKey); + const privateKey = PrivateKey.fromHex( + getPrivateKeyHexFromSecretKey( + Conversions.base64to16(signingAccount.secretKey) + ), + publicKey.cryptoAlg + ); + + const result = eip712Repository.signTypedData({ + typedData, + privateKey, + options + }); + + responseSentRef.current = true; + sendSdkResponseToSpecificTab( + sdkMethod.signTypedDataResponse( + { + cancelled: false, + signature: result.signature, + digest: result.digest, + publicKey: result.publicKey, + hashArtifacts: result.hashArtifacts, + error: null + }, + { requestId } + ), + requestTabId + ); + closeCurrentWindow(); + } catch { + responseSentRef.current = true; + sendSdkResponseToSpecificTab( + sdkMethod.signTypedDataError( + Error(ErrorMessages.signTypedData.SIGNING_FAILED.description), + { requestId } + ), + requestTabId + ); + closeCurrentWindow(); + } + }, [ + typedData, + options, + signingAccount.publicKey, + signingAccount.secretKey, + requestId, + requestTabId + ]); + + useEffect(() => { + window.addEventListener('beforeunload', handleCancel); + + return () => window.removeEventListener('beforeunload', handleCancel); + }, [handleCancel]); + + const renderFooter = () => { + if (shouldTryToConnectAccount) { + return () => ( + + + Only connect with sites you trust + + + + + ); + } + + return () => ( + + + + + ); + }; + + return ( + ( + ( + setShowRawJson(false)} + /> + ) + : undefined + } + /> + )} + renderContent={() => { + if (shouldTryToConnectAccount) { + return ( + + ); + } + + return isLoadingSignatureRequest || !signatureRequest ? ( + + ) : showRawJson ? ( + + ) : ( + setShowRawJson(true)} + /> + ); + }} + renderFooter={renderFooter()} + /> + ); +} diff --git a/src/apps/signature-request/pages/sign-eip712/sign-eip712-content.tsx b/src/apps/signature-request/pages/sign-eip712/sign-eip712-content.tsx new file mode 100644 index 000000000..4fab6a6bd --- /dev/null +++ b/src/apps/signature-request/pages/sign-eip712/sign-eip712-content.tsx @@ -0,0 +1,145 @@ +import { IEIP712DisplayRow, IEIP712SignatureRequest } from 'casper-wallet-core'; +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import styled from 'styled-components'; + +import { + AlignedFlexRow, + ContentContainer, + FlexColumn, + PageContainer, + SpacingSize, + VerticalSpaceContainer +} from '@libs/layout'; +import { List, SvgIcon, Tile, Typography } from '@libs/ui/components'; +import { MaybeLink } from '@libs/ui/components/maybe-link/maybe-link'; +import { getFaviconUrlFromOrigin } from '@libs/ui/components/site-favicon-badge/site-favicon-badge'; + +import { Eip712DisplayRow, Eip712NetworkRow } from './eip712-display-row'; + +const RowDataContainer = styled(AlignedFlexRow)` + margin: 16px; + width: unset; + min-height: 56px; + cursor: pointer; +`; + +const Favicon = styled.img` + width: 40px; + height: 40px; + + object-fit: contain; + object-position: center; + + border-radius: ${({ theme }) => theme.borderRadius.twenty}px; +`; + +interface SignEip712ContentProps { + signatureRequest: IEIP712SignatureRequest; + origin: string | null; + handlePressShowRawJson: () => void; +} + +export function SignEip712Content({ + signatureRequest, + origin, + handlePressShowRawJson +}: SignEip712ContentProps) { + const { t } = useTranslation(); + const faviconUrl = getFaviconUrlFromOrigin(origin); + + const networkRow: IEIP712DisplayRow = { + label: t('Network'), + value: signatureRequest.chainName, + displayValue: signatureRequest.chainName, + presentation: 'string', + type: '', + copyValue: null, + accountInfo: null, + contractPackage: null + }; + + return ( + + + + + {faviconUrl && ( + <> + + + )} + + + Signature Request + + + + {origin} + + + + + + + ({ + id: `domain-${index}-${row.label}`, + label: row.label, + value: row + })) + ]} + renderRow={({ id, value }) => + id === 'domain-network' ? ( + + ) : ( + + ) + } + marginLeftForItemSeparatorLine={16} + /> + + ({ + id: `message-${index}-${row.label}`, + label: row.label, + value: row + }))} + renderRow={({ value }) => } + marginLeftForItemSeparatorLine={16} + /> + + + + + Show raw data + + + + + + + ); +} diff --git a/src/apps/signature-request/router/paths.ts b/src/apps/signature-request/router/paths.ts index 7d77ecae0..c514c1ad8 100644 --- a/src/apps/signature-request/router/paths.ts +++ b/src/apps/signature-request/router/paths.ts @@ -2,5 +2,6 @@ export enum RouterPath { Any = '*', SignDeploy = '/sign-deploy', SignMessage = '/sign-message', + SignEip712 = '/sign-eip712', DecryptMessage = '/decrypt-message' } diff --git a/src/background/create-open-window.ts b/src/background/create-open-window.ts index f4909190d..40bcd4bcc 100644 --- a/src/background/create-open-window.ts +++ b/src/background/create-open-window.ts @@ -8,6 +8,7 @@ export enum WindowApp { SwitchAccount = 'SwitchAccount', SignatureRequestDeploy = 'SignatureRequestDeploy', SignatureRequestMessage = 'SignatureRequestMessage', + SignatureRequestEip712 = 'SignatureRequestEip712', DecryptMessageRequest = 'DecryptMessageRequest' } @@ -34,6 +35,8 @@ function getUrlByWindowApp( return `signature-request.html${searchParamsWithPrefix}#${RouterPath.SignDeploy}`; case WindowApp.SignatureRequestMessage: return `signature-request.html${searchParamsWithPrefix}#${RouterPath.SignMessage}`; + case WindowApp.SignatureRequestEip712: + return `signature-request.html${searchParamsWithPrefix}#${RouterPath.SignEip712}`; case WindowApp.DecryptMessageRequest: return `signature-request.html${searchParamsWithPrefix}#${RouterPath.DecryptMessage}`; default: diff --git a/src/background/index.ts b/src/background/index.ts index c9e31d922..3c68a22f3 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -90,6 +90,7 @@ import { anotherAccountConnected, deployPayloadReceived, deploysReseted, + eip712PayloadReceived, hideAccountFromListChanged, secretPhraseCreated, siteConnected, @@ -467,6 +468,40 @@ runtime.onMessage.addListener( return sendResponse(undefined); } + case getType(sdkMethod.signTypedDataRequest): { + const origin = getUrlOrigin(sender.url); + if (!origin) { + return sendError(CannotGetSenderOriginError()); + } + + const senderTabId = sender.tab?.id; + + if (senderTabId == null) { + return sendError(Error('Missing sender tab id')); + } + + const { signingPublicKeyHex, typedData, options } = action.payload; + + store.dispatch( + eip712PayloadReceived({ + id: action.meta.requestId, + json: JSON.stringify({ typedData, options }) + }) + ); + + openWindow({ + windowApp: WindowApp.SignatureRequestEip712, + searchParams: { + requestId: action.meta.requestId, + signingPublicKeyHex, + origin, + tabId: String(senderTabId) + } + }); + + return sendResponse(undefined); + } + case getType(sdkMethod.decryptMessageRequest): { const origin = getUrlOrigin(sender.url); diff --git a/src/background/redux/sagas/check-casper2-network-saga.ts b/src/background/redux/sagas/check-casper2-network-saga.ts index 848d3381a..f41069a55 100644 --- a/src/background/redux/sagas/check-casper2-network-saga.ts +++ b/src/background/redux/sagas/check-casper2-network-saga.ts @@ -1,5 +1,4 @@ -import { HttpHandler, RpcClient } from 'casper-js-sdk'; -import { InfoGetStatusResult } from 'casper-js-sdk/dist/rpc/response'; +import { HttpHandler, InfoGetStatusResult, RpcClient } from 'casper-js-sdk'; import { call, put, select, takeLatest } from 'redux-saga/effects'; import { getType } from 'typesafe-actions'; diff --git a/src/background/redux/sagas/vault-sagas.ts b/src/background/redux/sagas/vault-sagas.ts index 832953e45..bfdefa2cc 100644 --- a/src/background/redux/sagas/vault-sagas.ts +++ b/src/background/redux/sagas/vault-sagas.ts @@ -52,6 +52,7 @@ import { anotherAccountConnected, deployPayloadReceived, deploysReseted, + eip712PayloadReceived, hideAccountFromListChanged, siteConnected, siteDisconnected, @@ -103,6 +104,7 @@ export function* vaultSagas() { getType(activeAccountChanged), getType(activeTimeoutDurationSettingChanged), getType(deployPayloadReceived), + getType(eip712PayloadReceived), getType(hideAccountFromListChanged) ], updateVaultCipher diff --git a/src/background/redux/vault/actions.ts b/src/background/redux/vault/actions.ts index 9919d8471..60ac9778a 100644 --- a/src/background/redux/vault/actions.ts +++ b/src/background/redux/vault/actions.ts @@ -69,6 +69,11 @@ export const deployPayloadReceived = createAction('DEPLOY_PAYLOAD_RECEIVED')<{ json: string; }>(); +export const eip712PayloadReceived = createAction('EIP712_PAYLOAD_RECEIVED')<{ + id: string; + json: string; +}>(); + export const hideAccountFromListChanged = createAction( 'HIDE_ACCOUNT_FROM_LIST_CHANGED' )<{ diff --git a/src/background/redux/vault/reducer.ts b/src/background/redux/vault/reducer.ts index ad965d8c8..0b8c51bcc 100644 --- a/src/background/redux/vault/reducer.ts +++ b/src/background/redux/vault/reducer.ts @@ -14,6 +14,7 @@ import { anotherAccountConnected, deployPayloadReceived, deploysReseted, + eip712PayloadReceived, hideAccountFromListChanged, secretPhraseCreated, siteConnected, @@ -31,7 +32,8 @@ const initialState: State = { accountNamesByOriginDict: {}, siteNameByOriginDict: {}, activeAccountName: null, - jsonById: {} + jsonById: {}, + eip712ById: {} }; export const reducer = createReducer(initialState) @@ -47,7 +49,8 @@ export const reducer = createReducer(initialState) accounts, activeAccountName, secretPhrase, - jsonById + jsonById, + eip712ById } }: ReturnType ) => ({ @@ -57,7 +60,11 @@ export const reducer = createReducer(initialState) activeAccountName, secretPhrase, jsonById: - Object.keys(state.jsonById).length === 0 ? jsonById : state.jsonById + Object.keys(state.jsonById).length === 0 ? jsonById : state.jsonById, + eip712ById: + Object.keys(state.eip712ById).length === 0 + ? eip712ById + : state.eip712ById }) ) .handleAction( @@ -304,6 +311,13 @@ export const reducer = createReducer(initialState) jsonById: { [payload.id]: payload.json } }) ) + .handleAction( + eip712PayloadReceived, + (state, { payload }: ReturnType): State => ({ + ...state, + eip712ById: { [payload.id]: payload.json } + }) + ) .handleAction( hideAccountFromListChanged, ( diff --git a/src/background/redux/vault/selectors.ts b/src/background/redux/vault/selectors.ts index 85ea1c6b0..48583057e 100644 --- a/src/background/redux/vault/selectors.ts +++ b/src/background/redux/vault/selectors.ts @@ -242,3 +242,6 @@ export const selectCountOfConnectedSites = createSelector( ); export const selectDeploysJsonById = (state: RootState) => state.vault.jsonById; + +export const selectEip712JsonById = (state: RootState) => + state.vault.eip712ById; diff --git a/src/background/redux/vault/types.ts b/src/background/redux/vault/types.ts index 4534307a3..d82862dbd 100644 --- a/src/background/redux/vault/types.ts +++ b/src/background/redux/vault/types.ts @@ -10,4 +10,5 @@ export type VaultState = { siteNameByOriginDict: SiteNameByOriginDict; activeAccountName: string | null; jsonById: Record; + eip712ById: Record; }; diff --git a/src/background/wallet-repositories.ts b/src/background/wallet-repositories.ts index 3586beb6d..97108119e 100644 --- a/src/background/wallet-repositories.ts +++ b/src/background/wallet-repositories.ts @@ -9,7 +9,8 @@ const { onRampRepository, appEventsRepository, txSignatureRequestRepository, - contractPackageRepository + contractPackageRepository, + eip712Repository } = setupRepositories(); export { @@ -21,5 +22,6 @@ export { onRampRepository, appEventsRepository, txSignatureRequestRepository, - contractPackageRepository + contractPackageRepository, + eip712Repository }; diff --git a/src/constants.ts b/src/constants.ts index af2044465..488695175 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -406,5 +406,22 @@ export const ErrorMessages = { description: 'Ledger hardware wallets do not support message decryption. Use a software wallet instead.' } + }, + signTypedData: { + LEDGER_NOT_SUPPORTED: { + message: 'Typed data signing with Ledger is not supported', + description: + 'Ledger hardware wallets do not support EIP-712 typed data signing. Use a software wallet instead.' + }, + SIGNING_FAILED: { + message: 'Failed to sign the typed data', + description: + 'An unexpected error occurred while signing the EIP-712 typed data. Please try again.' + }, + INVALID_TYPED_DATA: { + message: 'Invalid Typed Data', + description: + 'The EIP-712 typed data provided is invalid and cannot be processed. Please check the input and try again.' + } } }; diff --git a/src/content/index.ts b/src/content/index.ts index d3eb8c22b..71cbbeb7c 100644 --- a/src/content/index.ts +++ b/src/content/index.ts @@ -24,6 +24,8 @@ async function handleSdkMessage(message: SdkEvent | SdkMethod) { case getType(sdkMethod.signResponse): case getType(sdkMethod.signMessageError): case getType(sdkMethod.signMessageResponse): + case getType(sdkMethod.signTypedDataResponse): + case getType(sdkMethod.signTypedDataError): case getType(sdkMethod.decryptMessageResponse): case getType(sdkMethod.decryptMessageError): case getType(sdkMethod.encryptMessageResponse): diff --git a/src/content/sdk-method.ts b/src/content/sdk-method.ts index 4b56eb67a..c2f3b90d3 100644 --- a/src/content/sdk-method.ts +++ b/src/content/sdk-method.ts @@ -1,6 +1,7 @@ import { ActionType, createAction, createCustomAction } from 'typesafe-actions'; import { SdkError } from './sdk-errors'; +import type { SignTypedDataParams, SignTypedDataResult } from './sdk-types'; export const SdkMethodEventType = { Request: 'CasperWalletMethod:Request', @@ -60,6 +61,21 @@ export const sdkMethod = { Error, Meta >(), + signTypedDataRequest: createAction('CasperWalletProvider:SignTypedData')< + { + typedData: SignTypedDataParams['typedData']; + options?: SignTypedDataParams['options']; + signingPublicKeyHex: string; + }, + Meta + >(), + signTypedDataResponse: createAction( + 'CasperWalletProvider:SignTypedData:Response' + )(), + signTypedDataError: createAction('CasperWalletProvider:SignTypedData:Error')< + Error, + Meta + >(), encryptMessageRequest: createAction('CasperWalletProvider:EncryptMessage')< { message: string; diff --git a/src/content/sdk-types.ts b/src/content/sdk-types.ts index 80e55f68c..cb15c1139 100644 --- a/src/content/sdk-types.ts +++ b/src/content/sdk-types.ts @@ -16,6 +16,40 @@ export enum CasperWalletSupports { signDeploy = 'sign-deploy', signTransactionV1 = 'sign-transactionv1', signMessage = 'sign-message', + signTypedData = 'sign-typed-data', messageEncryption = 'message-encryption', messageDecryption = 'message-decryption' } + +export interface SignTypedDataParams { + typedData: { + domain: Record; + types: Record>; + primaryType: string; + message: Record; + }; + options?: { + domainTypes?: Array<{ name: string; type: string }>; + returnHashArtifacts?: boolean; + rejectUnknownFields?: boolean; + }; +} + +export interface EIP712HashArtifacts { + domainTypeString?: string; + domain?: Record; + domainSeparator?: string; + canonicalTypeString?: string; + typeHash?: string; + structHash?: string; +} + +export interface SignTypedDataResult { + cancelled: boolean; + signature: string | null; + digest: string | null; + publicKey: string | null; + error: string | null; + errorCode?: string; + hashArtifacts?: EIP712HashArtifacts; +} diff --git a/src/content/sdk.ts b/src/content/sdk.ts index 6966f0fae..182f201cd 100644 --- a/src/content/sdk.ts +++ b/src/content/sdk.ts @@ -7,6 +7,7 @@ import { isSDKMethod, sdkMethod } from './sdk-method'; +import { SignTypedDataParams, SignTypedDataResult } from './sdk-types'; export type SignatureResponse = | { @@ -202,6 +203,25 @@ export const CasperWalletProvider = (options?: CasperWalletProviderOptions) => { }; }); }, + signTypedData: ( + params: SignTypedDataParams, + signingPublicKeyHex: string + ): Promise => { + return fetchFromBackground< + ReturnType<(typeof sdkMethod)['signTypedDataResponse']>['payload'] + >( + sdkMethod.signTypedDataRequest( + { + typedData: params.typedData, + options: params.options, + signingPublicKeyHex + }, + { + requestId: generateRequestId() + } + ) + ); + }, /** * Get the encrypted message from the Casper Wallet extension * @returns returns an encrypted message. diff --git a/src/fixtures/initial-state-for-popup-tests.ts b/src/fixtures/initial-state-for-popup-tests.ts index 632c3cdc0..8ab960ddf 100644 --- a/src/fixtures/initial-state-for-popup-tests.ts +++ b/src/fixtures/initial-state-for-popup-tests.ts @@ -69,7 +69,8 @@ export const initialStateForPopupTests: RootState = { accountNamesByOriginDict: {}, siteNameByOriginDict: {}, activeAccountName: 'Account 1', - jsonById: {} + jsonById: {}, + eip712ById: {} }, windowManagement: { windowId: null diff --git a/src/libs/services/signature-request-service/index.ts b/src/libs/services/signature-request-service/index.ts index 41c6e24e9..d88498435 100644 --- a/src/libs/services/signature-request-service/index.ts +++ b/src/libs/services/signature-request-service/index.ts @@ -1 +1,2 @@ +export * from './use-fetch-data-for-eip712-request'; export * from './use-fetch-data-for-signature-request'; diff --git a/src/libs/services/signature-request-service/use-fetch-data-for-eip712-request.ts b/src/libs/services/signature-request-service/use-fetch-data-for-eip712-request.ts new file mode 100644 index 000000000..d871c01ed --- /dev/null +++ b/src/libs/services/signature-request-service/use-fetch-data-for-eip712-request.ts @@ -0,0 +1,64 @@ +import { useQuery } from '@tanstack/react-query'; +import { + IEIP712SignTypedDataOptions, + IEIP712TypedData +} from 'casper-wallet-core'; + +import { ErrorMessages } from '@src/constants'; + +import { sendSdkResponseToSpecificTab } from '@background/send-sdk-response-to-specific-tab'; +import { eip712Repository } from '@background/wallet-repositories'; + +import { sdkMethod } from '@content/sdk-method'; + +interface IUseFetchDataForEip712RequestParams { + typedData?: IEIP712TypedData; + options?: IEIP712SignTypedDataOptions; + signingPublicKeyHex: string; + requestId: string; + requestTabId: number; +} + +export const useFetchDataForEip712Request = ({ + typedData, + options, + signingPublicKeyHex, + requestId, + requestTabId +}: IUseFetchDataForEip712RequestParams) => { + const { data: signatureRequest, isFetching: isLoadingSignatureRequest } = + useQuery({ + queryKey: [ + 'FetchDataForEip712Request', + typedData, + signingPublicKeyHex, + requestId + ], + queryFn: async () => { + try { + if (typedData) { + return eip712Repository.prepareSignatureRequest({ + typedData, + signingPublicKeyHex, + options, + withProxyHeader: false + }); + } + } catch (e) { + const error = Error( + ErrorMessages.signTransaction.INVALID_TRANSACTION_JSON.description + ); + sendSdkResponseToSpecificTab( + sdkMethod.signTypedDataError(error, { requestId }), + requestTabId + ); + throw error; + } + }, + enabled: Boolean(typedData && signingPublicKeyHex), + refetchInterval: false, + throwOnError: true + }); + + return { signatureRequest, isLoadingSignatureRequest }; +}; diff --git a/src/libs/services/tx-builders.ts b/src/libs/services/tx-builders.ts index d15852d47..c072966dc 100644 --- a/src/libs/services/tx-builders.ts +++ b/src/libs/services/tx-builders.ts @@ -1,5 +1,9 @@ import { Deploy, + IMakeAuctionManagerDeployParams, + IMakeCep18TransferDeployParams, + IMakeCsprTransferDeployParams, + IMakeNftTransferDeployParams, makeAuctionManagerDeploy, makeAuctionManagerTransaction, makeCep18TransferDeploy, @@ -7,10 +11,6 @@ import { makeCsprTransferDeploy, makeCsprTransferTransaction } from 'casper-js-sdk'; -import { IMakeAuctionManagerDeployParams } from 'casper-js-sdk/dist/utils/auction-manager'; -import { IMakeCep18TransferDeployParams } from 'casper-js-sdk/dist/utils/cep-18-transfer'; -import { IMakeNftTransferDeployParams } from 'casper-js-sdk/dist/utils/cep-nft-transfer'; -import { IMakeCsprTransferDeployParams } from 'casper-js-sdk/dist/utils/cspr-transfer'; import { NFTTokenStandard, makeNftTransferTransaction diff --git a/src/libs/ui/utils/formatters.ts b/src/libs/ui/utils/formatters.ts index 2441cbfbf..eb3988f01 100644 --- a/src/libs/ui/utils/formatters.ts +++ b/src/libs/ui/utils/formatters.ts @@ -1,9 +1,7 @@ import { createIntl, createIntlCache } from '@formatjs/intl'; import Big from 'big.js'; -// eslint-disable-next-line import/no-duplicates -import { formatDistanceToNowStrict } from 'date-fns'; -// eslint-disable-next-line import/no-duplicates -import en from 'date-fns/locale/en-US'; +import { type FormatDistanceToken, formatDistanceToNowStrict } from 'date-fns'; +import { enUS as en } from 'date-fns/locale/en-US'; import { MOTES_PER_CSPR_RATE } from '@src/constants'; @@ -17,15 +15,23 @@ const intl = createIntl( cache ); -const formatDistanceTokens = { +const formatDistanceTokens: Record = { lessThanXSeconds: 'second', xSeconds: 'second', + halfAMinute: 'minute', lessThanXMinutes: 'minute', xMinutes: 'minute', + aboutXHours: 'hour', xHours: 'hour', xDays: 'day', + aboutXWeeks: 'week', + xWeeks: 'week', + aboutXMonths: 'month', xMonths: 'month', - xYears: 'year' + aboutXYears: 'year', + xYears: 'year', + overXYears: 'year', + almostXYears: 'year' }; export const formatTimestamp = (value: string): string => { @@ -73,7 +79,7 @@ export const formatDate = (isoString: string): string => { }; const formatDistance = ( - token: keyof typeof formatDistanceTokens, + token: FormatDistanceToken, count: number, options: any ) => { diff --git a/src/utils.ts b/src/utils.ts index 1f8928fb0..7b55c8951 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -211,6 +211,7 @@ export const getActiveAccountSupports = (activeAccount: Account) => { : [ CasperWalletSupports.signDeploy, CasperWalletSupports.signMessage, + CasperWalletSupports.signTypedData, CasperWalletSupports.signTransactionV1, CasperWalletSupports.messageEncryption, CasperWalletSupports.messageDecryption