SpiralFi
Docs The well X

Docs

SpiralFi loops tokenised real-world assets on Robinhood Chain. This page is the whole mechanism, in the order it happens.

What it is

A Spiral is a vault. It holds one tokenised asset, borrows USDG against it, and keeps its exposure at a fixed number of turns. You deposit dollars and hold a share of the loop.

Three things follow from that shape, and they are the whole design:

The loop

Done by hand, looping is five to ten transactions: supply, borrow, swap, supply, borrow, swap. Each pays gas, each moves the pool, and the last buy is at a worse price than the first.

It is one piece of arithmetic. Deposit a dollar, borrow l against it, buy, borrow l², buy again — after d turns you hold

L(d, l) = 1 + l + l² + … + l^(d-1)

dollars of exposure per dollar of equity. The vault borrows L − 1 and makes one swap. Same position, one transaction, one price.

Depth and what it costs

Each turn borrows at 75 % loan-to-value, so the depths open at launch come to:

depthexposureborrowed per dollarthe loop's own LTV
1 turn1.00x$0.000 %
3 turns2.31x$1.3156.8 %
5 turns3.05x$2.0567.2 %

The carry is the borrow rate on the borrowed part — rate × (L − 1) — read from the reserve at that moment, not from a brochure. It moves with how much of the reserve is in use. The console shows it before you agree to anything.

A deeper loop is not a better one. It is more exposure to the same price and more borrowing against it, and the page shows you both numbers next to each other on purpose.

Shares

Depositing mints shares of the vault's equity — collateral valued at the window price, minus what it owes the reserve. Shares are minted on the equity your deposit actually added, measured after the swap, so a large ticket pays its own slippage and the holders already in the vault are untouched by it.

The vault counts its own collateral in a state variable rather than reading its balance, so sending tokens to it cannot move anybody's share price.

Trance

A note is

commitment    = Poseidon(nullifier, secret, vaultId, classId)
nullifierHash = Poseidon(nullifier, 1)

inserted into a twenty-level Poseidon tree. Spending it proves, in zero knowledge: some unspent leaf of this tree, on this vault, at this size, whose secrets the caller knows. The proof is built in your browser; the two secrets never leave the tab.

There are three sizes and no more. Every extra size splits the crowd an exit hides in, and the size of that crowd is published rather than guessed at.

The proof also binds one field element the contract computes from the recipient, the relayer, the relayer's fee, the minimum out and the deadline. Lifting a proof out of the mempool and pointing it somewhere else changes that element, and the verifier rejects it.

A note is spent whole, and the string is the only copy. There is no account to recover it from — which is the same sentence as “no account to read”.

Wake

Price moves, leverage drifts. Selling x dollars of collateral and repaying the proceeds leaves equity where it was and lowers both sides, so the leverage after a sale of x is (C − x) / E. Setting that to the target gives

x = C − L·E

directly — no iteration, no turn-by-turn unwind. wake() pays the caller 0.10 % of what it moves and refuses to run at all while the loop is inside its band.

Past the hard ceiling on debt (88 % of collateral) it stops being routine: the cooldown is ignored, the sale is floored more loosely, and it unwinds all the way down rather than back to target. A crash is exactly when spot and the window disagree, and a tight floor at that moment does not protect anybody — it locks the door.

tighten() is the other direction, for when the price moved in the loop's favour.

Market hours

A tokenised share trades on chain every second of the week. The company it tracks does not. The contract knows the New York session — 09:30 to 16:00, Monday to Friday, with the United States daylight-saving rule applied properly — and while it is shut:

Market holidays are not modelled: there is no way to know them on chain and a hard-coded table would be wrong within a year.

The reserve

Lenders put USDG in; the vaults are the only borrowers, and they borrow against collateral the protocol itself is holding. The rate is the usual kinked curve — 2 % idle, 9 % at an 85 % kink, 75 % at the top — which is what makes a deep loop stop paying before it empties the pool. A tenth of the interest goes to SPIRAL stakers.

$SPIRAL

Every fee is taken in USDG and paid to the staking contract: 0.20 % on the way in, 0.20 % on the way out, and a tenth of the interest the loops pay. Staking receives those dollars and opens the deeper loops. Holding does nothing.

One billion, minted once in the constructor. No owner, no setter, no pause, no blacklist, no mint path. At the open the token takes 20 % on a buy and 20 % on a sell for sixty seconds counted from the first trade, with a 2 % wallet cap over the same sixty seconds; both end by themselves and there is no function that could extend either.

Price

Every number the protocol risks money on comes from the asset's own USDG pool, as a half-hour time-weighted price. slot0 — the price after the last swap — is never used for a decision: one transaction can put it anywhere.

A feed also publishes how far spot sits from the window. When the two disagree by more than 5 %, the protocol declines to open or rebalance until the pool settles. Exits are deliberately not gated that way: a gate on the way out is a lock.

Sizes and limits

The caps are sized against the pool that exists, not against ambition. Measured on 2026-10-31, the SPY/USDG 0.05 % pool moves 19 basis points on a $7 500 sale and runs out of concentrated liquidity somewhere past $150 000. An exit sells leverage times the equity, so a $5 000 ticket at 2.31x is an $11 500 sale.

limitat launch
equity per loop$50 000
borrowed per loop$100 000
slippage floor, keeper paths2 %
slippage floor, your own paths6 %

Governance raises the first two as the book deepens. Nothing in a vault can raise them, because a vault has no owner.

Who can change what

Everything that shapes a vault — its asset, its depth, its loan-to-value, its band, its fees, its ceiling — is fixed in that vault's constructor and held in an immutable. There is no setter for any of it. A vault with different numbers is a different vault.

What is left is a list, and the registry keeps it:

The registry cannot take money, cannot move a fee, and cannot touch a price feed that has already been written.

Addresses

Published here the moment they exist on chain, and read by this page from the same file the deploy script writes.

SPIRAL—
pool—
registry—
reserve—
oracle—
trance—
staking—
verifier—

The proving key, the circuit and the ceremony beacon are served from this origin: vkey beacon