Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/rhinestonewtf/warp-router/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The Router supports two fundamental operation types: Fill and Claim. These operations enable different settlement patterns with distinct security models and use cases.
Fill operations require atomic signature authorization, while claim operations rely on protocol-level authorization already validated by the underlying protocol.

Operation Types

Fill Operations

Settlement operations that fulfill user orders by transferring assets and executing target operations. Require atomic signature from authorized signer.

Claim Operations

Resource unlock operations that claim user assets from protocols. Execute without atomic signatures as they rely on protocol-level authorization.

Fill Operations

Fill operations represent settlement transactions where a solver fulfills a user’s order:

Architecture

RouterLogic.sol
function optimized_routeFill921336808(
    bytes[] calldata relayerContexts,
    bytes calldata encodedAdapterCalldatas,
    bytes calldata atomicFillSignature
) public payable virtual nonReentrant {
    // Compute hash of encoded calldata
    bytes32 hash = encodedAdapterCalldatas.hashCalldata();
    
    // Validate atomic signature
    require(_isAtomic(hash, atomicFillSignature), IRouter.InvalidAtomicity());
    
    // Process each operation with adapter caching
    for (uint256 i; i < length;) {
        bytes calldata adapterCalldata = adapterCalldatas[i];
        bytes4 selector = bytes4(adapterCalldata[:4]);
        
        // Route to adapter or handle special selectors
        if (!_processDirectFillRoute(selector, adapterCalldata)) {
            // Regular adapter call with relayer context
            require(
                selector == adapter.callAdapterWithRelayerContext(
                    relayerContexts[relayerContextIndex],
                    adapterCalldata
                ),
                IRouter.AdapterCallFailed()
            );
            unchecked { ++relayerContextIndex; }
        }
        unchecked { ++i; }
    }
}

Atomic Signature Security

The atomic fill signer prevents unauthorized operation execution:
RouterLogic.sol
function _isAtomic(bytes32 hash, bytes calldata atomicSig) 
    internal virtual returns (bool atomic) 
{
    address signer = $atomicFillSigner;
    require(signer != address(0), IRouter.AtomicSignerNotSet());
    
    // Verify signature came from authorized signer
    atomic = (signer == ECDSA.recoverCalldata(hash, atomicSig));
}
Security Model:
  • Only the designated atomicFillSigner can authorize fill batches
  • Each signature is bound to specific calldata via hash
  • Prevents unauthorized solvers from executing user operations
  • Setting signer to address(0) pauses all fill operations

Fill Flow Example

Same-chain Compact fill operation:
1

Solver Submits Fill

router.optimized_routeFill921336808(
    relayerContexts,      // [tokenInRecipient]
    encodedAdapterCalldatas,  // [samechain_compact_handleFill calldata]
    atomicFillSignature   // Signature from atomicFillSigner
);
2

Router Validates Signature

bytes32 hash = encodedAdapterCalldatas.hashCalldata();
require(_isAtomic(hash, atomicFillSignature));
3

Router Delegates to Adapter

adapter.callAdapterWithRelayerContext(
    relayerContext,
    adapterCalldata
);
4

Adapter Pre-Funds Recipient

_prefundRecipient({
    from: msg.sender,  // Solver
    to: order.recipient,
    tokenOut: order.tokenOut
});
5

Arbiter Validates & Unlocks

SameChainArbiter.handleCompact_NotarizedChain({
    order: order,
    sigs: userSigs,
    relayer: tokenInRecipient  // From relayer context
});
6

Execute Target Operations

EXECUTOR.executeOpsWithoutSignature(
    order.recipient,
    order.targetOps
);

Fill Operation Security

1. Atomic Fill Signature

   Validates entire batch from Router perspective
   
2. User Signatures (in arbiter)

   Validates individual orders from user perspective
   
3. Protocol Authorization

   Compact/Permit2 validates token movements

Claim Operations

Claim operations unlock user resources from protocols without requiring atomic signatures:

Architecture

RouterLogic.sol
function routeClaim(
    bytes[] calldata relayerContexts,
    bytes[] calldata adapterCalldatas
) external payable nonReentrant {
    // No atomic signature validation required
    
    uint256 length = adapterCalldatas.length;
    uint256 relayerContextIndex;
    bytes4 prevSelector;
    address adapter;
    
    for (uint256 i; i < length;) {
        bytes calldata adapterCalldata = adapterCalldatas[i];
        bytes4 selector = bytes4(adapterCalldata[:4]);
        
        // Handle special selectors or route to adapter
        if (!_processDirectClaimRoute(selector, adapterCalldata)) {
            if (selector != prevSelector) {
                adapter = selector.withClaimAdapter().adapterAddress();
                prevSelector = selector;
            }
            
            require(
                selector == adapter.callAdapterWithRelayerContext(
                    relayerContexts[relayerContextIndex],
                    adapterCalldata
                ),
                IRouter.AdapterCallFailed()
            );
            unchecked { ++relayerContextIndex; }
        }
        unchecked { ++i; }
    }
}

Claim vs Fill

AspectFillClaim
Atomic SignatureRequired ✅Not required ❌
Protocol AuthCompact/Permit2 sigsCompact/Permit2 sigs
Use CaseSettlement fulfillmentResource unlocking
InitiatorSolverUser or solver

Single Claim Operation

