Skip to content

Proof, not promises.

Every number here is generated from an evidence file in the repository and carries the strongest label it has earned: local reproduction, simulation, testnet or mainnet. Unmeasured results say PENDING, never zero.

Headline results

Over-limit order rejected by the contract (testnet)
LeverageExceeded
Testnet verifiedMonad Testnet0x7097805b…34f3fd(opens explorer)

An over-limit (6.00x) order sent directly to Desk.trade() on Monad testnet was mined and reverted by the contract with LeverageExceeded

Earned graduation, keeper-executed (testnet)
400 AUSD credit
Testnet verifiedMonad Testnet0x4e616c99…2587ed(opens explorer)

A desk earned +2% over two closed trades on Monad testnet, graduated to tier 1 and was funded by the pool

Settlement paid by contract (testnet)
99948853 base units; 4.925 s submit-to-verified
Testnet verifiedMonad Testnet0xcf831720…7c208d(opens explorer)

A flat desk was closed on Monad testnet and its residual AUSD was paid by contract; the trader's higher balance was read back on an independent RPC

Completed Monad testnet results, each with its transaction. Mainnet results are not claimed; they are listed under “Not yet proven” and in the mainnet section below.

1. Testnet mechanism proof

An over-limit (6.00x) order sent directly to Desk.trade() on Monad testnet was mined and reverted by the contract with LeverageExceeded
LeverageExceededTestnet verified
Method

Real testnet transaction; receipt and post-state read on an independent RPC at the receipt block

source: scripts/src/testnet-demo.ts

A desk earned +2% over two closed trades on Monad testnet, graduated to tier 1 and was funded by the pool
400 AUSD creditTestnet verified
Method

receipt, Graduated event and desk state read on the independent RPC at the receipt block

source: scripts/src/verify-graduation.ts

The Imprest keeper sent a contract-accepted graduate() on Monad testnet after simulating it
0x4e616c9939a95c0419c32d915dbb964e5074e724fe242a9e963be853f32587edTestnet verified
Method

sender 0x9c4f9b38ce13a1552aa6fcb505cffb3e70fafcc0 is the keeper wallet; receipt success and tier 1 read on the independent RPC; gas 518571

source: scripts/src/verify-graduation.ts

A trader-signed EIP-712 trade intent was submitted to Desk.tradeWithSig by the Imprest relayer (relayer paid gas), filled on Perpl testnet, and the resulting position was confirmed on an independent RPC
0xb2e83f497ed2667296c0c8d9dab07c41557c21d90fc0f1de8558fc9cb344665eTestnet verified
Method

Relayer simulated, then submitted with gas 698427; position lots read on the verification RPC at the receipt block (359) equal the TradeExecuted lotsAfter

source: scripts/src/graduation-attempt.ts

A flat desk was closed on Monad testnet and its residual AUSD was paid by contract; the trader's higher balance was read back on an independent RPC
99948853 base units; 4.925 s submit-to-verifiedTestnet verified
Method

Real testnet transaction; receipt and post-state read on an independent RPC at the receipt block

source: scripts/src/testnet-demo.ts

Perpl's exchange on Monad Testnet has whitelisting disabled, so a desk contract can open its own account without a whitelist step
falseTestnet verified
Method

read-only eth_call at a pinned block on two independent RPC providers; readbacks agree

source: scripts/src/gate0.ts

Perpl's Monad Testnet exchange margins in Agora AUSD (0xa9012a055bd4e0eDfF8Ce09f960291C09D5322dC)
AUSDTestnet verified
Method

read-only eth_call getExchangeInfo() and ERC20 symbol() at a pinned block on two RPCs

source: scripts/src/gate0.ts

Minimum AUSD to open a Perpl account on Monad Testnet
100.00Testnet verified
Method

read-only eth_call getMinAccountOpenCNS() at a pinned block on two RPCs

source: scripts/src/gate0.ts

Share of sampled testnet blocks where Perpl's BTC mark was older than its 60 s max age (when desks refuse new risk)
0/90Testnet verified
Method

read-only eth_call getPerpetualInfo at sampled historical blocks; age = block.timestamp - markTimestamp; window blocks 68991040-68993265, stride 25

source: scripts/src/mark-staleness.ts

Mark age on testnet over the sampled window: median 20s, max 49s (BTC). Desks refuse new risk whenever the mark is older than 60 s.

Not yet proven

Pending items are shown as pending, never as zero. Nothing here is forced: a profit claim needs genuine realized profit above the high-water mark.

Realized profit claimed on Monad testnet and verified on an independent RPC
PENDING
Monad Testnet
Method

