Skip to main content

Verify the Hedge

THE THESIS

Everything on the dashboard must be reproducible from a node

Vessel's claim is narrow: the book is long spot, short the same notional on a perp venue, and net delta stays inside 1% of gross notional. A claim that narrow can be checked with a node and a calculator. This page is the procedure.

It is split by time. The first half runs today against Monad mainnet and testnet. The second half runs the moment Vessel publishes its contract addresses. The method does not change between the two; only the placeholders fill in.

Testnet, unaudited, hedged against a simulated venue

Vessel is experimental software in testnet. The deployed contracts hedge against SimVenue SIMVENUE — SIMULATED, a simulated venue with an owner-settable funding rate, behind the IVenue interface. Perpl integration is next. Nothing below claims a live Perpl position exists for Vessel yet.

1. Tooling

You need cast from Foundry and curl. Nothing else. Every number on this page is an eth_call, an eth_getCode, an eth_getLogs, or a public HTTP GET.

The two networks, as published by Monad's documentation:

# Monad mainnet
export MONAD_RPC=https://rpc.monad.xyz # chain ID 143 (0x8F)

# Monad testnet
export MONAD_TESTNET_RPC=https://testnet-rpc.monad.xyz # chain ID 10143 (0x279F)

Two facts about reading Monad that matter for verification. eth_call simulates against speculative state and returns accurate results, so reads are fine at latest. For anything you intend to treat as irreversible, query at the finalized block tag; full finality is 600ms (see docs.monad.xyz).

2. What you can verify today

Each block below runs unchanged. Expected outputs are stated where the source states them.

Chain identity

cast chain-id --rpc-url https://rpc.monad.xyz
# expected: 143

cast chain-id --rpc-url https://testnet-rpc.monad.xyz
# expected: 10143

If either number differs, stop. You are not talking to Monad, and nothing after this line means anything.

The Perpl Exchange contract exists on mainnet

Perpl is the perp CLOB the litepaper names for the short leg. Its mainnet Exchange address is published by Perpl's docs and is the sole entry in the monad-crypto/protocols registry file for Perpl.

cast code 0x34B6552d57a35a1D042CcAe1951BD1C370112a6F --rpc-url https://rpc.monad.xyz
# expected: a long hex string, not 0x

An empty 0x would mean no contract lives there. Perpl's testnet Exchange is 0x1964C32f0bE608E7D29302AFF5E61268E72080cc; swap the address and RPC to check it.

The Perpl public context endpoint

No auth. Returns chain, instances, tokens, and markets.

curl -s https://app.perpl.xyz/api/v1/pub/context | head -c 2000

Live mainnet market IDs per Perpl's docs: BTC=1, MON=10, ETH=20, SOL=31, HYPE=40, ZEC=50. If your response lists those, the venue Vessel intends to hedge on is reachable and serving. Rate limits are documented at github.com/PerplFoundation/api-docs; market data and trading are separate servers with separate limits.

The Kuru mainnet Router has bytecode

Kuru is the on-chain order book the litepaper names for the spot leg. Router (market factory) address per docs.kuru.io:

cast code 0xd651346d7c789536ebf06dc72aE3C8502cd695CC --rpc-url https://rpc.monad.xyz
# expected: non-empty bytecode

Kuru is live on both networks. What is not verifiable is any Vessel route through it; that integration is in development and nothing is deployed for you to read.

The PuddleSwap testnet factory resolves WMON/USDC

Vessel implements IUniswapV2Router02 for spot routing; the venue behind that interface on testnet is PuddleSwap, a stock Uniswap V2 fork. Its addresses are project-published only; no third-party registry vouches for them, and the bytecode has not been independently compared to stock V2. With that stated, the factory's getPair is a plain view call:

cast call 0xd498f5beBD0C9f1FE0135a0Cf942dA67Ee6e8A9B \
"getPair(address,address)(address)" \
0x97B3070F9Da6C002343862b35E68Bd8e22608943 \
0x534b2f3A21130d7a60830c2Df862319e593943A3 \
--rpc-url https://testnet-rpc.monad.xyz
# expected: a pair address, or the zero address if no pool exists

The two arguments are PuddleSwap's testnet WMON and USDC. Note these are not the mainnet WMON and USDC addresses; PuddleSwap has no mainnet deployment, so there is no mainnet variant of this command.

3. What you will verify at Gate-0

Gate-0 is the point at which Vessel publishes the Engine, Hull, and venue addresses on github.com/Lemma-Development-Labs/vessel and this site's address page fills in. Until then the three exports below are placeholders. Everything else in each command is a real value.

export ENGINE=0x... # unpublished as of 2026-08-29
export HULL=0x... # unpublished as of 2026-08-29
export VENUE=0x... # SimVenue today; Perpl adapter next
export RPC=https://testnet-rpc.monad.xyz

