Category: security, bug
Problem
dabdub_contracts/contracts/settlement_ledger/src/lib.rs's record_settlement is documented as "Admin-only (called by NestJS backend)" and validates only internal arithmetic consistency (fee + net == amount, non-negative values, no duplicate payment_id). It never calls into payment_escrow (e.g. get_payment) to confirm that payment_id corresponds to a real, Released escrow payment, that amount matches the escrow's recorded amount, or that merchant matches the escrow's merchant. Records are append-only (line 78: assert!(!env.storage().persistent().has(&key), "settlement already recorded")) with no update or delete function anywhere in the contract.
Impact
The settlement ledger is meant to be the permanent on-chain audit trail of fiat settlements, but it is entirely a function of trust in the admin key / backend process. A bug in the NestJS backend, or a compromised admin key, can write an arbitrary, permanent, immutable settlement record for a payment_id that was never actually released in escrow (or for the wrong amount/merchant) — and because there is no correction mechanism, a bad record can never be fixed once written, only ignored by downstream consumers who happen to notice.
Suggested fix
Have record_settlement cross-call payment_escrow::get_payment(payment_id) and assert status == Released, amount == payment.amount, and merchant == payment.merchant before writing the record (accepting the payment_escrow contract address as constructor/admin-settable config, similar to set_registry in payment_escrow). If full validation is infeasible, at minimum add a documented dispute/void mechanism for erroneous records.
Category: security, bug
Problem
dabdub_contracts/contracts/settlement_ledger/src/lib.rs'srecord_settlementis documented as "Admin-only (called by NestJS backend)" and validates only internal arithmetic consistency (fee + net == amount, non-negative values, no duplicatepayment_id). It never calls intopayment_escrow(e.g.get_payment) to confirm thatpayment_idcorresponds to a real,Releasedescrow payment, thatamountmatches the escrow's recorded amount, or thatmerchantmatches the escrow's merchant. Records are append-only (line 78:assert!(!env.storage().persistent().has(&key), "settlement already recorded")) with no update or delete function anywhere in the contract.Impact
The settlement ledger is meant to be the permanent on-chain audit trail of fiat settlements, but it is entirely a function of trust in the admin key / backend process. A bug in the NestJS backend, or a compromised admin key, can write an arbitrary, permanent, immutable settlement record for a
payment_idthat was never actually released in escrow (or for the wrong amount/merchant) — and because there is no correction mechanism, a bad record can never be fixed once written, only ignored by downstream consumers who happen to notice.Suggested fix
Have
record_settlementcross-callpayment_escrow::get_payment(payment_id)and assertstatus == Released,amount == payment.amount, andmerchant == payment.merchantbefore writing the record (accepting the payment_escrow contract address as constructor/admin-settable config, similar toset_registryin payment_escrow). If full validation is infeasible, at minimum add a documented dispute/void mechanism for erroneous records.