Skip to content
All docs

Verification table and browser walkthrough

Historical snapshot. Sections 1–3 were recorded at commit 301157e (301157e799408343a48e28a05344d6c657f6e667), which the live site reported on 2026-10-09 13:01 UTC. The site has been redeployed since: the current commit is whatever https://imprest-chi.vercel.app/api/version reports. Later updates are dated sections below.

Evidence types:

  • LOCAL: agent-executed tests on this machine.
  • SCRIPT: agent-sent testnet transactions.
  • BROWSER: the owner, in the live site.
  • READ: chain state read by the agent.

1. Verification table

#ItemTypeProcedureExpectedActualEvidenceOpen dependency
1Closed desk on Dashboard, Claims, RiskBROWSER + LOCALOwner screenshots; audit-fixes.spec.ts with trader 0x976C…25fESettlement card, no risk or eligibilityPASS (owner screenshots; 3 tests)web/e2e/audit-fixes.spec.ts—
2Closed desk on /app/deskLOCALSame spec: "desk page shows the settlement…"No −100%, no progress, no Graduate, tier marked "closed at this tier"PASSsameOwner re-check on the live site
3Closed desk on /app/trade/btcLOCALSame spec: "trade shows one settled-desk message…"One message; no risk meter, estimates, breach text or submitPASSsameOwner re-check on the live site
4Sign-out clears data immediately, including across tabsLOCAL + BROWSERSpec (fails on the old hook); owner cross-tab checkNumbers gone without a refreshPASS (owner confirmed both tabs)same—
5History without the indexerLOCALpages.spec.ts no-indexer test (runs in CI)Loads; says how far back it reaches; links to the explorerPASSCIFull history needs a hosted indexer
6Graduation succeeds when eligibleLOCALforge test --match-test graduat (Perpl bytecode)Tier 1, credit fundedPASS (test_fullLifecycle…, _graduate helper)proof/local/graduation-claim-tests.log—
7Graduation rejected when premature, not flat, too few trades, under 24 h, or pool pausedLOCAL + SCRIPTforge; testnet premature-rejection receiptNamed revertPASSlog; testnet_premature_graduation_rejected.json—
8Graduation with insufficient pool liquidityLOCALtest_graduation_insufficientPoolLiquidityRevertsAndLeavesDeskUnchangedInsufficientIdle(credit, idle); desk unchangedPASSlog—
9Real graduation on testnetSCRIPTKeeper graduate() on desk 0xfc65…FCC6Tier 1, 400 AUSD creditPASS: tx 0x4e616c99…87edproof/receipts/testnet/graduation-attempt/05-graduate-by-keeper.jsonNot repeated in a browser (no eligible desk)
10Profitable claim: fees first, exact 80/5/15 split, no double claimLOCALtest_claim_payoutSplitIsExactAfterFeesAndCannotRepeat, test_claim_feesNettedFirstAndBookedOnce, test_claim_hwmPreventsReclaimAfterLossAndRecoveryExact amounts; second claim NothingToClaimPASSlogA testnet claim needs a desk with genuine profit (none exists)
11Eligible testnet desksREAD, block 69545167riskState of all 3 desks—None eligible: 0x1197…c597 closed; 0x4802…bFaD closed; 0xfc65…FCC6 active tier 1, equity 493.713365 vs HWM 502.289849 + fees 0.015997 (8.59 short)this table—
12Relayer handles browser preflightLOCALrelayer unit test; curl -X OPTIONS localhost:8787/v1/intents204, CORS headersPASSrelayer/test/relayer.test.ts—
13Gasless trading from the public site——Relayer reachable over HTTPS from imprest-chi.vercel.appBLOCKED—The relayer runs only on the owner's PC. Making it public needs the owner's approval (section 3).
14Contract suiteLOCALcd contracts && forge testall pass123/123log—
15Browser suiteLOCALPW_PORT=3400 pnpm test:e2eall pass81 passed, 25 skipped by designproof/local/web-e2e-results.json—
16CICIGitHub Actions on push4 jobs greengreen on the latest runsGitHub Actions—
17Deployed commitREADGET /api/versionMatches main301157elive response—

