Canonical Payer Identity and Receipt-Binding Discipline

  • Name: canonical payer identity and receipt-binding discipline
  • Category: payment-boundary reference / issuance-and-settlement design note / payer-authenticity checklist / payment-proof binding companion
  • Summary: Healthy value-moving systems keep one simple answer to three questions fused together: who is allowed to pay, what exact rail proves that payment happened, and which single claim, mint, purchase, exercise, or withdrawal that payment is allowed to satisfy. The decisive split is usually not exotic: this actor may pay, this rail really received value, and this exact effect may consume that value once are three different promotions, and protocols break when they collapse them into one vague payment happened story. They also break when they let caller-supplied contracts self-certify as the payer, let ambient msg.value masquerade as fresh payment for many logical subcalls, or treat a helper callback / transfer attempt as if it were already canonical receipt. This note exists to keep the positive-design target explicit: the payer identity is protocol-pinned, the receipt proof is measured from a bounded protocol-owned rail, and the resulting receipt is consumed exactly once by one narrow accounting story.

Fast classifier

  • Stay here when the sharper design question is what exact object should have proven who paid, through which rail, for which single claim?
  • Use the shorthand authenticate payer, then measure receipt, then consume it once for mint, auction, vault, staking, or settlement paths that currently blur those three steps together.
  • Pivot first to root-of-trust substitution when the exploit-side task is classifying how a counterfeit payer endpoint, fake helper, or caller-supplied contract became the thing allowed to define payment reality, as in Visor.
  • Pivot first to ambient payment-context reuse when the exploit-side task is classifying how one real native-asset payment was replayed across many logical subcalls, as in Opyn and MISO.
  • Pivot first to actual-receipt-accounting-and-token-semantics-admission-discipline when the sharper question is token semantic compatibility at the asset-admission boundary rather than payer identity or one-shot receipt consumption.

Why this note exists

  • Some exploits are best understood as payment-side trust-boundary failures rather than only as reentrancy, weird-token, or generic access-control bugs.
  • Visor Finance is the clean counterfeit-payer-endpoint case: the protocol let a caller-supplied contract both self-certify as the authorized payer and author the code path that supposedly performed payment.
  • Opyn and Sushi MISO are the clean ambient native payment is not action-local payment proof cousins.
  • Those incidents differ in mechanics, but the healthy-side question underneath them is consistent: before the protocol mints a claim or honors a payout, what exact protocol-owned object proves who funded this effect and proves that the same payment proof has not already been spent by another logical action?

Three payment facts reviewers should not collapse

  1. This actor may fund the action is not yet payment happened

    • Payer identity is an authorization fact.
    • It answers who is allowed to supply value, not whether value actually arrived.
    • Visor-style bugs blur this boundary by letting a counterfeit helper both claim payer authority and narrate payment success.
  2. Payment arrived on one rail is not yet this claim may spend it

    • A measured receipt is an accounting fact.
    • It answers whether value landed on the intended rail, not which mint, exercise, refund branch, or reserve-backed claim is entitled to consume it.
    • Opyn and MISO are the clean reminder that one real outer-call payment can still be the wrong receipt object for many inner actions.
  3. One claim consumed this receipt is not the same thing as ambient payment context still exists

    • Claim binding is a consumption fact.
    • Once one action has spent the receipt, later code should not be able to rediscover the same msg.value, callback result, or helper-local state as fresh funding authority.
    • The healthy discipline is to keep authorization, receipt measurement, and one-shot consumption machine-checkable as three sequential promotions rather than one fuzzy payment story.

The six joints a healthy payment boundary should keep fused

1. Canonical payer identity

  • The protocol needs one exact answer to who is allowed to fund the current action: the caller, a pre-approved third-party payer, a route-local escrow, a resolver inventory bucket, or some other explicitly admitted source.
  • A caller-supplied contract says it owns the payer role is weaker than the protocol itself can name the one payer identity or payer class this flow allows.
  • If a helper, module, or delegated payer is allowed, the protocol should still authenticate that helper against protocol-owned config, not against the helper’s own self-description.

2. Canonical payment rail

  • The protocol also needs one bounded rail that can prove payment: measured token receipt, debited WETH balance, route-local escrow spend, or one explicitly consumed native-asset budget.
  • A helper callback ran, transfer was attempted, or msg.value is nonzero are not yet payment truth by themselves.
  • The important design move is to make the rail machine-checkable and narrow enough that other paths cannot quietly impersonate it.

3. Receipt measurement distinct from payer authentication

  • Payer authentication and receipt proof are separate checks.
  • A real payer identity does not prove assets arrived, and a real balance increase does not prove the intended payer funded it.
  • Healthy designs establish both facts explicitly before they recognize value.

