Skip to content

Commit

Permalink
chore: add settle pending onchain payments script
Browse files Browse the repository at this point in the history
  • Loading branch information
dolcalmi committed Nov 28, 2024
1 parent c18a491 commit 18f7c58
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 6 deletions.
117 changes: 117 additions & 0 deletions core/api/src/debug/settle-pending-onchain-payments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* how to run:
*
* pnpm tsx src/debug/void-onchain-payment.ts <journal id> <payout id>
*
* <journal id>: journal id to void.
* <payout id>: bria payout id
*/

import { Wallets } from "@/app"

import { OnChainService } from "@/services/bria"
import { LedgerService } from "@/services/ledger"
import * as LedgerFacade from "@/services/ledger/facade"
import { lndsConnect } from "@/services/lnd/auth"
import { isUp } from "@/services/lnd/health"
import { setupMongoConnection } from "@/services/mongodb"

const onChainService = OnChainService()

const processPayment = async (payment: LedgerTransaction<WalletCurrency>) => {
const payout = await onChainService.findPayoutByLedgerJournalId(payment.journalId)
if (payout instanceof Error) {
return new Error(`Failed to get payout: ${payout.name} - ${payout.message}`)
}
if (!payout.batchId || !payout.txId || !payout.vout) {
return new Error("Missing required payout details")
}

const setTxIdResult = await LedgerFacade.setOnChainTxIdByPayoutId({
payoutId: payout.id,
txId: payout.txId,
vout: payout.vout,
})
if (setTxIdResult instanceof Error) {
return new Error(
`Failed to set transaction ID: ${setTxIdResult.name} - ${setTxIdResult.message}`,
)
}

const settledPayout = await Wallets.settlePayout(payout.id)
if (settledPayout instanceof Error) {
return new Error(
`Failed to settle payout: ${settledPayout.name} - ${settledPayout.message}`,
)
}

return true
}

const settlePendingOnchainPayments = async () => {
const pendingPayments = LedgerService().listPendingOnchainPayments()
if (pendingPayments instanceof Error) return pendingPayments

let totalPayments = 0
let successCount = 0
let errorCount = 0
const errors = []

for await (const payment of pendingPayments) {
totalPayments++
console.log(`Processing payment ${totalPayments}`)

const result = await processPayment(payment)
if (result instanceof Error) {
errorCount++
errors.push({
journalId: payment.journalId,
payoutId: payment.payoutId,
error: result.message,
})
console.error(`Failed to process payment: ${result.message}`)
continue
}

successCount++
console.log(
`Successfully processed payout ${payment.payoutId} for journal ${payment.journalId}`,
)
}

return {
successCount,
errorCount,
total: totalPayments,
errors,
}
}

const main = async () => {
const result = await settlePendingOnchainPayments()
if (result instanceof Error) {
console.error("Error:", result)
return
}
console.log("Settlement process completed")
console.log(`Total Processed: ${result.total}`)
console.log(`Successful: ${result.successCount}`)
console.log(`Failed: ${result.errorCount}`)

if (result.errors.length > 0) {
console.log("\nErrors:")
result.errors.forEach(({ journalId, payoutId, error }) => {
console.log(
`- Journal ${journalId}${payoutId ? ` (Payout ${payoutId})` : ""}: ${error}`,
)
})
}
}