Not yet measured. See docs/FINAL_STATUS.md for the gate that produces it.

source: pending

AUSD paid out by contract to real traders on Monad mainnet
PENDING
Monad Mainnet
Method

Not yet measured. See docs/FINAL_STATUS.md for the gate that produces it.

source: pending

Seconds from claim submission to an independently verified wallet balance on mainnet
PENDING
Monad Mainnet
Method

Not yet measured. See docs/FINAL_STATUS.md for the gate that produces it.

source: pending

Pool principal loss beyond first-loss stakes across included mainnet breaches
PENDING
Monad Mainnet
Method

Not yet measured. See docs/FINAL_STATUS.md for the gate that produces it.

source: pending

2. Contract deployments

Network
Monad testnet (chain 10143)
Mainnet
not deployed

3. Guarded order rejection

Frontend validation explains a block before signing. The contract rejection below is the one that matters: the order goes straight to Desk.trade() with no UI involved.

An order one unit over the 5.00x leverage cap (5.01x) is rejected by the Desk contract with LeverageExceeded, and no position exists afterwards
trueLocal reproduction
Method

Direct Desk.trade() call bypassing any frontend; Perpl bytecode harness

source: contracts/test/unit/DeskPolicy.t.sol::test_leverage_oneUnitOverCapRejected

An over-limit (6.00x) order sent directly to Desk.trade() on Monad testnet was mined and reverted by the contract with LeverageExceeded
LeverageExceededTestnet verified
Method

Real testnet transaction; receipt and post-state read on an independent RPC at the receipt block

source: scripts/src/testnet-demo.ts

4. Live venue integration

In a fork of live Monad testnet, a desk opened its own Perpl account, filled an IOC order against the real testnet order book, and closed reduce-only; a 6x order was refused by the contract
100 lots (BTC, 0.00001 BTC each)Simulated
Method

cd contracts && FORK_TESTNET=true forge test --match-contract TestnetFork -vv

source: contracts/test/perpl/TestnetFork.t.sol

Fork run log
[PASS] test_fork_deskOpensRealPerplAccountAndPolicyHolds() (gas: 6535834)
Logs:
live testnet BTC mark (PNS): 829935
mark valid at fork block: true
fork block.timestamp: 1791386710
fork block.number: 69005251
perp markTimestamp: 1791386680
perp oracleTimestampSec: 1791386662
lots filled against live testnet book: 100
desk equity after fill (base units): 99968366
lots after reduce-only close: 0

5. Claim verification

A payout counts only when an independent RPC reads the higher balance. The forced mismatch fixture proves an unverified state can never become success: it ends in verification failed. See packages/core/test/core.test.ts, "forced mismatch fixture ends in verification_failed".

Realized profit claimed on Monad testnet and verified on an independent RPC
PENDING
Monad Testnet
Method

Not yet measured. See docs/FINAL_STATUS.md for the gate that produces it.

source: pending

Seconds from claim submission to an independently verified wallet balance on mainnet
PENDING
Monad Mainnet
Method

Not yet measured. See docs/FINAL_STATUS.md for the gate that produces it.

source: pending

6. Accounting

Independent Python reference model (exact rational arithmetic, no shared code) agrees with contract outputs from real desk executions
26/26Local reproduction
Method

Independent Fraction-based model of PRD v3 rules vs outputs observed from real desk executions on Perpl exchange bytecode rc_v1.1.7-203.

source: proof/reference_model/run_reference.py

accrual
6/6
claims
6/6
settlements
6/6
paired desk identities
8/8

7. Security and invariant tests

Imprest contract tests passing against Perpl's own exchange bytecode (rc_v1.1.7-203) on a local EVM
120/120Local reproduction
local Foundry EVMn = 120 (all contract tests excluding the opt-in testnet fork test)proof/local/forge-test-results.json(opens the evidence file on GitHub)
Method

cd contracts && forge test --json --no-match-contract TestnetFork

source: contracts/test

Stateful invariant campaign (cash-basis pool identity, principal and exposure ledgers, fee subledger and cap, closed desks empty) held across every run
31200Local reproduction
Method

forge invariant runs; coverage: 1300 desks, 723 random trades, 491 graduations, 741 settlements, 1 claims

source: contracts/test/invariant/PoolInvariant.t.sol

ReferenceVectorsTest3/3
  • test_vectors_accrual()
  • test_vectors_claims()
  • test_vectors_settlements()