4. One-shot receipt consumption

  • A measured receipt should become economic truth once, for one action, in one bounded accounting frame.
  • If the same msg.value, callback-delivered balance increase, or delegated-transfer outcome can satisfy two exercises, two mints, many refunds, or a mint plus a later reserve publication, the payment boundary is already split.
  • Good systems keep a journaled answer to whether a receipt is still unconsumed, partially consumed, or fully consumed.

5. Delegated or third-party payer scope

  • Some systems legitimately need relayers, delegated wallets, payer modules, vault helpers, or sponsor contracts.
  • The healthy version gives those delegates narrow, typed authority: exact assets, exact routes, exact stage, exact recipient class, exact spend budget, and explicit revocation / expiry.
  • The unhealthy version lets a caller choose the helper first and only then asks whether the helper looks self-consistent enough to trust.

6. Claim binding after payment

  • Even after receipt is proven, the protocol still must bind that receipt to one claim story: one share mint, one option exercise, one auction commitment, one route settlement, one reward recognition event.
  • A payment that is real but not claim-bound becomes ambient accounting authority for whatever later subsystem consults it.
  • The nearby claim-side companion there is reserve-backed claim truth as a single security object.

What sound design looks like

  • Payment authority is a three-step promotion, not one vague event

    • Healthy systems can point to three separate answers in order: who was allowed to fund the action, what exact rail proved value arrived, and which exact claim or effect consumed that receipt.
    • The protocol should fail closed at each step instead of letting a later successful-looking fact retroactively justify an earlier missing one.
    • This is the compact healthy-side answer to why counterfeit payer, ambient msg.value, and helper-self-certification bugs feel different in code but collapse into the same review failure.
  • The protocol names who may pay before the user chooses how to pretend to pay

    • Healthy paths start from a protocol-owned answer to payer identity: the caller, a pinned delegate class, a market-local escrow, or an immutable settlement inventory bucket.
    • The first question is is this the exact payer or payer rail we meant to trust?, not did the supplied helper say convincing things about itself?
  • Payment proof comes from one bounded rail, not from ambient context

    • Token paths prove payment from measured receipt on the intended rail.
    • Native-asset paths prove payment from an explicitly consumed internal budget, not from rereading ambient msg.value inside repeated subcalls.
    • Delegated helper paths prove payment from post-conditions the helper cannot self-author into existence.
  • Authentication and accounting happen in the right order

    • First authenticate the payer or payer class.
    • Then measure the receipt on the intended rail.
    • Then consume that receipt exactly once into the intended claim or effect.
    • Reordering those steps is how protocols end up trusting counterfeit payer endpoints or replaying one real payment many times.
  • Third-party payer support is route-local and typed

    • If a sponsor, module, or delegated payer is necessary, its authority should be scoped to exact methods, assets, recipients, and budgets.
    • Generic this helper can define who paid and whether payment happened authority is too broad for issuance or settlement code.
  • Native and token payment models converge on the same conservation rule

    • Whether the user pays in ETH, WETH, or ERC-20, the protocol should still be able to prove that cumulative claims and refunds from one transaction do not imply more consideration than the admitted payer rail actually supplied.
    • If native support only works by treating ambient call context as special truth, the design is carrying extra hidden risk.

Healthy architectural shapes

1. Pinned delegated-payer adapters

  • If contract payers are supported, they should come from a protocol-owned allowlist, immutable config, or typed registry entry.
  • The adapter should expose one narrow semantic contract: how much can be paid, which asset can be paid, to whom, and how the protocol will verify that the payment actually landed.
  • User supplies arbitrary contract with expected methods is the anti-pattern.

2. Route-local or action-local payment journals

  • Each mint, exercise, auction commitment, or settlement step should consume from an action-local receipt object or remaining-value budget.
  • This is the clean positive-design answer to msg.value replay and repeated refund branches.
  • The system should always be able to answer how much payment remains unconsumed for this exact action?

3. Measured receipt plus payer binding

  • The strongest path is often measure exact receipt on the intended rail plus bind that receipt to the already-authenticated payer / action id.
  • That avoids both counterfeit-payer stories and ambient-balance stories.
  • It also makes later mint, withdrawal, or reserve-recognition code simpler because payment provenance is already settled upstream.

4. Immediate normalization of native payments into explicit ledgers

  • Many native-asset paths become safer if the protocol immediately wraps into WETH or records a debited internal budget before business logic repeats.
  • The goal is not stylistic purity; it is to stop ambient EVM context from becoming reusable payment evidence.

5. Explicit downgrade policy for fallback payer rails

  • If degraded mode leaves only one relayer, helper, sponsor, or emergency payer rail alive, that rail should inherit less authority than healthy mode, not the same broad mint or settlement power.
  • A surviving fallback payer path should have narrower assets, smaller budgets, fewer destinations, and easier pause semantics.

