Overview
x402r uses an additive modular fee system: totalFee = protocolFee + operatorFee. Each layer is independently configurable, and fees are split between a shared protocol recipient and a per-operator fee recipient.
Fee Architecture
Two Fee Layers
Layer Configured By Mutability Recipient Protocol Fee ProtocolFeeConfig (shared)Swappable calculator with 7-day timelock protocolFeeRecipient on ProtocolFeeConfigOperator Fee FEE_CALCULATOR (per-operator)Immutable — set at deploy time FEE_RECIPIENT on operator
Example Calculation
For a 1000 USDC payment with 50 bps protocol fee + 250 bps operator fee:
Component Rate Amount Goes To Protocol Fee 50 bps (0.5%) 5.00 USDC protocolFeeRecipientOperator Fee 250 bps (2.5%) 25.00 USDC FEE_RECIPIENTTotal Fee 300 bps (3%) 30.00 USDC Receiver Gets 970.00 USDC Payment receiver
IFeeCalculator Interface
Both protocol and operator fees use the same interface:
interface IFeeCalculator {
/// @notice Calculate fee in basis points for a payment action
/// @param paymentInfo The payment info struct
/// @param amount The payment amount
/// @param caller The address initiating the action
/// @return feeBps The fee in basis points (e.g., 50 = 0.5%)
function calculateFee (
AuthCaptureEscrow . PaymentInfo calldata paymentInfo ,
uint256 amount ,
address caller
) external view returns ( uint256 feeBps );
}
This enables flexible fee models — static rates, volume-based tiers, per-token pricing, or any custom logic.
StaticFeeCalculator
The simplest implementation — returns a fixed basis points value for every payment:
contract StaticFeeCalculator is IFeeCalculator {
uint256 public immutable FEE_BPS;
constructor ( uint256 _feeBps ) {
if (_feeBps > 10000 ) revert FeeTooHigh ();
FEE_BPS = _feeBps;
}
function calculateFee (
AuthCaptureEscrow . PaymentInfo calldata ,
uint256 ,
address
) external view override returns ( uint256 feeBps ) {
return FEE_BPS;
}
}
Deploy via StaticFeeCalculatorFactory for deterministic CREATE2 addresses:
// Deploy a 250 bps (2.5%) fee calculator
const calculatorAddress = await staticFeeCalculatorFactory . write . deploy ([ 250 ]);
// Same fee rate = same address (idempotent)
const sameAddress = await staticFeeCalculatorFactory . write . deploy ([ 250 ]);
Fee Locking
Fees are locked at authorization time to prevent protocol fee changes from breaking already-authorized payments.
struct AuthorizedFees {
uint16 totalFeeBps; // Combined protocol + operator
uint16 protocolFeeBps; // Stored separately for accurate distribution
}
mapping ( bytes32 paymentInfoHash => AuthorizedFees) public authorizedFees;
Flow:
authorize() calculates fees and stores them in authorizedFees[hash]
release() uses the stored fees — not the current calculator rates
Protocol fee timelocks can’t break already-authorized payments
charge() calculates fees inline since it authorizes and captures atomically — there’s no gap where fees could change.
Fee Bounds Validation
Payers commit to an acceptable fee range via minFeeBps and maxFeeBps in PaymentInfo. The operator validates at authorize() and charge() time:
( uint16 totalFeeBps, uint16 protocolFeeBps) = _calculateFees (paymentInfo, amount);
if (totalFeeBps < paymentInfo.minFeeBps || totalFeeBps > paymentInfo.maxFeeBps) {
revert FeeBoundsIncompatible (totalFeeBps, paymentInfo.minFeeBps, paymentInfo.maxFeeBps);
}
This ensures payers always know the fee range they’re agreeing to.
Fee Distribution
Fees accumulate in the operator contract and are distributed via distributeFees():
// Anyone can call to distribute fees for a token
operator. distributeFees (usdcAddress);
// Protocol share → protocolFeeRecipient
// Operator share → FEE_RECIPIENT
How it works:
Check operator’s token balance
Protocol share = accumulatedProtocolFees[token] (tracked per-token)
Operator share = remaining balance
Transfer protocol share to protocolFeeRecipient
Transfer operator share to FEE_RECIPIENT
Reset accumulated tracking to 0
distributeFees() is permissionless — anyone can trigger distribution. This prevents fees from being stuck in the operator.
ProtocolFeeConfig
Shared protocol-level fee governance with built-in safety:
Constants
Parameter Value MAX_PROTOCOL_FEE_BPS500 (5%) TIMELOCK_DELAY7 days
Calculator Changes (7-Day Timelock)
// Step 1: Queue new calculator
await protocolFeeConfig . queueCalculator ( newCalculatorAddress );
// Emits CalculatorChangeQueued(newCalculator, executeAfter)
// Step 2: Wait 7 days
// Step 3: Execute
await protocolFeeConfig . executeCalculator ();
// Emits CalculatorChangeExecuted(newCalculator)
// Or cancel:
await protocolFeeConfig . cancelCalculator ();
Recipient Changes (7-Day Timelock)
// Step 1: Queue new recipient
await protocolFeeConfig . queueRecipient ( newRecipientAddress );
// Step 2: Wait 7 days
// Step 3: Execute
await protocolFeeConfig . executeRecipient ();
Operator fees are immutable — set at deploy time via IFeeCalculator and FEE_RECIPIENT. Only protocol fees can be changed (with 7-day timelock). Already-authorized payments use locked fee rates regardless.
Disabling Protocol Fees
Set the protocol fee calculator to address(0) to disable protocol fees entirely. The operator will calculate 0 bps for the protocol layer.
FEE_RECIPIENT Roles
The operator’s FEE_RECIPIENT varies by use case:
Use Case FEE_RECIPIENT Description Marketplace Arbiter address Arbiter earns fees for dispute resolution Subscription Service provider Provider earns fees for service delivery DAO Grants DAO multisig Fees return to treasury Platform Platform treasury Platform monetizes via fees B2B Invoice Platform address Platform earns for facilitation
Fee Configuration Comparison
Use Case Total Fee Protocol Operator Receiver on 1000 USDC E-Commerce Marketplace 300 bps (3%) 50 bps 250 bps 970.00 USDC Task Execution Platform 1300 bps (13%) 100 bps 1200 bps 870.00 USDC Subscription / SaaS 500 bps (5%) 50 bps 450 bps 950.00 USDC Managed Escrow 800 bps (8%) 0 bps 800 bps 920.00 USDC B2B Invoice 100 bps (1%) 25 bps 75 bps 990.00 USDC Freelance / Gig 1000 bps (10%) 100 bps 900 bps 900.00 USDC
Next Steps
PaymentOperator See how fees integrate with PaymentOperator.
Factories Deploy operators with fee configuration.
Examples Complete fee configurations for common use cases.
Architecture Understand the full payment flow.