This guide walks through implementing cross-chain orders on Signet. By the end, you’ll have working code for both sides of the order flow: creating orders as an application, and filling orders as a market participant.
What you’ll build:
- Order creation: Express user intent through signed Permit2 structures
- Order filling: Compete to fulfill orders profitably
- Bundle coordination: Atomic execution across chains
Prerequisites:
- Rust development environment
- Funded wallet on Parmigiana testnet
- Familiarity with The Filler Economy concepts
All code is available in signet-orders.
Part 1: Creating Orders
Orders express cross-chain intent. A user says: “I’ll give you X on Signet if you give me Y on Ethereum.” Applications construct and sign these orders on behalf of users.
The SendOrder Pattern
Code: src/order.rs
impl SendOrder {
/// Sign an order and submit it to the transaction cache
pub async fn sign_and_send_order(&self, order: Order) -> Result<()> {
let signed = self.sign_order(order).await?;
self.send_order(signed).await
}
/// Sign an order without submitting
pub async fn sign_order(&self, order: Order) -> Result<SignedOrder> {
// Build Permit2 authorization and sign
// ...
}
/// Submit a pre-signed order to the transaction cache
pub async fn send_order(&self, signed: SignedOrder) -> Result<()> {
self.tx_cache.forward_order(signed).await
}
}What’s happening:
- Order construction: Build the order specifying inputs (what user gives) and outputs (what user wants)
- Permit2 signing: User approves token spend with a single signature (no separate approve tx)
- Submission: Order goes to the transaction cache where Fillers discover it
Order Structure
pub struct SignedOrder {
pub permit: Permit2Batch, // Single signature authorization
pub outputs: Vec<Output>, // What user wants to receive
}
pub struct Output {
pub token: Address, // Asset (e.g., WETH)
pub amount: Uint<256, 4>, // Amount
pub recipient: Address, // Where to send it
pub chainId: u32, // Which chain (1 = Ethereum)
}Orders have a ~10 minute validity window. If no Filler accepts within that window, the order expires and nothing happens.
Part 2: Filling Orders
Fillers are market participants who fulfill orders for profit. They provide the outputs users want and receive the inputs users offer.
The Filler Pattern
Code: src/filler.rs
impl Filler {
pub async fn fill(&self, orders: Vec<SignedOrder>) -> Result<()> {
// 1. Construct and sign Permit2 fills for destination chains
let fills = self.construct_fills(&orders).await?;
// 2. Build atomic bundle combining initiates and fills
let bundle = self.build_signet_bundle(&orders, &fills).await?;
// 3. Submit to Transaction Cache for builder inclusion
self.tx_cache.submit_bundle(bundle).await
}
}What’s happening:
- Construct fills: Filler signs their side of the trade (providing outputs to user)
- Build bundle: Combine order execution with fill execution
- Submit: Bundle goes to builders for atomic inclusion
Two Filling Strategies
Aggregate execution — All orders in one bundle:
pub async fn fill(&self, orders: Vec<SignedOrder>) -> Result<()> {
// Submit all Orders/Fills in single Bundle
// Atomic: all succeed or none do
}Pros: Gas efficient, can reuse capital across orders Cons: Single failure breaks entire bundle
Individual execution — Each order in its own bundle:
pub async fn fill_individually(&self, orders: Vec<SignedOrder>) -> Result<()> {
// Submit each Order/Fill in separate Bundle
// Independent: orders succeed or fail separately
}Pros: Resilient, simple validation Cons: Higher gas, can’t optimize across orders
Choose based on your risk tolerance and order flow.
Part 3: Bundle Construction
Orders and fills execute atomically through Signet’s bundle system.
let bundle = SignetEthBundle {
host_fills: Some(permit2_fills), // Ethereum-side actions
bundle: EthSendBundle {
txs: signet_transactions, // Signet-side transactions
block_number: target_block,
reverting_tx_hashes: vec![], // No partial execution
}
};Key properties:
- Block-specific: Bundles target a specific block number
- Atomic: All transactions succeed or none do
- Retry-able: Failed bundles can target the next block
The OrderDetector in Signet’s EVM enforces atomicity: order transactions only execute if corresponding fills exist.
Part 4: Running the Example
The roundtrip example demonstrates the complete flow.
Setup
1. Configure environment:
export CHAIN_NAME=parmigiana
export RU_RPC_URL=https://rpc.parmigiana.signet.sh/
export SIGNER_KEY=[your private key or AWS KMS key ID]
export SIGNER_CHAIN_ID=888882. Fund your wallet:
Your signing key needs:
- Input tokens (what you’re swapping from)
- Output tokens (what you’re providing to users)
- Gas: ETH on Ethereum, USD on Signet
3. Approve Permit2:
cast send [TOKEN_ADDRESS] "approve(address,uint256)" \
0x000000000022D473030F116dDEE9F6B43aC78BA3 \
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff \
--rpc-url $RPC_URLPermit2 uses the same address on Parmigiana and Ethereum mainnet.
Run
Rollup → Host swap:
cargo run --bin order-roundtrip-exampleRollup → Rollup swap:
cargo run --bin order-roundtrip-example -- --rollupThe example supports both AWS KMS and local private keys.
Production Considerations
When building production systems:
Profitability analysis: Calculate spreads accounting for gas costs, inventory costs, and competition.
Key management: Use AWS KMS, HashiCorp Vault, or hardware security modules. Never use raw private keys in production.
Monitoring: Track fill rates, win rates against competitors, and inventory levels.
Error handling: Implement retry logic for network failures. Bundles can target subsequent blocks if the first attempt fails.
Gas optimization: Choose aggregate vs individual filling based on your order patterns. High-volume, correlated orders benefit from aggregation.
Integration Patterns
Cross-chain DeFi: Let users open positions on Ethereum using Signet collateral—instantly.
Payment routing: Route payments to the cheapest chain based on current liquidity.
Arbitrage: Capture price differences while providing useful execution to users.
Wallet interfaces: Offer “swap to any chain” without users understanding the mechanics.
Resources
Code:
signet-orders— Complete examplessignet-sdk— Core types and utilities
Infrastructure:
- Parmigiana Faucet — Fund your dev keys
- Parmigiana Explorer — Monitor execution
Concepts:
- The Filler Economy — How markets work
- Application Controlled Execution — What apps can do
Questions? Get in touch.