Design questions worth asking first

  • What exact object proves who is funding this action?
  • What exact object proves that the funding actually arrived on the intended rail?
  • Could a helper, callback, or nested subcall observe the same payment proof twice?
  • If the payer is a contract or delegate, what protocol-owned mechanism pins it as canonical instead of letting it self-certify?
  • Is the measured receipt consumed immediately into one action-local journal entry, or can later code still reinterpret it?
  • If this path supports native assets, what prevents one outer-call payment from being reused across many logical actions?
  • If this path supports sponsors or relayers, what exactly limits the spend scope and revocation semantics of that delegated rail?

Red flags that usually mean the payment boundary is underdesigned

  • from is a contract, so we call its hook and trust that as payment proof
  • owner() or a similar self-reported field is treated as if it proves canonical payer authority
  • msg.value is read directly inside a function that batching, multicall, loops, or delegatecall can reach many times
  • one payment rail is authenticated, but another rail can still satisfy the same claim logic with weaker proof
  • helpers, modules, or payer contracts are interface-checked but not pinned to protocol-owned identity
  • receipt is measured, but there is no journal proving which action consumed it and whether it can still be reused
  • refund logic reads ambient payment context rather than action-local consumed value

Failure-prevention checklist

  • Write down the exact payer classes each high-value flow supports: direct caller, pinned delegate, route-local escrow, sponsor inventory, or none. Reject every other payer story by default.
  • Recompute or load delegated-payer identity from protocol-owned state. Do not let arbitrary user-supplied contracts self-certify as the payer boundary for mint or withdrawal logic.
  • Separate payer authentication from receipt measurement. Require both before any claim, mint, exercise, or refund path can advance.
  • For token paths, measure payment on the intended rail with actual receipt checks strong enough for the path’s semantics; for native paths, convert ambient value into an explicit remaining-value budget or equivalent one-shot receipt object before repeated logic can observe it.
  • Consume payment receipts exactly once into an action-local journal. The same payment proof must not satisfy repeated mints, exercises, commitments, or refunds.
  • Make refund math derive from value allocated / consumed by this action, not from raw ambient msg.value or helper-local self-reporting.
  • Scope delegated-payer authority tightly by asset, route, method, amount, recipient class, and expiry. Make revocation operationally cheap.
  • Fuzz composed payment flows: arbitrary helper contracts, fake payer adapters, repeated batched payable subcalls, mixed success / revert branches, partial fills near caps, and mint-plus-redeem paths in one transaction.
  • Emit enough telemetry to reconstruct payer provenance after the fact: which payer rail was used, which action id consumed it, how much value was recognized, and whether any receipt remained unspent.
  • Keep pause and incident-response controls aimed at the actual payment-composition surface, not only the business function that later consumes the payment.

Audit prompts

  • What exact object proves that this payer is canonical for this action, and could the protocol derive or load that object instead of accepting it from the caller?
  • If the payer is a contract, helper, module, or relayer, what prevents that endpoint from self-certifying both payer identity and payment success?
  • Where is actual receipt measured, and what prevents a helper callback, balance seed, or ambient native-payment context from substituting for that proof?
  • Can the same payment proof be observed by two logical subcalls, two mint paths, a mint plus a refund leg, or a mint plus a later reserve / reward publication step?
  • If msg.value is involved anywhere, which exact local ledger says how much of it remains unconsumed for this action after each nested step?
  • Does the protocol bind the measured receipt to one action id, user, route, market, or position before any downstream subsystem can reinterpret it?
  • Which delegated-payer paths remain live during degraded mode or incident response, and what exact authority do they retain automatically?
  • If a partial fill or refund branch exists, what prevents repeated execution from paying out more native value or claim value than the admitted payer rail supplied?
  • What telemetry or state lets operators answer who paid, through what rail, for which effect without reconstructing it from folklore?

Lessons that should already be standard

  • Who may pay?, did payment actually happen on the intended rail?, and which exact effect consumed that receipt? are three different security questions. Good systems answer all three explicitly and in order.
  • A caller-supplied helper should never get to both define payer identity and author the evidence that payment succeeded.
  • Ambient msg.value is transaction context, not reusable action-local payment proof.
  • Delegated payers are security boundaries, not convenience wrappers. If they stay generic, they become counterfeit mint or refund rails.
  • Real receipt still needs claim binding. Otherwise one genuine payment can become many entitlement stories.
  • Native and token payment paths should satisfy the same conservation rule even if their mechanics differ.
  • Refund code is payment code. Repeated refund branches often reveal payment-boundary mistakes faster than repeated mint branches.
  • The protocol should always be able to answer one boring operational question: which exact payer rail funded this exact effect, and has that receipt already been consumed?

Where this note helps most

Internal linkages

Control surface