From 80ee5b422f2e876f372de3377f7077b99829dc85 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Sun, 6 Oct 2024 21:43:43 -0700 Subject: [PATCH] Move ledger UI functions into UI This makes the ledger module platform independent --- .../src/commands/wallet/chainport/send.ts | 3 +- ironfish-cli/src/commands/wallet/mint.ts | 3 +- ironfish-cli/src/commands/wallet/send.ts | 3 +- ironfish-cli/src/ledger/index.ts | 1 - ironfish-cli/src/ledger/ui.ts | 98 ------------------- ironfish-cli/src/ui/ledger.ts | 98 ++++++++++++++++++- 6 files changed, 99 insertions(+), 107 deletions(-) delete mode 100644 ironfish-cli/src/ledger/ui.ts diff --git a/ironfish-cli/src/commands/wallet/chainport/send.ts b/ironfish-cli/src/commands/wallet/chainport/send.ts index 51c89f3c35..1612feeb79 100644 --- a/ironfish-cli/src/commands/wallet/chainport/send.ts +++ b/ironfish-cli/src/commands/wallet/chainport/send.ts @@ -17,7 +17,6 @@ import { Flags, ux } from '@oclif/core' import inquirer from 'inquirer' import { IronfishCommand } from '../../../command' import { HexFlag, IronFlag, RemoteFlags, ValueFlag } from '../../../flags' -import { sendTransactionWithLedger } from '../../../ledger' import * as ui from '../../../ui' import { ChainportBridgeTransaction, @@ -135,7 +134,7 @@ export class BridgeCommand extends IronfishCommand { } if (flags.ledger) { - await sendTransactionWithLedger( + await ui.sendTransactionWithLedger( client, rawTransaction, from, diff --git a/ironfish-cli/src/commands/wallet/mint.ts b/ironfish-cli/src/commands/wallet/mint.ts index 57b4868eed..d2b5d8a294 100644 --- a/ironfish-cli/src/commands/wallet/mint.ts +++ b/ironfish-cli/src/commands/wallet/mint.ts @@ -17,7 +17,6 @@ import { import { Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../command' import { IronFlag, RemoteFlags, ValueFlag } from '../../flags' -import { sendTransactionWithLedger } from '../../ledger' import * as ui from '../../ui' import { useAccount } from '../../utils' import { promptCurrency } from '../../utils/currency' @@ -314,7 +313,7 @@ This will create tokens and increase supply for a given asset.` ) if (flags.ledger) { - await sendTransactionWithLedger( + await ui.sendTransactionWithLedger( client, raw, account, diff --git a/ironfish-cli/src/commands/wallet/send.ts b/ironfish-cli/src/commands/wallet/send.ts index c76da5898f..50d3875b89 100644 --- a/ironfish-cli/src/commands/wallet/send.ts +++ b/ironfish-cli/src/commands/wallet/send.ts @@ -14,7 +14,6 @@ import { import { Flags } from '@oclif/core' import { IronfishCommand } from '../../command' import { HexFlag, IronFlag, RemoteFlags, ValueFlag } from '../../flags' -import { sendTransactionWithLedger } from '../../ledger' import * as ui from '../../ui' import { useAccount } from '../../utils' import { promptCurrency } from '../../utils/currency' @@ -258,7 +257,7 @@ export class Send extends IronfishCommand { } if (flags.ledger) { - await sendTransactionWithLedger( + await ui.sendTransactionWithLedger( client, raw, from, diff --git a/ironfish-cli/src/ledger/index.ts b/ironfish-cli/src/ledger/index.ts index 4de4c815f8..3fe51ee586 100644 --- a/ironfish-cli/src/ledger/index.ts +++ b/ironfish-cli/src/ledger/index.ts @@ -4,4 +4,3 @@ export * from './ledger' export * from './ledgerMultiSigner' export * from './ledgerSingleSigner' -export * from './ui' diff --git a/ironfish-cli/src/ledger/ui.ts b/ironfish-cli/src/ledger/ui.ts deleted file mode 100644 index c37567877b..0000000000 --- a/ironfish-cli/src/ledger/ui.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { - CurrencyUtils, - Logger, - RawTransaction, - RawTransactionSerde, - RpcClient, - Transaction, -} from '@ironfish/sdk' -import { Errors, ux } from '@oclif/core' -import * as ui from '../ui' -import { watchTransaction } from '../utils/transaction' -import { LedgerSingleSigner } from './ledgerSingleSigner' - -export async function sendTransactionWithLedger( - client: RpcClient, - raw: RawTransaction, - from: string | undefined, - watch: boolean, - confirm: boolean, - logger?: Logger, -): Promise { - const ledger = new LedgerSingleSigner(logger) - - try { - await ledger.connect() - } catch (e) { - if (e instanceof Error) { - Errors.error(e.message) - } else { - throw e - } - } - - const publicKey = (await client.wallet.getAccountPublicKey({ account: from })).content - .publicKey - - const ledgerPublicKey = await ledger.getPublicAddress() - - if (publicKey !== ledgerPublicKey) { - Errors.error( - `The public key on the ledger device does not match the public key of the account '${from}'`, - ) - } - - const buildTransactionResponse = await client.wallet.buildTransaction({ - account: from, - rawTransaction: RawTransactionSerde.serialize(raw).toString('hex'), - }) - - const unsignedTransaction = buildTransactionResponse.content.unsignedTransaction - - ux.stdout('Please confirm the transaction on your Ledger device') - - const signature = (await ledger.sign(unsignedTransaction)).toString('hex') - - ux.stdout(`\nSignature: ${signature}`) - - const addSignatureResponse = await client.wallet.addSignature({ - unsignedTransaction, - signature, - }) - - const signedTransaction = addSignatureResponse.content.transaction - const bytes = Buffer.from(signedTransaction, 'hex') - - const transaction = new Transaction(bytes) - - ux.stdout(`\nSigned Transaction: ${signedTransaction}`) - ux.stdout(`\nHash: ${transaction.hash().toString('hex')}`) - ux.stdout(`Fee: ${CurrencyUtils.render(transaction.fee(), true)}`) - - await ui.confirmOrQuit('Would you like to broadcast this transaction?', confirm) - - const addTransactionResponse = await client.wallet.addTransaction({ - transaction: signedTransaction, - broadcast: true, - }) - - if (addTransactionResponse.content.accepted === false) { - Errors.error( - `Transaction '${transaction.hash().toString('hex')}' was not accepted into the mempool`, - ) - } - - if (watch) { - ux.stdout('') - - await watchTransaction({ - client, - logger, - account: from, - hash: transaction.hash().toString('hex'), - }) - } -} diff --git a/ironfish-cli/src/ui/ledger.ts b/ironfish-cli/src/ui/ledger.ts index 7465e1fafd..ff0923d294 100644 --- a/ironfish-cli/src/ui/ledger.ts +++ b/ironfish-cli/src/ui/ledger.ts @@ -2,8 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { PromiseUtils } from '@ironfish/sdk' -import { ux } from '@oclif/core' +import { + CurrencyUtils, + Logger, + PromiseUtils, + RawTransaction, + RawTransactionSerde, + RpcClient, + Transaction, +} from '@ironfish/sdk' +import { Errors, ux } from '@oclif/core' import inquirer from 'inquirer' import { Ledger, @@ -14,7 +22,10 @@ import { LedgerDeviceLockedError, LedgerGPAuthFailed, LedgerPortIsBusyError, + LedgerSingleSigner, } from '../ledger' +import * as ui from '../ui' +import { watchTransaction } from '../utils/transaction' export async function ledger({ ledger, @@ -101,3 +112,86 @@ export async function ledger({ } } } + +export async function sendTransactionWithLedger( + client: RpcClient, + raw: RawTransaction, + from: string | undefined, + watch: boolean, + confirm: boolean, + logger?: Logger, +): Promise { + const ledger = new LedgerSingleSigner(logger) + + try { + await ledger.connect() + } catch (e) { + if (e instanceof Error) { + Errors.error(e.message) + } else { + throw e + } + } + + const publicKey = (await client.wallet.getAccountPublicKey({ account: from })).content + .publicKey + + const ledgerPublicKey = await ledger.getPublicAddress() + + if (publicKey !== ledgerPublicKey) { + Errors.error( + `The public key on the ledger device does not match the public key of the account '${from}'`, + ) + } + + const buildTransactionResponse = await client.wallet.buildTransaction({ + account: from, + rawTransaction: RawTransactionSerde.serialize(raw).toString('hex'), + }) + + const unsignedTransaction = buildTransactionResponse.content.unsignedTransaction + + ux.stdout('Please confirm the transaction on your Ledger device') + + const signature = (await ledger.sign(unsignedTransaction)).toString('hex') + + ux.stdout(`\nSignature: ${signature}`) + + const addSignatureResponse = await client.wallet.addSignature({ + unsignedTransaction, + signature, + }) + + const signedTransaction = addSignatureResponse.content.transaction + const bytes = Buffer.from(signedTransaction, 'hex') + + const transaction = new Transaction(bytes) + + ux.stdout(`\nSigned Transaction: ${signedTransaction}`) + ux.stdout(`\nHash: ${transaction.hash().toString('hex')}`) + ux.stdout(`Fee: ${CurrencyUtils.render(transaction.fee(), true)}`) + + await ui.confirmOrQuit('Would you like to broadcast this transaction?', confirm) + + const addTransactionResponse = await client.wallet.addTransaction({ + transaction: signedTransaction, + broadcast: true, + }) + + if (addTransactionResponse.content.accepted === false) { + Errors.error( + `Transaction '${transaction.hash().toString('hex')}' was not accepted into the mempool`, + ) + } + + if (watch) { + ux.stdout('') + + await watchTransaction({ + client, + logger, + account: from, + hash: transaction.hash().toString('hex'), + }) + } +}