Documentation

How EarnPad works.

What a vault does with a deposit, how the coin allocation is worked out, what happens inside the launch transaction, and every limit the factory refuses to bend. Written against the deployed contracts.

Overview

A coin normally launches by taking money off the people who buy it. On EarnPad the money is never taken. Depositors park USDG behind a coin, the USDG earns interest in spUSDG, and the interest is what buys the coin's liquidity. The principal is untouched by every code path in the launch, and it can be withdrawn in any block, including after the coin has already minted.

Two words are used throughout and mean different things. A vault is one launch: an EarnPool contract deployed by the factory, holding deposits and a target. A coin is the LaunchToken that vault mints once it hits that target. One vault mints at most one coin, once, and never again.

Everything below is enforced by contracts with no owner, no admin key and no upgrade path. The factory takes no fee and holds no funds. There is nobody to ask for an exception and nobody who can grant one.

Lifecycle

A vault is in exactly one of three phases. It starts in Funding and leaves it once, in one direction, for good.

PhaseEnumWhat is possible
Funding0 Deposits open. Interest accrues toward the target. Anyone can withdraw principal. Either launch() or cancel() ends it.
Launched1 The coin exists and trades. Deposits are shut. Withdrawals and claims stay open forever.
Cancelled2 No coin was minted. The interest earned is handed back to depositors alongside their principal.

The moment a vault leaves Funding it records closedAt, and the clock that drives allocation stops at that timestamp. Nothing after the close changes anyone's share of the coin.

Depositing and withdrawing

deposit(assets) takes USDG, six decimals, and puts it into spUSDG in the same transaction. It is earning before the block closes. Your balance is recorded in principal[you], which is the exact number of USDG the vault owes you back and the only number a withdrawal reads.

Every vault has a per-depositor cap, maxPerAddress, and the factory refuses to deploy a vault without one. A deposit that would carry your principal over the cap reverts. This is what stops one address quietly owning a launch.

Withdrawing

withdraw(assets) works in every phase, for the full amount you put in, with no lock and no notice period. While the vault is still funding it pulls exactly your principal out of spUSDG and leaves the interest behind, because the interest belongs to the launch. After the close the remaining spUSDG position is frozen against the remaining principal, so interest that accrues after the launch leaves with you rather than piling up in the contract.

Withdrawing does not destroy the allocation you have already earned. Weight measures interest you already contributed, and leaving does not undo that. You can take your money out and still claim your coins later.

How allocation is worked out

Allocation is principal-seconds, not a balance at a snapshot. One USDG parked for one second is one unit of weight. Your share of the depositor pot is your weight over the total weight at the close.

This is not an arbitrary formula. Every deposit sits in the same venue earning the same rate, so principal-seconds is exactly proportional to the USDG of interest each depositor contributed. The people who paid for the liquidity get the coins, in proportion to what they paid.

It also makes sniping pointless. A whale who deposits the day before a launch fires buys one day of weight against everybody else's months. There is no snapshot to time.

Reading it live

  • weightOf(who) is your principal-seconds as of this block.
  • totalWeightNow() is the denominator as of this block.
  • allocationOf(who) is the coins you would get if the vault launched now.

All three keep moving while the vault is funding, because other people are still earning weight beside you. They freeze at closedAt.

Sizing a launch

This is the part creators get wrong. The target is denominated in interest, not in deposits, so the capital a vault needs is much larger than the target. At a rate r over a window of t years, the deposits required are target / (r × t).

reading the live spUSDG rate

TargetWindowDeposits it needs
$1,00090 days...
$5,000180 days...
$20,000365 days...
$100,000365 days...

A vault that asks for more interest than its depositors can produce before the deadline never mints anything. It is not a loss for anyone, because cancelling returns principal and interest both, but it is months spent for nothing. Pick a target the capital you can actually attract will reach.

Opening a vault

EarnPad.create(cfg) is permissionless and costs the creator nothing but gas. The caller is recorded as the creator; passing somebody else's address in is not possible. Every field below is fixed at deployment and can never be changed afterwards.

