From 7441c21bf7a9b259aa124e442aad8327be72708b Mon Sep 17 00:00:00 2001 From: wd021 Date: Wed, 15 Jun 2022 03:05:20 +0100 Subject: [PATCH] feat: new cli commands `accounts:notes` and `accounts:transactions` (#1086) * new cli command - accounts:transactions new cli command - accounts:transactions * fix linting * add assertHasAccount() in getTransactionNotes * use variable 'account' instead of 'accountName' * update code to latest version * fix linting * refactor: convert accounts:transactions -> accounts:notes * refactor: getNotes type change * feat: new accounts:transactions command * fix: linting on test files * make review changes * fix test fail * fix lint --- ironfish-cli/README.md | 2 + .../src/commands/accounts/notes.test.ts | 59 +++++++ ironfish-cli/src/commands/accounts/notes.ts | 56 +++++++ .../commands/accounts/transactions.test.ts | 121 ++++++++++++++ .../src/commands/accounts/transactions.ts | 117 +++++++++++++ ironfish/src/account/accounts.ts | 156 ++++++++++++++++++ ironfish/src/rpc/clients/client.ts | 33 ++++ ironfish/src/rpc/routes/accounts/getNotes.ts | 52 ++++++ .../src/rpc/routes/accounts/getTransaction.ts | 79 +++++++++ .../rpc/routes/accounts/getTransactions.ts | 60 +++++++ ironfish/src/rpc/routes/accounts/index.ts | 3 + 11 files changed, 738 insertions(+) create mode 100644 ironfish-cli/src/commands/accounts/notes.test.ts create mode 100644 ironfish-cli/src/commands/accounts/notes.ts create mode 100644 ironfish-cli/src/commands/accounts/transactions.test.ts create mode 100644 ironfish-cli/src/commands/accounts/transactions.ts create mode 100644 ironfish/src/rpc/routes/accounts/getNotes.ts create mode 100644 ironfish/src/rpc/routes/accounts/getTransaction.ts create mode 100644 ironfish/src/rpc/routes/accounts/getTransactions.ts diff --git a/ironfish-cli/README.md b/ironfish-cli/README.md index dbfceeb28a..40bcf7193c 100644 --- a/ironfish-cli/README.md +++ b/ironfish-cli/README.md @@ -27,6 +27,8 @@ Interact with the node in a new terminal window: - Request a small amount of $IRON for testing payments - `yarn start accounts:pay` - Send $IRON to another account +- `yarn start accounts:transactions [account]` + - Display transactions from and to your account ### Start a node and start mining Run these commands in two different terminals: diff --git a/ironfish-cli/src/commands/accounts/notes.test.ts b/ironfish-cli/src/commands/accounts/notes.test.ts new file mode 100644 index 0000000000..e0b9b06519 --- /dev/null +++ b/ironfish-cli/src/commands/accounts/notes.test.ts @@ -0,0 +1,59 @@ +/* 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 { GetAccountNotesResponse } from '@ironfish/sdk' +import { expect as expectCli, test } from '@oclif/test' + +describe('accounts:notes', () => { + const responseContent: GetAccountNotesResponse = { + account: 'default', + notes: [ + { + spender: true, + amount: 1, + memo: 'foo', + noteTxHash: '1fa5f38c446e52f8842d8c861507744fc3f354992610e1661e033ef316e2d3d1', + }, + ], + } + + beforeAll(() => { + jest.doMock('@ironfish/sdk', () => { + const originalModule = jest.requireActual('@ironfish/sdk') + const client = { + connect: jest.fn(), + getAccountNotes: jest.fn().mockImplementation(() => ({ + content: responseContent, + })), + } + const module: typeof jest = { + ...originalModule, + IronfishSdk: { + init: jest.fn().mockImplementation(() => ({ + connectRpc: jest.fn().mockResolvedValue(client), + client, + })), + }, + } + return module + }) + }) + + afterAll(() => { + jest.dontMock('@ironfish/sdk') + }) + + describe('fetching the notes for an account', () => { + test + .stdout() + .command(['accounts:notes', 'default']) + .exit(0) + .it('logs the notes for the given account', (ctx) => { + expectCli(ctx.stdout).include(responseContent.account) + expectCli(ctx.stdout).include(responseContent.notes[0].spender ? `✔` : `x`) + expectCli(ctx.stdout).include(responseContent.notes[0].amount) + expectCli(ctx.stdout).include(responseContent.notes[0].memo) + expectCli(ctx.stdout).include(responseContent.notes[0].noteTxHash) + }) + }) +}) diff --git a/ironfish-cli/src/commands/accounts/notes.ts b/ironfish-cli/src/commands/accounts/notes.ts new file mode 100644 index 0000000000..0c2198d70b --- /dev/null +++ b/ironfish-cli/src/commands/accounts/notes.ts @@ -0,0 +1,56 @@ +/* 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 { oreToIron } from '@ironfish/sdk' +import { CliUx } from '@oclif/core' +import { IronfishCommand } from '../../command' +import { RemoteFlags } from '../../flags' + +export class NotesCommand extends IronfishCommand { + static description = `Display the account notes` + + static flags = { + ...RemoteFlags, + } + + static args = [ + { + name: 'account', + parse: (input: string): Promise => Promise.resolve(input.trim()), + required: false, + description: 'name of the account to get notes for', + }, + ] + + async start(): Promise { + const { args } = await this.parse(NotesCommand) + const account = args.account as string | undefined + + const client = await this.sdk.connectRpc() + + const response = await client.getAccountNotes({ account }) + + const { account: accountResponse, notes } = response.content + + this.log(`\n ${accountResponse} - Account notes\n`) + + CliUx.ux.table(notes, { + isSpender: { + header: 'Spender', + get: (row) => (row.spender ? `✔` : `x`), + }, + amount: { + header: 'Amount ($IRON)', + get: (row) => oreToIron(row.amount), + }, + memo: { + header: 'Memo', + }, + noteTxHash: { + header: 'From Transaction', + }, + }) + + this.log(`\n`) + } +} diff --git a/ironfish-cli/src/commands/accounts/transactions.test.ts b/ironfish-cli/src/commands/accounts/transactions.test.ts new file mode 100644 index 0000000000..2beda1cb84 --- /dev/null +++ b/ironfish-cli/src/commands/accounts/transactions.test.ts @@ -0,0 +1,121 @@ +/* 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 { GetAccountTransactionResponse, GetAccountTransactionsResponse } from '@ironfish/sdk' +import { expect as expectCli, test } from '@oclif/test' + +describe('accounts:transactions', () => { + const account = 'default' + const transactionHash = '1fa5f38c446e52f8842d8c861507744fc3f354992610e1661e033ef316e2d3d1' + + const responseContentTransactions: GetAccountTransactionsResponse = { + account, + transactions: [ + { + creator: true, + status: 'completed', + hash: '1fa5f38c446e52f8842d8c861507744fc3f354992610e1661e033ef316e2d3d1', + isMinersFee: false, + fee: 0.1, + notes: 1, + spends: 1, + }, + ], + } + + const responseContentTransaction: GetAccountTransactionResponse = { + account, + transactionHash, + transactionInfo: { + status: 'completed', + isMinersFee: false, + fee: 0.1, + notes: 1, + spends: 1, + }, + transactionNotes: [ + { + spender: true, + amount: 1, + memo: 'foo', + }, + ], + } + + beforeAll(() => { + jest.doMock('@ironfish/sdk', () => { + const originalModule = jest.requireActual('@ironfish/sdk') + const client = { + connect: jest.fn(), + getAccountTransactions: jest.fn().mockImplementation(() => ({ + content: responseContentTransactions, + })), + getAccountTransaction: jest.fn().mockImplementation(() => ({ + content: responseContentTransaction, + })), + } + const module: typeof jest = { + ...originalModule, + IronfishSdk: { + init: jest.fn().mockImplementation(() => ({ + connectRpc: jest.fn().mockResolvedValue(client), + client, + })), + }, + } + return module + }) + }) + + afterAll(() => { + jest.dontMock('@ironfish/sdk') + }) + + describe('fetching transactions for an account', () => { + test + .stdout() + .command(['accounts:transactions', `-a ${account}`]) + .exit(0) + .it('logs the transactions for the given account', (ctx) => { + expectCli(ctx.stdout).include(responseContentTransactions.account) + expectCli(ctx.stdout).include( + responseContentTransactions.transactions[0].creator ? `✔` : `x`, + ) + expectCli(ctx.stdout).include(responseContentTransactions.transactions[0].status) + expectCli(ctx.stdout).include(responseContentTransactions.transactions[0].hash) + expectCli(ctx.stdout).include( + responseContentTransactions.transactions[0].isMinersFee ? `✔` : `x`, + ) + expectCli(ctx.stdout).include(responseContentTransactions.transactions[0].fee) + expectCli(ctx.stdout).include(responseContentTransactions.transactions[0].notes) + expectCli(ctx.stdout).include(responseContentTransactions.transactions[0].spends) + }) + }) + + describe('fetching details of specific transaction', () => { + test + .stdout() + .command(['accounts:transactions', `-t ${transactionHash}`]) + .exit(0) + .it('logs the transaction details and notes for the given hash', (ctx) => { + expectCli(ctx.stdout).include(responseContentTransaction.account) + expectCli(ctx.stdout).include(responseContentTransaction.transactionHash) + + // transaction details + expectCli(ctx.stdout).include(responseContentTransaction.transactionInfo?.status) + expectCli(ctx.stdout).include( + responseContentTransaction.transactionInfo?.isMinersFee ? `✔` : `x`, + ) + expectCli(ctx.stdout).include(responseContentTransaction.transactionInfo?.fee) + expectCli(ctx.stdout).include(responseContentTransaction.transactionInfo?.notes) + expectCli(ctx.stdout).include(responseContentTransaction.transactionInfo?.spends) + + // transaction notes + expectCli(ctx.stdout).include( + responseContentTransaction.transactionNotes[0].spender ? `✔` : `x`, + ) + expectCli(ctx.stdout).include(responseContentTransaction.transactionNotes[0].amount) + expectCli(ctx.stdout).include(responseContentTransaction.transactionNotes[0].memo) + }) + }) +}) diff --git a/ironfish-cli/src/commands/accounts/transactions.ts b/ironfish-cli/src/commands/accounts/transactions.ts new file mode 100644 index 0000000000..01087c61b2 --- /dev/null +++ b/ironfish-cli/src/commands/accounts/transactions.ts @@ -0,0 +1,117 @@ +/* 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 { oreToIron } from '@ironfish/sdk' +import { CliUx, Flags } from '@oclif/core' +import { IronfishCommand } from '../../command' +import { RemoteFlags } from '../../flags' + +export class TransactionsCommand extends IronfishCommand { + static description = `Display the account transactions` + + static flags = { + ...RemoteFlags, + account: Flags.string({ + char: 'a', + description: 'account transactions', + }), + hash: Flags.string({ + char: 't', + description: 'details of transaction hash', + }), + } + + async start(): Promise { + const { flags } = await this.parse(TransactionsCommand) + const account = flags.account?.trim() + const hash = flags.hash?.trim() + + if (hash) { + await this.getTransaction(account, hash) + } else { + await this.getTransactions(account) + } + } + + async getTransaction(account: string | undefined, hash: string): Promise { + const client = await this.sdk.connectRpc() + + const response = await client.getAccountTransaction({ account, hash }) + + const { + account: accountResponse, + transactionHash, + transactionInfo, + transactionNotes, + } = response.content + + this.log(`Account: ${accountResponse}`) + + if (transactionInfo !== null) { + this.log( + `Transaction: ${transactionHash}\nStatus: ${transactionInfo.status}\nMiner Fee: ${ + transactionInfo.isMinersFee ? `✔` : `x` + }\nFee ($ORE): ${transactionInfo.fee}\nSpends: ${transactionInfo.spends}\n`, + ) + } + + if (transactionNotes.length > 0) { + this.log(`---Notes---\n`) + + CliUx.ux.table(transactionNotes, { + isSpender: { + header: 'Spender', + get: (row) => (row.spender ? `✔` : `x`), + }, + amount: { + header: 'Amount ($IRON)', + get: (row) => oreToIron(row.amount), + }, + memo: { + header: 'Memo', + }, + }) + } + + this.log(`\n`) + } + + async getTransactions(account: string | undefined): Promise { + const client = await this.sdk.connectRpc() + + const response = await client.getAccountTransactions({ account }) + + const { account: accountResponse, transactions } = response.content + + this.log(`\n ${String(accountResponse)} - Account transactions\n`) + + CliUx.ux.table(transactions, { + status: { + header: 'Status', + }, + creator: { + header: 'Creator', + get: (row) => (row.creator ? `✔` : `x`), + }, + hash: { + header: 'Hash', + }, + isMinersFee: { + header: 'Miner Fee', + get: (row) => (row.isMinersFee ? `✔` : `x`), + }, + fee: { + header: 'Fee ($ORE)', + get: (row) => row.fee, + }, + notes: { + header: 'Notes', + }, + spends: { + header: 'Spends', + }, + }) + + this.log(`\n`) + } +} diff --git a/ironfish/src/account/accounts.ts b/ironfish/src/account/accounts.ts index 60f76f5b57..cb057572d3 100644 --- a/ironfish/src/account/accounts.ts +++ b/ironfish/src/account/accounts.ts @@ -618,6 +618,46 @@ export class Accounts { this.scan = null } + getNotes(account: Account): { + notes: { + spender: boolean + amount: number + memo: string + noteTxHash: string + }[] + } { + this.assertHasAccount(account) + + const notes = [] + + for (const transactionMapValue of this.transactionMap.values()) { + const transaction = transactionMapValue.transaction + + for (const note of transaction.notes()) { + // Try decrypting the note as the owner + let decryptedNote = note.decryptNoteForOwner(account.incomingViewKey) + let spender = false + + if (!decryptedNote) { + // Try decrypting the note as the spender + decryptedNote = note.decryptNoteForSpender(account.outgoingViewKey) + spender = true + } + + if (decryptedNote && decryptedNote.value() !== BigInt(0)) { + notes.push({ + spender, + amount: Number(decryptedNote.value()), + memo: decryptedNote.memo().replace(/\x00/g, ''), + noteTxHash: transaction.hash().toString('hex'), + }) + } + } + } + + return { notes } + } + private async getUnspentNotes(account: Account): Promise< ReadonlyArray<{ hash: string @@ -989,6 +1029,122 @@ export class Accounts { await this.scanTransactions() } + getTransactions(account: Account): { + transactions: { + creator: boolean + status: string + hash: string + isMinersFee: boolean + fee: number + notes: number + spends: number + }[] + } { + this.assertHasAccount(account) + + const transactions = [] + + for (const transactionMapValue of this.transactionMap.values()) { + const transaction = transactionMapValue.transaction + + // check if account created transaction + let transactionCreator = false + let transactionRecipient = false + + for (const note of transaction.notes()) { + if (note.decryptNoteForSpender(account.outgoingViewKey)) { + transactionCreator = true + break + } else if (note.decryptNoteForOwner(account.incomingViewKey)) { + transactionRecipient = true + } + } + + if (transactionCreator || transactionRecipient) { + transactions.push({ + creator: transactionCreator, + status: + transactionMapValue.blockHash && transactionMapValue.submittedSequence + ? 'completed' + : 'pending', + hash: transaction.hash().toString('hex'), + isMinersFee: transaction.isMinersFee(), + fee: Number(transaction.fee()), + notes: transaction.notesLength(), + spends: transaction.spendsLength(), + }) + } + } + + return { transactions } + } + + getTransaction( + account: Account, + hash: string, + ): { + transactionInfo: { + status: string + isMinersFee: boolean + fee: number + notes: number + spends: number + } | null + transactionNotes: { + spender: boolean + amount: number + memo: string + }[] + } { + this.assertHasAccount(account) + + let transactionInfo = null + const transactionNotes = [] + + const transactionMapValue = this.transactionMap.get(Buffer.from(hash, 'hex')) + + if (transactionMapValue) { + const transaction = transactionMapValue.transaction + + if (transaction.hash().toString('hex') === hash) { + for (const note of transaction.notes()) { + // Try decrypting the note as the owner + let decryptedNote = note.decryptNoteForOwner(account.incomingViewKey) + let spender = false + + if (!decryptedNote) { + // Try decrypting the note as the spender + decryptedNote = note.decryptNoteForSpender(account.outgoingViewKey) + spender = true + } + + if (decryptedNote && decryptedNote.value() !== BigInt(0)) { + transactionNotes.push({ + spender, + amount: Number(decryptedNote.value()), + memo: decryptedNote.memo().replace(/\x00/g, ''), + }) + } + } + + if (transactionNotes.length > 0) { + transactionInfo = { + status: + transactionMapValue.blockHash && transactionMapValue.submittedSequence + ? 'completed' + : 'pending', + isMinersFee: transaction.isMinersFee(), + fee: Number(transaction.fee()), + notes: transaction.notesLength(), + spends: transaction.spendsLength(), + } + } + } + } + + return { transactionInfo, transactionNotes } + } + async importAccount(toImport: Partial): Promise { validateAccount(toImport) diff --git a/ironfish/src/rpc/clients/client.ts b/ironfish/src/rpc/clients/client.ts index 337fdf9c12..271ffdd641 100644 --- a/ironfish/src/rpc/clients/client.ts +++ b/ironfish/src/rpc/clients/client.ts @@ -9,8 +9,14 @@ import { BlockTemplateStreamResponse, CreateAccountRequest, CreateAccountResponse, + GetAccountNotesRequest, + GetAccountNotesResponse, GetAccountsRequest, GetAccountsResponse, + GetAccountTransactionRequest, + GetAccountTransactionResponse, + GetAccountTransactionsRequest, + GetAccountTransactionsResponse, GetBalanceRequest, GetBalanceResponse, GetBlockInfoRequest, @@ -198,6 +204,33 @@ export abstract class RpcClient { ).waitForEnd() } + async getAccountNotes( + params: GetAccountNotesRequest = {}, + ): Promise> { + return await this.request( + `${ApiNamespace.account}/getAccountNotes`, + params, + ).waitForEnd() + } + + async getAccountTransaction( + params: GetAccountTransactionRequest, + ): Promise> { + return await this.request( + `${ApiNamespace.account}/getAccountTransaction`, + params, + ).waitForEnd() + } + + async getAccountTransactions( + params: GetAccountTransactionsRequest, + ): Promise> { + return await this.request( + `${ApiNamespace.account}/getAccountTransactions`, + params, + ).waitForEnd() + } + async getPeers( params: GetPeersRequest = undefined, ): Promise> { diff --git a/ironfish/src/rpc/routes/accounts/getNotes.ts b/ironfish/src/rpc/routes/accounts/getNotes.ts new file mode 100644 index 0000000000..43ac2c995d --- /dev/null +++ b/ironfish/src/rpc/routes/accounts/getNotes.ts @@ -0,0 +1,52 @@ +/* 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 * as yup from 'yup' +import { ApiNamespace, router } from '../router' +import { getAccount } from './utils' + +export type GetAccountNotesRequest = { account?: string } + +export type GetAccountNotesResponse = { + account: string + notes: { + spender: boolean + amount: number + memo: string + noteTxHash: string + }[] +} + +export const GetAccountNotesRequestSchema: yup.ObjectSchema = yup + .object({ + account: yup.string().strip(true), + }) + .defined() + +export const GetAccountNotesResponseSchema: yup.ObjectSchema = yup + .object({ + account: yup.string().defined(), + notes: yup + .array( + yup + .object({ + spender: yup.boolean().defined(), + amount: yup.number().defined(), + memo: yup.string().trim().defined(), + noteTxHash: yup.string().defined(), + }) + .defined(), + ) + .defined(), + }) + .defined() + +router.register( + `${ApiNamespace.account}/getAccountNotes`, + GetAccountNotesRequestSchema, + (request, node): void => { + const account = getAccount(node, request.data.account) + const { notes } = node.accounts.getNotes(account) + request.end({ account: account.displayName, notes }) + }, +) diff --git a/ironfish/src/rpc/routes/accounts/getTransaction.ts b/ironfish/src/rpc/routes/accounts/getTransaction.ts new file mode 100644 index 0000000000..7166c75de3 --- /dev/null +++ b/ironfish/src/rpc/routes/accounts/getTransaction.ts @@ -0,0 +1,79 @@ +/* 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 * as yup from 'yup' +import { ApiNamespace, router } from '../router' +import { getAccount } from './utils' + +export type GetAccountTransactionRequest = { account?: string; hash: string } + +export type GetAccountTransactionResponse = { + account: string + transactionHash: string + transactionInfo: { + status: string + isMinersFee: boolean + fee: number + notes: number + spends: number + } | null + transactionNotes: { + spender: boolean + amount: number + memo: string + }[] +} + +export const GetAccountTransactionRequestSchema: yup.ObjectSchema = + yup + .object({ + account: yup.string().strip(true), + hash: yup.string().defined(), + }) + .defined() + +export const GetAccountTransactionResponseSchema: yup.ObjectSchema = + yup + .object({ + account: yup.string().defined(), + transactionHash: yup.string().defined(), + transactionInfo: yup + .object({ + status: yup.string().defined(), + isMinersFee: yup.boolean().defined(), + fee: yup.number().defined(), + notes: yup.number().defined(), + spends: yup.number().defined(), + }) + .defined(), + transactionNotes: yup + .array( + yup + .object({ + spender: yup.boolean().defined(), + amount: yup.number().defined(), + memo: yup.string().trim().defined(), + }) + .defined(), + ) + .defined(), + }) + .defined() + +router.register( + `${ApiNamespace.account}/getAccountTransaction`, + GetAccountTransactionRequestSchema, + (request, node): void => { + const account = getAccount(node, request.data.account) + const { transactionInfo, transactionNotes } = node.accounts.getTransaction( + account, + request.data.hash, + ) + request.end({ + account: account.displayName, + transactionHash: request.data.hash, + transactionInfo, + transactionNotes, + }) + }, +) diff --git a/ironfish/src/rpc/routes/accounts/getTransactions.ts b/ironfish/src/rpc/routes/accounts/getTransactions.ts new file mode 100644 index 0000000000..8f111f3343 --- /dev/null +++ b/ironfish/src/rpc/routes/accounts/getTransactions.ts @@ -0,0 +1,60 @@ +/* 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 * as yup from 'yup' +import { ApiNamespace, router } from '../router' +import { getAccount } from './utils' + +export type GetAccountTransactionsRequest = { account?: string } + +export type GetAccountTransactionsResponse = { + account: string + transactions: { + creator: boolean + status: string + hash: string + isMinersFee: boolean + fee: number + notes: number + spends: number + }[] +} + +export const GetAccountTransactionsRequestSchema: yup.ObjectSchema = + yup + .object({ + account: yup.string().strip(true), + }) + .defined() + +export const GetAccountTransactionsResponseSchema: yup.ObjectSchema = + yup + .object({ + account: yup.string().defined(), + transactions: yup + .array( + yup + .object({ + creator: yup.boolean().defined(), + status: yup.string().defined(), + hash: yup.string().defined(), + isMinersFee: yup.boolean().defined(), + fee: yup.number().defined(), + notes: yup.number().defined(), + spends: yup.number().defined(), + }) + .defined(), + ) + .defined(), + }) + .defined() + +router.register( + `${ApiNamespace.account}/getAccountTransactions`, + GetAccountTransactionsRequestSchema, + (request, node): void => { + const account = getAccount(node, request.data.account) + const { transactions } = node.accounts.getTransactions(account) + request.end({ account: account.displayName, transactions }) + }, +) diff --git a/ironfish/src/rpc/routes/accounts/index.ts b/ironfish/src/rpc/routes/accounts/index.ts index 62d4ac1738..6ebea60a94 100644 --- a/ironfish/src/rpc/routes/accounts/index.ts +++ b/ironfish/src/rpc/routes/accounts/index.ts @@ -6,8 +6,11 @@ export * from './create' export * from './exportAccount' export * from './getAccounts' export * from './getDefaultAccount' +export * from './getNotes' export * from './getBalance' export * from './getPublicKey' +export * from './getTransaction' +export * from './getTransactions' export * from './importAccount' export * from './removeAccount' export * from './rescanAccount'