All checks were successful
deploy / deploy (push) Successful in 24s
Proof: The active NEAR Intents funded market-maker loop needs a first-class operator withdrawal action so funded inventory can be exited through repo-controlled code rather than manual follow-up. Assumptions: The configured signer key is also a full-access key on the named NEAR account, and external-chain exits for active OMFT assets are triggered by intents.near::ft_withdraw with the token contract as receiver_id plus memo=WITHDRAW_TO:<destination>. Still fake: Strategy and executor remain disarmed, no live inventory is credited yet, and no live mainnet trade quote has been submitted.
5.7 KiB
5.7 KiB
Operator Runbook
This turn implements the first funded market-maker loop for the active BTC/EURe pair on NEAR Intents.
Verified venue flow
The implementation follows the official NEAR Intents market-maker path:
- Funding handles come from the Passive Deposit/Withdrawal Service
deposit_addressRPC for the configured treasury chains. - Spendable inventory comes from the Verifier internal ledger on
intents.nearviamt_batch_balance_of. - Pending deposits remain non-spendable and are tracked from
recent_deposits. - Real market-maker execution is a Solver Relay
quote_responsecarrying a signedtoken_diff. - Named NEAR accounts need the executor public key registered on
intents.nearviaadd_public_keybefore live submission will succeed.
The Message Bus settles matched intents on-chain after a user accepts the quote. The executor therefore submits quote responses; it does not bridge or top up inventory on the hot path.
Required env and secrets
Minimum required runtime values:
NEAR_INTENTS_API_KEYNEAR_INTENTS_ACCOUNT_IDNEAR_INTENTS_SIGNER_PRIVATE_KEYPOSTGRES_URL
Before the first live attempt on a named NEAR account, register the executor public key on intents.near from that named account:
near contract call-function as-transaction \
intents.near add_public_key json-args '{
"public_key": "ed25519:<executor-public-key>"
}' prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' \
sign-as <ACCOUNT_ID> network-config mainnet sign-with-keychain send
The executor stays disarmed by default even after the key is registered.
Local bring-up
npm install
cp .env.example .env
# fill NEAR_INTENTS_API_KEY, NEAR_INTENTS_ACCOUNT_ID, NEAR_INTENTS_SIGNER_PRIVATE_KEY
docker compose up -d --build
Services:
near-intents-ingestmarket-reference-ingestliquidity-managerinventory-synchistory-writerstrategy-enginetrade-executor
Control APIs
Default local ports:
8081near-intents-ingest8082market-reference-ingest8083inventory-sync8084liquidity-manager8085history-writer8086strategy-engine8087trade-executor
Common inspection:
curl -s http://127.0.0.1:8081/healthz
curl -s http://127.0.0.1:8081/state
curl -s http://127.0.0.1:8086/state
curl -s http://127.0.0.1:8087/state
Useful controls:
curl -s -X POST http://127.0.0.1:8082/refresh
curl -s -X POST http://127.0.0.1:8083/refresh
curl -s -X POST http://127.0.0.1:8084/refresh
curl -s -X POST http://127.0.0.1:8084/withdrawal-estimate \
-H 'content-type: application/json' \
-d '{"asset_id":"nep141:gnosis-0x420ca0f9b9b604ce0fd9c18ef134c705e5fa3430.omft.near","amount":"5000000000000000000","destination_address":"0xYourGnosisAddress"}'
curl -s -X POST http://127.0.0.1:8084/freeze-withdrawals \
-H 'content-type: application/json' \
-d '{"frozen":false}'
curl -s -X POST http://127.0.0.1:8084/withdraw \
-H 'content-type: application/json' \
-d '{"asset_id":"nep141:gnosis-0x420ca0f9b9b604ce0fd9c18ef134c705e5fa3430.omft.near","amount":"5000000000000000000","destination_address":"0xYourGnosisAddress"}'
curl -s -X POST http://127.0.0.1:8086/arm
curl -s -X POST http://127.0.0.1:8086/disarm
curl -s -X PUT http://127.0.0.1:8086/limits \
-H 'content-type: application/json' \
-d '{"max_notional_eure":5}'
curl -s -X POST http://127.0.0.1:8087/arm
curl -s -X POST http://127.0.0.1:8087/disarm
Track a withdrawal so it stays visible in liquidity and inventory state:
curl -s -X POST http://127.0.0.1:8084/track-withdrawal \
-H 'content-type: application/json' \
-d '{"withdrawal_hash":"<near-burn-tx-hash>","asset_id":"nep141:btc.omft.near","chain":"btc:mainnet","amount":"1000"}'
Notes:
- Deposit addresses are built in.
liquidity-managerrefreshes them from the bridgedeposit_addressRPC and exposes them through/state. - The repo withdrawal action is for external-chain exits on the active assets. It submits
intents.near::ft_withdraw, using the active OMFT token contract asreceiver_idandmemo=WITHDRAW_TO:<destination>, then tracks the returned NEAR transaction hash through bridgewithdrawal_status. - Withdrawals stay frozen by default. Unfreeze explicitly before calling
/withdraw, then freeze them again after the operation if you do not want further exits.
Safe arming sequence
- Confirm
market-reference-ingestis publishing fresh BTC/EUR data. - Confirm
inventory-syncshows credited spendable balances onintents.near. - Confirm
liquidity-managershows the expected deposit handle and any pending funding separately from spendable inventory. - Confirm
history-writerhas PostgreSQL connectivity. - Keep
STRATEGY_MAX_NOTIONAL_EURE=5for the first live test. - Arm
strategy-enginefirst. - Observe actionable decisions without venue errors.
- Arm
trade-executoronly when the signer key is registered and funded inventory is already credited.
What to inspect after a live attempt
decision.trade_decisionfor the reasoning chain.cmd.execute_tradefor the emitted quote response command.exec.trade_resultfor submission outcome.- PostgreSQL tables:
swap_demand_eventsmarket_price_eventsintent_inventory_snapshotsliquidity_actionstrade_decisionsexecute_trade_commandstrade_execution_results
Still fake
- Settlement follow-up after user quote acceptance is only visible indirectly through Solver Relay quote-status observations; the repo records the live quote-response attempt, not an end-user acceptance flow it does not control.
- The executor checks signer registration best-effort. If the verifier key-check view surface changes, the live submission itself remains the definitive signal.