The executor settles a signed LeveragedOrder on the prod
LeverageRfqEngine (Sonic mainnet, chainId 146). You don't pass it calldata —
you POST a signed order, the executor resolves the DEX route (incl. the
ftUSD ↔ X composite via MintAndRedeem + Odos), signs the fill with its own
KMS-managed relayer key, and broadcasts it. Engine does not gate by
caller — security is the user's EIP-712 signature recovered inside
_openLeverageFlash.
POST https://api.flyingtulip.com/mm/leverage/orders # submit
GET https://api.flyingtulip.com/mm/leverage/orders/<id> # poll status
GET https://api.flyingtulip.com/mm/leverage/config # engine / sessionManager / executor / chain
GET /leverage/config on prod returns:
{
"chainId": 146,
"signingChainId": 146,
"sessionManager": "0x109AE72778a0260571b9767477204F1ce41FBdff",
"sessionManagerDomainName": "FT SessionManager",
"leverageRfqEngine": "0x8263a07504d93cB95e0a74f3627bb15faaf140e2",
"executorAddress": "0xa505815A526f1200c17B7ffaE0067318d734b9d8",
"mode": "executor"
}leverageRfqEngineis what your script will use as the EIP-712verifyingContractwhen signing.executorAddressis the relayer that broadcasts (handy for monitoring; no authz needed by the engine, just FYI).
action, user, sellToken, buyToken, sellAmount, buyAmount, validTo, feeAmount
— slippage is buyAmount (the min-out floor; the executor also runs its
own oracle-floor check).
pip install requests eth-account
USER_PK=0x<key> python submit_leverage_order.pyThat script:
- Hits
/leverage/configto discover the engine + chainId. - EIP-712 signs a
LeveragedOrderwithdomain = LeverageRfqEngine. - POSTs the order +
ownerSignatureto/leverage/orders. - Polls
/leverage/orders/<id>to terminal status (filled|failed|expired|cancelled).
Edit the action, sell_token, buy_token, sell_amount, buy_amount at
the top before running.
bun add viem
USER_PK=0x<key> bun submit-leverage-order.tsSame flow, viem signTypedData.
{
"sigType": "eip712",
"orderType": "swap", // "open" | "swap" | "close"
"isOpen": false,
"user": "0xUSER",
"order": {
"action": 2, // 0 OPEN | 1 CLOSE | 2 SWAP
"user": "0xUSER",
"sellToken": "0xF7D85E…ftUSD",
"buyToken": "0xE5DA20…stS",
"sellAmount": "1000000",
"buyAmount": "<min-out>",
"validTo": 1779999999,
"feeAmount": "0",
"kind": 0, "partiallyFillable": false, "sellTokenBalance": 0, "buyTokenBalance": 0
},
"ownerSignature": "0xSIG"
}EIP-712 over the order; the domain is the engine:
domain = { name: "LeverageRfqEngine", version: "1", chainId: 146, verifyingContract: <ENGINE from /config> }
types = { LeveragedOrder: [action uint8, user address, sellToken address, buyToken address,
sellAmount uint256, buyAmount uint256, validTo uint32, feeAmount uint256] }
digest = keccak256(0x1901 ‖ domainSeparator ‖ structHash) // recovered signer must == user
- EOA user →
sigType:"eip712",ownerSignature(see scripts). - Contract user (e.g. a strategy) →
sigType:"erc1271"; the contract'sisValidSignature()must validate that digest. Body is otherwise identical.
cast signer alternative (after you compute the EIP-712 digest):
cast wallet sign --no-hash <digest> --private-key $PK
| Token / contract | Address | Decimals |
|---|---|---|
| USDC | 0x29219dd400f2Bf60E5a23d13Be72B486D4038894 |
6 |
| wS | 0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38 |
18 |
| stS | 0xE5DA20F15420aD15DE0fa650600aFc998bbE3955 |
18 |
| ftUSD (prod) | 0xF7D85EC4E7710f71992752eac2111312e73E9C9C |
6 |
| LeverageRfqEngine (prod) | 0x8263a07504d93cB95e0a74f3627bb15faaf140e2 |
— |
| SessionManager (prod) | 0x109AE72778a0260571b9767477204F1ce41FBdff |
— |
| PositionsManager (prod) | 0xbe4050a73a7Fb384c65E885a15C33461A4B20055 |
— |
| MintAndRedeem (prod) | 0x0C6f8eC81c3eA5BFf06F6CD0791780f9f050eE31 |
— |
| SwapFiller (FT-owned, prod) | 0x621b86771b4d5b4a57fBa9DfCBbAcd89f33aF82a |
— |
| Executor relayer (FYI) | 0xa505815A526f1200c17B7ffaE0067318d734b9d8 |
— |
Always cross-check against the live registry:
https://flyingtulipdotcom.github.io/deployments/prod-sonic.json /
prod-sonic-ftusd.toon.
approveBorrow(engine, <token>)onPositionsManagerfor each token your order touches (allows the engine to borrow on your behalf for OPEN).approve(<token>, engine, max)for each token you'll sell (lets the engine pull the user's tokens for SWAP / CLOSE).- For session-flow orders:
SessionManager.createSession(...)with the executor as the delegate.
(Or just use our examples script's setup step which does all 3 — see
leverage-executor/examples/e2e-order-flow.ts.)
spot-buy-sts SWAP of 0.1 ftUSD → 2.272 stS via the composite
ftUSD→USDC→stS route filled in 4 seconds:
0xe2da2ee4…cd54d
(block 71422081, gas 1.58M, ~$0.003 of S).