UNPKG

@ledgerhq/coin-stellar

Version:
118 lines 5 kB
// SPDX-FileCopyrightText: © 2026 LEDGER SAS // SPDX-License-Identifier: Apache-2.0 import { rejectBalanceOptions } from '@ledgerhq/coin-module-framework/api/getBalance/rejectBalanceOptions'; import { craftTransactionData } from '@ledgerhq/coin-module-framework/logic/craftTransactionData'; import { xdr } from '@stellar/stellar-sdk'; import coinConfig from '../config'; import { broadcast, combine, craftTransaction, estimateFees, getBalance, getBlock, getBlockInfo, lastBlock, validateIntent, } from '../logic'; import { listOperations } from '../logic'; import { validateAddress } from '../logic/validateAddress'; import { fetchSequence, registerHorizonInterceptors } from '../network'; export function createApi(config) { coinConfig.setCoinConfig(() => ({ ...config, status: { type: 'active' } })); registerHorizonInterceptors(); return { broadcast, combine: compose, craftTransaction: craft, craftRawTransaction: (_transaction, _sender, _publicKey, _sequence) => { throw new Error('craftRawTransaction is not supported'); }, estimateFees: estimate, getBalance: (address, options) => rejectBalanceOptions(() => getBalance(address), options), lastBlock, listOperations: operations, getBlock, getBlockInfo, getStakes(_address, _cursor) { throw new Error('getStakes is not supported'); }, getRewards(_address, _cursor) { throw new Error('getRewards is not supported'); }, validateIntent, getNextSequence: async (address) => { const sequence = await fetchSequence(address); return BigInt(sequence.plus(1).toFixed()); }, getValidators(_cursor) { throw new Error('getValidators is not supported'); }, validateAddress, craftTransactionData, }; } async function craft(transactionIntent, customFees) { const fees = customFees?.value || (await estimateFees()); // NOTE: check how many memos, throw if more than one? // if (transactionIntent.memos && transactionIntent.memos.length > 1) { // throw new Error("Stellar only supports one memo per transaction."); // } const memo = 'memo' in transactionIntent ? transactionIntent.memo : undefined; const hasMemoValue = memo && memo.type !== 'NO_MEMO'; const tx = await craftTransaction({ address: transactionIntent.sender }, { type: transactionIntent.type, recipient: transactionIntent.recipient, amount: transactionIntent.amount, fee: fees, ...(transactionIntent.asset.type !== 'native' && 'assetReference' in transactionIntent.asset ? { assetCode: transactionIntent.asset.assetReference, assetIssuer: transactionIntent.asset.assetOwner, } : {}), memoType: memo?.type, ...(hasMemoValue ? { memoValue: memo.value } : {}), }); // Note: the API returns the signature base, not the full XDR, see BACK-8727 for more context return { transaction: tx.signatureBase }; } function compose(tx, signature, pubkey) { if (!pubkey) { throw new Error('Missing pubkey'); } // note: accept here `TransactionEnvelope` or `TransactionSignaturePayload`, see BACK-8727 for more context return combine(envelopeFromAnyXDR(tx, 'base64'), signature, pubkey); } async function estimate(_transactionIntent) { const value = await estimateFees(); return { value }; } async function operations(address, options) { const { items, next } = await listOperations(address, options); return { items, next: next || undefined }; } /** * Deserialize a transaction envelope, also accepting transaction signature payload form. * * @param input serialized `TransactionEnvelope` or `TransactionSignaturePayload` * @param format serialization encoding */ export function envelopeFromAnyXDR(input, format) { try { return xdr.TransactionEnvelope.fromXDR(input, format); } catch (envelopeError) { try { return signatureBaseToEnvelope(xdr.TransactionSignaturePayload.fromXDR(input, format)); } catch (signatureBaseError) { throw new Error(`Failed decoding transaction as an envelope (${envelopeError}) or as a signature base: (${signatureBaseError})`); } } } /** * Convert a `TransactionSignaturePayload` into a `TransactionEnvelope`. * * @param signatureBase deserialized `TransactionSignaturePayload` */ function signatureBaseToEnvelope(signatureBase) { const tx = signatureBase.taggedTransaction().value(); if (tx instanceof xdr.Transaction) { return xdr.TransactionEnvelope.envelopeTypeTx(new xdr.TransactionV1Envelope({ tx, signatures: [] })); } else { return xdr.TransactionEnvelope.envelopeTypeTxFeeBump(new xdr.FeeBumpTransactionEnvelope({ tx, signatures: [] })); } } //# sourceMappingURL=index.js.map