FieldLimitNotes
name1 to 64 bytesThe coin's name.
symbol1 to 11 bytesThe coin's ticker.
description600 bytesStored on chain, not in an index this project controls.
image200 byteshttps:// or ipfs://.
website200 bytesOptional.
xHandle40 bytesHandle only, no URL.
telegram40 bytesHandle only, no URL.
yieldTargetabove zeroUSDG of interest that fires the launch. See sizing.
deadline7 to 365 days outAfter it passes, anyone can cancel.
creatorBps1,000 max10% of supply is the ceiling on the creator's cut.
depositorBpssee belowSupply reserved for the people who funded it.
maxPerAddressrequiredPer-depositor principal cap. Zero is rejected.
devBuyat most the capThe creator's own opening deposit, pulled in the same transaction. Optional.

The four refusals

These are the reason the factory exists. They are in code rather than in a document, so a vault that violates one cannot be deployed at all:

  • The creator can never take more than 10% of supply.
  • At least 40% of supply has to end up in the liquidity position.
  • Every vault has a per-depositor cap, so one address cannot own a launch.
  • The deadline is bounded at both ends, so capital is never committed open-endedly.

The split is checked as depositorBps + creatorBps ≤ 10,000 with at least 4,000 left over for liquidity. A launch that wants different numbers has to deploy its own factory, and anyone reading a vault can see which factory made it.

devBuy is an ordinary deposit in every respect. It counts against the creator's own maxPerAddress, earns weight the same way, and can be withdrawn whenever the creator likes. It buys no privilege.

The launch transaction

launch() is permissionless. Once the interest is there, nobody has to be asked and nobody can hold it back, including the creator. It reverts unless yieldAccrued() has reached the target plus a dust reserve of 0.001 USDG.

Inside one transaction, in this order:

  • Funding closes and the weight clock stops.
  • The surplus above principal is redeemed out of spUSDG. It is counted in shares rather than assets, so rounding can never leave the vault a unit short of the principal it owes.
  • The coin is deployed with a fixed supply of 1,000,000,000. The creator's cut transfers out immediately; the rest stays with the vault.
  • The whole raise is sold for ETH through the ETH/USDG pool.
  • A Uniswap v4 pool is opened for the coin against that ETH, at the price the raise implies, and the liquidity is minted into it.
  • Anything the mint could not absorb is credited to the depositors rather than kept.

The opening price cannot be known in advance, because it depends on how much ETH the raise actually buys. The pool is priced and initialised after the swap has settled, inside the same callback.

The dust reserve is charged to the launch, never to a depositor. It exists because converting between assets and shares rounds, and a vault that promises principal back whole cannot be one unit short on the last withdrawal.

The market that opens

A coin quoted in USDG is not a coin anybody trades, so the raise is sold for ETH first and the market is opened against that. Native ETH sorts below every token address, so it is always currency0.

PropertyValue
VenueUniswap v4
QuoteNative ETH
Fee tier1%
Tick spacing200
RangeFull
HooksNone
Route to ETHETH/USDG, 0.01%, spacing 1

The fee tier is 1% deliberately. A new coin is volatile, and the fee stream is part of what depositors are paid, so it is set high rather than low.

The liquidity cannot be pulled

The position is minted to the vault contract and there is no function that removes it. No owner, no timelock, no withdrawal path. The only call that touches the position after launch is collect(), which harvests fees with a zero-delta modify and cannot change the liquidity.

Getting paid

A depositor in a launched vault is owed four separate things:

WhatAssetPaid by
PrincipalUSDGwithdraw(), any block, any phase
Coin allocationThe coinclaim(), once
Trading feesETHclaim(), repeatedly
Trading feesThe coinclaim(), repeatedly

The pool earns fees in both of its currencies, so depositors are paid in both. These are tracked in separate accumulators, because mixing a cancelled vault's USDG refund with a launched vault's ETH and coin fees would lose track of which is which.

collect()

Fees sit in the Uniswap position until somebody sweeps them into the vault's claim accounting. collect() does that and is permissionless, because people who are owed fees should not need anyone's cooperation to be paid. Calling claim() pays out whatever has been swept so far; calling collect() first sweeps everything up to the current block.

claim() is safe to call as often as you like. The coin allocation only ever pays once; the fee streams pay whatever has accrued since your last call.

When a launch fails

cancel() is open to the creator at any time, and to anyone at all once the deadline has passed. It closes funding and hands the accrued interest back to the depositors, split on exactly the weights the coin allocation would have used.

So the failure case returns principal and interest. A depositor in a vault that never launches ends up with more USDG than they put in. What they spent is time and the option to have earned that interest somewhere else.

The creator being able to cancel early is not a rug. Cancelling mints no coin, pays the creator nothing, and returns every USDG in the vault to the people who deposited it. The worst a creator can do is end a launch that was not going to work.

Function reference

EarnPad, the factory

FunctionWhoWhat
create(cfg)anyoneDeploys a vault and returns its address.
poolCount()viewHow many vaults exist.
pools(i)viewThe vault at index i.
isPool(addr)viewWhether this factory deployed that vault.
venue(), asset()viewspUSDG and USDG, read off the venue rather than assumed.

EarnPool, one per vault

FunctionWhoWhat
deposit(assets)anyonePark USDG. Funding phase only, capped per address.
withdraw(assets)depositorTake principal back. Any phase, any block.
launch()anyoneMint the coin and open its market. Needs the target met.
cancel()creator, or anyone after the deadlineGive up and return the interest to depositors.
collect()anyoneSweep the position's trading fees into claims.
claim()depositorTake the allocation and any fees owed.
yieldAccrued()viewUSDG of interest standing behind the launch right now.
allocationOf(who)viewCoins that address would get at current weights.
feesOwed(who)viewUnclaimed USDG, ETH and coin fees.
phase()view0 funding, 1 launched, 2 cancelled.

Addresses

Robinhood Chain, id 4663.

Deployed contracts chain 4663
EarnPad factory0xc3110d0477e7a46fDbFeB38cD95F3b6F969549a2
Pool deployer0x828D9Df54B08d4CFA28142ac2AA306de16051fb1
spUSDG, the venue0xde770c84FE66E063336b31737cFE9790f18c4087
USDG, the deposit asset0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
Uniswap v4 PoolManager0x8366a39CC670B4001A1121B8F6A443A643e40951
$EarnPad token0x9Bc95D15D3f280916D0304D085e87045A3E671Bc
$EarnPad is not a vault coin. It is the project's own token and it has no role in the protocol today: it grants nothing, collects nothing, and is not required to deposit, launch or claim. Coins minted by vaults are separate contracts with their own addresses.

A vault deployed by this factory answers isPool(addr) with true. Anything claiming to be an EarnPad vault that fails that check was not made here.

Risk

The contracts are unaudited. 42 tests pass, six of them against live mainnet state, and no third party has reviewed the code. That is the largest risk here and it is not reduced by anything else on this page.

Venue risk

Deposits sit in spUSDG, not in EarnPad. If that vault loses value, is paused, or stops allowing withdrawals, deposits are affected regardless of what EarnPad's own code does. The rate it pays is also not fixed, so how long a launch takes is not fixed either.

The mechanism is slow

Interest-funded liquidity needs a lot of capital parked for a long time. See sizing a launch. A vault whose target is out of reach simply never mints.

Launch price depends on routing

The raise is sold for ETH through a single pool, and the coin's opening price is set by what that swap returns. A large raise against thin routing liquidity gets a worse price, and the coin opens accordingly.

Configuration is permanent

Target, deadline, splits and caps are immutable from the moment a vault is deployed. There is no function to lower a target or extend a deadline. A vault configured badly stays configured badly until somebody cancels it.

The creator holds up to 10%

The creator's cut transfers to them at launch with no vesting. It is capped at 10% of supply and is visible on chain before you deposit, in creatorBps.