Reference

Contracts

Two small contracts: a factory that creates one vault per token, and the vault itself. Neither is upgradeable, and Feeroute holds no role in either.

Addresses

Robinhood Chain mainnet, chain id 4663. Explorer: Blockscout.

ContractAddress
Feeroute factory0x4cAa25fdd5a05e0c630761D2D1f20fbC0787b01B
Feeroute vaultsOne per token, created by the factory. Browse them on Explore.
Pons v2 launch factory0x7eD598BcEf8bd9Edd8C97A195C6d13f40801EC7e
Pons fee escrow0xd3AFEB2a57f70eF218Aa82451c51B2fb0416Ac9e
Multicall30xcA11bde05977b3631167028862bE2a173976CA11

FeerouteFactory

Creates vaults. Only the token's current Pons fee recipient may create its vault, and becomes its owner. The factory doesn't route fees itself; that is a separate call on the Pons factory.

FunctionWhoWhat it does
createVault(token, splits, creatorCapBps, timelock)The token's fee recipientDeploys a vault owned by the caller with the first split live. ETH-quoted curves only.
vaultOf(token)ViewLatest vault created for a token
vaults(i) · vaultCount()ViewEvery vault ever created

FeerouteVault

Split entry

FeerouteVault.Split
struct Split {
    address account;      // who gets paid
    uint16  bps;          // share in basis points, all entries add up to 10_000
    uint32  vestSeconds;  // 0 = paid at once, up to 365 days
    bytes32 label;        // on-chain name shown on the report
}

Write functions

FunctionWhoWhat it does
sweep()AnyoneClaim from the Pons escrow, then split everything not owed yet by the active shares
proposeSplits(splits)OwnerQueue a new split and start the timelock
executeSplits()Anyone, after the timelockSweep with the old split, then activate the new one
cancelSplits()OwnerDrop the queued split
proposeRoute(recipient)OwnerQueue moving the Pons fee route away from the vault
executeRoute()Anyone, after the timelockSweep, then call transferCreatorFeeRecipient on Pons
cancelRoute()OwnerDrop the queued route change
release(account)AnyonePay account everything it can take now; funds only go to account
releaseTo(to)RecipientTake your own unlocked share to another address
claimFromPons()AnyoneFallback claim from the escrow with all available gas
claimTokenFromPons(token)AnyoneClaim ERC-20 credit from the escrow; not split, the owner rescues it
rescueToken(token)OwnerSend the vault's balance of an ERC-20 to the owner
transferOwnership(to) · acceptOwnership()Owner, then new ownerTwo-step ownership handover

Views

FunctionReturns
splits()The active split
pendingSplits()The queued split and when it can execute (0 if none)
pendingRoute() · pendingRouteEta()The queued route target and when it can execute
sweepable()ETH in the vault the next sweep would split
pendingInPons()ETH credited to the vault in the Pons escrow, claimed by the next sweep
releasable(account)What the account could release right now
vesting(account)ETH still locked for the account, and when it fully unlocks
claimable(account)Deferred payments and unlocked vesting already set aside
totalSwept() · totalOwed()Lifetime ETH split, and ETH held for recipients
splitsVersion() · creatorCapBps() · timelock()Current split version and the fixed settings
token() · curve() · ponsFactory() · owner()What the vault is bound to

Constants

ConstantValueMeaning
MAX_RECIPIENTS10Entries per split
TOTAL_BPS10,000Shares must add up to this
MAX_VEST365 daysLongest vesting period
MAX_TIMELOCK30 daysLongest timelock
PAYOUT_GAS50,000Gas forwarded to each recipient during a sweep
CLAIM_GAS500,000Gas given to the escrow claim inside a sweep

Events

The public report and the Activity page are built from these, so everything they show can be checked on-chain.

FeerouteVault events
Swept(uint64 indexed splitsVersion, uint256 amount, address indexed caller)
Paid(address indexed account, uint256 amount)
PaymentDeferred(address indexed account, uint256 amount)
Vested(address indexed account, uint256 amount, uint64 end)
Released(address indexed account, address indexed to, uint256 amount)
ClaimedFromPons(address indexed escrow, uint256 amount)
ClaimedTokenFromPons(address indexed escrow, address indexed token, uint256 amount)
SplitsProposed(uint64 indexed version, Split[] splits, uint64 eta)
SplitsExecuted(uint64 indexed version, Split[] splits)
SplitsCancelled(uint64 indexed version)
RouteProposed(address indexed newRecipient, uint64 eta)
RouteChanged(address indexed newRecipient)
RouteCancelled(address indexed newRecipient)
TokenRescued(address indexed token, address indexed to, uint256 amount)

FeerouteFactory:
VaultCreated(address indexed token, address indexed vault, address indexed owner,
             address curve, uint16 creatorCapBps, uint32 timelock)

Errors

ErrorRaised when
InvalidRecipientCountA split has no entries or more than 10
InvalidAccountA recipient or route target is the zero address or the vault
DuplicateAccountThe same wallet appears twice
ZeroShareAn entry has 0 bps
InvalidTotalShares don't add up to 10,000 bps
InvalidVest · InvalidTimelock · InvalidCapA setting is above its maximum
CreatorCapExceededThe owner's address would hold more than the cap
ProposalPendingA proposal of the same kind is already queued
NoProposal · TimelockActiveNothing to execute or cancel, or it's too early
NothingToReleaseThe account has nothing unlocked
InsufficientGasThe transaction can't cover a full escrow claim or payout
TransferFailedA release couldn't be delivered
UnknownToken · NotFeeRecipient · UnsupportedQuoteFactory: not a Pons v2 token, caller isn't its fee recipient, or the curve isn't quoted in ETH

From the command line

You don't need this site to use a vault. With Foundry's cast and the public RPC:

terminal
RPC=https://rpc.mainnet.chain.robinhood.com
VAULT=0x…   # from Explore or the report

# the active split
cast call $VAULT "splits()((address,uint16,uint32,bytes32)[])" --rpc-url $RPC

# fees ready now, and fees waiting in Pons
cast call $VAULT "sweepable()(uint256)" --rpc-url $RPC
cast call $VAULT "pendingInPons()(uint256)" --rpc-url $RPC

# split them (anyone can)
cast send $VAULT "sweep()" --rpc-url $RPC --account <your keystore>

# take what has unlocked for a recipient
cast send $VAULT "release(address)" <recipient> --rpc-url $RPC --account <your keystore>