MPP - Charge Mode
| ID | Check Name | Spec Reference | What It Checks | Pass Criteria |
|---|---|---|---|---|
MPP-01 | Charge Settlement On-Chain | MPP Charge Guide; CAP-46 transfer events | The charge settles on-chain for exactly what the target advertised | The target's own 402 challenge is read first, unpaid, capturing the advertised amount (base units), currency and recipient, and the network it names in methodDetails.network (one naming none is signed for testnet, as the SDK's client does). Wasit signs only for the run's network: a challenge for another, whether on the unpaid read or on the request actually paid, gets no verdict (ERROR (configuration)) and nothing is signed, and a network that is not stellar:testnet or stellar:pubnet fails. The payment goes through the run's RPC endpoint, which must be https since the SDK's client accepts no other, and is made once: a target that answers 402 after being paid fails with that status rather than being paid again. After payment, Stellar RPC getTransaction confirms the transaction referenced by the Payment-Receipt header succeeded, and its CAP-46 transfer contract event shows exactly one balance change: the advertised amount, to the advertised recipient, emitted by the advertised token contract, sent from this run's own payer. Verifying the event rather than the transaction envelope means a token contract whose transfer moves a different amount than its arguments claim is still caught. Verifying the sender means a target cannot satisfy the check by referencing some pre-existing transaction it did not cause. Verified against @stellar/mpp@0.7.1 — mode "pull" (default): onProgress only fires through "signed", so the settled tx reference comes from Payment-Receipt, not onProgress. The transfer event's data is read in both forms the protocol defines: a bare i128 amount (CAP-46), and the map { amount, to_muxed_id } that CAP-67 emits for a muxed destination, whose base address is in the topic. An advertised muxed recipient (M...) therefore matches only when both the base account and the muxed id match. A transaction RPC cannot find is a FAIL only after RPC has closed ten more ledgers without it; an RPC that stops advancing before then gives no verdict (ERROR (harness)), since its silence says nothing about the target. |
Note on MPP-01's cost (Week 2). MPP-01 is not destructive: nothing is
permanently ended and the check can be run again. But it is not free and not
idempotent — every run settles a real payment from the payer key and moves
testnet funds, and repeated runs spend repeatedly. This is inherent to the check
rather than an implementation choice: charge mode has no dry-run, and a
settlement that did not happen cannot be verified on-chain. Both front ends say
so before running, and the MCP tool declares idempotentHint: false.
Settlement is read from Stellar RPC rather than Horizon, so the same endpoint
serves every MPP check and --rpc-url applies uniformly. The tradeoff is
retention: RPC keeps only recent history, so MPP-01 must verify a settlement it
just triggered, not an arbitrary past one. That matches how the check is used —
it pays, then verifies what it paid.