setupMongoConnection()
.then(async (mongoose) => {
await Promise.all(lndsConnect.map((lndParams) => isUp(lndParams)))
await main()
if (mongoose) await mongoose.connection.close()
})
.catch((err) => console.log(err))
4 changes: 4 additions & 0 deletions core/api/src/domain/bitcoin/onchain/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type OnChainAddressRequestId = string & { readonly brand: unique symbol }
type BlockId = string & { readonly brand: unique symbol }
type OnChainTxHash = string & { readonly brand: unique symbol }
type PayoutId = string & { readonly brand: unique symbol }
type BatchId = string & { readonly brand: unique symbol }
type OnChainTxVout = number & { readonly brand: unique symbol }
type ScanDepth = number & { readonly brand: unique symbol }
type TxOut = {
Expand Down Expand Up @@ -92,6 +93,9 @@ type OnChainPayout = {
id: PayoutId
journalId: LedgerJournalId
batchInclusionEstimatedAt: number | undefined
batchId: BatchId | undefined
txId: OnChainTxHash | undefined
vout: OnChainTxVout | undefined
}

type OnChainEventHandler = (event: OnChainEvent) => true | ApplicationError
Expand Down
4 changes: 4 additions & 0 deletions core/api/src/domain/ledger/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,10 @@ interface ILedgerService {

listWalletIdsWithPendingPayments: () => AsyncGenerator<WalletId> | LedgerServiceError

listPendingOnchainPayments: () =>
| AsyncGenerator<LedgerTransaction<WalletCurrency>>
| LedgerServiceError

addColdStorageTxReceive<T extends DisplayCurrency>(
args: AddColdStorageTxReceiveArgs<T>,
): Promise<LedgerJournal | LedgerServiceError>
Expand Down
6 changes: 6 additions & 0 deletions core/api/src/services/bria/grpc-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
EstimatePayoutFeeResponse,
GetAddressRequest,
GetAddressResponse,
GetBatchRequest,
GetBatchResponse,
GetPayoutRequest,
GetPayoutResponse,
GetWalletBalanceSummaryRequest,
Expand Down Expand Up @@ -58,3 +60,7 @@ export const estimatePayoutFee = promisify<
>(bitcoinBridgeClient.estimatePayoutFee.bind(bitcoinBridgeClient))

export const subscribeAll = bitcoinBridgeClient.subscribeAll.bind(bitcoinBridgeClient)

export const getBatch = promisify<GetBatchRequest, Metadata, GetBatchResponse>(
bitcoinBridgeClient.getBatch.bind(bitcoinBridgeClient),
)
11 changes: 5 additions & 6 deletions core/api/src/services/bria/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,16 +233,15 @@ export const OnChainService = (): IOnChainService => {

const response = await getPayout(request, metadata)
const foundPayout = response.getPayout()

if (foundPayout === undefined) return new PayoutNotFoundError()
let batchInclusionEstimatedAt = undefined
if (foundPayout.hasBatchInclusionEstimatedAt()) {
batchInclusionEstimatedAt = foundPayout.getBatchInclusionEstimatedAt()
}

return {
id: foundPayout.getId() as PayoutId,
journalId: foundPayout.getExternalId() as LedgerJournalId,
batchInclusionEstimatedAt,
batchInclusionEstimatedAt: foundPayout.getBatchInclusionEstimatedAt(),
batchId: foundPayout.getBatchId() as BatchId,
txId: foundPayout.getTxId() as OnChainTxHash,
vout: foundPayout.getVout() as OnChainTxVout,
}
} catch (err) {
if (
Expand Down
19 changes: 19 additions & 0 deletions core/api/src/services/ledger/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,24 @@ export const LedgerService = (): ILedgerService => {
}
}

const listPendingOnchainPayments = async function* ():
| AsyncGenerator<LedgerTransaction<WalletCurrency>>
| LedgerServiceError {
try {
const transactions = Transaction.find({
type: LedgerTransactionType.OnchainPayment,
pending: true,
account_path: liabilitiesMainAccount,
}).cursor({ batchSize: 100 })

for await (const tx of transactions) {
yield translateToLedgerTx(tx)
}
} catch (error) {
return new UnknownLedgerError(error)
}
}

return wrapAsyncFunctionsToRunInSpan({
namespace: "services.ledger",
fns: {
Expand All @@ -516,6 +534,7 @@ export const LedgerService = (): ILedgerService => {
isLnTxRecorded,
getWalletIdByPaymentHash,
listWalletIdsWithPendingPayments,
listPendingOnchainPayments,
...admin,
...send,
},
Expand Down

0 comments on commit 18f7c58

Please sign in to comment.