diff --git a/components/forms/TransactionSigning.js b/components/forms/TransactionSigning.js index 5dc5ca0..0dba118 100644 --- a/components/forms/TransactionSigning.js +++ b/components/forms/TransactionSigning.js @@ -1,24 +1,27 @@ -import React, { useState, useEffect } from "react"; +import React, { useState } from "react"; import axios from "axios"; import { toBase64 } from "@cosmjs/encoding"; -import { SigningStargateClient } from "@cosmjs/stargate"; +import { assertIsDeliverTxFailure, SigningStargateClient } from "@cosmjs/stargate"; +import { assert } from "@cosmjs/utils"; import { useAppContext } from "../../context/AppContext"; import Button from "../inputs/Button"; import HashView from "../dataViews/HashView"; import StackableContainer from "../layout/StackableContainer"; +import TransportWebUSB from "@ledgerhq/hw-transport-webusb"; +import { LedgerSigner } from "@cosmjs/ledger-amino"; +import { makeCosmoshubPath } from "@cosmjs/amino"; + const TransactionSigning = (props) => { const { state } = useAppContext(); const [walletAccount, setWalletAccount] = useState(null); const [sigError, setSigError] = useState(null); const [hasSigned, setHasSigned] = useState(false); + const [walletType, setWalletType] = useState(""); + const [ledgerSigner, setLedgerSigner] = useState({}); - useEffect(() => { - connectWallet(); - }, []); - - const connectWallet = async () => { + const connectKeplr = async () => { try { await window.keplr.enable(state.chain.chainId); const tempWalletAccount = await window.keplr.getKey(state.chain.chainId); @@ -27,58 +30,76 @@ const TransactionSigning = (props) => { ); setWalletAccount(tempWalletAccount); setHasSigned(tempHasSigned); + setWalletType("keplr"); } catch (e) { console.log("enable err: ", e); } }; - const signTransaction = async () => { - try { - window.keplr.defaultOptions = { - sign: { - preferNoSetMemo: true, - preferNoSetFee: true, - disableBalanceCheck: true, - }, - }; - const offlineSigner = window.getOfflineSignerOnlyAmino(state.chain.chainId); - const signingClient = await SigningStargateClient.offline(offlineSigner); - const signerData = { - accountNumber: props.tx.accountNumber, - sequence: props.tx.sequence, - chainId: state.chain.chainId, - }; - const { bodyBytes, signatures } = await signingClient.sign( - walletAccount.bech32Address, - props.tx.msgs, - props.tx.fee, - props.tx.memo, - signerData, - ); - // check existing signatures - const bases64EncodedSignature = toBase64(signatures[0]); - const bases64EncodedBodyBytes = toBase64(bodyBytes); - const prevSigMatch = props.signatures.findIndex( - (signature) => signature.signature === bases64EncodedSignature, - ); + const connectLedger = async () => { + // Prepare ledger + const ledgerTransport = await TransportWebUSB.create(120_000, 120_000); - if (prevSigMatch > -1) { - setSigError("This account has already signed."); - } else { - const signature = { - bodyBytes: bases64EncodedBodyBytes, - signature: bases64EncodedSignature, - address: walletAccount.bech32Address, - }; - const _res = await axios.post( - `/api/transaction/${props.transactionID}/signature`, - signature, - ); - props.addSignature(signature); - setHasSigned(true); - } - } catch (error) { - console.log("Error creating signature:", error); + // Setup signer + const offlineSigner = new LedgerSigner(ledgerTransport, { + hdPaths: [makeCosmoshubPath(0)], + prefix: state.chain.addressPrefix, + }); + + const accounts = await offlineSigner.getAccounts(); + const tempWalletAccount = accounts[0].address; + + const tempHasSigned = props.signatures.some( + (sig) => sig.address === tempWalletAccount.bech32Address, + ); + setWalletAccount(tempWalletAccount); + setHasSigned(tempHasSigned); + setLedgerSigner(offlineSigner); + setWalletType("ledger"); + }; + + const signTransaction = async () => { + const offlineSigner = + walletType === "keplr" ? window.getOfflineSignerOnlyAmino(state.chain.chainId) : ledgerSigner; + + const signerAddress = walletType === "keplr" + ? walletAccount.bech32Address + : (await ledgerSigner.getAccounts())[0]?.address; + assert(signerAddress, "Missing signer address"); + const signingClient = await SigningStargateClient.offline(offlineSigner); + + const signerData = { + accountNumber: props.tx.accountNumber, + sequence: props.tx.sequence, + chainId: state.chain.chainId, + }; + + const { bodyBytes, signatures } = await signingClient.sign( + signerAddress, + props.tx.msgs, + props.tx.fee, + props.tx.memo, + signerData, + ); + + // check existing signatures + const bases64EncodedSignature = toBase64(signatures[0]); + const bases64EncodedBodyBytes = toBase64(bodyBytes); + const prevSigMatch = props.signatures.findIndex( + (signature) => signature.signature === bases64EncodedSignature, + ); + + if (prevSigMatch > -1) { + setSigError("This account has already signed."); + } else { + const signature = { + bodyBytes: bases64EncodedBodyBytes, + signature: bases64EncodedSignature, + address: signerAddress, + }; + const _res = await axios.post(`/api/transaction/${props.transactionID}/signature`, signature); + props.addSignature(signature); + setHasSigned(true); } }; @@ -99,7 +120,10 @@ const TransactionSigning = (props) => { {walletAccount ? (