2. Browser walkthrough (owner)

Use the account you already have (0x976C…25fE); it holds testnet AUSD and MON. Gas figures are measured from your own earlier transactions at about 102 gwei. Monad charges the full gas limit, and the app sets that limit at 1.15× a simulated estimate. You approve each transaction with your passkey; nobody else needs your passkey or any key.

Flow A: re-check the closed-desk fixes (no transactions)

  1. https://imprest-chi.vercel.app/app/desk: expect "Desk closed", 99.943818 AUSD returned,

−0.056182 AUSD net, Evaluation "(closed at this tier)", no Graduate button and no −100%.

  1. https://imprest-chi.vercel.app/app/trade/btc: expect one message, "This desk is closed and settled",

and no risk meter, estimates or buy/sell button.

Flow B: a new desk, one small trade, direct (you pay gas)

StepWhereActionTokensGas, you payExpected result
B1/app/desk"1. Approve AUSD" for 100none move (approval only)about 0.005 MONAllowance 100 AUSD for DeskFactory 0x1569…c544
B2/app/desk"2. Open desk", demo cohort, 100 AUSD100 AUSD into your new desk's Perpl accountabout 0.59 MON (measured: 5.82M gas)A new desk appears on the dashboard, Active, Evaluation
B3/app/trade/btcLong, IOC, size 0.001, leverage 3x, default limit pricenone (margin stays in the desk)about 0.07 MON (measured: 0.069)Position 0.001 BTC long; Fills shows "limit … · mark …"
B4/app/trade/btcShort, reduce-only, size 0.001noneabout 0.06 MON (measured: 0.056)Position flat; one closed trade
B5 (optional)/app/claims"Close desk"your remaining stake returns to your walletabout 0.045 MON (measured: 0.044)"Desk closed" with the exact amount returned

Stop at any step. Before B2, the app's preview shows the stake. Before B3, it shows notional, margin, fee, and distance to the nearest limit.

Flow C: gasless trade (only after section 3 is done)

StepActionTokensGasExpected result
C1On an active desk, /app/trade/btc, choose "Relayer (signed intent)", Long 0.001 BTC IOC 3xnoneyou pay nothing; the relayer 0x017F…b728 pays about 0.07 MONPosition 0.001 BTC; on Monadscan the transaction's sender is the relayer, not you
C2Same, Short reduce-only 0.001nonerelayer pays about 0.06 MONFlat

Flow D: graduation and claim in the browser

Only if a desk genuinely reaches +2% over two closed trades (graduation), or realized profit above its high-water mark (claim). The app shows progress on /app/desk and eligibility on /app/claims. Nobody should trade just to produce this.

3. Making the relayer public (needs your decision)

The relayer runs on your PC at localhost:8787, which the public website can't reach. Options:

  1. Free and temporary: a Cloudflare quick tunnel from your PC. Run

cloudflared tunnel --url http://localhost:8787 (no account, no cost). It prints a https://…trycloudflare.com address. It works while your PC is on and that command runs; the address changes on every restart. I did not start it myself: opening a public entry point to your PC needs your explicit approval.

  1. Permanent: host the relayer on a small server (Railway, Fly.io or a VPS; usually a few dollars a

month and an account in your name). It needs the relayer key in that server's environment.

Then, for either option:

  • In Vercel → Project → Settings → Environment Variables, add NEXT_PUBLIC_RELAYER_URL set to the

HTTPS address, and redeploy (it is baked in at build time).

  • Check: https://imprest-chi.vercel.app/status should show "Relayer: operational".
  • The relayer wallet holds about 4.83 MON, enough for about 65 relayed trades. Abuse is limited: only

registered desks, valid trader signatures, simulation before sending, 20 intents per trader per minute (RELAYER_QUOTA_PER_TRADER_PER_MIN; lower it for a public relayer), and a gas cap.

4. Update 2026-10-12: owner walkthrough on desk 0x640a…5DBf

What the owner did in the live site (OBSERVED by the owner; transactions checked on Monadscan):