The function names in this section describe what the read must return. The exact signatures are not published; the repository contains only a README and LICENSE as of 2026-08-29 UNVERIFIED — PENDING GATE-0. When the ABI lands, substitute the real selector and keep the arithmetic.

Read the spot balance

The spot leg is an ERC-20 balance held by the Engine. balanceOf is standard and needs no Vessel ABI.

export SPOT_TOKEN=0x... # the spot asset the engine holds; unpublished
cast call $SPOT_TOKEN "balanceOf(address)(uint256)" $ENGINE --rpc-url $RPC

Multiply by the mark price you get from Perpl's public context (or, while SimVenue is the venue, from the venue's own mark view) to get spot notional in USD.

Read the venue position through IVenue

The team brief states the venue sits behind an IVenue interface. The view that returns the short's size and mark is unnamed in any public artifact UNVERIFIED — PENDING GATE-0; the template assumes one call returning size and mark.

# template — replace the signature with the published IVenue view
cast call $VENUE "position(address)(int256,uint256)" $ENGINE --rpc-url $RPC
# returns: signed size (negative = short), mark price

Recompute net delta and check the band

Take the two reads above and do the arithmetic yourself. Do not read a "delta" field from anything.

spot_notional = spot_balance × mark
short_notional = |short_size| × mark
gross_notional = spot_notional + short_notional
net_delta = spot_notional − short_notional
band_check = |net_delta| / gross_notional ≤ 0.01

The litepaper's commitment is that band_check holds, and that a breach triggers a rebalance no later than the 4-hour timer. At launch leverage λ ≤ 2 the shape you should see is roughly two-thirds of TVL in spot and the same two-thirds hedged, with one-third posted as margin and a 10% idle USDC buffer unhedged. The buffer is deliberately outside the hedge; do not count it in spot_notional.

As a one-liner once the values are in hand:

# illustrative, not a forecast: numbers are placeholders for the arithmetic
SPOT=1000000; SHORT=995000
echo "scale=6; ($SPOT - $SHORT) / ($SPOT + $SHORT)" | bc
# 0.002506 → inside the 1% band

Replay one epoch of the conservation invariant

The invariant enforced in code and fuzzed, per the litepaper, is:

ΔNAV_Hull + ΔNAV_Ballast + ΔReserve + fees = G

for every epoch, where G is gross funding for that epoch. The settlement event that carries these fields is unpublished; its name and field order are not in any public artifact UNVERIFIED — PENDING GATE-0. The replay is the same regardless of the event's shape:

# template — replace the topic with the published settlement event signature
cast logs --from-block $FROM --to-block $TO \
--address $ENGINE \
"Settled(uint256 epoch,int256 g,int256 dHull,int256 dBallast,int256 dReserve,uint256 fees)" \
--rpc-url $RPC

Then, for each event, in whatever units the contract emits (check the decimals of the accounting token before comparing):

lhs = dHull + dBallast + dReserve + fees
rhs = g
assert lhs == rhs

Two further checks fall out of the same fields, both stated by the litepaper:

  • Fees are 10% of max(G, 0): fees == 0.10 × max(g, 0), so a negative epoch emits zero fees.
  • Hull accrues at the contract-parameter rate: dHull == r_H × H × Δt, where r_H is read from the Hull series (not inferred from history) and Δt is the epoch length. In a negative epoch dBallast goes negative first, then dReserve; dHull only goes negative once both are exhausted, and that is a credit event by definition.

If lhs != rhs for any epoch, the invariant is broken and the protocol has a bug.

4. What is not yet checkable

Vessel addresses are unpublished

As of 2026-08-29 the Vessel repository contains a README and a LICENSE. The Engine, Hull, Ballast, Reserve, and SimVenue addresses are not published UNVERIFIED — PENDING GATE-0. Every $ENGINE, $HULL, $VENUE, and $SPOT_TOKEN in section 3 is therefore a placeholder. The IVenue view signature and the settlement event signature are not published UNVERIFIED — PENDING GATE-0 either.

Every command above becomes runnable the moment the address page fills in. The gap is the current state of the protocol, not of the method.

The address page is at /developers/addresses. Its rows render an amber chip until an address is verified against bytecode on the named network.

5. The contract with the reader

If a number on the Vessel dashboard cannot be reproduced from this page, one of two things is true. Either this page is incomplete, or the dashboard is showing something no reader can verify. Both are bugs. Both are reported the same way, through /security/disclosure.

This page carries no numbers of its own beyond addresses, chain IDs, and the litepaper's parameters. Its purpose is to make sure you never have to take the dashboard's word for anything.

Verified from: vessel.wtf/litepaper · vessel-repo · vessel-team-brief · monskills/addresses · docs.monad.xyz · docs.perpl.xyz · github/PerplFoundation · monad-crypto/protocols · docs.kuru.io · puddleswap.org — Sat Aug 29 2026 00:00:00 GMT+0000 (Coordinated Universal Time) · How we verify this site →