diff --git a/modules/abstract-lightning/src/codecs/api/withdraw.ts b/modules/abstract-lightning/src/codecs/api/withdraw.ts index c0494dfba3..74800c1b37 100644 --- a/modules/abstract-lightning/src/codecs/api/withdraw.ts +++ b/modules/abstract-lightning/src/codecs/api/withdraw.ts @@ -1,20 +1,22 @@ import * as t from 'io-ts'; -import { LightningOnchainRecipient } from '@bitgo/public-types'; +import { LightningOnchainRequest, optionalString } from '@bitgo/public-types'; import { PendingApprovalData, TxRequestState } from '@bitgo/sdk-core'; -import { BigIntFromString } from 'io-ts-types'; export const WithdrawStatusDelivered = 'delivered'; export const WithdrawStatusFailed = 'failed'; export const WithdrawStatus = t.union([t.literal(WithdrawStatusDelivered), t.literal(WithdrawStatusFailed)]); -export const LightningOnchainWithdrawParams = t.type({ - recipients: t.array(LightningOnchainRecipient), - satsPerVbyte: BigIntFromString, - // todo:(current) add passphrase - // passphrase: t.string, -}); - +export const LightningOnchainWithdrawParams = t.intersection([ + LightningOnchainRequest, + t.type({ + passphrase: t.string, + }), + t.partial({ + sequenceId: optionalString, + comment: optionalString, + }), +]); export type LightningOnchainWithdrawParams = t.TypeOf; export const LndCreateWithdrawResponse = t.intersection( diff --git a/modules/abstract-lightning/src/wallet/lightning.ts b/modules/abstract-lightning/src/wallet/lightning.ts index 3801d4a9a2..7f2c0be975 100644 --- a/modules/abstract-lightning/src/wallet/lightning.ts +++ b/modules/abstract-lightning/src/wallet/lightning.ts @@ -29,7 +29,7 @@ import { ListPaymentsResponse, LndCreateWithdrawResponse, } from '../codecs'; -import { LightningPaymentIntent, LightningPaymentRequest } from '@bitgo/public-types'; +import { LightningPaymentIntent, LightningPaymentRequest, LightningOnchainRequest } from '@bitgo/public-types'; export type PayInvoiceResponse = { /** @@ -167,6 +167,9 @@ export interface ILightningWallet { * @param {LightningOnchainWithdrawParams} params - Withdraw parameters * @param {LightningOnchainRecipient[]} params.recipients - The recipients to pay * @param {bigint} params.satsPerVbyte - Value for sats per virtual byte + * @param {string} params.passphrase - The wallet passphrase + * @param {string} [params.sequenceId] - Optional sequence ID for the respective withdraw transfer + * @param {string} [params.comment] - Optional comment for the respective withdraw transfer * @returns {Promise} Withdraw result containing transaction request details and status */ withdrawOnchain(params: LightningOnchainWithdrawParams): Promise; @@ -336,12 +339,25 @@ export class LightningWallet implements ILightningWallet { const reqId = new RequestTracer(); this.wallet.bitgo.setRequestTracer(reqId); + const { userAuthKey } = await getLightningAuthKeychains(this.wallet); + const userAuthKeyEncryptedPrv = userAuthKey.encryptedPrv; + if (!userAuthKeyEncryptedPrv) { + throw new Error(`user auth key is missing encrypted private key`); + } + const signature = createMessageSignature( + t.exact(LightningOnchainRequest).encode(params), + this.wallet.bitgo.decrypt({ password: params.passphrase, input: userAuthKeyEncryptedPrv }) + ); + const paymentIntent: { intent: LightningPaymentIntent } = { intent: { - onchainRequest: { + comment: params.comment, + sequenceId: params.sequenceId, + signedRequest: { recipients: params.recipients, satsPerVbyte: params.satsPerVbyte, }, + signature, intentType: 'payment', }, }; diff --git a/modules/bitgo/test/v2/unit/lightning/lightningWallets.ts b/modules/bitgo/test/v2/unit/lightning/lightningWallets.ts index 784823e29b..cb3b0f4283 100644 --- a/modules/bitgo/test/v2/unit/lightning/lightningWallets.ts +++ b/modules/bitgo/test/v2/unit/lightning/lightningWallets.ts @@ -825,6 +825,7 @@ describe('Lightning wallets', function () { }, ], satsPerVbyte: 15n, + passphrase: 'password123', }; const txRequestResponse = { @@ -973,6 +974,7 @@ describe('Lightning wallets', function () { }, ], satsPerVbyte: 15n, + passphrase: 'password123', }; const txRequestResponse = { diff --git a/modules/express/test/unit/lightning/lightningWithdrawRoutes.test.ts b/modules/express/test/unit/lightning/lightningWithdrawRoutes.test.ts index 29a69cfef7..a941dcca8a 100644 --- a/modules/express/test/unit/lightning/lightningWithdrawRoutes.test.ts +++ b/modules/express/test/unit/lightning/lightningWithdrawRoutes.test.ts @@ -32,6 +32,7 @@ describe('Lightning Withdraw Routes', () => { }, ], satsPerVbyte: '15', + passphrase: 'password123', }; const expectedResponse: LightningOnchainWithdrawResponse = { @@ -84,6 +85,7 @@ describe('Lightning Withdraw Routes', () => { // we decode the amountMsat string to bigint, it should be in bigint format when passed to payInvoice should(firstArg).have.property('recipients', decodedRecipients); should(firstArg).have.property('satsPerVbyte', BigInt(inputParams.satsPerVbyte)); + should(firstArg).have.property('passphrase', BigInt(inputParams.satsPerVbyte)); }); it('should throw an error if the satsPerVbyte is missing in the request params', async () => { @@ -94,6 +96,7 @@ describe('Lightning Withdraw Routes', () => { address: 'bcrt1qjq48cqk2u80hewdcndf539m8nnnvt845nl68x7', }, ], + passphrase: 'password123', }; const req = mockRequestObject({ @@ -110,6 +113,29 @@ describe('Lightning Withdraw Routes', () => { it('should throw an error if the recipients is missing in the request params', async () => { const inputParams = { satsPerVbyte: '15', + passphrase: 'password123', + }; + + const req = mockRequestObject({ + params: { id: 'testWalletId', coin }, + body: inputParams, + }); + req.bitgo = bitgo; + + await should(handleLightningWithdraw(req)).be.rejectedWith( + 'Invalid request body for withdrawing on chain lightning balance' + ); + }); + + it('should throw an error if passphrase is missing in the request params', async () => { + const inputParams = { + satsPerVbyte: '15', + recipients: [ + { + amountSat: '500000', + address: 'bcrt1qjq48cqk2u80hewdcndf539m8nnnvt845nl68x7', + }, + ], }; const req = mockRequestObject({