diff --git a/packages/payments/src/node/lnd.test.ts b/packages/payments/src/node/lnd.test.ts index 7766f68..284853f 100644 --- a/packages/payments/src/node/lnd.test.ts +++ b/packages/payments/src/node/lnd.test.ts @@ -54,7 +54,7 @@ describe('sendLndPayment', () => { restHost: host, // base64 macaroon → should be sent as hex macaroon: Buffer.from([0x02, 0x01, 0x03]).toString('base64'), - body: { payment_request: 'lnbc1', timeout_seconds: 60 }, + body: { payment_request: 'lnbc1', fee_limit_sat: '10', timeout_seconds: 60 }, onUpdate: (s) => seen.push(s), }); @@ -78,7 +78,7 @@ describe('sendLndPayment', () => { sendLndPayment({ restHost: host, macaroon: '020103', - body: { payment_request: 'lnbc1', timeout_seconds: 60 }, + body: { payment_request: 'lnbc1', fee_limit_sat: '10', timeout_seconds: 60 }, }), (err: unknown) => err instanceof PaymentSendError && /no route/.test((err as Error).message), ); diff --git a/packages/payments/src/node/types.ts b/packages/payments/src/node/types.ts index 9c00508..2786560 100644 --- a/packages/payments/src/node/types.ts +++ b/packages/payments/src/node/types.ts @@ -5,6 +5,7 @@ export type PaymentLifecycleStatus = 'UNKNOWN' | 'INITIATED' | 'IN_FLIGHT' | 'SU export interface SendLndPaymentBody { payment_request: string; amt?: string; + fee_limit_sat: string; timeout_seconds: number; } @@ -32,6 +33,7 @@ export interface LndPaymentUpdate { export interface SendAssetPaymentBody { payment_request: { payment_request: string; + fee_limit_sat: string; timeout_seconds: number; }; /** diff --git a/packages/payments/src/resources/transactions.send.test.ts b/packages/payments/src/resources/transactions.send.test.ts index efc919d..6b26498 100644 --- a/packages/payments/src/resources/transactions.send.test.ts +++ b/packages/payments/src/resources/transactions.send.test.ts @@ -15,13 +15,18 @@ const MACAROON_HEX = '0201036c6e6402240a'; const SYMMETRIC_KEY = bytesToHex(new Uint8Array(64).map((_, i) => (i * 5 + 1) & 0xff)); let server: Server | undefined; +let lastBody: unknown; afterEach(async () => { if (server) await new Promise((resolve) => server!.close(() => resolve())); server = undefined; + lastBody = undefined; }); async function startNode(lines: object[]): Promise { - server = createServer((_req, res: ServerResponse) => { + server = createServer(async (req, res: ServerResponse) => { + const chunks: Buffer[] = []; + for await (const chunk of req) chunks.push(chunk as Buffer); + lastBody = JSON.parse(Buffer.concat(chunks).toString('utf8') || '{}'); res.writeHead(200, { 'content-type': 'application/json' }); for (const line of lines) res.write(`${JSON.stringify(line)}\n`); res.end(); @@ -114,6 +119,7 @@ describe('Transactions.send', () => { assert.equal(result.payment.paymentHash, 'ph'); assert.equal(result.transaction.payment_request, 'lnbc1xyz'); assert.deepEqual(statuses, ['IN_FLIGHT', 'SUCCEEDED']); + assert.equal((lastBody as { fee_limit_sat: string }).fee_limit_sat, '4294967296'); }); it('creates a sandbox send without a password and returns payment: null', async () => { diff --git a/packages/payments/src/resources/transactions.ts b/packages/payments/src/resources/transactions.ts index 0150eba..f95400d 100644 --- a/packages/payments/src/resources/transactions.ts +++ b/packages/payments/src/resources/transactions.ts @@ -18,6 +18,13 @@ import type { SendDestination, SendParams, SendResult } from './transactions.typ const DEFAULT_TIMEOUT_SECONDS = 60; +/** + * Fee limit sent to LND/litd for every send. Fixed at 2^32 sats — far beyond + * any real routing fee — so the node never rejects a payment for lacking a + * fee limit, without exposing fee-limit configuration to callers. + */ +const FEE_LIMIT_SATS = '4294967296'; + /** * Taproot asset group keys come from GraphQL as hex (a 33-byte compressed * secp256k1 pubkey → 66 hex chars). litd's REST gateway expects the `group_key` @@ -161,6 +168,7 @@ export class Transactions { body: { payment_request: { payment_request: transaction.payment_request, + fee_limit_sat: FEE_LIMIT_SATS, timeout_seconds: timeoutSeconds, }, ...(wallet.asset.taproot_asset_details?.group_key @@ -175,6 +183,7 @@ export class Transactions { body: { payment_request: transaction.payment_request, ...(lndAmountSats(destination) ? { amt: lndAmountSats(destination) } : {}), + fee_limit_sat: FEE_LIMIT_SATS, timeout_seconds: timeoutSeconds, }, });