SettlementMathTest8/8
  • testFuzz_accrue_chunkedEqualsSingle(uint256,uint256,uint256)(512 runs)
  • testFuzz_accrue_neverExceedsCap(uint256,uint256,uint256,uint256)(512 runs)
  • testFuzz_claimSplit_conservesGross(uint256,uint256,uint256,uint16,uint16)(512 runs)
  • testFuzz_waterfall_conservesCashAndOrdersPriority(uint256,uint256,uint256,uint256)(512 runs)
  • test_accrue_prdTier1Month()
  • test_claimSplit_prdIllustrativeMonth()
  • test_waterfall_prdExampleExactTradingLoss()
  • test_waterfall_severeGapExhaustsResidual()
PoolInvariantTest6/6
  • invariant_cashBasisIdentity()(64 runs)
  • invariant_closedDesksHoldNothing()(64 runs)
  • invariant_feeSubledgerAndCap()(64 runs)
  • invariant_idleBackedByCash()(64 runs)
  • invariant_principalAndExposureLedgers()(64 runs)
  • test_handlerCanTrade()
PerplProbeTest8/8
  • test_probe_closeAndWithdrawEverything()
  • test_probe_contractOwnedAccountOpensAndTrades()
  • test_probe_deltaPnlTracksMark()
  • test_probe_fokNoLiquidityReverts()
  • test_probe_iocNoLiquidityDoesNotRevert()
  • test_probe_markGoesStaleAfterMaxAge()
  • test_probe_maxNegPnlRejectsOffMarketOpen()
  • test_probe_venueClampsLeverageAboveListing()
GasProfileTest1/1
  • test_gas_profile()
AttacksTest24/24
  • test_attack_offMarketCloseIntoAccompliceBidRefused()
  • test_caps_bootstrapImmediateOnlyBeforeFactoryWiring()
  • test_caps_evaluationDesksDoNotTouchPoolExposure()
  • test_caps_grossCapAcrossDesksCannotBeBypassed()
  • test_caps_increaseIsTimelocked()
  • test_caps_perDeskNotionalCap()
  • test_caps_reductionAlwaysPossibleEvenOverCap()
  • test_keeper_cannotGraduateIneligibleOrEnforceHealthy()
  • test_lp_cannotWithdrawDeployedPrincipal()
  • test_relay_alteredFieldsRejected()
  • test_relay_expiredDeadlineRejected()
  • test_relay_lowerNonceRejected()
  • test_relay_malleableHighSRejected()
  • test_relay_signatureForAnotherDeskRejected()
  • test_relay_signedOrderStillPolicyChecked()
  • test_relay_validSignedOrderExecutesOnce()
  • test_relay_wrongSignerRejected()
  • test_unauth_deskCannotDrawCreditForAnotherDesk()
  • test_unauth_deskCannotUnderReportExposure()
  • test_unauth_operatorCannotMoveFunds()
  • test_unauth_poolMoneyPathsRequireRegisteredDesk()
  • test_unauth_strangerCannotTradeClaimClose()
  • test_venue_haltedExchangeBlocksTradingButEnforceStatePersists()
  • test_venue_perplErrorsSurfaceNamed()
PairedDeskTest8/8
  • test_paired_calm()
  • test_paired_gap15_cap1000()
  • test_paired_gap15_cap1500()
  • test_paired_gap15_cap2500()
  • test_paired_gap8_cap2500()
  • test_paired_gapDown15_cap2500()
  • test_paired_trend_delayedKeeper()
  • test_paired_trend_keeperEachStep()
ReentrancyTest5/5
  • test_reentry_claimDuringTrade()
  • test_reentry_closeDuringTrade()
  • test_reentry_enforceDuringTrade()
  • test_reentry_graduateDuringTrade()
  • test_reentry_tradeDuringTrade()
DeskLifecycleTest4/4
  • test_evaluationTrade_opensAndClosesPosition()
  • test_fullLifecycle_demoCohort_graduateTradeClaimClose()
  • test_openDesk_ownsPerplAccountWithFullStake()
  • test_predictDesk_matchesDeployment()
