Skip to main content

P2P Swap Contract Overview

Signature Verification

P2PSwap uses Core.sol's centralized signature verification via validateAndConsumeNonce() with hash-based operation validation through P2PSwapHashUtils.

The P2P Swap Contract is a decentralized token exchange system enabling trustless peer-to-peer token swaps with an order book marketplace.

Core Features

Order Management

  • Order Creation (makeOrder): Create swap orders offering one token for another
  • Order Cancellation (cancelOrder): Cancel unfilled orders and reclaim tokens
  • Order Fulfillment (dispatchOrder): Fill existing orders with proportional fee model
  • Market Discovery: Automatic market creation for new token pairs via bytes32 hash IDs

Fee Structures

  • Proportional Fee: Percentage-based fees (configurable rate, default 5%)
  • Three-Way Distribution: Fees split between sellers (50%), MATE stakers (10%), service treasury (40%)
  • Time-Delayed Governance: All fee parameter changes require 1-day timelock

Integration

  • Core.sol: Centralized signature verification and nonce management
  • P2PSwapHashUtils: Hash generation for all operations
  • Token Transfers: via requestPay() (lock) and makeCaPay() (release)
  • Staking Rewards: 1-2x MATE rewards for staker executors
  • EIP-191 Signatures: Authorization for all operations

Architecture

Centralized Verification Pattern

All P2PSwap user functions follow this pattern:

// 1. Centralized signature verification
core.validateAndConsumeNonce(
user,
senderExecutor, // Controls msg.sender
P2PSwapHashUtils.hashDataFor[Operation](...),
originExecutor, // Controls tx.origin
nonce,
true, // Always async execution
signature
);

// 2. Operation-specific business logic
// ... market lookup, order validation ...

// 3. Token transfers via Core
requestPay(user, token, amount, priorityFee, originExecutor, noncePay, true, signaturePay);
makeCaPay(recipient, token, amount);

// 4. Staker rewards
if (core.isAddressStaker(msg.sender)) {
_sendReward(msg.sender, multiplier);
}

Hash Functions

P2PSwap uses P2PSwapHashUtils for operation-specific hash generation:

// makeOrder: Locks offeredToken and creates order
hashDataForMakeOrder(offeredToken, requestedToken, offeredAmount, requestedAmount)

// cancelOrder: Refunds offeredToken and deletes order
hashDataForCancelOrder(offeredToken, requestedToken, orderId)

// dispatchOrder: Executes trade (partial or full fill)
hashDataForDispatchOrder(offeredToken, requestedToken, orderId, amountOut, amountInMax)

Signature Format

Universal signature format for all P2PSwap operations:

{evvmId},{senderExecutor},{hashPayload},{originExecutor},{nonce},true

Where:

  • evvmId: Chain ID (from block.chainid)
  • senderExecutor: Address that can execute the operation via msg.sender
  • hashPayload: Result from P2PSwapHashUtils.hashDataFor[Operation](...)
  • originExecutor: EOA address that will initiate the transaction (verified with tx.origin)
  • nonce: User's nonce for P2PSwap service (from core.getNonce(user, p2pSwapAddress))
  • true: Always async execution (isAsyncExec = true)

Dual-Executor Transaction Model

P2PSwap uses two independent executor parameters for flexible transaction control:

senderExecutor (msg.sender control)

Controls which address can call the contract function:

core.validateAndConsumeNonce(
user,
senderExecutor, // Validates msg.sender
hashPayload,
originExecutor,
nonce,
true,
signature
);

Common Patterns:

  • address(0): Anyone can execute (maximum flexibility)
  • specificAddress: Only that address can call the function (restriction)
  • serviceAddress: Service can execute on behalf of user

originExecutor (tx.origin control)

Controls which EOA can initiate the entire transaction:

// Validated inside Core.sol
if (originExecutor != address(0) && tx.origin != originExecutor) {
revert Core__InvalidExecutor();
}

Common Patterns:

  • address(0): Any EOA can initiate (maximum flexibility)
  • userEOA: Only that EOA can initiate the transaction (personal execution)
  • trustedEOA: Only specific EOA can initiate (delegation)

P2PSwap Functions & Executors

FunctionTypical senderExecutorTypical originExecutor
makeOrderaddress(0)address(0)
cancelOrderaddress(0)user or address(0)
dispatchOrderaddress(0)user or address(0)

Nonce Management

Centralized async nonces in Core.sol (unified tracking):

  • Query nonce: core.getNonce(user, address(p2pSwap))
  • Automatic consumption: Handled by core.validateAndConsumeNonce()
  • Per-user, per-service nonce tracking

