UNPKG

@ledgerhq/coin-stellar

Version:
147 lines (126 loc) 4.77 kB
// SPDX-FileCopyrightText: © 2026 LEDGER SAS // SPDX-License-Identifier: Apache-2.0 import { NetworkDown } from '@ledgerhq/errors' import { Horizon, Memo } from '@stellar/stellar-sdk' import { getRecipientAccount, loadAccount } from '../network' import { StellarAssetRequired, StellarMuxedAccountNotExist } from '../types' import { craftTransaction } from './craftTransaction' import { buildChangeTrustOperation, buildCreateAccountOperation, buildPaymentOperation, buildTransactionBuilder, } from './sdkWrapper' jest.mock('../network', () => ({ loadAccount: jest.fn(), getRecipientAccount: jest.fn(), })) jest.mock('./sdkWrapper', () => ({ buildChangeTrustOperation: jest.fn(), buildCreateAccountOperation: jest.fn(), buildPaymentOperation: jest.fn(), buildTransactionBuilder: jest.fn(), })) describe('craftTransaction', () => { const account = { address: 'GABC' } const transaction = { type: 'payment', recipient: 'GRECIPIENT', amount: 10000000n, fee: 100n, } const craftedTransaction = { toXDR: jest.fn().mockReturnValue('tx-xdr'), signatureBase: jest.fn().mockReturnValue(Buffer.from('signature-base')), } const txBuilder = { addOperation: jest.fn(), addMemo: jest.fn(), setTimeout: jest.fn(), build: jest.fn().mockReturnValue(craftedTransaction), } beforeEach(() => { jest.clearAllMocks() txBuilder.setTimeout.mockReturnValue(txBuilder) jest .mocked(buildTransactionBuilder) .mockReturnValue(txBuilder as unknown as ReturnType<typeof buildTransactionBuilder>) jest.mocked(loadAccount).mockResolvedValue({ id: 'source-account' } as Horizon.AccountResponse) jest.mocked(getRecipientAccount).mockResolvedValue({ id: 'GRECIPIENT', isMuxedAccount: false, assetIds: [], }) jest .mocked(buildPaymentOperation) .mockReturnValue('payment-op' as unknown as ReturnType<typeof buildPaymentOperation>) jest .mocked(buildCreateAccountOperation) .mockReturnValue( 'create-account-op' as unknown as ReturnType<typeof buildCreateAccountOperation> ) jest .mocked(buildChangeTrustOperation) .mockReturnValue('change-trust-op' as unknown as ReturnType<typeof buildChangeTrustOperation>) }) it('throws NetworkDown when source account cannot be loaded', async () => { jest.mocked(loadAccount).mockResolvedValue(null) await expect(craftTransaction(account, transaction)).rejects.toThrow(NetworkDown) }) it('throws StellarAssetRequired for changeTrust without asset code and issuer', async () => { await expect( craftTransaction(account, { ...transaction, type: 'changeTrust', assetCode: undefined, assetIssuer: undefined, }) ).rejects.toThrow(StellarAssetRequired) }) it('builds a changeTrust operation when type is changeTrust', async () => { const result = await craftTransaction(account, { ...transaction, type: 'changeTrust', assetCode: 'USDC', assetIssuer: 'GISSUER', }) expect(buildChangeTrustOperation).toHaveBeenCalledWith('USDC', 'GISSUER') expect(txBuilder.addOperation).toHaveBeenCalledWith('change-trust-op') expect(getRecipientAccount).not.toHaveBeenCalled() expect(result.xdr).toBe('tx-xdr') expect(result.signatureBase).toBe('c2lnbmF0dXJlLWJhc2U=') }) it('builds a payment operation and memo when recipient exists', async () => { const memoTextSpy = jest.spyOn(Memo, 'text') await craftTransaction(account, { ...transaction, memoType: 'MEMO_TEXT', memoValue: 'hello', }) expect(buildPaymentOperation).toHaveBeenCalledWith({ destination: 'GRECIPIENT', amount: 10000000n, assetCode: undefined, assetIssuer: undefined, }) expect(txBuilder.addOperation).toHaveBeenCalledWith('payment-op') expect(memoTextSpy).toHaveBeenCalledWith('hello') expect(txBuilder.addMemo).toHaveBeenCalledTimes(1) }) it('throws StellarMuxedAccountNotExist when missing muxed recipient account', async () => { jest .mocked(getRecipientAccount) .mockResolvedValue({ id: null, isMuxedAccount: true, assetIds: [] }) await expect(craftTransaction(account, transaction)).rejects.toThrow( StellarMuxedAccountNotExist ) }) it('builds createAccount operation when recipient does not exist and is not muxed', async () => { jest .mocked(getRecipientAccount) .mockResolvedValue({ id: null, isMuxedAccount: false, assetIds: [] }) await craftTransaction(account, transaction) expect(buildCreateAccountOperation).toHaveBeenCalledWith('GRECIPIENT', 10000000n) expect(txBuilder.addOperation).toHaveBeenCalledWith('create-account-op') }) })