From 3d925e703ce7890753f36b752fada17a0a9db774 Mon Sep 17 00:00:00 2001 From: michael1011 Date: Mon, 21 Oct 2024 14:14:31 +0200 Subject: [PATCH] feat: remember custom derivation paths for swaps --- src/components/ConnectWallet.tsx | 37 ++++++++++++++--- src/components/ContractTransaction.tsx | 14 +++++-- src/components/CreateButton.tsx | 15 ++++++- src/components/HardwareDerivationPaths.tsx | 15 ++++--- src/components/LockupEvm.tsx | 19 +++++++-- src/components/RefundButton.tsx | 10 +++-- src/context/Web3.tsx | 47 +++++++++++++++++++--- src/status/TransactionConfirmed.tsx | 10 +++-- src/utils/hardware/HadwareSigner.ts | 1 + src/utils/hardware/LedgerSigner.ts | 4 ++ src/utils/hardware/TrezorSigner.ts | 4 ++ src/utils/swapCreator.ts | 2 + 12 files changed, 144 insertions(+), 34 deletions(-) diff --git a/src/components/ConnectWallet.tsx b/src/components/ConnectWallet.tsx index 0c9e4059..681017d6 100644 --- a/src/components/ConnectWallet.tsx +++ b/src/components/ConnectWallet.tsx @@ -22,7 +22,9 @@ import HardwareDerivationPaths, { connect } from "./HardwareDerivationPaths"; const Modal = ({ show, setShow, + derivationPath, }: { + derivationPath: string; show: Accessor; setShow: Setter; }) => { @@ -50,7 +52,13 @@ const Modal = ({ return; } - await connect(notify, connectProvider, provider); + await connect( + notify, + connectProvider, + providers, + provider, + derivationPath, + ); }}>
{ +const ConnectModal = ({ derivationPath }: { derivationPath: string }) => { const { t, notify } = useGlobalContext(); const { providers, connectProvider } = useWeb3Signer(); @@ -122,13 +130,19 @@ const ConnectModal = () => { connect( notify, connectProvider, + providers, Object.values(providers())[0].info, + derivationPath, ).then(); } }}> {t("connect_wallet")} - + ); }; @@ -167,7 +181,11 @@ const ShowAddress = ({ ); }; -export const ConnectAddress = ({ address }: { address: string }) => { +export const ConnectAddress = ({ + address, +}: { + address: { address: string; derivationPath?: string }; +}) => { const { t, notify } = useGlobalContext(); const { connectProviderForAddress } = useWeb3Signer(); @@ -176,7 +194,10 @@ export const ConnectAddress = ({ address }: { address: string }) => { class="btn" onClick={async () => { try { - await connectProviderForAddress(address); + await connectProviderForAddress( + address.address, + address.derivationPath, + ); } catch (e) { log.error( `Provider connect for address ${address} failed: ${formatError(e)}`, @@ -213,8 +234,10 @@ export const SwitchNetwork = () => { }; const ConnectWallet = ({ + derivationPath, addressOverride, }: { + derivationPath?: string; addressOverride?: Accessor; }) => { const { t } = useGlobalContext(); @@ -249,7 +272,9 @@ const ConnectWallet = ({ {t("no_wallet")} }> - }> + }> }> Promise; children?: any; - address: string; + showHr?: boolean; buttonText: string; promptText?: string; - showHr?: boolean; waitingText?: string; + address: { address: string; derivationPath?: string }; }) => { const { notify } = useGlobalContext(); const { signer, getContracts } = useWeb3Signer(); @@ -42,10 +42,16 @@ const ContractTransaction = ({ }> + + }> }> diff --git a/src/components/CreateButton.tsx b/src/components/CreateButton.tsx index 68d8b7f8..bb0c84ba 100644 --- a/src/components/CreateButton.tsx +++ b/src/components/CreateButton.tsx @@ -8,11 +8,12 @@ import { SwapType } from "../consts/Enums"; import { ButtonLabelParams } from "../consts/Types"; import { useCreateContext } from "../context/Create"; import { useGlobalContext } from "../context/Global"; -import { useWeb3Signer } from "../context/Web3"; +import { customDerivationPathRdns, useWeb3Signer } from "../context/Web3"; import { GasNeededToClaim, getSmartWalletAddress } from "../rif/Signer"; import { fetchBolt12Invoice, getPairs } from "../utils/boltzClient"; import { formatAmount } from "../utils/denomination"; import { formatError } from "../utils/errors"; +import { HardwareSigner } from "../utils/hardware/HadwareSigner"; import { coalesceLn } from "../utils/helper"; import { fetchBip353, fetchLnurl } from "../utils/invoice"; import { @@ -62,7 +63,7 @@ export const CreateButton = () => { bolt12Offer, setBolt12Offer, } = useCreateContext(); - const { getEtherSwap, signer } = useWeb3Signer(); + const { getEtherSwap, signer, providers } = useWeb3Signer(); const [buttonDisable, setButtonDisable] = createSignal(false); const [buttonClass, setButtonClass] = createSignal("btn"); @@ -307,6 +308,7 @@ export const CreateButton = () => { return; } + console.log(providers()[signer().rdns].provider); await setSwapStorage({ ...data, signer: @@ -314,6 +316,15 @@ export const CreateButton = () => { swapType() !== SwapType.Submarine ? signer()?.address : undefined, + derivationPath: + swapType() !== SwapType.Submarine && + signer() !== undefined && + customDerivationPathRdns.includes(signer().rdns) + ? ( + providers()[signer().rdns] + .provider as unknown as HardwareSigner + ).getDerivationPath() + : undefined, }); setInvoice(""); diff --git a/src/components/HardwareDerivationPaths.tsx b/src/components/HardwareDerivationPaths.tsx index 261c3ce3..a3bb536e 100644 --- a/src/components/HardwareDerivationPaths.tsx +++ b/src/components/HardwareDerivationPaths.tsx @@ -28,9 +28,17 @@ import LoadingSpinner from "./LoadingSpinner"; export const connect = async ( notify: (type: string, message: string) => void, connectProvider: (rdns: string) => Promise, + providers: Accessor>, provider: EIP6963ProviderInfo, + derivationPath?: string, ) => { try { + if (derivationPath !== undefined) { + const prov = providers()[provider.rdns] + .provider as unknown as HardwareSigner; + prov.setDerivationPath(derivationPath); + } + await connectProvider(provider.rdns); } catch (e) { log.error( @@ -51,12 +59,7 @@ const connectHardware = async ( try { setLoading(true); - const hardwareProvider = provider(); - const prov = providers()[hardwareProvider.rdns] - .provider as unknown as HardwareSigner; - prov.setDerivationPath(path); - - await connect(notify, connectProvider, hardwareProvider); + await connect(notify, connectProvider, providers, provider(), path); } finally { setLoading(false); } diff --git a/src/components/LockupEvm.tsx b/src/components/LockupEvm.tsx index 742a20c9..db580e08 100644 --- a/src/components/LockupEvm.tsx +++ b/src/components/LockupEvm.tsx @@ -1,7 +1,8 @@ import { Show, createEffect, createSignal } from "solid-js"; import { useGlobalContext } from "../context/Global"; -import { useWeb3Signer } from "../context/Web3"; +import { customDerivationPathRdns, useWeb3Signer } from "../context/Web3"; +import { HardwareSigner } from "../utils/hardware/HadwareSigner"; import { prefix0x, satoshiToWei } from "../utils/rootstock"; import ConnectWallet from "./ConnectWallet"; import ContractTransaction from "./ContractTransaction"; @@ -26,6 +27,7 @@ const LockupEvm = ({ preimageHash, claimAddress, signerAddress, + derivationPath, timeoutBlockHeight, }: { swapId: string; @@ -33,9 +35,10 @@ const LockupEvm = ({ preimageHash: string; claimAddress: string; signerAddress: string; + derivationPath?: string; timeoutBlockHeight: number; }) => { - const { getEtherSwap, signer } = useWeb3Signer(); + const { getEtherSwap, signer, providers } = useWeb3Signer(); const { t, getSwap, setSwapStorage } = useGlobalContext(); const value = () => satoshiToWei(amount); @@ -58,7 +61,7 @@ const LockupEvm = ({ fallback={}> { - const contract = await getEtherSwap(); + const contract = getEtherSwap(); const tx = await contract.lock( prefix0x(preimageHash), claimAddress, @@ -70,10 +73,18 @@ const LockupEvm = ({ const currentSwap = await getSwap(swapId); currentSwap.lockupTx = tx.hash; currentSwap.signer = signer().address; + + if (customDerivationPathRdns.includes(signer().rdns)) { + currentSwap.derivationPath = ( + providers()[signer().rdns] + .provider as unknown as HardwareSigner + ).getDerivationPath(); + } + await setSwapStorage(currentSwap); }} children={} - address={signerAddress} + address={{ derivationPath, address: signerAddress }} buttonText={t("send")} promptText={t("transaction_prompt", { button: t("send") })} waitingText={t("tx_in_mempool_subline")} diff --git a/src/components/RefundButton.tsx b/src/components/RefundButton.tsx index d9d35a5a..37b4b9b9 100644 --- a/src/components/RefundButton.tsx +++ b/src/components/RefundButton.tsx @@ -25,20 +25,22 @@ import ContractTransaction from "./ContractTransaction"; export const RefundEvm = ({ swapId, - setRefundTxHash, amount, claimAddress, preimageHash, signerAddress, + derivationPath, + setRefundTxHash, timeoutBlockHeight, }: { swapId?: string; - setRefundTxHash?: Setter; amount: number; preimageHash: string; claimAddress: string; signerAddress: string; + derivationPath?: string; timeoutBlockHeight: number; + setRefundTxHash?: Setter; }) => { const { setSwap } = usePayContext(); const { getEtherSwap, signer } = useWeb3Signer(); @@ -94,7 +96,7 @@ export const RefundEvm = ({ await tx.wait(1); }} - address={signerAddress} + address={{ derivationPath, address: signerAddress }} buttonText={t("refund")} /> ); @@ -131,6 +133,7 @@ const RefundButton = ({ signerAddress={submarine.signer} amount={submarine.expectedAmount} claimAddress={submarine.claimAddress} + derivationPath={submarine.derivationPath} timeoutBlockHeight={submarine.timeoutBlockHeight} preimageHash={decodeInvoice(submarine.invoice).preimageHash} /> @@ -143,6 +146,7 @@ const RefundButton = ({ swapId={chain.id} signerAddress={chain.signer} amount={chain.lockupDetails.amount} + derivationPath={chain.derivationPath} claimAddress={chain.lockupDetails.claimAddress} timeoutBlockHeight={chain.lockupDetails.timeoutBlockHeight} preimageHash={crypto diff --git a/src/context/Web3.tsx b/src/context/Web3.tsx index 5ec286a2..e9d5f4b3 100644 --- a/src/context/Web3.tsx +++ b/src/context/Web3.tsx @@ -16,6 +16,7 @@ import { config } from "../config"; import { RBTC } from "../consts/Assets"; import { EIP1193Provider, EIP6963ProviderDetail } from "../consts/Types"; import { Contracts, getContracts } from "../utils/boltzClient"; +import { HardwareSigner } from "../utils/hardware/HadwareSigner"; import LedgerSigner from "../utils/hardware/LedgerSigner"; import TrezorSigner from "../utils/hardware/TrezorSigner"; import { useGlobalContext } from "./Global"; @@ -40,10 +41,23 @@ export type Signer = JsonRpcSigner & { rdns: string; }; +enum HardwareRdns { + Ledger = "ledger", + Trezor = "trezor", +} + +const customDerivationPathRdns: string[] = [ + HardwareRdns.Ledger, + HardwareRdns.Trezor, +]; + const Web3SignerContext = createContext<{ providers: Accessor>; connectProvider: (rdns: string) => Promise; - connectProviderForAddress: (address: string) => Promise; + connectProviderForAddress: ( + address: string, + derivationPath?: string, + ) => Promise; signer: Accessor; clearSigner: () => void; @@ -69,9 +83,9 @@ const Web3SignerProvider = (props: { info: { name: "Ledger", uuid: "ledger", - rdns: "ledger", icon: LedgerIcon, isHardware: true, + rdns: HardwareRdns.Ledger, disabled: navigator.hid === undefined, }, }, @@ -80,9 +94,9 @@ const Web3SignerProvider = (props: { info: { name: "Trezor", uuid: "trezor", - rdns: "trezor", icon: TrezorIcon, isHardware: true, + rdns: HardwareRdns.Trezor, }, }, }); @@ -121,8 +135,24 @@ const Web3SignerProvider = (props: { ) as unknown as EtherSwap; }; - const connectProviderForAddress = async (address: string) => - connectProvider(await getRdnsForAddress(address)); + const connectProviderForAddress = async ( + address: string, + derivationPath?: string, + ) => { + const rdns = await getRdnsForAddress(address); + + if (derivationPath !== undefined) { + log.debug( + `Setting derivation path (${derivationPath}) for signer:`, + rdns, + ); + const prov = providers()[rdns] + .provider as unknown as HardwareSigner; + prov.setDerivationPath(derivationPath); + } + + await connectProvider(rdns); + }; const connectProvider = async (rdns: string) => { const wallet = providers()[rdns]; @@ -223,4 +253,9 @@ const Web3SignerProvider = (props: { const useWeb3Signer = () => useContext(Web3SignerContext); -export { useWeb3Signer, Web3SignerProvider, EtherSwapAbi }; +export { + useWeb3Signer, + Web3SignerProvider, + EtherSwapAbi, + customDerivationPathRdns, +}; diff --git a/src/status/TransactionConfirmed.tsx b/src/status/TransactionConfirmed.tsx index c8fe0648..da185645 100644 --- a/src/status/TransactionConfirmed.tsx +++ b/src/status/TransactionConfirmed.tsx @@ -15,19 +15,21 @@ const ClaimEvm = ({ swapId, amount, preimage, + assetReceive, signerAddress, refundAddress, + derivationPath, timeoutBlockHeight, - assetReceive, }: { amount: number; swapId: string; useRif: boolean; preimage: string; + assetReceive: string; signerAddress: string; refundAddress: string; + derivationPath: string; timeoutBlockHeight: number; - assetReceive: string; }) => { const { getEtherSwap, signer } = useWeb3Signer(); const { t, getSwap, setSwapStorage } = useGlobalContext(); @@ -65,7 +67,7 @@ const ClaimEvm = ({ setSwap(currentSwap); await setSwapStorage(currentSwap); }} - address={signerAddress} + address={{ derivationPath, address: signerAddress }} buttonText={t("continue")} promptText={t("transaction_prompt_receive", { button: t("continue"), @@ -91,6 +93,7 @@ const TransactionConfirmed = () => { preimage={chain.preimage} signerAddress={chain.signer} amount={chain.claimDetails.amount} + derivationPath={chain.derivationPath} refundAddress={chain.claimDetails.refundAddress} timeoutBlockHeight={chain.claimDetails.timeoutBlockHeight} assetReceive={chain.assetReceive} @@ -108,6 +111,7 @@ const TransactionConfirmed = () => { amount={reverse.onchainAmount} signerAddress={reverse.signer} refundAddress={reverse.refundAddress} + derivationPath={reverse.derivationPath} timeoutBlockHeight={reverse.timeoutBlockHeight} assetReceive={reverse.assetReceive} /> diff --git a/src/utils/hardware/HadwareSigner.ts b/src/utils/hardware/HadwareSigner.ts index 959586d0..87c0fcad 100644 --- a/src/utils/hardware/HadwareSigner.ts +++ b/src/utils/hardware/HadwareSigner.ts @@ -11,5 +11,6 @@ export const derivationPathsTestnet = { }; export interface HardwareSigner { + getDerivationPath(): string; setDerivationPath(path: string): void; } diff --git a/src/utils/hardware/LedgerSigner.ts b/src/utils/hardware/LedgerSigner.ts index fa599c9f..ae2c2e4f 100644 --- a/src/utils/hardware/LedgerSigner.ts +++ b/src/utils/hardware/LedgerSigner.ts @@ -34,6 +34,10 @@ class LedgerSigner implements EIP1193Provider, HardwareSigner { ); } + public getDerivationPath = () => { + return this.derivationPath; + }; + public setDerivationPath = (path: string) => { this.derivationPath = path; }; diff --git a/src/utils/hardware/TrezorSigner.ts b/src/utils/hardware/TrezorSigner.ts index 2a289232..daa85f19 100644 --- a/src/utils/hardware/TrezorSigner.ts +++ b/src/utils/hardware/TrezorSigner.ts @@ -30,6 +30,10 @@ class TrezorSigner implements EIP1193Provider, HardwareSigner { this.setDerivationPath(derivationPaths.Ethereum); } + public getDerivationPath = () => { + return this.derivationPath; + }; + public setDerivationPath = (path: string) => { this.derivationPath = `m/${path}`; }; diff --git a/src/utils/swapCreator.ts b/src/utils/swapCreator.ts index 9e259d7d..de9c1d04 100644 --- a/src/utils/swapCreator.ts +++ b/src/utils/swapCreator.ts @@ -35,6 +35,8 @@ export type SwapBase = { useRif: boolean; signer?: string; + // Set for hardware wallet signers + derivationPath?: string; }; export type SubmarineSwap = SwapBase &