Leaseboard

№ V · Particulars

The technical sheet.

Written against the contracts as they stand today, before deployment. Every constant below is a value in the source; every parameter marked at deployment is a value chosen by the deploy script and settable thereafter only within the limits stated.

System

Five contracts, no proxies

Nothing here is upgradeable. There is no proxy, no implementation pointer, and no delegatecall to code that could be swapped later. The addresses that need to know about each other are wired exactly once, at deployment, by an address that becomes unable to wire anything again the moment it has.

Contracts
ContractResponsibility
RentToken$RENT itself. ERC-20 with permit and vote checkpoints. Fixed supply minted once in the constructor; no mint path afterwards. Accepts a burn instruction from the hook, applied only to the hook's own balance.
RentHookThe Uniswap v4 hook. Holds the lease state, charges rent, splits it, returns the active fee to the pool on every swap, and redeems the three shares.
RentStakingSingle-asset vault: stake $RENT, earn the staker share of rent. Funded only by the hook, which can only add.
RentGovernorSnapshot voting over three bounded parameters. The only address the hook accepts those changes from.
RentLauncherThe only address permitted to bring the pool into existence, and it does so seeded, in one transaction. One-shot.

The hook

Two hook points · beforeInitialize, beforeSwap

The hook subscribes to two v4 callbacks and no others. It takes no share of any swap: it returns no swap delta, and it never mints, takes or settles a currency on the swap path.

beforeInitialize refuses every caller except the launcher, and refuses any pool key that is not the canonical ETH/RENT dynamic-fee pool at tick spacing 200. It can succeed once.

beforeSwap settles any rent that has accrued since the last time the lease was touched, then returns the currently active fee as a dynamic-fee override — the leaseholder's fee if there is one, the default if the board is vacant.

// lease
bid(uint256 rentPerBlock, uint256 deposit)   take or retain the board
deposit(uint256 amount)                      top up, holder only
setFee(uint24 fee)                           holder only, within the band
pokeRent()                                   anyone: settle a quiet pool
withdrawRefund()                             collect an escrowed refund

// redemption — anyone may call
distributeToLPs()                            donate the LP share into the pool
distributeToStakers()                        push the staker share to the vault
burnAccrued()                                burn the burn share

// views
currentFee() manager() rentPerBlock() managerDeposit()
pendingRent() isVacant() lpBucket() stakerBucket() burnBucket()

Rent accounting

Lazy accrual · exact split

Rent owed is rentPerBlock × (current block − last settled block). Settlement happens inside whatever transaction next touches the lease, and the settled-to block always advances to the current block, so an arbitrarily long quiet period is charged exactly once and never twice.

If the amount owed is at least the whole remaining deposit, the whole deposit is charged and the lease ends with nothing to return. Otherwise the amount owed is charged, and if what is left cannot cover one more block the lease ends and the remainder is returned.

The charged amount is divided into three balances. The staker and burn shares are computed by basis points; the liquidity share is the remainder, which is what makes the identity exact: the three balances always sum to precisely what was charged, with no dust left unassigned and nothing over-credited.

Refunds owed to a leaseholder are attempted as a direct transfer and, if that transfer does not succeed for any reason, credited to them to withdraw later. This is deliberate: it means a refund can never be the thing that reverts a swap or blocks a takeover.

Staking vault

Single asset · no lock-up · no owner
stake(uint256 amount)      unstake(uint256 amount)
claim()                    pending(address user)

Rewards are tracked with an accumulator over total staked, which is the standard shape for this: crediting is O(1) per distribution regardless of how many stakers there are, and each staker's entitlement is computed from the accumulator's movement since they last touched it. Rounding always leaves dust in the vault rather than promising more than it holds.

A distribution that arrives while nobody is staked is held aside and folded into the next one. Someone who stakes after a distribution receives no part of it.

Governor

Snapshot · quorum · re-clamped on arrival
proposeFeeBand(uint24 minFee, uint24 maxFee, uint24 defaultFee)
proposeSplit(uint16 lpBps, uint16 stakerBps)
proposeMinOutbid(uint16 outbidBps)
vote(uint256 id, bool support)
execute(uint256 id)

A proposal records a snapshot timepoint strictly earlier than the block it was created in, and every vote is weighed at that timepoint. A balance that did not exist at the snapshot has no weight, which is what makes a borrowed balance useless here.

Execution is possible only after the window closes, only once, only if the votes in favour reach the quorum measured against the supply at the snapshot, and only if there are more for than against. The hook then re-checks every value against its own fixed limits before storing it, so a governor that passed something out of range would simply be rejected.

Changing the fee band also re-clamps a sitting leaseholder's fee into the new band, so a band change cannot leave an out-of-band fee live.

Launch

One transaction, seeded

The pool is created by the launcher and by nothing else. In a single transaction it initialises the pool at the chosen price and mints the initial liquidity position, so there is no moment at which the pool exists unseeded or at a price somebody else chose. Any attempt to initialise the canonical pool from any other caller is rejected by the hook.

The board is vacant at the first block. Until somebody bids, the pool charges the default fee.

Constants and parameters

Fixed in code vs. chosen at deployment
Fixed in the contracts — no path changes these
ValueSetting
Lowest fee the pool can ever charge0.01 %
Highest fee the pool can ever charge3.00 %
Liquidity share floor50 %
Burn share ceiling20 %
Outbid increment ceiling50 %
Total supply1 000 000 000
Pool tick spacing200
Chosen at deployment — movable only by vote, within the limits above
ParameterAt deployment
Fee band, low0.01 %
Fee band, high3.00 %
Default fee when vacant0.30 %
Rent share — liquidity providers70 %
Rent share — stakers25 %
Rent share — burn5 %
Minimum outbid increment5 %
Governor voting window3 days
Governor quorum4 % of supply

Trust and limits

The things worth knowing before you decide anything
  • The liquidity share is paid in $RENT, on one side. It is donated to the pool and accrues like a fee, but it is not a balanced payout, and an LP whose range is not covering the current price at the moment of a donation receives no part of that donation.
  • A donation needs liquidity in range. If there is none at that instant the share waits in the hook rather than being paid. It is not lost, but it is not paid either until somebody calls again with liquidity in range.
  • A well-funded holder can sit at the top of the band. Nothing stops a leaseholder with deep pockets from setting the fee to 3.00 % and continuing to pay for it. They are paying rent to the pool the entire time, and anyone may outbid them at any block, but they cannot be forced out for free.
  • The initial liquidity position is an NFT held by the deploying address. It is not locked by a contract and not burned.
  • Rent volume is unknown. All three shares are shares of a quantity nobody can size in advance. The contracts fix the proportions, not the amount.
  • External dependencies. The pool relies on Uniswap v4's PoolManager and PositionManager. A defect there is a defect here.

Status

As of this page
Deploymentstate
Mainnet contracts
not deployed
Contract address
does not exist yet
Pool
not initialised
Test suite
101 tests passing
Independent external audit
none
Token sale
none — there is no sale

The test suite covers the auction arithmetic, the exactness of the rent split under randomised sequences, the lapse and takeover paths, the vault's accounting, the governor's snapshot behaviour, and the pool behaviour against a fork of live Uniswap v4. That is internal work by the people who wrote it. It is not an audit, and this page does not claim it is one.