For individual claim operations:
RouterLogic.sol
function routeClaim(
    bytes calldata relayerContext,
    bytes calldata adapterCalldata
) public payable nonReentrant {
    bytes4 selector = bytes4(adapterCalldata[:4]);
    
    // Handle special selectors or route to adapter
    if (!_processDirectClaimRoute(selector, adapterCalldata)) {
        address adapter = selector.withClaimAdapter().adapterAddress();
        require(
            selector == adapter.callAdapterWithRelayerContext(
                relayerContext,
                adapterCalldata
            ),
            IRouter.AdapterCallFailed()
        );
    }
}
Single claim operations are optimized for gas efficiency when only one claim is needed.

Batch Processing

Both fill and claim operations support atomic batch execution:

Batch Atomicity

// All operations in batch must succeed
for (uint256 i; i < length;) {
    // Execute operation i
    require(success, AdapterCallFailed());
    unchecked { ++i; }
}

// If any operation fails:
// - Entire batch reverts
// - No partial state changes
// - All gas consumed up to failure point

Relayer Context Consumption

RouterLogic.sol
// Each regular adapter call consumes one relayer context
uint256 relayerContextIndex;

for (uint256 i; i < length;) {
    if (!isSpecialSelector) {
        // Regular adapter call - consume context
        adapter.callAdapterWithRelayerContext(
            relayerContexts[relayerContextIndex],
            adapterCalldata
        );
        unchecked { ++relayerContextIndex; }
    }
    // Special selectors don't consume contexts
    unchecked { ++i; }
}

// Validate all contexts were consumed
require(relayerContextsLength == relayerContextIndex, LengthMismatch());
Context Validation:The Router validates that the number of relayer contexts matches the number of regular adapter calls. Special selectors (singleCall, multiCall, fee collection) don’t consume contexts.

Special Selectors

Both fill and claim operations support special selectors that bypass adapter lookup:
DirectRoutes.sol
function _processDirectFillRoute(
    bytes4 selector,
    bytes calldata data
) internal returns (bool processed) {
    if (selector == this.singleCall.selector) {
        // Handle single call directly
        processed = true;
    } else if (selector == this.multiCall.selector) {
        // Handle multi call directly
        processed = true;
    } else if (selector == this.collectFees.selector) {
        // Handle fee collection directly
        processed = true;
    }
    // Returns false for regular adapters
}
Benefits:

Gas Savings

Saves ~2,600+ gas by avoiding SLOAD and DELEGATECALL overhead

No Context

Special selectors don’t consume relayer contexts

Built-in

Common operations handled natively by Router

Gas Optimizations

Adapter Caching

RouterLogic.sol
bytes4 prevSelector;
address adapter;

for (uint256 i; i < length;) {
    bytes4 selector = bytes4(adapterCalldata[:4]);
    
    if (selector != prevSelector) {
        // Cache miss - load adapter from storage
        adapter = selector.withFillAdapter().adapterAddress();
        prevSelector = selector;
    }
    // Cache hit - reuse adapter (saves ~2,100 gas)
    
    adapter.callAdapter(adapterCalldata);
}

Encoded Calldata (Fill Only)

// Traditional approach:
function routeFill(bytes[] calldata calldatas) {
    // Decodes array immediately (~200-500 gas per element)
}

// Optimized approach:
function optimized_routeFill921336808(
    bytes calldata encodedAdapterCalldatas
) {
    // Decode using assembly - operates on calldata directly
    assembly {
        let o := add(encodedAdapterCalldatas.offset, calldataload(...))
        adapterCalldatas.offset := add(o, 0x20)
        length := calldataload(o)
    }
}

Context Tracking

// Efficient context consumption tracking
uint256 relayerContextIndex;

if (!isSpecialSelector) {
    // Regular adapter - consume context
    unchecked { ++relayerContextIndex; }
}
// No increment for special selectors

Use Cases

Cross-Chain Settlement
  • User swaps ETH on Ethereum for USDC on Arbitrum
  • Solver fills order by providing USDC on Arbitrum
  • Solver claims ETH on Ethereum after settlement
Same-Chain Settlement
  • User swaps USDC for DAI on same chain
  • Solver pre-funds DAI to recipient
  • Solver claims USDC after arbiter validation
Intent Execution
  • User authorizes multi-step operations
  • Solver executes operations via IntentExecutor
  • Solver receives compensation after execution

Error Handling

Fill Errors

error InvalidAtomicity();      // Atomic signature invalid
error AtomicSignerNotSet();    // Signer is address(0)
error AdapterCallFailed();     // Adapter execution failed
error LengthMismatch();        // Context/calldata length mismatch

Claim Errors

error AdapterCallFailed();     // Adapter execution failed
error LengthMismatch();        // Context/calldata length mismatch

Best Practices

Use Fill for Settlements

When solvers are fulfilling user orders, always use fill operations with atomic signatures for security.

Use Claim for Unlocks

When users or solvers are unlocking resources without settlement context, use claim operations.

Batch Operations

Combine multiple operations into atomic batches to ensure all-or-nothing execution.

Context Management

Ensure relayer context array length matches number of regular adapter calls (excluding special selectors).

Router System

Learn about Router architecture and operation routing

Adapters

Understand adapter architecture and delegatecall pattern

Arbiters

Learn about arbiter layer and settlement validation

Build docs developers (and LLMs) love