Payment Processing

P2PSwap uses two Core payment patterns:

requestPay() - Locks tokens in Core

requestPay(user, token, amount, priorityFee, originExecutor, noncePay, true, signaturePay);

Used for:

  • Locking offeredToken when creating orders (makeOrder)
  • Collecting requestedToken + fees when filling orders (dispatchOrder)
  • Collecting priority fees from users

makeCaPay() - Releases tokens from Core

makeCaPay(recipient, token, amount);

Used for:

  • Refunding offeredToken when canceling (cancelOrder)
  • Transferring offeredToken to buyer (dispatchOrder)
  • Refunding overpayment (dispatchOrder)
  • Paying priority fees to staker executors (makeOrder, cancelOrder)
  • Withdrawing accumulated fees (acceptWithdrawal)

Market Structure

Market Creation

Markets are automatically created for new token pairs using a deterministic bytes32 hash:

bytes32 marketId = keccak256(abi.encodePacked(tokenA, tokenB));

Key Properties:

  • Markets are identified by bytes32 hash of the token pair
  • Each market has independent order ID space (1, 2, 3...)
  • Deleted orders leave gaps that get reused
  • maxSlot tracks highest order ID ever used
  • ordersAvailable counts active orders
  • medianPrice stores the volume-weighted average price (VWAP)

Order Storage

Orders stored per-market with minimal on-chain data:

orders[marketId][orderId] = Order({
seller: userAddress,
offeredAmount: totalOfferedAmount,
requestedAmount: totalRequestedAmount,
amountAvailable: remainingOfferedAmount
});

Order Lifecycle:

  1. Created: Seller locks offeredToken, order active
  2. Partially Filled: Buyer pays requestedToken + fee, receives portion of offeredToken
  3. Fully Filled: All offeredToken distributed, order slot freed
  4. Cancelled: Seller reclaims remaining offeredToken, order deleted

Economic Model

Fee Distribution

When an order is filled, fees are distributed:

RecipientSharePurpose
Seller50% of feeReward for providing liquidity
Service Treasury40% of feeProtocol sustainability
MATE Stakers10% of feeStaker rewards pool

Default Fee Rate: 5% (500 / 10,000)

Example (100 USDC order with 5% fee):

  • Fee: 5 USDC
  • Seller receives: 100 USDC (order amount) + 2.5 USDC (50% of fee)
  • Treasury accumulates: 2 USDC (40% of fee)
  • Stakers share: 0.5 USDC (10% of fee)

Staker Rewards

Executors who are registered stakers receive MATE token rewards:

OperationReward MultiplierNotes
makeOrder1xOrder creation
cancelOrder1xOrder cancellation
dispatchOrder2xOrder fulfillment

Where 1x = core.getRewardAmount() (MATE tokens)

Priority Fee: Additional fee paid by user to executor. The token varies by operation: offeredToken for makeOrder, MATE/principal token for cancelOrder, requestedToken for dispatchOrder.

  • Optional but incentivizes faster execution
  • Paid before operation execution
  • Distributed to executor if they're a staker (makeOrder, cancelOrder)
  • Distributed to executor regardless of staker status (dispatchOrder)

Operation Summary

OperationPurposeHash Function
makeOrderCreate order, lock offeredTokenhashDataForMakeOrder(offeredToken, requestedToken, offeredAmount, requestedAmount)
cancelOrderCancel order, refund offeredTokenhashDataForCancelOrder(offeredToken, requestedToken, orderId)
dispatchOrderFill order (partial or full)hashDataForDispatchOrder(offeredToken, requestedToken, orderId, amountOut, amountInMax)

Admin Functions

P2PSwap includes time-locked governance for parameter updates:

  • Fee Configuration: Update percentageFee (proportional fee rate)
  • Reward Percentages: Adjust seller/service/staker fee splits
  • Admin Transfer: Propose and accept new admin
  • Treasury Withdrawal: Extract accumulated service fees

All admin changes require a 1-day timelock period before acceptance.

Security Features

  • Nonce Protection: Centralized Core.sol nonce prevents replay attacks
  • Signature Validation: EIP-191 signatures for all operations
  • Ownership Verification: Only order owner can cancel
  • Atomic Operations: Order fill is atomic (payment -> transfer -> delete)
  • Token Locking: User tokens locked in Core.sol during order lifetime
  • Overpayment Refund: Excess payments automatically refunded
  • Time-Delayed Governance: Admin changes require 1-day timelock

License: EVVM-NONCOMMERCIAL-1.0
Contract: P2PSwap.sol