DeskPolicyTest28/28
  • testFuzz_leverage_aboveCapAlwaysRejected(uint256)(512 runs)
  • testFuzz_priceBand(uint256)(512 runs)
  • test_control_healthyTraderFiftyTradesNoEnforcement()
  • test_dailyLoss_breachBlocksNewRiskButAboveFloor()
  • test_dailyLoss_measuredFromFirstTradeOfDay()
  • test_dailyLoss_resetsNextUtcDay()
  • test_floor_breachedDeskRejectsNewRiskAndReduce()
  • test_floor_postTradeBreachRevertsWholeTransaction()
  • test_leverage_exactlyAtCapAccepted()
  • test_leverage_oneUnitOverCapRejected()
  • test_leverage_zeroRejected()
  • test_lots_zeroRejected()
  • test_market_notAllowlistedRejected()
  • test_negPnlStamp_perplRejectsFillFarFromMarkWithinBand()
  • test_negPnlStamp_venueRefusesAddingToUnderwaterPosition()
  • test_orderType_fokStampedAndRevertsAtomicallyWithoutLiquidity()
  • test_orderType_iocWithoutLiquidityLeavesNoPosition()
  • test_orderType_stampIsAlwaysImmediate()
  • test_priceBand_appliesToClosesToo()
  • test_priceBand_exactlyAtBandAccepted()
  • test_priceBand_oneTickOutsideRejected()
  • test_reduceOnly_largerThanPositionRejected()
  • test_reduceOnly_withoutPositionRejected()
  • test_reduceOnly_wrongSideRejected()
  • test_side_invalidRejected()
  • test_staleMark_enforceWaits()
  • test_staleMark_refusesNewRiskAllowsReduceOnly()
  • test_stamp_negPnlAndExecutionBlock()
DeskSettlementTest25/25
  • test_claim_feesNettedFirstAndBookedOnce()
  • test_claim_hwmPreventsReclaimAfterLossAndRecovery()
  • test_claim_onlyTrader()
  • test_claim_unrealizedProfitCannotBeWithdrawn()
  • test_close_onlyTrader()
  • test_close_voluntaryPaysNoKeeperAndRequiresFlat()
  • test_enforce_bankruptPositionWithoutVenueInsuranceStaysEnforcing()
  • test_enforce_breachSettlesWaterfallAndPaysKeeperOnce()
  • test_enforce_dailyLossReason()
  • test_enforce_gapPastStakeRecordsPrincipalLossAndZeroKeeper()
  • test_enforce_healthyDeskRejected()
  • test_enforce_partialCloseDoesNotSettleAndPaysNothing()
  • test_factoryPause_blocksNewDesksOnly()
  • test_fees_capStopsAccrualAndNewRiskButAllowsReduceAndEnforce()
  • test_fees_chunkedCheckpointsCannotRoundFeesAway()
  • test_fees_evaluationDeskAccruesNothing()
  • test_fees_exposedIntervalAccruesExactlyOnceDespitePositionsAndCheckpoints()
  • test_fees_externalLiquidationCannotEraseExposureTime()
  • test_fees_flatFundedDeskAccruesNothingForAMonth()
  • test_graduation_pausedPoolBlocksButCloseStillWorks()
  • test_graduation_prematureRejectsOnchain()
  • test_graduation_requiresFlat()
  • test_graduation_requiresTradeCount()
  • test_graduation_standardCohortRequires24h()
  • test_settledDeskIsInert()

8. Paired-desk self-attack

One actor runs a long desk and a short desk, both graduated, scored together. This measures a scenario envelope; it is not a universal loss bound. Imprest makes no Sybil-resistance claim; it caps the damage with onchain gross-exposure and per-desk notional limits.

Data table: scroll sideways to see every column.

Paired-desk scenarios
ScenarioMoveDesk capActor netPrincipal lossPool profit shareKeeperPool net
calm_cap25000.5%2500.009.900.001.320.001.32
gap15pct_cap100015.0%1000.006.7318.0318.620.000.60
gap15pct_cap150015.0%1500.0056.4080.8227.940.00-52.88
gap15pct_cap250015.0%2500.00155.73206.3946.560.00-159.83
gap8pct_cap25008.0%2500.0039.2360.6524.720.00-35.94
gapdown15pct_cap2500-15.0%2500.00156.08206.3046.600.00-159.70
trend3pct_atomic_keeper_cap25003.0%2500.0046.460.009.240.509.24
trend3pct_delayed_keeper_cap25003.0%2500.00-1.210.009.240.509.24

All rows: LOCAL_REPRODUCTION on Perpl exchange bytecode, 100 AUSD stakes, 500 AUSD funded desks, AUSD units. Finding: under a 15% instantaneous gap, a per-desk notional cap of 2x desk size kept the pool net near zero; the deployment script uses that ratio. Early keeper enforcement acted as a free stop for the losing leg in the trend scenario.

Replay experiment: atomic enforcement vs keeper

