TESTNET ONLINE: PECORINO PECORINO

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:

  1. Constructs a Permit2 authorization for your input tokens
  2. Signs the order with your wallet
  3. 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:

  1. Constructs Permit2 structs to fill Orders on destination chains
  2. Builds a Signet Bundle batching initiate and fill transactions
  3. 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:

StrategyGas EfficiencyComplexityFailure Handling
TogetherHighMediumAll-or-nothing
IndividuallyLowerLowIndependent

Complete Example (bin/roundtrip.rs)

The roundtrip example demonstrates a full order lifecycle:

  1. Creates an example order
  2. Signs and sends to Transaction Cache
  3. Queries available orders
  4. 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

  1. Clone the signet-orders repository
  2. Run the roundtrip example
  3. Modify the example to fit your use case
  4. Implement custom filler logic
  5. Deploy to production

For on-chain order creation, check out Solidity Orders.