StepTransactionResult
Approve 100 AUSD0x53ca5b57…26e093Allowance 100 AUSD, read back on the second RPC at block 70360557
Open desk0x16172296…483a15Desk 0x640ac0F4a2191Ae5A9522291fE4bca786d515DBf, Perpl account 1127, demo cohort
Open 0.001 BTC long0x24324645…d8bc50Entry 83,448.6
Close (manual short, reduce-only)0x84f6914a…dfc175Flat; gas 0.058 MON
Profit claimnoneEquity 99.911529 vs HWM 100: no profit, so the button was correctly disabled

Claimable desks for wallet 0x976C…25fE (READ, second RPC rpc-testnet.monadinfra.com, block 70376530):

DeskStatusFlatEquityHWMFeesEligible profit
0x480227b5a9c4ef965dc61ee60A5A666A336bbFaDClosedyes0100.0000000— (closed)
0x640ac0F4a2191Ae5A9522291fE4bca786d515DBfActive, Evaluationyes99.911529100.00000000: not claimable

There is no genuinely profitable desk this wallet owns, so a browser profit claim cannot be performed today without new profitable trading. Nothing was traded or changed to manufacture one. Contract tests cover the claim instead (section 1, plus test_claim_zeroProfitRejected).

Fixes from this walkthrough (agent-executed tests at the commit that adds this section):

ItemFixTest
"Close" did nothingIt was a link to the page you were already on, with no pre-fill. Now a Close position button (sticky in the table, full-width on phones) pre-fills opposite side, current size, reduce-only and IOC, scrolls to the ticket and says what was prepared. Never auto-submits. The position is re-read before signing (already flat: nothing sent; size changed: updated and asks again).close-position.spec.ts (BTC long/short, ETH long, keyboard, 390 px, flat desk, desktop reach without horizontal scroll). The position read is intercepted; nothing is sent.
Receipt showed from-primary-receiptExpected values are now filled from the primary receipt's TradeExecuted event (numeric position size, filled lots, full/partial/none) and compared with Perpl getPosition on the second RPC at the same blocktypecheck; next real trade shows numbers
"Policy check BLOCKED" after a successful closeAfter a verified full close: "Position closed", reduce-only reset, no stale block. Partial and no-fill outcomes are explained.code; next real close
Several desks per accountA desk picker appears when an account has more than one desk, remembered per accountaudit-fixes.spec.ts picker test
Graduation called twice; zero-profit claimMaxTierReached, credit unchanged; NothingToClaimforge test 125/125

Still open:

  • browser profit claim (needs genuine profit);
  • browser graduation (needs +2% over 2 closed trades);
  • browser gasless trading (needs a publicly hosted relayer);
  • full hosted history (needs a hosted indexer);
  • a real partial IOC fill observed in the browser;
  • the five remaining phone tabs.

5. 2026-10-13: fixes for the 2026-10-12 external audit (IP-001 to IP-017)

All are code fixes with local tests. No transaction was sent, no accounting or threshold changed, and nothing was redeployed onchain.

FindingFixEvidence
IP-001 receipt timeout shown as FailedNew unknown state; the hash is saved the moment it is broadcast; My receipts looks it up again; new sends are refused while one is unresolved (web). Mobile keeps the same guard for the app session only, with no storage across restarts.core tests (state machine)
IP-002 reversal shown as no fillFill = change in the SIGNED position just before and at the trade block, measured on the primary and re-read on the second RPC; History reads the samecore tests (flip, partial flip, exact close, no fill); contract test test_reversal_equalUnsignedSizesStillTraded
IP-003 LP totals omit enforcing / older desksEnforcing desks included until settled; up to 500 desks read; partial coverage labelledsource
IP-004 one provider could stand in for twoConfirmations use the primary only (no fallback); if it is down the badge says single RPC; badge names pool totals onlysource
IP-005 mobile history 1000-block ranges100-block windows, 4 at a time, one retry, partial disclosureapp typecheck
IP-006 mobile verification compared a constantSame signed-fill verifier as web; same-block receipt checkapp typecheck
IP-007 ?close=abc crashed the terminalOnly an allowlisted market id is accepted; others are removed7 malformed-link browser tests pass
IP-008 claim preview used unrealized equityPreview uses realized equity; "Eligible" only when active, flat and above HWMsource
IP-009 "desk is flat" after one marketOther markets are read before saying sosource
IP-010 "Max safe order" ignored pool caps"Max new order (est.)" = min(leverage, desk cap, market pool cap), names the binding limit; worst-case post-trade equitysource
IP-011 validator accepted a note as readbackVERIFIED needs a method plus a recorded reading; --onchain reconciles all 23 receipt transactions on both providers with decoded outcomesvalidate-evidence --onchain: PASSED, 23 reconciled
IP-012 stale docsEnvironment matrix, contracts README, test counts dated, this file marked as snapshotthis commit range
IP-014 address link target size24 px minimum heightaxe: 14/14 routes pass
IP-015 mobile session racesGeneration guard on passkey prompts, one session at a time, lock after 5 min in background, stale poll results droppedapp typecheck + tests
IP-016 no demo videoOwner action—
IP-017 networkidle fragilityNew tests wait for explicit UI states; older tests unchanged—

