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.
149 lines
5.7 KiB
Markdown
149 lines
5.7 KiB
Markdown
# 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:
|
|
|
|
1. Funding handles come from the Passive Deposit/Withdrawal Service `deposit_address` RPC for the configured treasury chains.
|
|
2. Spendable inventory comes from the Verifier internal ledger on `intents.near` via `mt_batch_balance_of`.
|
|
3. Pending deposits remain non-spendable and are tracked from `recent_deposits`.
|
|
4. Real market-maker execution is a Solver Relay `quote_response` carrying a signed `token_diff`.
|
|
5. Named NEAR accounts need the executor public key registered on `intents.near` via `add_public_key` before 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_KEY`
|
|
- `NEAR_INTENTS_ACCOUNT_ID`
|
|
- `NEAR_INTENTS_SIGNER_PRIVATE_KEY`
|
|
- `POSTGRES_URL`
|
|
|
|
Before the first live attempt on a named NEAR account, register the executor public key on `intents.near` from that named account:
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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-ingest`
|
|
- `market-reference-ingest`
|
|
- `liquidity-manager`
|
|
- `inventory-sync`
|
|
- `history-writer`
|
|
- `strategy-engine`
|
|
- `trade-executor`
|
|
|
|
## Control APIs
|
|
|
|
Default local ports:
|
|
|
|
- `8081` `near-intents-ingest`
|
|
- `8082` `market-reference-ingest`
|
|
- `8083` `inventory-sync`
|
|
- `8084` `liquidity-manager`
|
|
- `8085` `history-writer`
|
|
- `8086` `strategy-engine`
|
|
- `8087` `trade-executor`
|
|
|
|
Common inspection:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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-manager` refreshes them from the bridge `deposit_address` RPC 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 as `receiver_id` and `memo=WITHDRAW_TO:<destination>`, then tracks the returned NEAR transaction hash through bridge `withdrawal_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
|
|
|
|
1. Confirm `market-reference-ingest` is publishing fresh BTC/EUR data.
|
|
2. Confirm `inventory-sync` shows credited spendable balances on `intents.near`.
|
|
3. Confirm `liquidity-manager` shows the expected deposit handle and any pending funding separately from spendable inventory.
|
|
4. Confirm `history-writer` has PostgreSQL connectivity.
|
|
5. Keep `STRATEGY_MAX_NOTIONAL_EURE=5` for the first live test.
|
|
6. Arm `strategy-engine` first.
|
|
7. Observe actionable decisions without venue errors.
|
|
8. Arm `trade-executor` only when the signer key is registered and funded inventory is already credited.
|
|
|
|
## What to inspect after a live attempt
|
|
|
|
- `decision.trade_decision` for the reasoning chain.
|
|
- `cmd.execute_trade` for the emitted quote response command.
|
|
- `exec.trade_result` for submission outcome.
|
|
- PostgreSQL tables:
|
|
- `swap_demand_events`
|
|
- `market_price_events`
|
|
- `intent_inventory_snapshots`
|
|
- `liquidity_actions`
|
|
- `trade_decisions`
|
|
- `execute_trade_commands`
|
|
- `trade_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.
|