Skip to content

Instantly share code, notes, and snippets.

@patcito
Created May 26, 2026 14:34
Show Gist options
  • Select an option

  • Save patcito/bb791f750eb4e0875df198151b652a5d to your computer and use it in GitHub Desktop.

Select an option

Save patcito/bb791f750eb4e0875df198151b652a5d to your computer and use it in GitHub Desktop.
FlyingTulip leverage engine — on-chain broadcastOrder (no HTTP). Solidity snippet for strategies + Python EOA helper.

FlyingTulip leverage engine — on-chain broadcastOrder (no HTTP)

If you'd rather your Solidity strategy submit orders fully on-chain instead of POSTing to our HTTP API, just call engine.broadcastOrder(order). Our executor watches the engine's OrderBroadcast event on Sonic mainnet and fills the order for you within seconds.

This is permissionless: the engine has no caller check — security is the user's EIP-712 signature (or, for broadcastOrder specifically, the engine itself records preSignature[msg.sender, digest] = true and later short-circuits _isValidSig on that). You don't need any allowlist from us. Just have your strategy contract call broadcastOrder and we settle.

End-to-end smoke on dev 2026-05-26: user broadcast tx 0x64e27d7d filled by our executor in 9 seconds (fill tx 0x52be19e5). Same code is live on prod.

Addresses

Dev Prod
LeverageRfqEngine 0x8f143D84Ebf0751E56437A62BAB0528d1c8657BF 0x8263a07504d93cB95e0a74f3627bb15faaf140e2
ftUSD 0x04E6227d51Fc10AFb6c270017690a2B0a1d4427B 0xF7D85EC4E7710f71992752eac2111312e73E9C9C
USDC 0x29219dd400f2Bf60E5a23d13Be72B486D4038894 (chain-wide)
wS 0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38 (chain-wide)
stS 0xE5DA20F15420aD15DE0fa650600aFc998bbE3955 (chain-wide)
Our executor relayer (FYI) 0x794EB780F0F50591f376ea1f1492e0FD5d234d46 0xa505815A526f1200c17B7ffaE0067318d734b9d8

Chain: Sonic mainnet (chainId 146).

Solidity (the recommended path for a strategy contract)

interface ILeverageRfqEngine {
    struct LeveragedOrder {
        uint8   action;     // 0 OPEN | 1 CLOSE | 2 SWAP
        address user;       // MUST equal msg.sender (engine enforces)
        address sellToken;
        address buyToken;
        uint256 sellAmount;
        uint256 buyAmount;  // min-out (slippage floor) — SET A REAL VALUE
        uint32  validTo;
        uint256 feeAmount;
    }
    function broadcastOrder(LeveragedOrder calldata order) external returns (bytes32 digest);
    function cancelOrder(LeveragedOrder calldata order) external;
}

contract MyStrategy {
    ILeverageRfqEngine constant ENGINE = ILeverageRfqEngine(0x8263a07504d93cB95e0a74f3627bb15faaf140e2);
    address constant FTUSD = 0xF7D85EC4E7710f71992752eac2111312e73E9C9C;
    address constant STS   = 0xE5DA20F15420aD15DE0fa650600afc998bbE3955;

    // Open a collateral swap (0.1 ftUSD -> stS, valid 1h)
    function swapFtusdToStS() external {
        ILeverageRfqEngine.LeveragedOrder memory o = ILeverageRfqEngine.LeveragedOrder({
            action:     2,                              // SWAP
            user:       address(this),                  // engine requires user == msg.sender
            sellToken:  FTUSD,
            buyToken:   STS,
            sellAmount: 100_000,                        // 0.1 ftUSD (6dp)
            buyAmount:  1,                              // min stS out — SET A REAL FLOOR
            validTo:    uint32(block.timestamp + 3600),
            feeAmount:  0
        });
        bytes32 digest = ENGINE.broadcastOrder(o);
        // digest is the order's canonical ID; you can store it to track
        // / cancel later via ENGINE.cancelOrder(o).
    }
}

Prereqs your strategy must have done before its first broadcast:

  1. Approve borrow on PositionsManager (only for action=0 OPEN):
    IPositionsManager(PM).approveBorrow(ENGINE, sellToken);
  2. Approve token to engine for any sell token you'll trade:
    IERC20(sellToken).approve(ENGINE, type(uint256).max);
  3. Have collateral on PositionsManager for action=1 CLOSE or action=2 SWAP (the engine pulls from your PM position, swaps, deposits the proceeds back).

Once those are set up once, you can broadcastOrder as many times as you want.

Python (if you want to broadcast from a script instead of a contract)

pip install web3 eth-account
USER_PK=0x<key> python broadcast_order.py

See broadcast_order.py in this gist. It's the EOA equivalent — useful for testing or human-triggered orders.

What happens after broadcastOrder lands

  1. Engine emits OrderBroadcast(owner, digest, order) and sets preSignature[owner, digest] = true.
  2. Our executor's chain watcher (polling every 3s) picks up the event.
  3. Executor resolves the DEX route (Odos / KyberSwap / our composite ftUSD ↔ X via MintAndRedeem) and submits the fill via our SwapFiller.
  4. Engine's _isValidSig short-circuits on preSignature[user, digest], so the executor calls openLeverageFlash / swapCollateralFlash / closeLeverageFlash with sig="" and the engine accepts it.
  5. Within ~5–10 seconds of your broadcast tx confirming, the fill tx lands and your position updates on PositionsManager.

