From 9394d10e02a55353c3c7fa42fe1e5ac2a27ca6fc Mon Sep 17 00:00:00 2001 From: Milan Steiner Date: Thu, 24 Mar 2022 11:29:24 +0100 Subject: [PATCH] Adding Ledger Support to Sign TX --- components/forms/TransactionForm.js | 1 + components/forms/TransactionSigning.js | 126 +- lib/multisigHelpers.js | 1 + package-lock.json | 5516 +++++++++++++++++++++++- package.json | 2 + 5 files changed, 5575 insertions(+), 71 deletions(-) diff --git a/components/forms/TransactionForm.js b/components/forms/TransactionForm.js index f67804c..843def5 100644 --- a/components/forms/TransactionForm.js +++ b/components/forms/TransactionForm.js @@ -40,6 +40,7 @@ const TransactionForm = (props) => { value: msgSend, }; const fee = calculateFee(Number(txGas), gasPrice); + console.log(props.accountOnChain.accountNumber); return { accountNumber: props.accountOnChain.accountNumber, sequence: props.accountOnChain.sequence, diff --git a/components/forms/TransactionSigning.js b/components/forms/TransactionSigning.js index 5dc5ca0..c08cbaf 100644 --- a/components/forms/TransactionSigning.js +++ b/components/forms/TransactionSigning.js @@ -1,4 +1,4 @@ -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"; @@ -8,17 +8,19 @@ 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 +29,73 @@ 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 address = walletType === "keplr" ? walletAccount.bech32Address : ledgerSigner.accounts[0]?.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( + address, + 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: walletAccount.bech32Address, + }; + const _res = await axios.post(`/api/transaction/${props.transactionID}/signature`, signature); + props.addSignature(signature); + setHasSigned(true); } }; @@ -99,7 +116,10 @@ const TransactionSigning = (props) => { {walletAccount ? (