SMART CONTRACTS

TabRouter

TabRouter is the contract that settles ordinary Tab payments. A buyer signs an EIP-712 message; the contract verifies it, pulls the funds, sends them to the seller, and takes the 1% fee.

Constants

  • PLATFORM_FEE_BPS = 100 — 1% in basis points.
  • BPS_DENOMINATOR = 10_000
  • PAYMENT_TYPEHASH = keccak256 of "Payment(address from,address to,uint256 amount,uint256 fee,uint256 nonce,uint256 deadline)"

State

  • IERC20 STABLE — the stablecoin for this deployment (immutable).
  • address platformTreasury — fee recipient (owner-mutable).
  • mapping(address => mapping(uint256 => bool)) usedNonces — replay-protection set.
  • uint256 totalVolume, totalFeesCollected — lifetime accumulators.

The settlement function

function relayPayment(
    address from,
    address to,
    uint256 amount,
    uint256 fee,
    uint256 nonce,
    uint256 deadline,
    bytes calldata signature
) external nonReentrant;

The function performs, in order:

  1. Reject zero addresses, zero amounts, and a wrong fee (fee != calculateFee(amount)).
  2. Reject expired or replayed signatures.
  3. Reconstruct the EIP-712 digest and recover the signer.
  4. Mark the nonce consumed and bump the counters.
  5. safeTransferFrom amount to the recipient and fee to the treasury.
  6. Emit PaymentRelayed(from, to, amount, fee, nonce, digest).

Events

event PaymentRelayed(
    address indexed from,
    address indexed to,
    uint256 amount,
    uint256 fee,
    uint256 nonce,
    bytes32 indexed digest
);

event TreasuryUpdated(address indexed oldTreasury, address indexed newTreasury);

Errors

Custom errors keep gas down on revert paths: InvalidAmount, InvalidFee, ZeroAddress, ExpiredDeadline, NonceAlreadyUsed, InvalidSignature.

Building the signature client-side

import { ethers } from "ethers";

const domain = {
  name: "Tab Router",
  version: "1",
  chainId,
  verifyingContract: ROUTER_ADDRESS,
};
const types = {
  Payment: [
    { name: "from",     type: "address" },
    { name: "to",       type: "address" },
    { name: "amount",   type: "uint256" },
    { name: "fee",      type: "uint256" },
    { name: "nonce",    type: "uint256" },
    { name: "deadline", type: "uint256" },
  ],
};
const value = { from, to, amount, fee, nonce, deadline };
const signature = await signer.signTypedData(domain, types, value);

Who can call relayPayment?

Anyone. The signature is the authorization. If a Tab relayer is unreachable, the buyer (or anyone they hand the signed blob to) can submit the transaction directly. The router treats all callers identically — only the signer matters.