Pre-registered before the first run: if the median overshoot gap against a 1-second keeper is under 10 bps, the atomic-enforcement headline is dropped. It was. In-transaction checks stop limit-breaking orders, but a pure price move still needs a keeper in both arms, and the tested order flow rarely produced an order one arm refused and the other accepted. The advantage shows only in the tail against slow keepers.

Median overshoot past the floor: in-transaction enforcement vs a 1 s keeper, on replayed real BTC/ETH 1-second history (pre-registered protocol; null threshold 10 bps)
0 bps median gap (n=51); null threshold hit, overshoot headline droppedSimulated
Method

Python simulation of PROTOCOL.md on real Binance 1 s BTC/ETH history; not the Perpl-bytecode harness. Breaches per arm: treatment 48, 1 s 48, 5 s 48, 10 s 48; max overshoot treatment 94.624 bps vs 10 s keeper 150.866 bps; pool loss beyond stake 0 in every arm.

source: proof/experiments/replay/replay.py

Data table: scroll sideways to see every column.

Replay arms
ArmBreachesMedian overshootP90MaxPool loss beyond stake
Atomic (in-transaction)48/5124.917 bps44.788 bps94.624 bps0
Keeper 1 s48/5124.917 bps44.788 bps94.624 bps0
Keeper 5 s48/5123.474 bps71.703 bps142.975 bps0
Keeper 10 s48/5124.634 bps69.506 bps150.866 bps0

9. Local Perpl reproduction

Perpl's own exchange bytecode (perpl-sdk, revision rc_v1.1.7-203-g0e5902dd) runs on a local EVM with Monad's 128 KiB contract limit. Findings recorded by the probe suite: a contract can own a Perpl account; IOC with no liquidity leaves no position while FOK reverts; Perpl clamps order leverage to the listing maximum instead of rejecting it, so the desk's 5x check is load-bearing; the stamped 50 bps negative-PnL cap refuses adding to a position under water.

Guarded trade (open)
461,254
gas, Ethereum schedule
Guarded trade (close)
188,713
gas, Ethereum schedule
Graduate
207,105
gas, Ethereum schedule
Enforce + settle
449,684
gas, Ethereum schedule
Gas used by a guarded desk trade (policy checks + Perpl fill + floor re-check), measured on Foundry's Ethereum gas schedule
461254Local reproduction
Method

gasleft() delta around Desk.trade in contracts/test/perpl/GasProfile.t.sol

source: contracts/test/perpl/GasProfile.t.sol

forge Version: 1.5.1-stable · solc 0.8.28 (foundry.toml) · exchange sha256 9f0d259dd45bda91…

10. Mainnet dependency status

Perpl's exchange on Monad Mainnet has whitelisting disabled, so a desk contract can open its own account without a whitelist step
falseMainnet verified
Method

read-only eth_call at a pinned block on two independent RPC providers; readbacks agree

source: scripts/src/gate0.ts

Perpl's Monad Mainnet exchange margins in Agora AUSD (0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a)
AUSDMainnet verified
Method

read-only eth_call getExchangeInfo() and ERC20 symbol() at a pinned block on two RPCs

source: scripts/src/gate0.ts

Minimum AUSD to open a Perpl account on Monad Mainnet
10.00Mainnet verified
Method

read-only eth_call getMinAccountOpenCNS() at a pinned block on two RPCs

source: scripts/src/gate0.ts

AUSD paid out by contract to real traders on Monad mainnet
PENDING
Monad Mainnet
Method

Not yet measured. See docs/FINAL_STATUS.md for the gate that produces it.

source: pending

Real outside traders evaluated and funded on mainnet
PENDING
Monad Mainnet
Method

Not yet measured. See docs/FINAL_STATUS.md for the gate that produces it.

source: pending

The payable 0.5 AUSD keeper bounty exceeds measured Monad enforce() gas cost plus margin
PENDING
Monad Testnet
Method

Not yet measured. See docs/FINAL_STATUS.md for the gate that produces it.

source: pending

Median overshoot past the floor: in-transaction enforcement vs a 1 s keeper, on replayed real BTC/ETH 1-second history (pre-registered protocol; null threshold 10 bps)
0 bps median gap (n=51); null threshold hit, overshoot headline droppedSimulated
Method

Python simulation of PROTOCOL.md on real Binance 1 s BTC/ETH history; not the Perpl-bytecode harness. Breaches per arm: treatment 48, 1 s 48, 5 s 48, 10 s 48; max overshoot treatment 94.624 bps vs 10 s keeper 150.866 bps; pool loss beyond stake 0 in every arm.

source: proof/experiments/replay/replay.py

What needs mainnet, why, and what it costs is listed in Known limitations and Final status.