Overview
This is a proprietary trading firm on Solana. You buy an evaluation, you trade a simulated account priced from real on-chain data, and if you reach the target without breaking the risk rules the account becomes funded and pays out a share of subsequent profit. That part is conventional. What is not conventional is that every execution ships a signed receipt carrying the on-chain anchor it was priced from, so anyone with a public RPC endpoint can recompute the price and get the same answer down to the wei.
The product prices one microstructure and prices it exactly. Memecoins is native Solana memecoins, marked against the real state of a bonding curve or a PumpSwap pool at a given block — the impact you pay is the impact that existed, not a modelled approximation.
Two processes run at all times. The engine owns the database and does all the work: marking positions, checking risk, firing triggers, executing the order queue, watching the escrow contract, paying out. The web server does none of it. It queues and it reads.
How a fill is priced
The server never picks a price. It picks an anchor, and the anchor determines the price. There are exactly two anchor types.
Equities: the first Pyth update after the order
When an order is accepted, the engine stamps it with a server timestamp. The fill price is then the first round published by that instrument's feed whose updatedAt is strictly greater than serverTs. Nothing else is eligible. This is a batch auction: because the price you get is published after your order exists, front-running it is structurally impossible, and no artificial execution jitter is needed to pretend otherwise.
The universe is roughly a thousand US tickers, and the cadence is one update per second while the market is open. Each update is a message signed by the Wormhole guardians: the receipt carries those bytes, so its price is checkable offline — no RPC, no archive window, nothing that can expire. The resolver walks forward (waiting for the next update) as well as backward (for an order stamped in the past) to find the first eligible one rather than a convenient one. Outside the session the publish time simply stops advancing: that is a closed market, not a broken oracle, and market orders are refused while limits stay armed.
Memecoins: the state of the pool at block N
For a meme, serverTs resolves to a block: N is the first block whose timestamp is greater than the order timestamp, and when several blocks share a timestamp the lowest eligible number wins. Every piece of state is then read at blockTag: N, and the price impact of your size is computed against that state rather than modelled.
On a bonding curve, that means reading the virtual reserves, the real quote reserves, the graduation threshold and the fee, then replaying the curve contract's own quote functions, including the cap at the graduation threshold. On a graduated token it means reading slot0, liquidity, the tick bitmap and the ticks straight out of the pool manager, then running the full swap loop with tick traversal. The curve and the pool are never hardcoded: they are resolved on chain from the factory and confirmed by the creation and migration logs, whose transaction hashes go into the receipt so you can check the resolution too.
Above the slippage guard rail the market order is refused. It is never partially filled at a worse price, because a receipt that has been emitted must reproduce exactly, and a clipped fill would not. The cap value is on /rules. One more refusal worth knowing about: only pools quoted in ETH or WETH are supported, since USD conversion goes through the ETH/USD feed. A pool quoted in a stablecoin is rejected rather than converted approximately.
The receipt and how to verify it
Every fill writes a receipt. The body is serialized deterministically (sorted keys, no whitespace, integers as strings, no floating point value permitted), hashed with keccak256 and signed. Amounts are decimal strings with exactly eighteen decimals, so the verifier compares strings and a match is exact.
{
"formulaVersion": 1,
"orderId": "...", "accountId": "0x...",
"ticker": "NVDA", // equity leg
"mint": null, // meme leg: the token address
"side": "buy",
"sizeUsd": "500.000000000000000000",
"filledSizeUsd": "500.000000000000000000",
"serverTs": 1785248455,
"anchor": {
"type": "chainlink",
"feed": "0x...", "roundId": "...", "answer": "...", "updatedAt": 1785248512
},
// or: { "type": "pool", "block": N, "pool": "0x...", "sqrtPriceX96" | "reserves", "feeBps" }
"fillPriceUsd": "192.780000000000000000",
"qtyRaw": "2593630044610436767",
"qty": "2.593630044610436767",
"slippageBps": 0, "slippageCapBps": 800,
"spotPriceUsd": "192.780000000000000000",
"signer": "0x...", "payloadHash": "0x...", "sig": "0x..."
}formulaVersion pins the pricing formula. It exists so that a future change to the maths cannot silently invalidate old receipts: a verifier that does not know a version refuses the receipt instead of guessing. Version 1 is what everything currently emits.
Verification does two independent things, and both must pass:
- Cryptographic. Recompute the canonical hash, recover the address from the signature, compare it to the declared signer.
- On chain. Re-read the anchor, recompute the price, the quantity, the spot and the slippage from that state, and require exact equality.
The second check is the one that matters. A receipt that has been tampered with and then re-signed by a different key has a perfectly valid signature; only replaying the chain exposes it. Strict anchor mode, which is the default, also verifies that the anchor was the first eligible one: the previous round must predate the order, and so must block N minus one.
Two ways to run it. The /verify page does the whole thing in your browser against an RPC endpoint you choose; nothing is sent to our server, which is the point. Or from a clone of the repository:
node packages/fills/cli/verify.mjs <receipt.json> <rpc-url> # --signer 0x… pin the expected signer # --loose disable the strict first-eligible-anchor check # --json machine readable output # exit code 0 = verified, 1 = rejected, 2 = usage
One honest limit. Verifying a meme receipt re-reads pool state at block N. The public RPC serves roughly the last 5 million blocks, about six days, and beyond that it answers with a missing trie node error; you then need an archive endpoint. Equity receipts have no such window, because Pyth updates are immutable and getRoundData is enough forever.
The risk engine
The risk engine is a pure module: no I/O, time and prices enter as parameters. That is why every rule below is a deterministic unit test rather than a paragraph of intent.
Trailing drawdown on realized profit only. The high water mark moves up when you book profit, never when a position is merely up. Paper gains do not raise your floor, which means a position that runs and gives it all back does not leave you closer to death than before you opened it. The floor sits a fixed percentage under that realized high water mark; the percentage is per track and per plan, on /rules.
Daily loss, re-anchored at 00:00 UTC. At the daily rollover the engine freezes a reference equity, using the last known price, and the daily floor is a percentage under it. On the equity track this deliberately includes the opening gap: if a position gaps down over a weekend, the whole gap counts against the daily limit on Monday. Pretending otherwise would make the limit meaningless exactly when it matters.
Position caps by instrument. Exposure to a single instrument is capped as a share of equity at order time. On the meme track there is a second, tighter cap for tokens still on a bonding curve, and a liquidity downgrade rule: a graduated token whose observed liquidity has fallen under the floor is sized under the bonding cap instead. The rule follows the liquidity, not the label.
Breach is checked continuously, not only at order time. Every tick marks every open position and re-evaluates both floors. A position bleeding overnight with nobody watching can and will breach the account. Conversely, an account is never killed on a missing or stale mark: if the engine cannot price something, it does not get to conclude you lost money on it.
A breach records the reason, the day, the instrument, the equity, the floor it crossed and the deficit. It is not a status change, it is a receipt of its own.
Server-side triggers
The engine runs a continuous loop, roughly every two seconds. Each pass marks every position of every live account against real mainnet prices using the same maths the fill engine uses, handles the UTC rollover, checks drawdown and daily loss, evaluates triggers, drains the order queue, promotes accounts that have passed, and services the on-chain watcher and the payout queue.
This is worth stating plainly because the obvious alternative is common and bad. The reference product in this category evaluates stop-losses with a three second interval inside the browser: no open tab, no stop-loss. Here a trigger that is crossed queues an order stamped with the moment of the crossing, on the server, whether or not anyone is connected. Our proof run fired one with the session revoked for twelve minutes.
Everything the browser needs afterwards arrives over server-sent events: marks, fills, orders, triggers, breaches, promotions, payouts. The front end does not poll the product API.
Order types
Market, limit, stop-loss and take-profit. Stops and take-profits are reduce-only and sized by quantity, so they can only close what you actually hold and cannot flip you into a new position by accident. Version 1 is long only on both tracks, with no leverage. There is no time limit on an evaluation and holding overnight is allowed.
An order is validated against the ruleset, queued, and executed by the engine. The API waits briefly for the fill and then answers either filled with the receipt, or pending.
The account lifecycle
An account moves through evaluation, then funded, then either breached or a series of payouts. Buying an evaluation is a single on-chain transaction against the escrow contract; the event it emits is the account creation, watched and spawned by the engine. There is no payment to match and no quote to expire.
Promotion resets everything. Balance, equity and the high water mark are re-anchored to the starting balance, every position is flattened, every counter is zeroed and the cycle index increments. The evaluation profit is not paid out: it proved competence, it did not create a debt. The account then waits out the funding activation period before it can trade again.
At promotion the Merkle root of the account's fill journal is committed on chain and a Certificate of Funding is minted as an ERC-721 whose metadata carries that root. The certificate is not a shareable image, it is a commitment: given the certificate and the fill journal, anyone can prove that a given receipt belongs to that run and that the journal was not edited afterwards. Account identifiers are 32 byte values used as-is on chain, so reconciling an account with the registry, the certificate and the escrow requires no derivation.
A breach is terminal. The account stays readable forever, with its reason and its numbers, but it does not reset and it cannot be revived. Passing again means buying another evaluation.
Payouts
A payout request always returns a full quote, whether or not it is eligible. Every blocker is listed individually with the numbers behind it: how many winning days out of how many, how many hours are left on the cooldown, how much new profit this cycle is short by, what your best day represents. You never receive an opaque refusal.
The gates, in the order they are checked:
- the account is funded and past its activation period;
- a payout wallet is set (it can differ from the trading wallet);
- the cooldown since the last paid payout has elapsed;
- enough winning days above the daily threshold;
- enough total profit since promotion, and enough new profit this cycle;
- the consistency rule: your best single day, as a share of the cycle's profit, must sit under the limit for that payout number. The limit loosens as the payout number rises and eventually stops applying. One lucky day is not a track record;
- the amount clears the plan minimum and sits under the cap, which is the plan base cap plus a bonus per subsequent payout, itself capped.
All of those thresholds are on /rules, per plan. Payouts are discretionary within these published rules, and no settlement time is guaranteed; see Terms. Each paid payout closes a cycle and opens the next one, which is why new profit and consistency are measured from the last payout rather than from the start.
What we deliberately do not do
Most of the design here is a reaction to a specific product that got a lot right and a few important things wrong. Being explicit about the trade is more useful than claiming there was none.
| What we refused to copy | What we do instead |
|---|---|
| Evaluate stop-losses in the browser, on a three second interval. | A continuous server loop marks every open position and fires triggers with no client attached. Close the tab, close the laptop, revoke the session: the stop still fires. |
| Model slippage from a constant plus a curve fitted to pool size. | Compute the real impact against on-chain reserves at block N, plus the actual protocol fee. Past the guard rail the order is refused, never quietly clipped, because a receipt that was emitted must always reproduce exactly. |
| Match payment by memo plus a webhook, behind a quote that expires. | buyChallenge() on the escrow contract is the challenge creation. There is nothing to match and nothing to expire. |
| Accept any POST that carries a wallet address in its body. | A proven SIWE session is required on every write. Reads stay public, because public accounts are the product. |
| Print floating point artefacts on the rules page of a firm that sells trust in its arithmetic. | Fixed-point integers end to end. Every amount is served twice: a number to draw with, and an exact eighteen decimal string to compare with. |
| Restate the rule grid by hand in the handbook, the plan cards and the rules page. | One module, served by /api/rules, rendered by /rules. This handbook explains mechanisms and links out for every threshold. |
| Serve a static prerender with dead prices and empty leaderboards. | Server-sent events for everything that moves, and a loading skeleton rather than a dash when a value has not arrived yet. |
| Link terms and disclaimers in the footer, then return 404 in production. | Real legal pages, restricted jurisdictions stated up front, payout discretion written into the published rules. |
None of that makes the trading easier. The evaluation is hard on purpose, the fee is at risk in full, and the accounts are simulated. What it does mean is that when a fill goes against you, you can find out exactly why, from the chain, without asking us.