From 01274d7dd8b3cbb806b7e99924796f4867d65522 Mon Sep 17 00:00:00 2001 From: lbqds Date: Thu, 24 Oct 2024 08:07:29 +0800 Subject: [PATCH 1/6] Support signing unsigned txs using ledger accounts --- .../src/background/actionHandlers.ts | 53 +++++++--- .../src/ui/features/actions/ActionScreen.tsx | 4 +- .../actions/ApproveSignUnsignedTxScreen.tsx | 47 ++++++++- .../actions/ApproveTransactionScreen.tsx | 99 ++++--------------- .../src/ui/features/actions/LedgerStatus.tsx | 97 ++++++++++++++++++ 5 files changed, 199 insertions(+), 101 deletions(-) create mode 100644 packages/extension/src/ui/features/actions/LedgerStatus.tsx diff --git a/packages/extension/src/background/actionHandlers.ts b/packages/extension/src/background/actionHandlers.ts index 5546b11d..8a65bf02 100644 --- a/packages/extension/src/background/actionHandlers.ts +++ b/packages/extension/src/background/actionHandlers.ts @@ -1,3 +1,4 @@ +import { binToHex, codec, groupIndexOfTransaction, hexToBinUnsafe } from "@alephium/web3" import { getAccounts } from "../shared/account/store" import { ActionItem, @@ -14,6 +15,7 @@ import { BackgroundService } from "./background" import { openUi } from "./openUi" import { executeTransactionAction } from "./transactions/transactionExecution" import { transactionWatcher } from "./transactions/transactionWatcher" +import blake from 'blakejs' export const handleActionApproval = async ( action: ExtQueueItem, @@ -94,20 +96,29 @@ export const handleActionApproval = async ( } case "ALPH_SIGN_UNSIGNED_TX": { + const { signatureOpt } = additionalData as { signatureOpt: string | undefined } try { - const account = await wallet.getAccount({ - address: action.payload.signerAddress, - networkId: action.payload.networkId, - }) - if (!account) { - throw Error("No selected account") - } - - const result = await wallet.signUnsignedTx(account, action.payload) - - return { - type: "ALPH_SIGN_UNSIGNED_TX_SUCCESS", - data: { actionHash, result }, + if (signatureOpt === undefined) { + const account = await wallet.getAccount({ + address: action.payload.signerAddress, + networkId: action.payload.networkId, + }) + if (!account) { + throw Error("No selected account") + } + + const result = await wallet.signUnsignedTx(account, action.payload) + + return { + type: "ALPH_SIGN_UNSIGNED_TX_SUCCESS", + data: { actionHash, result }, + } + } else { + const result = { signature: signatureOpt, ...buildUnsignedTx(action.payload.unsignedTx) } + return { + type: "ALPH_SIGN_UNSIGNED_TX_SUCCESS", + data: { actionHash, result }, + } } } catch (error) { return { @@ -256,3 +267,19 @@ export const handleActionRejection = async ( assertNever(action) } } + +// TODO: use the function from web3-sdk +function buildUnsignedTx(unsignedTx: string) { + const unsignedTxBin = hexToBinUnsafe(unsignedTx) + const decoded = codec.unsignedTxCodec.decode(unsignedTxBin) + const txId = binToHex(blake.blake2b(unsignedTxBin, undefined, 32)) + const [fromGroup, toGroup] = groupIndexOfTransaction(decoded) + return { + fromGroup: fromGroup, + toGroup: toGroup, + unsignedTx, + txId, + gasAmount: decoded.gasAmount, + gasPrice: decoded.gasPrice + } +} diff --git a/packages/extension/src/ui/features/actions/ActionScreen.tsx b/packages/extension/src/ui/features/actions/ActionScreen.tsx index 89d6aa3e..275ffc28 100644 --- a/packages/extension/src/ui/features/actions/ActionScreen.tsx +++ b/packages/extension/src/ui/features/actions/ActionScreen.tsx @@ -207,8 +207,8 @@ export const ActionScreen: FC = () => { return ( { - await approveAction(action) + onSubmit={async (signatureOpt) => { + await approveAction(action, signatureOpt) useAppState.setState({ isLoading: true }) const result = await Promise.race([ waitForMessage( diff --git a/packages/extension/src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx b/packages/extension/src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx index 395b3d44..73d8c9a8 100644 --- a/packages/extension/src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx +++ b/packages/extension/src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx @@ -1,6 +1,6 @@ import { binToHex, hexToBinUnsafe, SignUnsignedTxParams } from "@alephium/web3" import { H6, H2, P4, CopyTooltip } from "@argent/ui" -import { FC } from "react" +import { FC, useCallback } from "react" import { ConfirmPageProps } from "./DeprecatedConfirmScreen" import { Box, Flex, VStack } from "@chakra-ui/react" @@ -9,30 +9,69 @@ import { AccountNetworkInfo } from "./transaction/AccountNetworkInfo" import blake from 'blakejs' import { TxHashContainer } from "./TxHashContainer" import { useTranslation } from "react-i18next" +import { getConfirmationTextByState, LedgerStatus, useLedgerApp } from "./LedgerStatus" +import { useNavigate } from "react-router-dom" interface ApproveSignUnsignedTxScreenProps extends Omit { params: SignUnsignedTxParams & { host: string } - onSubmit: (data: SignUnsignedTxParams) => void + onSubmit: (result: { signatureOpt: string | undefined }) => void } export const ApproveSignUnsignedTxScreen: FC = ({ params, onSubmit, + onReject, selectedAccount, ...props }) => { const { t } = useTranslation() const txId = binToHex(blake.blake2b(hexToBinUnsafe(params.unsignedTx), undefined, 32)) + + const navigate = useNavigate() + const useLedger = selectedAccount !== undefined && selectedAccount.signer.type === "ledger" + const ledgerSubmit = useCallback((signature: string) => { + onSubmit({ signatureOpt: signature }) + }, [onSubmit]) + const { ledgerState, ledgerApp, ledgerSign } = useLedgerApp({ + selectedAccount, + unsignedTx: params.unsignedTx, + onSubmit: ledgerSubmit, + navigate, + onReject + }) + return ( onSubmit(params)} + onSubmit={() => { + if (useLedger) { + ledgerSign() + } else { + onSubmit({ signatureOpt: undefined }) + } + }} + onReject={() => { + if (ledgerApp !== undefined) { + ledgerApp.close() + } + if (onReject !== undefined) { + onReject() + } else { + navigate(-1) + } + }} + footer={ + + + + } {...props} > { diff --git a/packages/extension/src/ui/features/actions/ApproveTransactionScreen.tsx b/packages/extension/src/ui/features/actions/ApproveTransactionScreen.tsx index 6980099f..14204f48 100644 --- a/packages/extension/src/ui/features/actions/ApproveTransactionScreen.tsx +++ b/packages/extension/src/ui/features/actions/ApproveTransactionScreen.tsx @@ -1,6 +1,5 @@ import { ALPH_TOKEN_ID, ONE_ALPH, prettifyTokenAmount, TransactionBuilder } from "@alephium/web3" -import { L1, icons } from "@argent/ui" -import { Flex, Text } from "@chakra-ui/react" +import { Flex } from "@chakra-ui/react" import { FC, useCallback, useEffect, useState } from "react" import { Navigate, useNavigate } from "react-router-dom" @@ -15,7 +14,6 @@ import { usePageTracking } from "../../services/analytics" import { rejectAction } from "../../services/backgroundActions" import { Account } from "../accounts/Account" import { useAllTokensWithBalance } from "../accountTokens/tokens.state" -import { LedgerAlephium } from "../ledger/utils" import { useNetwork } from "../networks/useNetworks" import { ConfirmScreen } from "./ConfirmScreen" import { ConfirmPageProps } from "./DeprecatedConfirmScreen" @@ -30,31 +28,7 @@ import { BigNumber } from "ethers" import { addTokenToBalances } from "../../../shared/token/balance" import { useTranslation } from "react-i18next" import i18n from "../../../i18n" - -const { AlertIcon } = icons - -const LedgerStatus = ({ ledgerState }: { ledgerState: string | undefined }): JSX.Element => { - const { t } = useTranslation() - return ( - ledgerState === "notfound" ? - - - - - - {t("The Ledger app is not connected")} - - - : <> - ) -} +import { getConfirmationTextByState, LedgerStatus, useLedgerApp } from "./LedgerStatus" const minimalGasFee = BigInt(20000) * BigInt(100000000000) @@ -207,14 +181,22 @@ export const ApproveTransactionScreen: FC = ({ selectedAccount?.networkId ?? "unknown", ) - const useLedger = - selectedAccount !== undefined && selectedAccount.signer.type === "ledger" - const [ledgerState, setLedgerState] = useState< - "detecting" | "notfound" | "signing" | "succeeded" | "failed" - >() - const [ledgerApp, setLedgerApp] = useState() const { tokenDetails: allUserTokens, tokenDetailsIsInitialising } = useAllTokensWithBalance(selectedAccount) + const useLedger = selectedAccount !== undefined && selectedAccount.signer.type === "ledger" + const ledgerSubmit = useCallback((signature: string) => { + if (buildResult) { + onSubmit({ ...buildResult, signature }) + } + }, [onSubmit, buildResult]) + const { ledgerState, ledgerApp, ledgerSign } = useLedgerApp({ + selectedAccount, + unsignedTx: buildResult?.result.unsignedTx, + onSubmit: ledgerSubmit, + navigate, + onReject + }) + // TODO: handle error useEffect(() => { const build = async () => { @@ -243,39 +225,6 @@ export const ApproveTransactionScreen: FC = ({ build() }, [nodeUrl, selectedAccount, transaction, tokenDetailsIsInitialising, actionHash, navigate, t]) - const ledgerSign = useCallback(async () => { - if (selectedAccount === undefined) { - return - } - setLedgerState(oldState => oldState === undefined ? "detecting" : oldState) - - if (buildResult) { - let app: LedgerAlephium | undefined - try { - app = await LedgerAlephium.create() - setLedgerApp(app) - setLedgerState("signing") - const unsignedTx = Buffer.from(buildResult.result.unsignedTx, "hex") - const signature = await app.signUnsignedTx(selectedAccount, unsignedTx) - setLedgerState("succeeded") - onSubmit({ ...buildResult, signature }) - } catch (e) { - if (app === undefined) { - setLedgerState(oldState => oldState === undefined || oldState === "detecting" ? "notfound" : oldState) - setTimeout(ledgerSign, 1000) - } else { - await app.close() - setLedgerState("failed") - if (onReject !== undefined) { - onReject() - } else { - navigate(-1) - } - } - } - } - }, [selectedAccount, buildResult, onSubmit, onReject, navigate]) - if (!selectedAccount) { rejectAction(actionHash, t("No account found for network {{ networkId }}", { networkId })) return @@ -287,21 +236,7 @@ export const ApproveTransactionScreen: FC = ({ return ( { + const { t } = useTranslation() + return ( + ledgerState === "notfound" ? + + + + + + {t("The Ledger app is not connected")} + + + : <> + ) +} + +export type LedgerConfirmation = "Sign with Ledger" | "Ledger: Detecting" | "Ledger: Signing" | "Ledger: Succeeded" | "Ledger: Failed" +export function getConfirmationTextByState(ledgerState: LedgerState | undefined): LedgerConfirmation { + return ledgerState === undefined + ? "Sign with Ledger" + : (ledgerState === "detecting") || (ledgerState === "notfound") + ? "Ledger: Detecting" + : ledgerState === "signing" + ? "Ledger: Signing" + : ledgerState === "succeeded" + ? "Ledger: Succeeded" + : "Ledger: Failed" +} + +export function useLedgerApp({ + selectedAccount, + unsignedTx, + onSubmit, + navigate, + onReject, +} : { + selectedAccount: Account | undefined, + unsignedTx: string | undefined, + onSubmit: (signature: string) => void, + navigate: (n: number) => void, + onReject?: () => void +}) { + const [ledgerState, setLedgerState] = useState() + const [ledgerApp, setLedgerApp] = useState() + + const ledgerSign = useCallback(async () => { + if (selectedAccount === undefined) { + return + } + setLedgerState(oldState => oldState === undefined ? "detecting" : oldState) + + let app: LedgerAlephium | undefined + if (unsignedTx !== undefined) { + try { + app = await LedgerAlephium.create() + setLedgerApp(app) + setLedgerState("signing") + const signature = await app.signUnsignedTx(selectedAccount, Buffer.from(unsignedTx, "hex")) + setLedgerState("succeeded") + onSubmit(signature) + } catch (e) { + if (app === undefined) { + setLedgerState(oldState => oldState === undefined || oldState === "detecting" ? "notfound" : oldState) + setTimeout(ledgerSign, 1000) + } else { + await app.close() + setLedgerState("failed") + if (onReject !== undefined) { + onReject() + } else { + navigate(-1) + } + } + } + } + }, [selectedAccount, onSubmit, onReject, navigate, unsignedTx]) + + return { ledgerState, ledgerApp, ledgerSign } +} From 5b6077c2890a73fcdb9493ec566725b92bbe6de7 Mon Sep 17 00:00:00 2001 From: lbqds Date: Thu, 24 Oct 2024 08:26:44 +0800 Subject: [PATCH 2/6] Improve signing error messages --- .../extension/locales/en-US/translation.json | 1 + .../src/background/actionHandlers.ts | 38 ++++++++++++------- .../src/ui/features/actions/ActionScreen.tsx | 29 +++++++++----- 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/packages/extension/locales/en-US/translation.json b/packages/extension/locales/en-US/translation.json index ad25c7b0..108d7eb3 100644 --- a/packages/extension/locales/en-US/translation.json +++ b/packages/extension/locales/en-US/translation.json @@ -186,6 +186,7 @@ "Transaction building failed": "Transaction building failed", "Sending transaction failed": "Sending transaction failed", "Sign unsigned tx failed": "Sign unsigned tx failed", + "Sign message failed": "Sign message failed", "Add Network": "Add Network", "Switch Network": "Switch Network", "Network ID": "Network ID", diff --git a/packages/extension/src/background/actionHandlers.ts b/packages/extension/src/background/actionHandlers.ts index 8a65bf02..a4e4a45c 100644 --- a/packages/extension/src/background/actionHandlers.ts +++ b/packages/extension/src/background/actionHandlers.ts @@ -76,22 +76,32 @@ export const handleActionApproval = async ( } case "ALPH_SIGN_MESSAGE": { - const account = await wallet.getAccount({ - address: action.payload.signerAddress, - networkId: action.payload.networkId, - }) - if (!account) { - throw Error("No selected account") - } + try { + const account = await wallet.getAccount({ + address: action.payload.signerAddress, + networkId: action.payload.networkId, + }) + if (!account) { + throw Error("No selected account") + } + if (account.signer.type === 'ledger') { + throw Error("Signing messages with Ledger accounts is not supported") + } - const result = await wallet.signMessage(account, action.payload) + const result = await wallet.signMessage(account, action.payload) - return { - type: "ALPH_SIGN_MESSAGE_SUCCESS", - data: { - signature: result.signature, - actionHash, - }, + return { + type: "ALPH_SIGN_MESSAGE_SUCCESS", + data: { + signature: result.signature, + actionHash, + }, + } + } catch (error) { + return { + type: "ALPH_SIGN_MESSAGE_FAILURE", + data: { actionHash, error: `${error}` }, + } } } diff --git a/packages/extension/src/ui/features/actions/ActionScreen.tsx b/packages/extension/src/ui/features/actions/ActionScreen.tsx index 275ffc28..be72485c 100644 --- a/packages/extension/src/ui/features/actions/ActionScreen.tsx +++ b/packages/extension/src/ui/features/actions/ActionScreen.tsx @@ -188,15 +188,26 @@ export const ActionScreen: FC = () => { onSubmit={async () => { await approveAction(action) useAppState.setState({ isLoading: true }) - await waitForMessage( - "ALPH_SIGN_MESSAGE_SUCCESS", - ({ data }) => data.actionHash === action.meta.hash, - ) - await analytics.track("signedMessage", { - networkId: selectedAccount?.networkId || t("unknown"), - }) - closePopupIfLastAction() - useAppState.setState({ isLoading: false }) + const result = await Promise.race([ + waitForMessage( + 'ALPH_SIGN_MESSAGE_SUCCESS', + ({ data }) => data.actionHash === action.meta.hash, + ), + waitForMessage( + 'ALPH_SIGN_MESSAGE_FAILURE', + ({ data }) => data.actionHash === action.meta.hash, + ), + ]) + if ("error" in result) { + useAppState.setState({ + error: `${t('Sign message failed')}: ${result.error}`, + isLoading: false, + }) + navigate(routes.error()) + } else { + closePopupIfLastAction() + useAppState.setState({ isLoading: false }) + } }} onReject={onReject} selectedAccount={signerAccount} From 8bd61344eee5ffd2ba03cec699906e706c3b1c05 Mon Sep 17 00:00:00 2001 From: lbqds Date: Thu, 24 Oct 2024 15:04:37 +0800 Subject: [PATCH 3/6] Update sdk to 1.8.3 --- packages/extension/package.json | 6 +- .../src/background/actionHandlers.ts | 25 ++----- .../actions/ApproveTransactionScreen.tsx | 2 +- yarn.lock | 73 ++++++++++--------- 4 files changed, 50 insertions(+), 56 deletions(-) diff --git a/packages/extension/package.json b/packages/extension/package.json index d14519d2..7c5019a5 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -7,9 +7,9 @@ "@alephium/get-extension-wallet": "^1.7.3", "@alephium/ledger-app": "0.6.0", "@alephium/token-list": "0.0.19", - "@alephium/web3": "^1.7.3", - "@alephium/web3-test": "^1.7.3", - "@alephium/web3-wallet": "^1.7.3", + "@alephium/web3": "^1.8.3", + "@alephium/web3-test": "^1.8.3", + "@alephium/web3-wallet": "^1.8.3", "@ledgerhq/hw-transport-webusb": "6.29.0", "@ledgerhq/hw-transport-webhid": "6.29.0", "@playwright/test": "^1.23.0", diff --git a/packages/extension/src/background/actionHandlers.ts b/packages/extension/src/background/actionHandlers.ts index a4e4a45c..e6dc4321 100644 --- a/packages/extension/src/background/actionHandlers.ts +++ b/packages/extension/src/background/actionHandlers.ts @@ -1,4 +1,4 @@ -import { binToHex, codec, groupIndexOfTransaction, hexToBinUnsafe } from "@alephium/web3" +import { binToHex, codec, groupIndexOfTransaction, hexToBinUnsafe, TransactionBuilder } from "@alephium/web3" import { getAccounts } from "../shared/account/store" import { ActionItem, @@ -16,6 +16,7 @@ import { openUi } from "./openUi" import { executeTransactionAction } from "./transactions/transactionExecution" import { transactionWatcher } from "./transactions/transactionWatcher" import blake from 'blakejs' +import { account } from "../../e2e/apis/account" export const handleActionApproval = async ( action: ExtQueueItem, @@ -124,7 +125,11 @@ export const handleActionApproval = async ( data: { actionHash, result }, } } else { - const result = { signature: signatureOpt, ...buildUnsignedTx(action.payload.unsignedTx) } + const signUnsignedTxResult = TransactionBuilder.buildUnsignedTx({ + signerAddress: account.address, + unsignedTx: action.payload.unsignedTx + }) + const result = { signature: signatureOpt, ...signUnsignedTxResult } return { type: "ALPH_SIGN_UNSIGNED_TX_SUCCESS", data: { actionHash, result }, @@ -277,19 +282,3 @@ export const handleActionRejection = async ( assertNever(action) } } - -// TODO: use the function from web3-sdk -function buildUnsignedTx(unsignedTx: string) { - const unsignedTxBin = hexToBinUnsafe(unsignedTx) - const decoded = codec.unsignedTxCodec.decode(unsignedTxBin) - const txId = binToHex(blake.blake2b(unsignedTxBin, undefined, 32)) - const [fromGroup, toGroup] = groupIndexOfTransaction(decoded) - return { - fromGroup: fromGroup, - toGroup: toGroup, - unsignedTx, - txId, - gasAmount: decoded.gasAmount, - gasPrice: decoded.gasPrice - } -} diff --git a/packages/extension/src/ui/features/actions/ApproveTransactionScreen.tsx b/packages/extension/src/ui/features/actions/ApproveTransactionScreen.tsx index 14204f48..88387a04 100644 --- a/packages/extension/src/ui/features/actions/ApproveTransactionScreen.tsx +++ b/packages/extension/src/ui/features/actions/ApproveTransactionScreen.tsx @@ -99,7 +99,7 @@ async function buildTransaction( return { type: transaction.type, params: transaction.params, - result: await builder.buildUnsignedTx(transaction.params), + result: TransactionBuilder.buildUnsignedTx(transaction.params), } } } diff --git a/yarn.lock b/yarn.lock index 1bcc8000..ce91ae1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42,13 +42,13 @@ dependencies: cross-fetch "^3.1.8" -"@alephium/web3-test@^1.7.3": - version "1.7.3" - resolved "https://registry.yarnpkg.com/@alephium/web3-test/-/web3-test-1.7.3.tgz#b26c5ce51e6d57f14a0fb75d7abc9f1f63540e56" - integrity sha512-Tz32fxXgyMREWz6Q9rCGYnPDoGIdf18kNZeBlQCZYgHc255okIW7CXPmvftUUS1gTStQJ9W7rFhtbje0MzzsDg== +"@alephium/web3-test@^1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@alephium/web3-test/-/web3-test-1.8.3.tgz#c2ac50374281eb2b416000c0b6e7e686bd967c26" + integrity sha512-btut4b4/uuAGzLAudatK0R7JA5HsVn+a+sbmzmFw26TGqAMiEdjBlFGHtXYowdlla+ozdgreiQARhXhCrMTlVA== dependencies: - "@alephium/web3" "^1.7.3" - "@alephium/web3-wallet" "^1.7.3" + "@alephium/web3" "^1.8.3" + "@alephium/web3-wallet" "^1.8.3" "@alephium/web3-wallet@^1.7.3": version "1.7.3" @@ -63,6 +63,19 @@ elliptic "6.5.4" fs-extra "10.0.1" +"@alephium/web3-wallet@^1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@alephium/web3-wallet/-/web3-wallet-1.8.3.tgz#34e331ffe65357caf36b0f4963926da958b42b48" + integrity sha512-tTdUbL31t7RSXL6LouZQ2rCQrZLuTbyH4S7j4+9JCkQ3xMH/nWyinIBPODqSBCOdVvZNGQ53B2g23GXvsZDrpA== + dependencies: + "@alephium/web3" "^1.8.3" + "@noble/secp256k1" "1.7.1" + "@types/node" "^16.18.23" + bip32 "3.1.0" + bip39 "3.0.4" + elliptic "6.5.4" + fs-extra "10.0.1" + "@alephium/web3@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@alephium/web3/-/web3-1.5.0.tgz#2949fd08fb1650a930c66730353957cdbef0dce0" @@ -97,6 +110,23 @@ path-browserify "^1.0.1" stream-browserify "^3.0.0" +"@alephium/web3@^1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@alephium/web3/-/web3-1.8.3.tgz#3cdcd5d3449783d2a7b7e6a16a7273bea6de343c" + integrity sha512-F2qrgcEZDZRv4RiN/7fGHphErsxSK5PjPrJWbffkSXak1z5xZT7aRsU6tr8gsubNF2JitmWL2i7P1oUJj8scFA== + dependencies: + "@noble/secp256k1" "1.7.1" + base-x "4.0.0" + bignumber.js "^9.1.1" + blakejs "1.2.1" + bn.js "5.2.1" + cross-fetch "^3.1.5" + crypto-browserify "^3.12.0" + elliptic "6.5.4" + eventemitter3 "^4.0.7" + path-browserify "^1.0.1" + stream-browserify "^3.0.0" + "@alloc/quick-lru@^5.2.0": version "5.2.0" resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" @@ -21286,16 +21316,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -21395,7 +21416,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -21409,13 +21430,6 @@ strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" @@ -23735,7 +23749,7 @@ worker-rpc@^0.1.0: dependencies: microevent.ts "~0.1.1" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -23753,15 +23767,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 72d739de7e3a45fc7a0a40046d8ce5b903b3fa5a Mon Sep 17 00:00:00 2001 From: lbqds Date: Thu, 24 Oct 2024 15:12:37 +0800 Subject: [PATCH 4/6] Rename unsigned tx to raw tx --- packages/extension/locales/en-US/translation.json | 6 +++--- packages/extension/src/ui/features/actions/ActionScreen.tsx | 2 +- .../src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/extension/locales/en-US/translation.json b/packages/extension/locales/en-US/translation.json index 108d7eb3..5087158a 100644 --- a/packages/extension/locales/en-US/translation.json +++ b/packages/extension/locales/en-US/translation.json @@ -185,7 +185,7 @@ "User disconnected": "User disconnected", "Transaction building failed": "Transaction building failed", "Sending transaction failed": "Sending transaction failed", - "Sign unsigned tx failed": "Sign unsigned tx failed", + "Sign raw tx failed": "Sign raw tx failed", "Sign message failed": "Sign message failed", "Add Network": "Add Network", "Switch Network": "Switch Network", @@ -207,8 +207,8 @@ "Sign Message": "Sign Message", "Signer": "Signer", "Hasher": "Hasher", - "Sign Unsigned TX": "Sign Unsigned TX", - "Unsigned TX": "Unsigned TX", + "Sign Raw TX": "Sign Raw TX", + "Raw TX": "Raw TX", "The Ledger app is not connected": "The Ledger app is not connected", "Insufficient token {{ tokenSymbol }}, expected at least {{ expectedStr }}, got {{ haveStr }}": "Insufficient token {{ tokenSymbol }}, expected at least {{ expectedStr }}, got {{ haveStr }}", "No account found for network {{ networkId }}": "No account found for network {{ networkId }}", diff --git a/packages/extension/src/ui/features/actions/ActionScreen.tsx b/packages/extension/src/ui/features/actions/ActionScreen.tsx index be72485c..160b10d2 100644 --- a/packages/extension/src/ui/features/actions/ActionScreen.tsx +++ b/packages/extension/src/ui/features/actions/ActionScreen.tsx @@ -233,7 +233,7 @@ export const ActionScreen: FC = () => { ]) if ("error" in result) { useAppState.setState({ - error: `${t('Sign unsigned tx failed')}: ${result.error}`, + error: `${t('Sign raw tx failed')}: ${result.error}`, isLoading: false, }) navigate(routes.error()) diff --git a/packages/extension/src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx b/packages/extension/src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx index 73d8c9a8..5df9faa3 100644 --- a/packages/extension/src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx +++ b/packages/extension/src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx @@ -84,7 +84,7 @@ export const ApproveSignUnsignedTxScreen: FC = mt="3" gap="6" > -

{t("Sign Unsigned TX")}

+

{t("Sign Raw TX")}

{params.host}
} @@ -105,7 +105,7 @@ export const ApproveSignUnsignedTxScreen: FC = w="full" > - {t("Unsigned TX")} + {t("Raw TX")} Date: Thu, 24 Oct 2024 15:15:10 +0800 Subject: [PATCH 5/6] Address comments --- .../actions/ApproveSignUnsignedTxScreen.tsx | 4 +- .../actions/ApproveTransactionScreen.tsx | 4 +- .../src/ui/features/actions/LedgerStatus.tsx | 70 +------------------ .../extension/src/ui/features/ledger/types.ts | 14 ++++ .../src/ui/features/ledger/useLedgerApp.ts | 55 +++++++++++++++ 5 files changed, 76 insertions(+), 71 deletions(-) create mode 100644 packages/extension/src/ui/features/ledger/types.ts create mode 100644 packages/extension/src/ui/features/ledger/useLedgerApp.ts diff --git a/packages/extension/src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx b/packages/extension/src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx index 5df9faa3..6c1df342 100644 --- a/packages/extension/src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx +++ b/packages/extension/src/ui/features/actions/ApproveSignUnsignedTxScreen.tsx @@ -9,8 +9,10 @@ import { AccountNetworkInfo } from "./transaction/AccountNetworkInfo" import blake from 'blakejs' import { TxHashContainer } from "./TxHashContainer" import { useTranslation } from "react-i18next" -import { getConfirmationTextByState, LedgerStatus, useLedgerApp } from "./LedgerStatus" +import { LedgerStatus } from "./LedgerStatus" import { useNavigate } from "react-router-dom" +import { useLedgerApp } from "../ledger/useLedgerApp" +import { getConfirmationTextByState } from "../ledger/types" interface ApproveSignUnsignedTxScreenProps extends Omit { diff --git a/packages/extension/src/ui/features/actions/ApproveTransactionScreen.tsx b/packages/extension/src/ui/features/actions/ApproveTransactionScreen.tsx index 88387a04..d33fa712 100644 --- a/packages/extension/src/ui/features/actions/ApproveTransactionScreen.tsx +++ b/packages/extension/src/ui/features/actions/ApproveTransactionScreen.tsx @@ -28,7 +28,9 @@ import { BigNumber } from "ethers" import { addTokenToBalances } from "../../../shared/token/balance" import { useTranslation } from "react-i18next" import i18n from "../../../i18n" -import { getConfirmationTextByState, LedgerStatus, useLedgerApp } from "./LedgerStatus" +import { LedgerStatus } from "./LedgerStatus" +import { useLedgerApp } from "../ledger/useLedgerApp" +import { getConfirmationTextByState } from "../ledger/types" const minimalGasFee = BigInt(20000) * BigInt(100000000000) diff --git a/packages/extension/src/ui/features/actions/LedgerStatus.tsx b/packages/extension/src/ui/features/actions/LedgerStatus.tsx index a2b2db06..c3136259 100644 --- a/packages/extension/src/ui/features/actions/LedgerStatus.tsx +++ b/packages/extension/src/ui/features/actions/LedgerStatus.tsx @@ -1,14 +1,10 @@ import { L1, icons } from "@argent/ui" import { Flex, Text } from "@chakra-ui/react" -import { useCallback, useState } from "react" import { useTranslation } from "react-i18next" -import { Account } from "../accounts/Account" -import { LedgerAlephium } from "../ledger/utils" +import { LedgerState } from "../ledger/types" const { AlertIcon } = icons -export type LedgerState = "detecting" | "notfound" | "signing" | "succeeded" | "failed" - export const LedgerStatus = ({ ledgerState }: { ledgerState: LedgerState | undefined }): JSX.Element => { const { t } = useTranslation() return ( @@ -31,67 +27,3 @@ export const LedgerStatus = ({ ledgerState }: { ledgerState: LedgerState | undef : <> ) } - -export type LedgerConfirmation = "Sign with Ledger" | "Ledger: Detecting" | "Ledger: Signing" | "Ledger: Succeeded" | "Ledger: Failed" -export function getConfirmationTextByState(ledgerState: LedgerState | undefined): LedgerConfirmation { - return ledgerState === undefined - ? "Sign with Ledger" - : (ledgerState === "detecting") || (ledgerState === "notfound") - ? "Ledger: Detecting" - : ledgerState === "signing" - ? "Ledger: Signing" - : ledgerState === "succeeded" - ? "Ledger: Succeeded" - : "Ledger: Failed" -} - -export function useLedgerApp({ - selectedAccount, - unsignedTx, - onSubmit, - navigate, - onReject, -} : { - selectedAccount: Account | undefined, - unsignedTx: string | undefined, - onSubmit: (signature: string) => void, - navigate: (n: number) => void, - onReject?: () => void -}) { - const [ledgerState, setLedgerState] = useState() - const [ledgerApp, setLedgerApp] = useState() - - const ledgerSign = useCallback(async () => { - if (selectedAccount === undefined) { - return - } - setLedgerState(oldState => oldState === undefined ? "detecting" : oldState) - - let app: LedgerAlephium | undefined - if (unsignedTx !== undefined) { - try { - app = await LedgerAlephium.create() - setLedgerApp(app) - setLedgerState("signing") - const signature = await app.signUnsignedTx(selectedAccount, Buffer.from(unsignedTx, "hex")) - setLedgerState("succeeded") - onSubmit(signature) - } catch (e) { - if (app === undefined) { - setLedgerState(oldState => oldState === undefined || oldState === "detecting" ? "notfound" : oldState) - setTimeout(ledgerSign, 1000) - } else { - await app.close() - setLedgerState("failed") - if (onReject !== undefined) { - onReject() - } else { - navigate(-1) - } - } - } - } - }, [selectedAccount, onSubmit, onReject, navigate, unsignedTx]) - - return { ledgerState, ledgerApp, ledgerSign } -} diff --git a/packages/extension/src/ui/features/ledger/types.ts b/packages/extension/src/ui/features/ledger/types.ts new file mode 100644 index 00000000..314db7e4 --- /dev/null +++ b/packages/extension/src/ui/features/ledger/types.ts @@ -0,0 +1,14 @@ +export type LedgerState = "detecting" | "notfound" | "signing" | "succeeded" | "failed" +export type LedgerConfirmation = "Sign with Ledger" | "Ledger: Detecting" | "Ledger: Signing" | "Ledger: Succeeded" | "Ledger: Failed" + +export function getConfirmationTextByState(ledgerState: LedgerState | undefined): LedgerConfirmation { + return ledgerState === undefined + ? "Sign with Ledger" + : (ledgerState === "detecting") || (ledgerState === "notfound") + ? "Ledger: Detecting" + : ledgerState === "signing" + ? "Ledger: Signing" + : ledgerState === "succeeded" + ? "Ledger: Succeeded" + : "Ledger: Failed" +} \ No newline at end of file diff --git a/packages/extension/src/ui/features/ledger/useLedgerApp.ts b/packages/extension/src/ui/features/ledger/useLedgerApp.ts new file mode 100644 index 00000000..cf759243 --- /dev/null +++ b/packages/extension/src/ui/features/ledger/useLedgerApp.ts @@ -0,0 +1,55 @@ +import { useCallback, useState } from "react" +import { Account } from "../accounts/Account" +import { LedgerAlephium } from "../ledger/utils" +import { LedgerState } from "./types" + +export function useLedgerApp({ + selectedAccount, + unsignedTx, + onSubmit, + navigate, + onReject, +} : { + selectedAccount: Account | undefined, + unsignedTx: string | undefined, + onSubmit: (signature: string) => void, + navigate: (n: number) => void, + onReject?: () => void +}) { + const [ledgerState, setLedgerState] = useState() + const [ledgerApp, setLedgerApp] = useState() + + const ledgerSign = useCallback(async () => { + if (selectedAccount === undefined) { + return + } + setLedgerState(oldState => oldState === undefined ? "detecting" : oldState) + + let app: LedgerAlephium | undefined + if (unsignedTx !== undefined) { + try { + app = await LedgerAlephium.create() + setLedgerApp(app) + setLedgerState("signing") + const signature = await app.signUnsignedTx(selectedAccount, Buffer.from(unsignedTx, "hex")) + setLedgerState("succeeded") + onSubmit(signature) + } catch (e) { + if (app === undefined) { + setLedgerState(oldState => oldState === undefined || oldState === "detecting" ? "notfound" : oldState) + setTimeout(ledgerSign, 1000) + } else { + await app.close() + setLedgerState("failed") + if (onReject !== undefined) { + onReject() + } else { + navigate(-1) + } + } + } + } + }, [selectedAccount, onSubmit, onReject, navigate, unsignedTx]) + + return { ledgerState, ledgerApp, ledgerSign } +} \ No newline at end of file From 2d9992af0317fc3fe32eb3af7b31c72c2d6d8b6a Mon Sep 17 00:00:00 2001 From: lbqds Date: Thu, 24 Oct 2024 15:34:15 +0800 Subject: [PATCH 6/6] Fix import --- .../src/background/actionHandlers.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/extension/src/background/actionHandlers.ts b/packages/extension/src/background/actionHandlers.ts index e6dc4321..d43de3c3 100644 --- a/packages/extension/src/background/actionHandlers.ts +++ b/packages/extension/src/background/actionHandlers.ts @@ -1,4 +1,4 @@ -import { binToHex, codec, groupIndexOfTransaction, hexToBinUnsafe, TransactionBuilder } from "@alephium/web3" +import { TransactionBuilder } from "@alephium/web3" import { getAccounts } from "../shared/account/store" import { ActionItem, @@ -15,8 +15,6 @@ import { BackgroundService } from "./background" import { openUi } from "./openUi" import { executeTransactionAction } from "./transactions/transactionExecution" import { transactionWatcher } from "./transactions/transactionWatcher" -import blake from 'blakejs' -import { account } from "../../e2e/apis/account" export const handleActionApproval = async ( action: ExtQueueItem, @@ -109,15 +107,14 @@ export const handleActionApproval = async ( case "ALPH_SIGN_UNSIGNED_TX": { const { signatureOpt } = additionalData as { signatureOpt: string | undefined } try { + const account = await wallet.getAccount({ + address: action.payload.signerAddress, + networkId: action.payload.networkId, + }) + if (!account) { + throw Error("No selected account") + } if (signatureOpt === undefined) { - const account = await wallet.getAccount({ - address: action.payload.signerAddress, - networkId: action.payload.networkId, - }) - if (!account) { - throw Error("No selected account") - } - const result = await wallet.signUnsignedTx(account, action.payload) return {