Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

withtally/permit-hook

Repository files navigation

Permitter

A smart contract system for enforcing KYC-based permissions and caps on Uniswap CCA (Continuous Combinatorial Auction) token sales. Uses EIP-712 signed permits issued by an off-chain server after KYC verification, validated on-chain during each bid.

Overview

Permitter acts as a validation hook for Uniswap CCA auctions, ensuring:

  • Only KYC-verified users can participate
  • Per-bidder token caps are enforced
  • Total ETH raise caps are enforced
  • Permits can be rotated if signing keys are compromised

Architecture

Bidder (Wallet)
    │
    ├─── Complete KYC ──────────► Sumsub KYC
    │                                  │
    │                                  │ Webhook: KYC approved
    │                                  ▼
    ├─── Request permit ────────► Tally Server
    │                                  │
    │◄── EIP-712 signature ────────────┘
    │
    └─── Submit bid with signature ───► Uniswap CCA ──► Permitter
                                                          │
                                        Validation passes/fails

Contracts

PermitterFactory

Deploys isolated Permitter instances for each auction using CREATE2 for deterministic addresses.

function createPermitter(
    address trustedSigner,
    uint256 maxTotalEth,
    uint256 maxTokensPerBidder,
    address owner,
    bytes32 salt
) external returns (address permitter);

Permitter

Implements bid validation using EIP-712 signed permits.

Permit Structure:

struct Permit {
    address bidder;       // Address authorized to bid
    uint256 maxBidAmount; // Maximum tokens this bidder can purchase (cumulative)
    uint256 expiry;       // Timestamp when permit expires
}

Key Functions:

  • validateBid() - Called by CCA to validate bids
  • updateMaxTotalEth() - Update total ETH cap (owner only)
  • updateMaxTokensPerBidder() - Update per-bidder cap (owner only)
  • updateTrustedSigner() - Rotate signing key (owner only)
  • pause() / unpause() - Emergency controls (owner only)

Usage

Deploying a Permitter

PermitterFactory factory = PermitterFactory(FACTORY_ADDRESS);
address permitter = factory.createPermitter(
    signerAddress,           // Trusted signer (backend)
    100 ether,               // Max total ETH
    1000 * 10**18,           // Max tokens per bidder
    ownerAddress,            // Contract owner
    keccak256("my-auction")  // Salt for deterministic address
);

Issuing Permits (Off-chain)

const domain = {
    name: 'Permitter',
    version: '1',
    chainId: 1,
    verifyingContract: permitterAddress
};

const types = {
    Permit: [
        { name: 'bidder', type: 'address' },
        { name: 'maxBidAmount', type: 'uint256' },
        { name: 'expiry', type: 'uint256' }
    ]
};

const permit = {
    bidder: userAddress,
    maxBidAmount: parseEther('1000'),
    expiry: Math.floor(Date.now() / 1000) + 86400 // 24 hours
};

const signature = await signer._signTypedData(domain, types, permit);

Placing a Bid

const permitData = ethers.utils.defaultAbiCoder.encode(
    ['tuple(address,uint256,uint256)', 'bytes'],
    [[permit.bidder, permit.maxBidAmount, permit.expiry], signature]
);

await ccaContract.placeBid(bidAmount, permitData);

Development

Built with Foundry.

Build

forge build

Test

forge test

Format

forge fmt

Security

Trust Model

  • Trusted Signer: Issues permits only to KYC-approved users
  • Owner: Can pause, update caps, rotate signer (cannot bypass signature verification)
  • Cryptographic Enforcement: EIP-712 signatures prevent forgery

Replay Protection

  • Domain separator includes chainId (prevents cross-chain replay)
  • Domain separator includes verifyingContract (prevents cross-auction replay)

Key Rotation

If the signing key is compromised:

  1. Owner calls updateTrustedSigner(newAddress)
  2. All old signatures become invalid immediately
  3. Users must request new permits

Disclaimer

This code has not been audited. Use at your own risk. A comprehensive security audit is recommended before any production deployment.

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors