Skip to main content
The contract holds tokens for two separate purposes: merchant-funded rewards and buyer payments. The same token can be used in both ledgers, but the obligations are not interchangeable.

Reward ledger

The name netDeposited is a contract field. In this implementation, funding increases it and unused-budget withdrawals are tracked separately in surplusWithdrawn.

Buyer-payment ledger

Across campaigns, the contract’s held balance must cover available rewards, pending rewards, finalized unclaimed rewards, pending buyer escrow, and claimable merchant proceeds. Historical claimed/refunded/withdrawn totals explain where funds went; they are no longer held liabilities.

Worked example

Assume a merchant deposits 100 SDUSD for rewards. A product costs 20 SDUSD and reserves a 2 SDUSD reward per attributed sale. There is sufficient liability capacity. At the end, reward funding reconciles as 100 = 2 claimed + 98 withdrawn. Buyer payments reconcile as 40 = 20 merchant-claimed + 20 refunded.

Liability cap versus available funds

For a campaign, used reward liability includes pendingReserved + vestedUnclaimed + rewardsClaimed. Finalizing or claiming a reward does not reset that lifetime usage. A refund releases a reservation. Funding the vault increases available coverage but does not automatically increase the policy’s campaign liability cap. A campaign can therefore have available funds and still hit its liability cap. Read both values before assuming a top-up will fix a rejected purchase.

How to reconcile

Use the contract’s getRewardLedger and getPriceLedger at a consistent block, then compare the API’s vault view and receipt allocations. Avoid combining snapshots from different blocks while a purchase or claim is executing. A mismatch should trigger investigation and canonical replay/verification, not a manual balance edit.