Still open, and only genuine market conditions or the owner can close them: a qualifying profit claim in the browser, a new graduation, a real partial fill, a public relayer and indexer, native passkeys, and the demo video.

6. 2026-10-13: Railway relayer preparation and remaining gaps

Commit range 509fa84…d660c0f. Executors: agent-local, meaning tests or scripts on the agent's machine, with no transactions; and owner-browser, meaning the owner signs on the live site. No transaction was sent in this section.

#ItemExecutor / envProcedureExpectedActualEvidence
R1Relayer image builds from a clean cloneagent-local, WSL Dockerfresh clone at 509fa84, docker build -f relayer/Dockerfile .image buildsbuiltbuild output in session log
R2Listens on 0.0.0.0 using PORTagent-local containerPORT=9911, start log, in-container request to 0.0.0.0listening on 0.0.0.0:9911as expectedcontainer log
R3Health and chainagent-local container (throwaway unfunded key)GET /health200, chainId 10143200, chainId 10143, balance 0curl output
R4CORS from the live siteagent-local containerOPTIONS with Origin: https://imprest-chi.vercel.app204, origin echoed204, origin echoed, Vary: Origincurl headers
R5Other origins refusedagent-local containerOPTIONS and POST from another origin403403curl
R6Signature validationagent-local containerforged 65-byte signature for desk 0x640a…5DBf401 INVALID_SIGNATURE401curl, relayer log
R7Wrong chain refusedagent-local containerchainId 143400400curl
R8Replay, deadline, quota, gas capagent-local unit testspnpm --filter @imprest/relayer testpass14/14vitest
R9No secrets in the imageagent-local containersearch the image for .env* and *.keynonenonefind output
R10Hosted on Railwayowner + agent-local probesRailway deploy; agent probed the public domain/health ok; live-origin preflight 204; other origin 403; forged signature 401as expected; wallet 0x017F…b728, chain 10143https://imprest-relayer-production-74f6.up.railway.app/health
R11Gasless trade from the browserowner-browser, testnetowner signed a BTC buy of 100 lots (IOC, 3x) on the live site; the hosted relayer submitted itVerified; tx from the relayer; owner MON unchangedPASS: full fill 0 → +100; relayer paid 0.06570024 MON; owner MON 3.16088747 before and after; identical on both RPCs0xc29c4b1f…635cec, block 70662751; proof/receipts/testnet/browser-gasless/01-open.json
R12Close button on a real positionowner-browser, testnetClose position prefilled a reduce-only IOC sell of 100 lots; owner signed; relayedflat, "desk is flat" messagePASS: +100 → 0, full fill, BTC and ETH flat; relayer paid 0.056251776 MON; equity 99.809207; 2 closed round trips0x76b48df8…fd4514, block 70663809; 02-close.json; proof/local/desk-inventory.md at block 70664122
I1Hosted indexer (Envio)owner + agent-local probesEnvio Development plan, three deployments; endpoint queriedsyncing, block height > 0OPEN: block height 0, 0 events, "Indexing Has Stopped" on every deployment; environment variables (API token) are a paid feature; logs unavailable (504)endpoints 4248d94, b107a7d, cba6cba; docs/HOSTING.md
E1Desk inventoryagent-local script, read-onlydesk-inventory at block 70643979, both RPCsevery claim and graduation condition listedno desk can claim or graduate; both RPCs agreeproof/local/desk-inventory.md
E2Browser profit claimowner-browser——OPEN: no eligible deskE1
E3Browser graduationowner-browser——OPEN: no eligible deskE1
P1Partial fills, deterministicagent-local, Perpl bytecodeIOC 100 vs 40 offered; reduce-only 40 vs 25 bid40 open; 15 remain, not flippedpasscontract tests
P2Live partial fillowner-browser——OPEN (both live trades filled fully). Top of book at block ~70646120 was 2.29 BTC and 38.2 ETH; this desk's whole capacity fills fullyPerpl book API
G1Enforcing desk in LP totalsagent-local unitlpExposure with 400 borrowed and 350 equity50 shortfallpasscore tests
G2Primary RPC downagent-local browserblock /api/rpc/primary, open /lp"single RPC only", never "confirmed on 2 RPCs"passe2e/rpc-failover.spec.ts
G3Claim preview uses realized equityagent-local unitopen-profitable, enforcing, flat, fees-only casesconsistent label and claimabilitypasscore tests
G4Closing one market of twoagent-local unitclosedMarketMessage with ETH still open"not flat"passcore tests
G5Maximum order, three limitsagent-local unitleverage, desk cap, market cap; funded desk 493.71≤ 1000 (desk cap); limit namedpasscore tests
G6Mobile pending record across restart———Documented limitation (memory only)docs/MOBILE.md
G7Stale documentsagent-localbanners on BUILD_STATE, FINAL_AUDIT, SPONSOR_MATRIX, TESTNETmarked as dated snapshotsdonecommits 00feb90…6bce83f
C1CIGitHub Actionspushall jobs greensee the latest run on mainGitHub Actions

