Rust Orders
Build applications that create and fill Signet Orders using Rust. This guide shows you how to implement market-making bots, monitoring tools, and custom filler strategies using the signet-sdk.
Quick Start
- Clone the examples repository:
1git clone https://github.com/init4tech/signet-orders
2cd signet-orders
Setup Environment
1export CHAIN_NAME="pecorino"
2export RU_RPC_URL="https://rpc.pecorino.signet.sh/"
3export SIGNER_KEY=[AWS KMS key ID or local private key]
4export SIGNER_CHAIN_ID="14174"
Fund Your Key
Your signer key needs:
- Input tokens on the Rollup
- Output tokens on the Host and/or Rollup
- Gas tokens for Rollup transactions
The example swaps 1 Rollup WETH → 1 Host WETH
by default, but you can edit freely.
Approve Permit2
Approve Permit2 to spend tokens (same address on all chains):
1cast send [TOKEN_ADDRESS] "approve(address,uint256)" \
2 0x000000000022D473030F116dDEE9F6B43aC78BA3 \
3 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff \
4 --rpc-url $RPC_URL
Run the Example
Run a Rollup-to-Host fill:
1cargo run --bin order-roundtrip-example
Run a Rollup-to-Rollup fill:
1cargo run --bin order-roundtrip-example -- --rollup
Et voilà! 🎉
Core Components
Creating Orders (src/order.rs
)
The SendOrder
struct handles order creation and initiation.
What it does:
- Constructs a Permit2 authorization for your input tokens
- Signs the order with your wallet
- Sends the signed order to the Transaction Cache
1use signet_types::signing::order::{UnsignedOrder, SignedOrder};
2use alloy::signers::Signer;
3
4// Create an order
5let order = Order {
6 inputs: vec![
7 Input {
8 token: weth_address,
9 amount: U256::from(1e18), // 1 WETH
10 }
11 ],
12 outputs: vec![
13 Output {
14 token: weth_address,
15 amount: U256::from(1e18),
16 recipient: your_address,
17 chainId: 1, // Ethereum mainnet
18 }
19 ],
20 deadline: current_timestamp + 3600,
21};
22
23// Sign and submit
24let unsigned_order = UnsignedOrder::from(ℴ)
25 .with_chain(chain_id, contract_address)
26 .with_nonce(permit2_nonce);
27
28let signed_order = unsigned_order.sign(&signer).await?;
Source: src/order.rs
Filling Orders (src/filler.rs
)
The Filler
struct demonstrates how to fill Signet Orders. Given a set of Orders, it:
- Constructs Permit2 structs to fill Orders on destination chains
- Builds a Signet Bundle batching
initiate
andfill
transactions - Sends the Bundle to the Transaction Cache for mining
Production Fillers need:
- Custom business logic to decide which orders to fill
- Advanced strategies (e.g., swaps to source liquidity between fills)
- Risk management and profitability calculations
Source: src/filler.rs
Filling Strategies
Fill Together (fill
)
Submits all Orders/Fills in a single Signet Bundle.
Behavior:
- Either all orders mine together or none do (atomic execution)
- More gas-efficient
- Allows reusing inputs from one order to fill another
When to use:
- You want atomic execution across multiple orders
- You can verify all orders are valid before submission
- You want to chain orders together
1filler.fill(orders).await?;
Fill Individually (fill_individually
)
Submits each Order/Fill in its own Bundle.
Behavior:
- Orders succeed or fail independently
- Simpler logic–rely on Builder simulation
- If an
initiate
reverts, the Bundle is discarded
When to use:
- You want to maximize fill rate
- You can’t pre-verify order validity
- You want simple error handling
1filler.fill_individually(orders).await?;
Trade-offs:
Strategy | Gas Efficiency | Complexity | Failure Handling |
---|---|---|---|
Together | High | Medium | All-or-nothing |
Individually | Lower | Low | Independent |
Complete Example (bin/roundtrip.rs
)
The roundtrip example demonstrates a full order lifecycle:
- Creates an example order
- Signs and sends to Transaction Cache
- Queries available orders
- Fills the example order
Customize the example:
- Swap any set of tokens on Host and/or Rollup
- Use multiple inputs and outputs
- Target either Host or Rollup chains
- Adjust amounts and deadlines
Source: bin/roundtrip.rs
Important Notes
Bundle Block Targeting
Signet Bundles target a specific block. If your Bundle isn’t included, it won’t mine in later blocks.
Solution: Re-run your submission to target a new block.
Permit2 Usage
All order creation and filling uses Permit2 for token approvals. Make sure:
- Users have approved Permit2 for input tokens
- Fillers have approved Permit2 for output tokens
- Approvals are on the correct chain (Host vs Rollup)
Nonce Management
Nonces default to current timestamp in microseconds. For custom nonces:
1let unsigned_order = UnsignedOrder::from(ℴ)
2 .with_chain(chain_id, contract_address)
3 .with_nonce(custom_nonce); // Use specific nonce
Ensure nonces are unique to prevent replay attacks.
Building a Market Maker
Use the Filler examples as a starting point:
1. Monitor Orders
Query the Transaction Cache for available orders:
1let orders = query_orders().await?;
2. Evaluate Profitability
Implement logic to determine which orders are profitable:
1for order in orders {
2 if is_profitable(ℴ) {
3 profitable_orders.push(order);
4 }
5}
3. Source Liquidity
Determine how to source output tokens:
- Hold inventory across chains
- Swap on DEXs just-in-time
- Use aggregators for best rates
4. Fill Orders
Submit fills using your chosen strategy:
1filler.fill(profitable_orders).await?;
5. Handle Failures
Implement retry logic and monitoring:
1match filler.fill(orders).await {
2 Ok(_) => log::info!("Orders filled successfully"),
3 Err(e) => {
4 log::error!("Fill failed: {}", e);
5 // Retry logic here
6 }
7}
Best Practices
Risk Management
- Calculate profitability before filling
- Set maximum position limits for inventory
- Monitor gas costs and include in profitability calculations
- Handle chain reorgs gracefully
Performance
- Batch orders when possible using
fill
- Use concurrent fills for independent orders
- Optimize gas by combining with other operations
- Monitor latency to beat other fillers
Error Handling
- Retry failed submissions with exponential backoff
- Handle reverted fills by marking orders as unfillable
- Monitor bundle inclusion and resubmit if needed
- Log failures for debugging and analysis
Security
- Never expose private keys in logs or code
- Validate order parameters before filling
- Check token approvals before attempting fills
- Monitor for unusual orders that might be exploits
Testing
Run the test suite:
1cargo test
Run with logging:
1RUST_LOG=debug cargo run --bin order-roundtrip-example
Advanced Topics
Custom Filler Strategies
Implement sophisticated filling logic:
- Arbitrage: Fill orders that create arbitrage opportunities
- Inventory management: Balance tokens across chains
- MEV capture: Combine fills with other profitable operations
Multi-Chain Fills
Fill orders that span multiple chains:
- Track state across Host and Rollup
- Coordinate atomic execution
- Handle partial fills
Order Monitoring
Build monitoring tools:
- Track order flow in real-time
- Analyze fill rates and profitability
- Alert on attractive opportunities
Next Steps
- Clone the signet-orders repository
- Run the roundtrip example
- Modify the example to fit your use case
- Implement custom filler logic
- Deploy to production
For on-chain order creation, check out Solidity Orders.