If something rejects the fill (insufficient collateral, sim revert, expired order), the order is parked for retry until validTo — our executor doesn't move funds it shouldn't.

Cancel an order before it fills

ENGINE.cancelOrder(sameOrderStructAsBroadcast);

Only order.user == msg.sender can cancel. The engine sets filledDigests[digest] = true to permanently prevent the fill.

Want the HTTP path instead?

Still supported and slightly faster (sub-second from our side): see the HTTP gist. Both paths can coexist; we de-dupe by order digest.

"""
Broadcast a LeveragedOrder on-chain (no HTTP).
pip install web3 eth-account
USER_PK=0x<key> python broadcast_order.py
This is the EOA equivalent of calling engine.broadcastOrder(order) from
your Solidity strategy. The engine emits OrderBroadcast + sets
preSignature[user, digest]=true; our chain watcher picks it up within
~3s and fills via our SwapFiller. No HTTP, no off-chain signature.
"""
import os
import time
from eth_account import Account
from web3 import Web3
# ─── pick your network ─────────────────────────────────────────────────────
NETWORK = os.environ.get("NETWORK", "prod").lower() # "dev" or "prod"
CFG = {
"prod": {
"rpc": os.environ["SONIC_RPC_URL"],
"engine": "0x8263a07504d93cB95e0a74f3627bb15faaf140e2",
"ftUSD": "0xF7D85EC4E7710f71992752eac2111312e73E9C9C",
},
"dev": {
"rpc": os.environ["SONIC_RPC_URL"],
"engine": "0x8f143D84Ebf0751E56437A62BAB0528d1c8657BF",
"ftUSD": "0x04E6227d51Fc10AFb6c270017690a2B0a1d4427B",
},
}[NETWORK]
# Sonic mainnet — chain-wide tokens (same address dev + prod):
USDC = "0x29219dd400f2Bf60E5a23d13Be72B486D4038894" # 6dp
WS = "0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38" # 18dp
STS = "0xE5DA20F15420aD15DE0fa650600afc998bbE3955" # 18dp
# ─── order params — EDIT THESE ────────────────────────────────────────────
action = 2 # 0 OPEN | 1 CLOSE | 2 SWAP
sell_token = CFG["ftUSD"]
buy_token = STS
sell_amount = 1 * 10**5 # 0.1 ftUSD (6dp)
buy_amount = 1 # min-out floor — SET A REAL VALUE
valid_to = int(time.time()) + 3600
fee_amount = 0
# ──────────────────────────────────────────────────────────────────────────
w3 = Web3(Web3.HTTPProvider(CFG["rpc"]))
acct = Account.from_key(os.environ["USER_PK"])
print(f"NETWORK={NETWORK} engine={CFG['engine']} user={acct.address}")
# Minimal engine ABI — only what we need.
ENGINE_ABI = [
{
"type": "function",
"name": "broadcastOrder",
"stateMutability": "nonpayable",
"inputs": [{
"name": "order", "type": "tuple",
"components": [
{"name": "action", "type": "uint8"},
{"name": "user", "type": "address"},
{"name": "sellToken", "type": "address"},
{"name": "buyToken", "type": "address"},
{"name": "sellAmount", "type": "uint256"},
{"name": "buyAmount", "type": "uint256"},
{"name": "validTo", "type": "uint32"},
{"name": "feeAmount", "type": "uint256"},
],
}],
"outputs": [{"name": "digest", "type": "bytes32"}],
},
]
engine = w3.eth.contract(address=Web3.to_checksum_address(CFG["engine"]), abi=ENGINE_ABI)
order_tuple = (
action,
acct.address,
Web3.to_checksum_address(sell_token),
Web3.to_checksum_address(buy_token),
sell_amount,
buy_amount,
valid_to,
fee_amount,
)
print(f"order = {order_tuple}")
# build + sign + send
tx = engine.functions.broadcastOrder(order_tuple).build_transaction({
"from": acct.address,
"nonce": w3.eth.get_transaction_count(acct.address),
"chainId": w3.eth.chain_id,
"gas": 200_000,
"maxFeePerGas": w3.to_wei(60, "gwei"),
"maxPriorityFeePerGas": w3.to_wei(1, "gwei"),
})
signed = acct.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f"broadcastOrder tx: {tx_hash.hex()}")
receipt = w3.eth.wait_for_transaction_receipt(tx_hash, timeout=60)
print(f" status={receipt.status} block={receipt.blockNumber} gas={receipt.gasUsed}")
# Find the OrderBroadcast log → digest is topic[2].
ORDER_BROADCAST_TOPIC = "0x7e37598d7e8b67ec1cd215b7c2d1e7e0b6f126cc2798b173066b0f3951ed3bc0"
for log in receipt.logs:
if log.address.lower() == CFG["engine"].lower() and log.topics[0].hex() == ORDER_BROADCAST_TOPIC:
digest = log.topics[2].hex()
print(f"OrderBroadcast emitted — digest=0x{digest}")
print(f"Watch our executor fill it within ~5-10s. Order ID will be `lev_chain_{digest[:16]}`.")
break
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment