Documentation

HELIX is a single immutable Uniswap v4 hook on Robinhood Chain that lets anyone provide ETH/HLX liquidity with leverage, at zero interest, and earn the pool's trading fees on the whole position. This page explains what the protocol does, the numbers behind it, every function, and where the risks are.

In plain words

You put ETH and HLX into a Uniswap pool through HELIX. HELIX adds more liquidity on top of yours, from a pool it owns called the seed. Up to five times what you put in.

The extra liquidity is a loan with no interest. What you owe is written down once, as an amount of ETH and an amount of HLX. It never grows.

While your position is open, it earns the pool's trading fees. Bigger position, bigger share. A small slice of every swap, 0.20%, goes to a treasury that buys HLX and burns half of it.

When you close, HELIX takes back what you owe and gives you the rest. If the price moves so far that 90% of your position is worth less than your debt, anyone can close it for you. They keep 5% of what is left. You get the rest.

Two things to remember. Leverage makes fees bigger and also makes price swings hurt more. And HELIX never trusts the live pool price: it uses its own slow-moving price, so nobody can trick it in one transaction.

Introduction

A normal liquidity position on Uniswap earns trading fees in proportion to its size. HELIX lets you make that position larger than your deposit. You bring ETH and HLX, choose a multiplier between 1.1x and 5x, and the hook adds the difference from a pool of liquidity it owns itself, called the seed. What you owe is written down once, as a fixed number of ETH and HLX, and never grows. While the position is open it earns the pool's 0.30% trading fee on all of its liquidity, the borrowed part included.

There is no lender, no interest rate, no governance and no admin. The whole HLX supply is minted into the hook at deploy. The deployer can do two things: launch once, and renounce. Every other rule is fixed in the constructor.

Three ideas make this safe to run against a live pool:

Leverage here makes your fees bigger and your price risk bigger. It is not a bet that HLX goes up. Read what leverage does to your result before going above 2x.

Key numbers

WhatValueNotes
Leverage range1.1x to 5xChosen per position, in steps of 0.01%. Fixed for the life of the position; repaying lowers it.
Interest0%Debt is a fixed token amount.
Entry fee2% of both tokens, onceETH part to insurance. HLX part half to insurance, half burned.
Pool fee0.50% per swap on HLXPaid by traders. 0.30% split by liquidity between positions and the seed, 0.20% to the treasury. Launched coins add their creator fee.
Liquidation line90% of position valueLiquidatable when 0.9 * V < D at the reference price.
Liquidation bonus5% of residual equityTo the liquidator. Never from the seed or insurance.
Reference price speed limit240 ticks per block, about 2.4%Frozen within a block.
Spot guard200 ticks, about 2%Actions revert while the pool is further than this from the reference.
Minimum hold5 blocksL1 blocks, about one minute. Repay and collect are allowed during the hold.
Minimum deposit0.05 ETH and 10 HLXKeeps liquidations worth the gas.
Supply1,000,000 HLX, fixedAll minted to the hook. Burns only reduce it.
Launch protection2% of supply per wallet for 3 minutesApplies to buys from the pool only. Lifts itself.
Admin functions after launchonerenounceOwnership. No pause, no fee switch, no withdrawal.

Concepts

The pool

One Uniswap v4 pool: native ETH as currency0, HLX as currency1, 0.30% fee, tick spacing 60, with HELIX as its hook. Anyone can swap in it. Only the hook can add or remove liquidity, so the hook is the pool's only liquidity provider and every position inside HELIX is a slice of that one full-range position.

The seed

The liquidity the hook owns outright. It is created at launch from the ETH the deployer sends plus part of the HLX supply, and it grows when fees are folded in, when positions repay, and when anyone donates through seed(). When you open a position, the borrowed part is relabeled from the seed to you; when you close, it is relabeled back. Nothing can withdraw the seed. There is no function for it.

Positions, equity and borrowed liquidity

A position is a record of how much liquidity is yours (liquidity), how much of it came from the seed (borrowed), and what you owe for it (debt0 in ETH, debt1 in HLX). The part you funded yourself is your equity liquidity. Leverage is simply liquidity / equity liquidity.

Fixed debt

At open, the borrowed liquidity is converted into token amounts at the reference price, rounded up, and stored. Those two numbers are your debt. They never change unless you repay. There is no interest, no funding rate and no rebasing.

Health

The ratio between what your liquidity is worth and what you owe, both measured in ETH at the reference price, scaled by the 90% liquidation line. Health of 1.35 means your position is worth 1.35 / 0.9 = 1.5 times its debt. Below 1, anyone may liquidate. Because your debt is a fixed basket of ETH and HLX and your liquidity is a full-range LP position, health falls when HLX moves far in either direction.

The reference price

A tick stored by the hook that moves toward the live pool price at most 240 ticks per block and is frozen within a block. Everything economic is priced at it. The live price only matters for one thing: if it strays more than 200 ticks from the reference, actions wait.

Insurance

Two balances held by the hook: reserve0 in ETH and reserve1 in HLX. Funded by the unseeded supply at launch, by entry fees, and by leftovers. Spent only to rebuild the seed when a position comes back short on one token.

Pending fees and fold

The seed's share of harvested trading fees waits in pending0 and pending1. Anyone can call fold() to add it to the seed as liquidity and keep a tip of at most 5%. This is how the seed grows without anyone funding it.

The receipt

Each position is an ERC-6909 token minted to the opener. It is soulbound: transfer, transferFrom, approve and setOperator all revert. The position belongs to the address that opened it until it is closed or liquidated.

A worked example

The pool trades at 90,000 HLX per ETH. You send 1 ETH and 90,000 HLX and choose 3x. The liquidity unit used below is the one Uniswap uses: for a full-range position, L = ETH * sqrt(P), and the square root of 90,000 is 300.

  1. Entry fee. 2% of each token: 0.02 ETH goes to the ETH insurance reserve. 1,800 HLX is split: 900 to the HLX reserve, 900 burned. You have 0.98 ETH and 88,200 HLX left.
  2. Equity liquidity. 0.98 * 300 = 294 units. The pool ratio is met exactly, so nothing is refunded. If you had sent 95,000 HLX, the extra 6,800 would come back in the same transaction.
  3. Borrow. At 3x the hook relabels 2 * 294 = 588 units from the seed to you. Your position is 882 units.
  4. Debt. 588 units at the reference price of 90,000: 588 / 300 = 1.96 ETH and 588 * 300 = 176,400 HLX. Written down once.
  5. What you hold. 882 units at 90,000 is 2.94 ETH and 264,600 HLX, worth 5.88 ETH. Your debt is worth 1.96 + 176,400 / 90,000 = 3.92 ETH. Your equity is 1.96 ETH, which is what you put in after the fee. Health is 0.9 * 5.88 / 3.92 = 1.35.
  6. Earning. If the pool has 30,000 units of liquidity in total, your 882 units earn 2.94% of every swap fee. On your own money alone you would have earned 0.98%.
  7. Liquidation prices. Solving health = 1 gives 17,670 and 458,400 HLX per ETH. HLX would have to rise 5.1x or fall 80% from here.
  8. Closing. Suppose nothing moved and you collected 0.0042 ETH and 391 HLX of fees along the way. Closing removes 882 units, returns 2.94 ETH and 264,600 HLX, keeps 1.96 ETH and 176,400 HLX as your debt, and pays you the rest: 0.98 ETH and 88,200 HLX, plus any fees not yet collected. The 588 units are rebuilt into the seed.

This is the position shown on the overview page.

What leverage does to your result

A leveraged LP position is a leveraged LP position, not a leveraged long. Three things move your equity:

The table shows the value of your equity after a price move, before fees, starting from the example above. "Plain LP" is the same 1.96 ETH of equity provided without leverage. "Hold" is keeping the two tokens in a wallet.

HLX priceHoldPlain LP2x3x5x
x 0.5-25%-29%-34%-38%-46%
x 0.8-10%-11%-11%-12%-13%
x 1.000000
x 1.2+10%+9%+9%+9%+8%
x 2.0+50%+41%+33%+24%+7%

The formula behind the table: with leverage m and an HLX price multiple r, your equity multiple before fees is m * sqrt(r) - (m - 1) * (1 + r) / 2. The first term is the LP position, which grows with the square root of the price. The second is the debt basket, half of which is HLX and moves one-for-one. Leverage stretches the gap between them.

What this means in practice:

Fees and yield

Where fees come from

Every swap in the HLX pool pays 0.50% of its input to the pool's liquidity provider (launched coins add their creator fee on top). Because the hook is the only liquidity provider, all of it lands on the hook's single full-range position, where Uniswap tracks it per unit of liquidity. Of that, 0.20% of volume is the protocol share and goes to the treasury; the remaining 0.30% is what positions and the seed earn, exactly as described below.

Harvesting

Before any principal changes (open, close, repay, liquidate, fold) and whenever someone collects, the hook harvests: it pokes the pool position with a zero-liquidity update, receives every fee accrued since the last harvest, and splits the two token amounts by liquidity.

protocol share t = f * TREASURY_SHARE_BPS / 10000, sent to the treasury (creator share likewise, on launched coins)
f = f - t - creator share
positions' share u = f * totalUserLiquidity / (seedLiquidity + totalUserLiquidity)
feeGrowthX128 += u * 2^128 / totalUserLiquidity
seed's share f - u, kept as pending until someone folds it

Each position stores the value of feeGrowthX128 at the moment it opened or last collected. What it is owed at any time is liquidity * (feeGrowthX128 - snapshot) / 2^128, per token. A position therefore earns from the block after it opens, on its whole liquidity, and never on fees that accrued before it existed.

Collecting

collectFees(id) pays the owner what is owed and moves the snapshot forward. Closing and liquidation pay it too, always to the owner; a liquidator never receives the owner's fees. pendingFees(id) shows the harvested part; fees that accrued in the pool since the last harvest by anyone are added the next time any action runs.

Yield

Call the pool's fee rate rate: fees earned per day by one unit of liquidity, divided by that unit's value. A position at leverage m earns m * rate on its equity, with no borrowing cost to subtract.

Pool valueDaily volumeDaily feesPlain LP2.5x5x
100 ETH5 ETH0.015 ETH5.5% a year14%27%
100 ETH20 ETH0.06 ETH22%55%110%
100 ETH50 ETH0.15 ETH55%137%274%

These are gross numbers, before the 2% entry fee and before any price move. The app measures the real rate from the pool's own fee counters over the time the page has been open and shows the projection for the leverage you pick.

The entry fee

fee0 = ceil(amount0 * 2%), all to reserve0 (ETH insurance)
fee1 = ceil(amount1 * 2%), half to reserve1 (HLX insurance), half burned

It is the only charge the protocol makes. There is no exit fee, no performance fee and no interest.

Fold

The seed's share of the fees accumulates in pending0 and pending1. fold(tipBps) pays the caller up to 5% of that bucket and adds the rest to the seed as liquidity at the current pool ratio. A deeper seed means larger positions can be opened, which generates more fees, which folds into a deeper seed. Nobody has to run this; anyone who wants the tip will.

Health and liquidation

Liquidity and value

For a full-range position of L units at price P (HLX per ETH):
ETH held = L / sqrt(P) HLX held = L * sqrt(P)
Value in ETH: V(P) = 2 * L / sqrt(P)

Health

V = ETH value of the position's liquidity at the reference price
D = debt0 + debt1 / P_ref
health = 0.9 * V / D liquidatable when health < 1

At the opening price, V is m times your equity and D is m - 1 times it, so health at open is 0.9 * m / (m - 1). That is the buffer you start with:

LeverageHealth at openLiquidated if HLX falls byLiquidated if HLX rises to
1.5x2.7096%27x
2x1.8091%10.9x
3x1.3580%5.1x
4x1.2071%3.5x
5x1.12563%2.7x

Both liquidation prices, in closed form

Setting health = 1 and solving for the square root of P gives a quadratic with two roots. The app computes both for every position.

x = sqrt(P_liq) = (0.9 * L +/- sqrt((0.9 * L)^2 - debt0 * debt1)) / debt0
P_low = x_low^2 P_high = x_high^2 (HLX per ETH; liquidatable outside the band)

What happens at liquidation

  1. Anyone calls liquidate(id, receiver). It reverts unless health is below 1 at the reference price, unless the live price is within the guard band, and unless no other liquidation happened in this block.
  2. Fees are harvested. The position's liquidity is removed from the pool.
  3. Per token: the debt amount is kept; of what remains, 5% goes to receiver and 95% to the position owner. The owner also receives every trading fee the position earned.
  4. The kept debt tokens, topped up from insurance if one side is short, are rebuilt into the seed.
  5. The receipt is burned.

Because liquidation triggers when 10% of position value is still equity, the owner of a liquidated position typically receives a meaningful remainder, not zero. In the 3x example a crash liquidation returns about 13% of the initial equity, a rally liquidation about 68%; at 5x the figures are about 29% and 78%.

Repay

repay(id, amount0, amount1) sends ETH and HLX in the pool ratio, turns them into liquidity, returns that liquidity to the seed and reduces your debt by exactly the tokens used. Your own liquidity is untouched, so health rises and both liquidation prices move away from the current price. Repaying is allowed at any time, including during the minimum hold. Excess is refunded.

Insurance and the seed

What flows in

What flows out

Only one thing: rebuilding borrowed liquidity when a position closes or is liquidated. The debt tokens the position returns were sized at the opening price. At a different price the same liquidity needs a different mix of ETH and HLX, so one side is short and the other is in surplus.

out_i = max(0, got_i - debt_i), paid to the owner (95% owner, 5% liquidator on liquidation)
avail_i = min(got_i, debt_i) + reserve_i
reLiq = min(borrowed, liquidityFor(avail0, avail1)), added back into the seed
reserve_i = avail_i - cost_i

The whole reserve of the scarce token is available for the rebuild, and the surplus of the other token is parked back in its reserve. No value leaves the protocol; it changes shape. If a reserve is fully drained, the seed is rebuilt with whatever liquidity the tokens support and comes back slightly smaller in liquidity units, while holding the surplus token in reserve for the next time.

The asymmetry to know about

The HLX reserve is deep from day one, because the unseeded supply is large. The ETH reserve starts near zero and grows only from entry fees. A strong HLX rally makes ETH the scarce side of every rebuild, so early in the protocol's life a rally can leave the seed temporarily smaller in liquidity units, with the excess HLX sitting in reserve. Every entry fee and every fold shrinks that gap.

Per-token settlement

Payouts are computed per token: max(0, got - debt) on ETH and again on HLX. If one side comes back below its debt, the shortfall is not taken from your surplus on the other side. Inside the liquidation band this is a small advantage to whoever closes, absorbed by insurance. It is bounded by the 90% line, which is why the line exists.

The reference price

A flash loan can move a pool a thousand-fold and put it back inside one transaction. Any lender that reads the pool price and acts on it in the same breath can be drained. HELIX never acts on the live price. It acts on a reference that is allowed to change only slowly.

function _projectOracleTick(int24 spot) internal view returns (int24 projected) {
    projected = oracleTick;
    if (oracleBlock == 0) return spot;
    if (block.number <= oracleBlock) return projected;             // frozen within a block
    uint256 maxMove = (block.number - oracleBlock) * MAX_TICK_DELTA_PER_BLOCK;
    int256 diff = int256(spot) - int256(projected);
    if (diff > 0 && uint256(diff) > maxMove) diff = int256(maxMove);
    else if (diff < 0 && uint256(-diff) > maxMove) diff = -int256(maxMove);
    projected = int24(int256(projected) + diff);
}

The reference is synced in beforeSwap with the tick before the swap, and at the top of every economic action. Whichever comes first in a block wins, and nothing can move it again until the next block. Then the guard:

if (|spotTick - oracleTick| > MAX_SPOT_DEVIATION_TICKS) revert PriceOutOfBand();

What this means

Lifecycle and ownership

  1. Deploy. The hook is deployed through CREATE2 with a mined salt, because Uniswap v4 reads a hook's permissions from the low bits of its address. The token is deployed next and mints the whole supply to the hook.
  2. Launch. The owner calls launch(sqrtPrice, seedHlx) with ETH. The pool is initialized at that price, the seed is created from the ETH sent plus seedHlx from the hook's own balance, everything else becomes insurance, started flips to true and the 3-minute launch protection begins. One shot: it cannot be called twice.
  3. Live. Anyone can swap, open, close, repay, collect, liquidate, fold, seed and claim. None of these involve the owner.
  4. Renounce. The owner calls renounceOwnership(). From then on no address holds any special power.

Between launch and renounce, the owner holds exactly one capability: renouncing. There is no pause, no parameter change, no fee switch, no upgrade path and no way to move the seed or the reserves.

The HLX token

Parameters

All immutable, set in the constructor. The values below are the Robinhood Chain deployment.

NameValueMeaning
MAX_LEVERAGE_BPS500005x maximum. MIN_LEVERAGE_BPS is a constant 11000, 1.1x.
LIQ_THRESHOLD_BPS9000Liquidatable when 90% of value is below debt. The constructor requires that the maximum leverage opens at health >= 1.
ORIG_FEE_BPS2002% entry fee on both tokens.
INSURANCE_SPLIT_BPS5000Share of the HLX fee kept as insurance; the rest is burned. The ETH fee is always insurance.
LIQ_BONUS_BPS500Liquidator's share of residual equity.
MAX_TICK_DELTA_PER_BLOCK240Reference price speed limit, about 2.4% per block.
MAX_SPOT_DEVIATION_TICKS200Guard band, about 2%.
MIN_HOLD_BLOCKS5No close for 5 L1 blocks after opening.
MIN_DEPOSIT_ETH / MIN_DEPOSIT_TOKEN0.05 ETH / 10 HLXAnti-dust floors.
MAX_FOLD_TIP_BPS500Constant. Cap on the keeper tip.
FEE / TICK_SPACING3000 / 60The only pool key the hook accepts.

Hook callbacks

CallbackBehaviour
afterInitializeAccepts exactly one pool: currency0 = ETH, currency1 = HLX, fee 3000, spacing 60, hooks = this. Anything else reverts InvalidPoolKey; a second pool reverts AlreadyInit. Stores the key and seeds the reference price with the initial tick.
beforeAddLiquidity / beforeRemoveLiquidityRevert ExternalLiquidityBlocked unless the caller is the hook. The hook is the only liquidity provider.
beforeSwapSyncs the reference price with the pre-swap tick. Never changes amounts or fees.

Uniswap v4 does not invoke hook callbacks when the hook itself is the caller. That is why launch() records the pool state directly after initializing, and why the hook's own liquidity changes do not pass through beforeAddLiquidity.

Launchpad

HELIX is also a factory. Anyone can launch a coin that comes with its own pool and its own copy of the HELIX hook, in one transaction, from the Launch page.

What a launch does

  1. Deploys the token: fixed supply, no owner, no mint, with the launch cap the creator chose.
  2. Deploys the hook from the audited HELIX bytecode (the factory checks it against a stored hash) with the creator's numbers: max leverage, liquidation line, creator fee.
  3. Mints the whole supply to the hook, initializes the pool at the chosen price, seeds it with the creator's ETH and the chosen share of supply, keeps the rest as insurance.
  4. Renounces the hook's ownership. The creator has no power over the coin from the first block.

Limits the factory enforces

SettingRange
Max leverage1.2x to 5x
Liquidation line85% to 95%
Creator fee0% to 3% of swap volume
Supply in the pool50% to 95%; the rest is insurance
Seedat least 0.1 ETH of the creator's own ETH
Launch capnone, or 0.5% to 10% per wallet for up to 1 hour
Launch fee0.01 ETH to the treasury

Fees on a launched coin

The pool fee is 0.30% + 0.20% + creator fee. Positions and the seed keep the 0.30%, exactly like the HLX pool. The 0.20% goes to the treasury. The creator fee goes to the creator's wallet, paid out at every harvest with the same gas-capped payout used everywhere else.

Why addresses are mined in your browser

Uniswap v4 reads a hook's permissions from the low bits of its address, so the hook has to be deployed at an address that carries them. The launch page tries salts until it finds one. The salts must start with your own address, so nobody can take a launch you prepared.

Listing a coin that already exists

A coin does not have to be born on HELIX to get leverage. A second, separate factory, HelixListingFactory, opens a HELIX pool for any standard coin on the chain. The List page drives it.

In plain words

You bring ETH and some of the coin. HELIX creates a new pool for that coin with its own leverage hook, puts your ETH and coins in it for good, keeps a slice of the coins as insurance, and drops ownership in the same transaction. From then on anyone can open leveraged positions on that coin, and you earn your fee on every swap in that pool.

What a listing does

  1. Checks the coin: 18 decimals, a standard transfer that returns true, and the exact amount arriving. Coins that tax or rebase on transfer are rejected.
  2. Deploys the hook from the same audited bytecode as every other HELIX hook, checked against the stored hash.
  3. Pulls your coins into the hook, opens the pool at the price your two amounts imply, and seeds it with your ETH and coins. The seed can never be withdrawn.
  4. Keeps the insurance coins in the hook to cover bad debt, then renounces ownership.

Limits

SettingRange
Max leverage1.2x to 5x
Liquidation line85% to 95%
Creator fee0% to 3% of swap volume
Seedat least 0.1 ETH plus the matching coins
Insuranceat least 5% of the coins you seed
Listing fee0.01 ETH to the treasury

How a listed coin differs from a launched coin

HELIX controls the pool and the hook, not the coin. If the coin's owner can pause transfers, blacklist wallets, mint or add a tax later, positions in that pool can lose value or get stuck. The coin page shows a warning on every listed coin.

Treasury

Every HELIX pool sends 0.20% of its swap volume to HelixBuyback, a contract with no owner and no withdrawal function. Launch fees go there too. Anyone can turn its balance into value for HLX holders in two steps:

  1. commitFlush() records the current pool price and liquidity for the caller.
  2. Between 3 and 100 blocks later, flush(minOut) buys HLX in the HLX pool with the ETH balance, burns 50% of all HLX held, and adds the other 50% together with the remaining ETH to the HLX seed as permanent liquidity. The caller keeps 0.5% of the ETH as a tip.

The buy has a floor computed along the pool curve at the committed price and liquidity, minus a 4% allowance. A pump between commit and flush, or a sandwich around the flush, pushes the real output under that floor and the whole flush reverts. Nobody can extract from the treasury; they can only delay a flush by a few blocks.

Fees paid in other coins

Pools pay fees in both ETH and the coin. The ETH side is ready for a flush. The coin side of every launched or listed coin can be sold for ETH by anyone with the same two steps: commitSell(hook), then sell(hook, minOut) between 3 and 100 blocks later, with the same price floor and the same tip. It only works on pools created by the two HELIX factories, and HLX itself is never sold.

One rule keeps this safe when a coin has more than one HELIX pool: a pool's fees can only be sold through that same pool. Every hook counts the coins it has sent to the treasury (treasuryToken1), and sell(hook) never sells more than that count. So nobody can open a tiny second pool for a coin, push its price where nobody is watching, and make the treasury dump another pool's fees into it. sellable(hook) shows what a pool can sell right now.

Result for HLX: supply falls with every flush, the HLX seed grows with every flush, and both are funded by activity on every coin launched on HELIX.

Functions

Every action runs inside PoolManager.unlock; the liquidity work happens in a callback that only the PoolManager can invoke. Every entry point is nonReentrant. Actions marked price guard first sync the reference price and require the live price to be within the band.

launch(uint160 sqrtPriceX96, uint256 seedToken) payable to uint128 liq
renounceOwnership()

owner onlyone shot

launch initializes the pool at sqrtPriceX96 if nobody has, adds msg.value ETH plus seedToken HLX from the hook's own balance as the seed, keeps unused ETH and every remaining HLX as insurance, and sets started and launchedAt. If someone pre-initialized the pool at a price more than about 6% away from the requested one it reverts InitPriceOutOfRange. renounceOwnership sets the owner to zero. Emits Launched, OwnershipRenounced.

seed(uint256 amount0, uint256 amount1) payable to uint128 liq

anyoneirreversible

Donate liquidity to the seed after launch. amount0 must equal msg.value; HLX is pulled only for what the pool ratio uses, so approve the hook first. Unused ETH is refunded. Emits Seeded.

open(uint256 amount0, uint256 amount1, uint16 leverageBps, uint128 minLiq, uint256 deadline) payable to uint256 id

anyoneprice guard
  1. Checks: deadline not passed, protocol started, 11000 <= leverageBps <= 50000, msg.value == amount0, both amounts above the minimums, no liquidation in this block.
  2. Entry fee, 2% of each side, rounded up. ETH fee to reserve0; HLX fee half to reserve1, half burned.
  3. The full amount1 is pulled from you.
  4. Inside the callback: fees are harvested; your equity liquidity is added at the live price; whatever the ratio did not use is refunded and Refunded is emitted.
  5. (leverageBps - 10000) / 10000 * equity liquidity is relabeled from the seed to you. The token amounts that liquidity represents at the reference price, rounded up, become debt0 and debt1. Reverts SeedTooThin if the seed cannot cover it.
  6. The position is stored with the current fee index as its snapshot, the receipt is minted, health must be at least 1 (Unhealthy), and the position's liquidity must be at least minLiq (Slippage).

Emits Opened(id, owner, liq, borrowed, debt0, debt1, leverageBps). Use previewOpen for minLiq.

close(uint256 id, uint256 minOut0, uint256 minOut1, uint256 deadline)

position ownerprice guardafter minimum hold

Reverts NotOwner for anyone else, NoPosition for an unknown id, MinHold before openedAt + 5 blocks. Harvests fees, removes the position's liquidity, keeps the debt amounts, pays you max(0, got - debt) per token plus every fee the position earned, and requires the payout to be at least minOut0 and minOut1 (Slippage). The kept tokens, topped up from insurance where needed, are rebuilt into the seed. Burns the receipt. Emits Closed and, if fees were owed, FeesCollected.

repay(uint256 id, uint256 amount0, uint256 amount1, uint256 deadline) payable

position ownerprice guard

Pay down debt with fresh ETH (msg.value == amount0) and HLX. Both are required (OneSidedRepay), each at most the corresponding debt (OverRepay). The amounts are turned into liquidity at the live ratio, capped at the position's borrowed liquidity, added to the seed, and the tokens actually used are subtracted from debt0, debt1 and borrowed. Unused amounts are refunded. Allowed during the minimum hold. Emits Repaid.

collectFees(uint256 id) to (uint256 fee0, uint256 fee1)

position owner

Harvests, pays the owner everything the position has earned since it opened or last collected, and moves the snapshot forward. Nothing here depends on price, so there is no guard and it works during the minimum hold. Emits FeesCollected when the amount is non-zero.

liquidate(uint256 id, address receiver)

anyoneprice guard

Reverts Healthy unless healthFactor(id) < 1e18 at the reference price, and OneLiqPerBlock if a liquidation already ran in this block. Settlement is the same as close, except that 5% of each token's residual goes to receiver and 95% to the owner. The owner also receives the position's fees. The bonus is never taken from the seed or the insurance. Burns the receipt. Emits Liquidated.

fold(uint256 tipBps)

anyoneprice guard

Harvests, pays the caller tipBps of the pending bucket (at most 500, TipTooHigh above), and adds the rest to the seed at the live ratio. Rounding dust goes to insurance. Emits Folded(keeper, added, tip0, tip1).

claim()

anyone with escrow

Payouts inside close, liquidate, repay and collect are sent with a gas cap so a hostile recipient cannot block them. If a send fails, the amount is credited to escrow0 / escrow1 for that address. claim() pays it out with full gas. NothingToClaim if empty. Emits Claimed.

Views

FunctionReturns
previewOpen(amount0, amount1, leverageBps)The position liquidity open would produce at the live price, fee rounding included. Multiply by 1 - slippage for minLiq. Returns 0 for a leverage outside the range.
positions(id)(owner, liquidity, borrowed, debt0, debt1, openedAt, leverageBps, feeSnap0, feeSnap1). Owner is zero for closed or unknown ids.
healthFactor(id)0.9 * V / D at the reference price, scaled by 1e18. Max uint for no debt or no position.
isLiquidatable(id)healthFactor < 1e18 for an existing position.
pendingFees(id)Fees harvested and not yet collected. Fees still sitting in the pool are added at the next harvest.
feeGrowth0X128() / feeGrowth1X128()Cumulative fees per unit of position liquidity, Q128.
oracleSqrtPrice()The reference price right now, as a sqrt price, projection included.
oracleTick() / oracleBlock()Stored reference tick and the block it was last synced in.
seedLiquidity() / totalUserLiquidity()Lendable liquidity, and liquidity labeled to open positions.
reserve0() / reserve1() / pending0() / pending1()Insurance and the seed's unfolded fee share.
escrow0(a) / escrow1(a)Undelivered payouts for an address.
started() / launchedAt() / owner() / lastLiquidationBlock()Lifecycle state.
poolKey() / poolId() / poolManager() / TOKEN()Pool binding.

Events

EventFields
LaunchedsqrtPriceX96, seedLiq, insuranceToken
Seededfrom, liq
Openedid, owner, liq, borrowed, debt0, debt1, leverageBps
Refundedto, amt0, amt1
Closedid, owner, out0, out1
Repaidid, owner, liqRepaid, debt0After, debt1After
FeesCollectedid, owner, fee0, fee1
FeesHarvestedtoPositions0, toPositions1, toSeed0, toSeed1
Liquidatedid, liquidator, receiver, bonus0, bonus1
Foldedkeeper, added, tip0, tip1
Escrowed / Claimedto, amt0, amt1
OwnershipRenouncednone

Errors

ErrorWhen
PriceOutOfBandThe live price is more than 200 ticks from the reference. Wait; the reference closes 240 ticks per block.
MinHoldClose within 5 blocks of opening.
BlockedByLiq / OneLiqPerBlockOpen in a block that had a liquidation / second liquidation in a block.
Healthy / UnhealthyLiquidating a healthy position / opening one that would start below health 1.
NotOwner / NoPositionActing on someone else's position / an id that does not exist.
BadLeverage / BelowMinDeposit / EthMismatch / Expired / SlippageInput validation.
OverRepay / OneSidedRepayRepaying more than owed / with only one token.
SeedTooThin / ZeroLiquidity / RoundingExceedsInputLiquidity edge cases.
NotStarted / AlreadyStarted / ZeroSeed / SeedExceedsBalance / InitPriceOutOfRangeLaunch lifecycle.
InvalidPoolKey / AlreadyInit / NotInitialized / ExternalLiquidityBlockedPool binding.
NonTransferableAny attempt to move or approve a receipt.
MaxWalletExceededToken: a buy above 2% of supply during the first 3 minutes.
TipTooHigh / NothingToClaim / TransferFailed / Reentrancy / BadParams / BadDelta / NotPoolManagerGuards.

Security model

HELIX is a lender that prices everything off the pool it lives in. These are the threats that matter for such a design and the answer built in for each.

ThreatDefence
Flash-loan manipulation: move the pool inside one transaction, act at the fake price, put it backThe reference price cannot move within a block and follows at most 240 ticks per block. Debt is priced at it. Every action refuses to run while the live price is more than 200 ticks away.
Multi-block manipulationHolding a moved price costs arbitrage losses for every block until the reference arrives. The minimum hold, one liquidation per block and no opens in a liquidation block remove the quick round trips that would make it pay.
Capturing other people's feesFees are harvested and split before any principal change. Each position only earns from its own snapshot forward. The fold tip is capped at 5%.
One-directional liquidation blind spotHealth is value-based and falls whether HLX rises or falls.
Draining the seed through shortfallsInsurance rebuilds borrowed liquidity first. The liquidator bonus is paid only from residual equity.
Attaching the hook to a look-alike poolafterInitialize accepts exactly one pool key.
Hostile recipients blocking closes or liquidationsGas-capped payouts with escrow and claim().
Reentrancy, direct callback calls, outside liquiditynonReentrant on every entry point; the unlock callback accepts only the PoolManager; only the hook may add or remove liquidity.
Insider riskSupply minted to the hook, no seed-withdrawal function, owner can only launch once and renounce, no tax and no treasury.
Launch sniping2% of supply per wallet on buys for the first 3 minutes.

Evidence

The Foundry suite has 29 tests. It checks that open and close round trips never shrink the seed (fuzzed); that manipulated opens and closes revert; that the reference price is rate-limited across blocks; that a copy of the contract with the guard disabled leaks value under the manipulation attack while the guarded contract moves nothing; that nobody can close, repay, collect on or liquidate someone else's healthy position; that receipts cannot be moved or approved; that the unlock callback cannot be called directly; that outside liquidity is blocked; that fees split pro rata by liquidity and pay out on collect, close and liquidation, always to the owner; that positions opened later get none of the earlier fees; that a 5x position opens at health 1.125 and is liquidatable inside its band; that liquidation works in both price directions; that only one liquidation runs per block; that repay lowers debt and raises health; that unused deposit is refunded; that minimum hold, minimum deposit, deadline and leverage bounds are enforced; that the launch cap works and lifts; that burns reduce supply; and that launch is one-shot with no seed-withdrawal path. A separate RPC smoke test drives the exact calls the website makes against a local chain.

Risks

Robinhood Chain notes

Integrating

Opening a position (ethers v6)

const hook  = new ethers.Contract(HOOK, HOOK_ABI, signer);
const token = new ethers.Contract(HLX,  ERC20_ABI, signer);
const a0 = ethers.parseEther("0.1"), a1 = ethers.parseEther("9000"); // pool ratio
const leverageBps = 25000; // 2.5x, allowed 11000 .. 50000
const deadline = Math.floor(Date.now() / 1000) + 600;
await (await token.approve(HOOK, a1)).wait();
const liq = await hook.previewOpen(a0, a1, leverageBps);
await hook.open.staticCall(a0, a1, leverageBps, liq * 99n / 100n, deadline, { value: a0 }); // surfaces reverts
const tx = await hook.open(a0, a1, leverageBps, liq * 99n / 100n, deadline, { value: a0 });
// later
await hook.collectFees(id);

Reading the pool price

const slot = ethers.keccak256(ethers.concat([poolId, ethers.zeroPadValue(ethers.toBeHex(6n), 32)]));
const data = BigInt(await poolManager.extsload(slot));
const sqrtPriceX96 = data & ((1n << 160n) - 1n);
const hlxPerEth = (Number(sqrtPriceX96) / 2 ** 96) ** 2;

Liquidation prices for a position

const p = await hook.positions(id);
const T = 0.9, l = Number(p.liquidity), a = Number(p.debt0), b = Number(p.debt1);
const s = Math.sqrt(T * T * l * l - a * b);
const pLow = ((T * l - s) / a) ** 2, pHigh = ((T * l + s) / a) ** 2; // HLX per ETH

Measuring the fee rate

// feeGrowthGlobal0X128 / 1X128 live at pool state slot + 1 and + 2
const base = BigInt(slot);
const g0 = BigInt(await poolManager.extsload(ethers.toBeHex(base + 1n, 32)));
const g1 = BigInt(await poolManager.extsload(ethers.toBeHex(base + 2n, 32)));
// sample twice, dt seconds apart: fees per unit L = (dg0 + dg1 / P) / 2**128, value per unit L = 2 / (sqrtPriceX96 / 2**96)
// APR = fees / value * (365 * 86400 / dt); a position at leverage m earns m * APR on equity

The current EVM block on Robinhood

const b = await provider.send("eth_getBlockByNumber", ["latest", false]);
const evmBlock = parseInt(b.l1BlockNumber, 16); // compare with positions(id).openedAt

Deploying

# simulate against Robinhood Chain; broadcasts nothing
forge script script/Deploy.s.sol:Deploy --rpc-url https://rpc.mainnet.chain.robinhood.com --sender YOUR_ADDRESS

# real deploy: the same command plus --broadcast and your wallet configuration
# optional: SEED_ETH_WEI, SEED_HELIX_WEI, START_TICK

The script predicts the token address from the deployer's nonce, mines the hook salt, deploys the hook and then the token, calls launch, and writes web/deployments/4663.json for the site. ETH sent above what the ratio needs becomes ETH insurance. After launch: verify both contracts on the explorer, watch a few positions open and close, then call renounceOwnership().

FAQ

Is this a leveraged long on HLX? No. It is a leveraged liquidity position. It earns more fees and carries more impermanent loss. If you want price exposure, hold the token.

Do I earn anything while the position is open? Yes. Your share of every swap fee, on your whole liquidity, borrowed part included. Collect at any time, or receive it when you close.

Does my debt grow? No. It is two fixed numbers written at open. There is no interest.

Why do I need both tokens? A full-range LP position always holds both. The app matches the HLX amount to your ETH at the live price, and any excess is refunded.

Why can I not transfer my position? Receipts are soulbound by design. No secondary market, no hidden owners, no way to sell a position to someone who does not understand it.

What is PriceOutOfBand? The pool moved faster than the reference price is allowed to follow. Wait a few blocks; the app shows the remaining distance.

Why can I not close yet? Minimum hold of 5 L1 blocks, about a minute. You can repay and collect fees in the meantime.

What happens if I get liquidated? Your liquidity is removed, your debt is kept, 5% of what remains goes to the liquidator and 95% to you, along with your fees. It triggers while 10% of position value is still yours, so it is rarely a total loss.

Can the team change the fee, pause the protocol or take the seed? No. Those functions do not exist. The owner can launch once and renounce.

Where do the fees go? Entry fee: ETH to insurance, HLX half to insurance and half burned. Trading fees: 0.30% to positions and the seed by liquidity, 0.20% to the treasury that buys and burns HLX, plus the creator fee on launched coins. Liquidation bonus: to the liquidator. No team wallet.

Can I launch my own coin? Yes. The Launch page deploys a token, a pool and a leverage hook in one transaction and gives up ownership in the same block. See Launchpad.

My coin launched somewhere else. Can it get leverage here? Yes. The List page opens a HELIX pool for a coin that already exists. See Listing a coin.

Can someone open a second pool for a HELIX coin and charge traders more? Not on HELIX. The listing factory refuses any coin that was launched here. Anyone can still open a plain Uniswap pool anywhere, as for every coin on every chain, but it is not a HELIX pool: it cannot move the HELIX price, cannot liquidate anyone, is not shown on this site, and the treasury never trades through it.

What is the seed, and who owns it? Liquidity the hook owns outright and lends from. Nobody can withdraw it. It grows from fees, repayments and donations.

Why is there a 2% cap for the first 3 minutes? So that bots cannot buy the opening. It applies to buys from the pool only and switches itself off.

Has it been audited? No. It has 29 tests, including a replay of the manipulation attack against a guard-disabled copy. Treat it as experimental software.

Glossary

TermMeaning
Liquidity unit, LUniswap's measure of a position's size. For a full-range position at price P, ETH held is L / sqrt(P) and HLX held is L * sqrt(P).
SeedThe hook's own liquidity, the lender of last and only resort.
Equity liquidityThe part of your position you paid for.
Borrowed liquidityThe part relabeled from the seed. Returned when you close.
DebtTwo fixed token amounts, ETH and HLX, owed for the borrowed liquidity.
Health0.9 * value / debt at the reference price. Below 1 means liquidatable.
Reference priceThe hook's rate-limited price. Also called the oracle in the code.
Guard bandThe 200 ticks around the reference within which the live price must be for actions to run.
TickUniswap's price unit. One tick is a 0.01% price step; 100 ticks is about 1%.
HarvestCollecting accrued fees from the pool and splitting them between positions and the seed.
FoldCompounding the seed's fee share into the seed. Anyone can do it for a tip.
InsuranceThe ETH and HLX reserves that rebuild the seed when a position comes back short on one token.
EscrowWhere a payout waits if it could not be delivered to your address.
ReceiptThe soulbound ERC-6909 token that represents your position.