Skip to main content

Unified Payment Verifier

Overview

UnifiedPaymentVerifierV2 (0x46A58Dc65587D4D7B8198C6A25eEdf5b2535Da94 on Base) is the production verifier for V3 contracts. It verifies standardized off‑chain attestations produced by the Attestation Service. It validates EIP‑712 signatures via a pluggable AttestationVerifier, enforces snapshot consistency with on‑chain intent state, prevents replay via nullifiers, and caps the release amount to the signaled intent amount.

note

The legacy UnifiedPaymentVerifier (0x16b3e4a3CA36D3A4bCA281767f15C7ADeF4ab163) is used only by the deprecated V3 Orchestrator. New intents use UnifiedPaymentVerifierV2 exclusively.


Typed Data

  • Primary type: PaymentAttestation(bytes32 intentHash,uint256 releaseAmount,bytes32 dataHash)
  • Domain (EIP‑712):
    • name = "UnifiedPaymentVerifier"
    • version = "1"
    • chainId = block.chainid
    • verifyingContract = address(this)

Attestation struct passed on‑chain

struct PaymentAttestation {
bytes32 intentHash; // binds to on-chain intent
uint256 releaseAmount; // token amount to release (pre-fees)
bytes32 dataHash; // keccak256(data)
bytes[] signatures; // witness signatures
bytes data; // abi.encode(PaymentDetails, IntentSnapshot)
bytes metadata; // optional; not signed
}

Data payload

struct PaymentDetails {
bytes32 method; // payment method (bytes32)
bytes32 payeeId; // hashed off-chain recipient id
uint256 amount; // smallest fiat unit (e.g., cents)
bytes32 currency; // fiat code hash
uint256 timestamp; // ms epoch
bytes32 paymentId; // hashed provider transaction id
}

struct IntentSnapshot {
bytes32 intentHash;
uint256 amount; // signaled token amount
bytes32 paymentMethod; // bytes32
bytes32 fiatCurrency; // bytes32
bytes32 payeeDetails; // bytes32 (must match PaymentDetails.payeeId)
uint256 conversionRate; // 1e18
uint256 signalTimestamp; // seconds
uint256 timestampBuffer; // ms/seconds window
}

Verification Flow

  1. Compute struct hash for (intentHash, releaseAmount, dataHash).
  2. Compute domain‑bound digest and verify witness signatures via AttestationVerifier.
  3. Check keccak256(attestation.data) == dataHash.
  4. Decode (PaymentDetails, IntentSnapshot) and validate snapshot against on‑chain Orchestrator.getIntent(intentHash) (amount, method, currency, conversionRate, signalTimestamp) and Escrow.getDepositPaymentMethodData(...).payeeDetails.
  5. Nullify the payment: keccak256(paymentMethod || paymentId); revert if already used.
  6. Cap releaseAmount to intent.amount.

Returns PaymentVerificationResult { success, intentHash, releaseAmount } to the Orchestrator.


Governance & Config

Provided by BaseUnifiedPaymentVerifier:

  • getPaymentMethods() — list of supported methods (bytes32).
  • addPaymentMethod(method) / removePaymentMethod(method) — owner‑only.
  • setAttestationVerifier(address) — update the witness verifier; emits AttestationVerifierUpdated.