For builders

Build on Permanent Collection.

The official 111 pool is a composable host. Anyone can route swaps through it, get credit on-chain, and earn a small builder fee — without forking liquidity, without weakening the core, without permission.

Attribution

Every swap can carry a sourceId and referrer field via hookData. The official hook emits a SwapAttribution event for every attributed swap. Permissionless, live from day one.

Referral fee

Up to 0.25% of swap volume can flow to the referrer on every attributed swap. The payment comes exclusively from the protocol slice; live-bid funding stays structurally untouched.

The three-leg fee split

Every swap takes a 6% baseline skim. The hook splits it at swap time into three legs. The math is enforced on-chain: no path exists for a referral payment to reduce live-bid funding.

5.00%of volume→ Patron (live bid)83% of skim
1.00%of volume→ Protocol controller17% of skim, minus any referral
0.25%of volume→ ReferralPayout (your address)paid from the protocol slice only

Quick start — frontend integration

Pass attribution data as the hookData argument of any Uniswap V4 swap on the 111 pool. Bad encoding falls through — the swap never reverts on malformed hookData.

import { encodeAbiParameters } from "viem";

// Both envelopes are encoded as 1-tuple structs (not 2-tuples of
// bytes). The hook decodes the OUTER bytes as
// `abi.decode(swapData, (PoolSwapData))` — a single struct argument.
// Encoding as a 2-tuple silently fails to decode and attribution is
// treated as empty.

const pcSwapData = encodeAbiParameters(
  [
    {
      type: "tuple",
      components: [
        {
          name: "attribution",
          type: "tuple",
          components: [
            { name: "sourceId",    type: "bytes32" },
            { name: "referrer",    type: "address" },
            { name: "campaignId",  type: "bytes16" },
            { name: "referralBps", type: "uint24"  },
          ],
        },
        { name: "extensionPayload", type: "bytes" },
      ],
    },
  ],
  [
    {
      attribution: {
        sourceId:    "0x...32-byte builder id...",
        referrer:    "0xYourPayoutAddress",
        campaignId:  "0x...16-byte campaign id (or zeros)...",
        referralBps: 250, // 0.25% of volume — clamped at the hook
      },
      extensionPayload: "0x", // reserved for future use
    },
  ],
);

const hookData = encodeAbiParameters(
  [
    {
      type: "tuple",
      components: [
        { name: "mevModuleSwapData",     type: "bytes" },
        { name: "poolExtensionSwapData", type: "bytes" },
      ],
    },
  ],
  [{ mevModuleSwapData: "0x", poolExtensionSwapData: pcSwapData }],
);

// Pass `hookData` as the final argument of your V4 swap call.

Claiming referral payouts

Referrals land in ReferralPayout within the same tx as the attributed swap — the hook flushes the credited referrer's accrual at the end of _afterSwap. No separate flush call is needed in normal operation; the balance is immediately claimable.

// View balance any time — populated by the credited swap
const owed = await referralPayout.balances(referrerAddress);

// Claim — by the referrer, or by anyone on the referrer's behalf
referralPayout.claim();                 // pulls msg.sender's balance
referralPayout.claimFor(referrerAddr);  // sends balance to referrerAddr

// There are no hook-side retry hatches: the hook flushes each swap's
// referral fresh inside _afterSwap and holds no balance between swaps.
// If a payout ever fails (reverting recipient / out of gas) the hook
// folds that swap's slice back into the protocol leg, nothing to retry.

Events to index

event SwapAttribution(
    PoolId  indexed poolId,
    address indexed swapper,
    address indexed referrer,
    bytes32 sourceId,
    bytes16 campaignId,
    uint256 quoteVolume,
    uint256 referralPaid
);

event SkimSplit(
    PoolId indexed poolId,
    uint256 quoteVolume,
    uint256 bountyAmount,
    uint256 protocolNet,
    uint256 referralPaid
);

event ReferralCredited (address indexed referrer, uint256 amount);
event ReferralClaimed  (address indexed referrer, uint256 amount);

What's locked vs. configurable

🔒 Locked forever

  • Baseline skim: 6%
  • Live-bid leg: 83% of skim
  • Max referral: 0.25% of volume
  • Live-bid invariance
  • Referral pulled from the protocol leg only
  • Permanent vault Punk immobility

🔓 Configurable (until 1y lock)

  • Adapter throttle parameters
  • Finder fee caps
  • Seller allowlist (no lock — forever)
  • Burn-step pacing
  • Extension binding (one-shot, then lockable)

The invariant, in plain code

For every swap S on the 111 pool:

bountyInflow(S)     ==  volume(S) × 5%   +  antiSniperExtra(S)
protocolInflow(S)
  +  referralPaid(S) ==  volume(S) × 1%

referralPaid(S)     <=  volume(S) × min(hookData.referralBps, 250) / 100_000
referralPaid(S)     ==  0   if  no referrer is attributed (stays in protocol leg)

Resources

Builders · Permanent Collection