Skip to content

Scripts and keeper commands ​

Run all commands from the repository root. The scripts are in scripts/. The keeper is in services/src/keeper.ts.

Root scripts ​

CommandEffect
pnpm local:chainStarts Anvil on 127.0.0.1:8545 with chain ID 31337 and one block each second
pnpm local:deployDeploys the contracts to the local chain and writes deployments/local.json
pnpm demo <command>Local demo commands: seed, serve, run, shock
pnpm testnet <command>Monad testnet commands. Loads the root .env file.
pnpm testnet:estimate-onchainRead-only gas estimates on Monad testnet
pnpm dev:servicesStarts the API, the relayer, and the indexer. Loads the root .env file.
pnpm keeper <command>One keeper action. Loads the root .env file.
pnpm dev:webStarts the web application on port 3000
pnpm dev:docsStarts this documentation site on port 5173
pnpm build:docsBuilds this documentation site
pnpm contracts:build, pnpm contracts:testforge build and forge test for contracts/
pnpm typecheck, pnpm test, pnpm buildWorkspace checks. See Checks and troubleshooting.
pnpm test:abi, pnpm test:smoke, pnpm test:ui, pnpm test:budgetSeparate checks. See the same page.

Local demo commands ​

The local commands use the public Anvil test mnemonic. They check that the RPC has chain ID 31337 before they send a transaction. Use RPC_URL to select a different local Anvil endpoint.

CommandEffect
pnpm demo seedDeposits 200,000 test USDC for Anvil accounts 0 and 1 in each market. Registers one ladder for each account. Sends the first quote of each market.
pnpm demo serveRuns the quote loop and the random taker. This is the default command.
pnpm demo runDoes local:deploy, seed, and serve in one command
pnpm demo shock <MON|ETH> <percent>Records a price shock for the running loop

pnpm demo serve ​

SettingValue
Quote periodOne quote call for each market each second
Quote life30 seconds
Quote centerFeed mark (MON 1.573, ETH 0.36) plus drift plus jump
Driftdrift += -0.02 * drift + noise, noise uniform from -2 to 2 ticks, limit 40 ticks, tick 0.001
JumpAdds the squared log return of each observation times 365 / 7. Loses half of its size each 60 seconds.
ObservationAttached to the quote when it is due. A pending shock, or else calm noise.
Calm noiseAnnual volatility 110% for MON and 60% for ETH, limited to a log return of 1%
Taker periodOne IOC order for each market each 2 seconds, from Anvil account 1
Taker order1 to 8 contracts, limit 2 ticks through the best price, at most 4 match attempts
Taker biasAbove a position of 30 contracts, the order reduces the position with a chance of 4 in 5

Each tenth quote logs the center, the drift, and the jump in ticks. See The demo loop.

pnpm demo shock ​

sh
pnpm demo shock MON -4
pnpm demo shock ETH 3

The percent is a simple price change, above -100 and at most 100. The default is -4. The command writes .local/demo-shock.json. The loop takes the value at the next observation of that market and removes it from the file. A new request for a market replaces a pending one.

The API route POST /demo/shock writes the same file.

Deployment variables ​

VariableDefaultEffect
LOCAL_OBSERVATION_SECONDS60Observation interval of a new local deployment. The Hedging demo uses 10.
LOCAL_MARKET_CONTRACTMarketMarket or QueuedMarket
RPC_URLhttp://127.0.0.1:8545Local Anvil endpoint

The initial values of a deployment are in scripts/common.ts: MON starts at an oracle price of 0.03 and a mark of 1.573. ETH starts at an oracle price of 3000 and a mark of 0.36.

Testnet commands ​

All testnet commands need DEPLOYER_PRIVATE_KEY. They check that the RPC has chain ID 10143. After deployment, the deployer must be the owner of the TestOracle.

CommandEffectExtra key
pnpm testnet deployDeploys the registry, the oracle, the token, and one Market for each asset
pnpm testnet deploy-fastThe same with QueuedMarket
pnpm testnet seedFunds the two makers and registers their laddersMAKER_PRIVATE_KEY
pnpm testnet seed-fastThe same, with the ladder shapes exchanged, so the other maker has the inside levelMAKER_PRIVATE_KEY
pnpm testnet serveQuote loop and random takerMAKER_PRIVATE_KEY, unless TAKER_SECONDS=0
pnpm testnet serve-fastOne quoteAndTake for each market each FAST_SECONDS
pnpm testnet recoverSubmits a current test price for each market
pnpm testnet shock [MON|ETH]Applies a log return of -0.04 at the next boundary. The default market is MON.
pnpm testnet rebound [MON|ETH]Applies a log return of +0.03 at the next boundary

The testnet markets use 300-second observations. See Run on Monad testnet for the sequence and Configuration for the variables.

Local shock and testnet shock use different units

pnpm demo shock MON -4 is a simple price change of -4%. pnpm testnet shock MON and the keeper shock are a log return of -0.04, which is about -3.92%.

Gas estimate ​

sh
pnpm testnet:estimate-onchain

The script compiles the contracts with Foundry, then calls eth_call and eth_estimateGas on testnet with code and storage overrides. It never sends a transaction. It saves .local/onchain-gas-estimate.json. Set FORGE_BIN if Foundry is not at ~/.foundry/bin/forge.

Keeper commands ​

sh
pnpm keeper oracle --market MON --price 0.05
pnpm keeper shock --market MON
pnpm keeper rebound --market MON
pnpm keeper recover --market MON --price 0.05
pnpm keeper liquidate --market MON --account 0x...

The keeper uses the same deployment file and RPC as the service. It signs with KEEPER_PRIVATE_KEY. Without that variable, it uses RELAYER_PRIVATE_KEY. Each command does one action and then stops.

CommandEffect
oracleSubmits an observation at the first pending boundary. --price sets the price. Without it, the price does not change.
shockThe same, with the price multiplied by exp(-0.04)
reboundThe same, with the price multiplied by exp(0.03)
recoverSubmits an observation at the current chain time. It does not restore missing returns.
liquidateChecks an account and sends a liquidation attempt
OptionDefaultUse
--marketMONMarket symbol
--priceCurrent oracle priceDecimal price for oracle and recover
--accountNoneAccount for liquidate
--timestampThe pending boundary, or the current time for recoverObservation timestamp

Rules of the oracle commands ​

  • The keeper key must be the owner of the TestOracle. On the local chain, that is Anvil account 0.
  • The keeper signs the test price. The market verifies the signature. No publish transaction is necessary.
  • oracle, shock, and rebound must run within 5 seconds after the boundary. If the boundary is older, use recover.
  • The commands refuse a deployment that is not marked as a test oracle.
  • Stop the operator loop before you use the same account. The loop and the keeper would use the same nonces and the same boundary.

A permitted short gap uses one endpoint return. A long gap skips funding and rebases. See Oracle and observations.

Rules of the liquidate command ​

The command stops if the mark is frozen. It reads the equity and the maintenance margin of the account, simulates the close, sends it, and waits for a receipt. It uses a match limit of 32.

Without --account, it scans the accounts in the indexed market events. It skips an account with a zero book position and an account above maintenance margin. A confirmed attempt can leave a part of the position or a funding obligation.

On the local chain, give the keeper the same variables as the API:

sh
DEPLOYMENT_FILE=deployments/local.json RPC_URL=http://127.0.0.1:8545 \
STATE_FILE=.local/local-indexer.json KEEPER_PRIVATE_KEY=0x... \
pnpm keeper liquidate --market MON

Type check of the scripts ​

sh
pnpm exec tsc -p scripts/tsconfig.json

Deployed on Monad testnet.