Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add settle pending onchain payments script #4668

Merged
merged 5 commits into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 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,115 @@
/**
* how to run:
*
* pnpm tsx src/debug/settle-pending-onchain-payments.ts
*
*/

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 === undefined) {
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),
)
16 changes: 10 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,17 @@ 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()
}

//fix issue with proto gen default values
const txId = (foundPayout.getTxId() as OnChainTxHash) || undefined
return {
id: foundPayout.getId() as PayoutId,
journalId: foundPayout.getExternalId() as LedgerJournalId,
batchInclusionEstimatedAt,
batchInclusionEstimatedAt: foundPayout.getBatchInclusionEstimatedAt(),
batchId: foundPayout.getBatchId() as BatchId,
txId,
vout: txId ? (foundPayout.getVout() as OnChainTxVout) : undefined,
}
} catch (err) {
if (
Expand Down Expand Up @@ -281,6 +282,9 @@ export const OnChainService = (): IOnChainService => {
id: response.getId() as PayoutId,
journalId,
batchInclusionEstimatedAt: response.getBatchInclusionEstimatedAt(),
batchId: undefined,
txId: undefined,
vout: undefined,
}
} catch (err) {
const errMsg = parseErrorMessageFromUnknown(err)
Expand Down
30 changes: 30 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,35 @@ export const LedgerService = (): ILedgerService => {
}
}

const listPendingOnchainPayments = async function* ():
| AsyncGenerator<LedgerTransaction<WalletCurrency>>
| LedgerServiceError {
try {
const bankOwnerWalletId = await caching.getBankOwnerWalletId()
const dealerUsdWalletId = await caching.getDealerUsdWalletId()
const dealerBtcWalletId = await caching.getDealerBtcWalletId()

const excludedAccounts = [
toLiabilitiesWalletId(bankOwnerWalletId),
toLiabilitiesWalletId(dealerUsdWalletId),
toLiabilitiesWalletId(dealerBtcWalletId),
]

const transactions = Transaction.find({
type: LedgerTransactionType.OnchainPayment,
pending: true,
account_path: liabilitiesMainAccount,
accounts: { $nin: excludedAccounts },
}).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 +545,7 @@ export const LedgerService = (): ILedgerService => {
isLnTxRecorded,
getWalletIdByPaymentHash,
listWalletIdsWithPendingPayments,
listPendingOnchainPayments,
...admin,
...send,
},
Expand Down
38 changes: 18 additions & 20 deletions typos.toml
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
files.extend-exclude = [
"dev",
"core/api/dev",
"core/api/src/domain/users/languages.ts",
"core/api/docker-compose.yml",
"quickstart/*.yml",
"quickstart/galoy",
"quickstart/dev",
"prelude",
"core/notifications/locales/*.yml",
"apps/admin-panel/components/notification/languages.ts",
"apps/pay/components/success-animation.json",
"dev",
"core/api/dev",
"core/api/src/domain/users/languages.ts",
"core/api/docker-compose.yml",
"quickstart/*.yml",
"quickstart/galoy",
"quickstart/dev",
"prelude",
"core/notifications/locales/*.yml",
"apps/admin-panel/components/notification/languages.ts",
"apps/pay/components/success-animation.json",
]

default.extend-ignore-identifiers-re = [
"^bc1\\w+",
"^lnbc\\w+",
"[Ww][Ss]",
]
default.extend-ignore-identifiers-re = ["^bc1\\w+", "^lnbc\\w+", "[Ww][Ss]"]

default.extend-ignore-re = ["\"eyJ[a-zA-Z0-9_\\-\\.]+\"",
"moneyImportantGovernement",
"GovernementCanPrintMoney",
"moneySocialAggrement",
default.extend-ignore-re = [
"\"eyJ[a-zA-Z0-9_\\-\\.]+\"",
"moneyImportantGovernement",
"GovernementCanPrintMoney",
"moneySocialAggrement",
]

[default.extend-words]
# returned from twilio API referenced in src/debug/text-sms.ts
Ons = "Ons"
Lsat = "Lsat"
quizs = "quizs"
nin = "nin"
Loading