Green CI does not establish a browser claim, graduation, gasless trade or phone behavior. Those rows stay open until the owner performs them and the transactions are recorded.

7. 2026-10-13: fixes for the second external audit (IP-018 to IP-024)

Code fixes with local tests; no transaction, deployment or accounting change.

FindingFixEvidence
IP-018 RPC errors read as a dropped transactionLookup failures are never "not found"; nothing auto-releases; the user may release a hash only after both RPCs definitely answer not-found for 10 min (My receipts). Mobile keeps blocking until a receipt is foundcore tests decideFromReceipts, releasable
IP-019 concurrent sendsSynchronous one-at-a-time gate before the first await, plus a cross-tab marker (web); same gate on mobilecore test: two concurrent runs, one send
IP-020 receipt not bound to the orderEvent must match perpId, side, order type, limit, lots, leverage, FOK and the signed nonce; transaction must target the desk (web and mobile)core tests eventMatchesOrder
IP-004 recovery provider scopeRecovery reads the primary without fallback; "mined" only when two distinct providers agreecore tests
IP-008 / IP-010 native previewsNative claims use the shared realized-only preview; native "Maximum position" relabelled as a leverage ceiling that pool caps may lowerapp typecheck and tests
IP-011 validator scopeRequired operation event from the named contract, sender, target and recorded inputs (lots, leverage, limit, nonce, equity) are checked on both providersvalidator PASS on 25 transactions; a tampered size fails
IP-012 relayer copyFAQ, README, KNOWN_LIMITATIONS, ENVIRONMENT_MATRIX: trades are gasless via the hosted relayer; other actions use trader MONcommits e6098ec…1743c06
IP-021 dead docs linkRepository links in rendered docs open on GitHub at the build commitsource
IP-022 indexer freshnessIndexed history used only when the indexer is within 50 blocks of head and under 100 rows; otherwise the labelled chain scansource
IP-023 unknown inputs shown as PASSPolicy is ok only when every check passes; ticket shows INCOMPLETE and cannot submit until mark, positions and policy are knowncore test
IP-024 reduce-only wordingREADME states the fresh-mark floor exception (closing then requires enforce())README

Still open: native durable pending record across restarts (documented), public demo video, owner-verified portal rules, the hosted indexer, and any market-dependent live result (profit claim, live partial fill).