Call Abstraction Protocol
Call Abstraction is the distilled core of CrossCall — the minimal specification that makes crosschain execution possible without bridges. Everything in the CrossCall stack is built on top of this primitive.
What Is Call Abstraction?
Call Abstraction separates who signs a transaction from who executes it and where.
In a normal EVM flow, the signer must have funds on the chain where they want to execute. Call Abstraction breaks that requirement: a signer on Chain A can authorize a transaction on Chain B, and a solver on Chain B executes it — while the signer's funds on Chain A pay for it, deterministically, via a crosschain message.
The result: native execution on any chain, funded from anywhere.
The Three Roles
Signer
The user or protocol authorizing a transaction. The signer:
- Holds funds on their origin chain (locked in their Escrow contract)
- Signs a call intent specifying what should execute and where
- Never needs to hold funds on the destination chain
Solver
A permissionless actor that executes on behalf of signers. The solver:
- Monitors the CrossCall mempool for call intents
- Fronts the liquidity required for execution on the destination chain
- Is deterministically repaid from the signer's escrow via Hyperlane
CrossCall Protocol
The infrastructure layer that:
- Validates intents and manages the mempool
- Routes solver payout through the CrossCall Paymaster + Hyperlane
- Guarantees atomicity — the solver is only paid if execution succeeded
The Call Intent
A call intent is the signed payload a signer produces. It contains everything a solver needs to execute and be paid.
// The execution specification
type ExecutionData struct {
DestinationChain string `json:"destinationChain"` // where to execute
TargetAddress string `json:"targetAddress"` // what contract
Asset string `json:"asset"` // payment asset
Amount string `json:"amount"` // locked amount
Calldata string `json:"calldata"` // what to call
}
// The ERC-4337 userop for execution on the destination chain
type UserOperation struct {
Sender string `json:"sender"`
Nonce string `json:"nonce"`
InitCode string `json:"initCode"`
CallData string `json:"callData"`
CallGasLimit string `json:"callGasLimit"`
VerificationGasLimit string `json:"verificationGasLimit"`
PreVerificationGas string `json:"preVerificationGas"`
MaxFeePerGas string `json:"maxFeePerGas"`
MaxPriorityFeePerGas string `json:"maxPriorityFeePerGas"`
PaymasterAndData string `json:"paymasterAndData"`
Signature string `json:"signature"`
}
// The complete blob submitted to the mempool
type ExecutableBlob struct {
ExecutionRequest ExecutionData `json:"execution"`
UserOp UserOperation `json:"userop"`
}
The PaymasterAndData field is generated by the CrossCall API and encodes the crosschain payment instruction:
struct PaymasterAndData {
address paymaster; // CrossCall Paymaster address
address owner; // signer identity
uint256 chainId; // origin chain (where escrow lives)
address asset; // asset locked in escrow
uint256 amount; // amount authorized for solver payout
}
Execution Flow
Signer (Origin Chain)
├── Locks funds in Escrow
└── Signs: ExecutionData + UserOperation
│
▼
CrossCall API
├── Simulates transaction
├── Estimates costs (gas, validation, solver bid)
└── Returns: lock request + userop
│
▼
CrossCall Client
├── Validates lock on-chain (origin RPC)
├── Simulates userop (with paymaster pre-execution)
└── Admits blob to Mempool
│
▼
Solver (Destination Chain)
├── [1] Pays CrossCall Paymaster (Hyperlane costs)
├── [2] Funds Signer's SCW (gas + execution)
└── [3] Executes Signer's UserOp ◄── ATOMIC
│
▼
CrossCall Paymaster
├── preOp: validates PaymasterAndData, builds Hyperlane message
└── postOp: pays Hyperlane IGP, redeposits stake, returns residual to solver
│
▼
Hyperlane Network
└── Delivers message to Origin Chain
│
▼
Signer's Escrow (Origin Chain)
├── Validates message hash
└── Pays solver: execution costs + bid ✓
Atomicity Guarantee
The entire execution bundle is atomic at two levels:
Bundle atomicity (destination chain): The solver's three-step bundle — fund paymaster, fund SCW, execute userop — is submitted as a single atomic ERC-4337 bundle. If the userop fails, the paymaster is never funded and the Hyperlane message is never sent.
Payout atomicity (crosschain): The CrossCall Paymaster only submits the Hyperlane payout message in postOp, which only fires if preOp and execution both succeeded. This means the solver payout is cryptographically guaranteed to occur if and only if execution succeeded.
There is no state where:
- Execution happens but the solver isn't paid
- The solver is paid but execution didn't happen
- Partial execution leaves funds stranded
Escrow Contract
Every signer has a deterministic Escrow address derived via create2. The Escrow is a lightweight ERC-1967 proxy.
function createEscrow(
bytes memory _initializer,
bytes32 _salt
) external returns (address proxy) {
bytes memory deploymentData = abi.encodePacked(
type(EscrowProxy).creationCode,
_ESCROWIMPL
);
bytes32 salt = _calcSalt(_initializer, _salt);
assembly ("memory-safe") {
proxy := create2(0x0, add(deploymentData, 0x20), mload(deploymentData), salt)
}
if (proxy == address(0)) revert();
assembly ("memory-safe") {
let succ := call(gas(), proxy, 0, add(_initializer, 0x20), mload(_initializer), 0, 0)
if eq(succ, 0) { revert(0, 0) }
}
return proxy;
}
The Escrow:
- Tracks locked and unlocked token balances per signer
- Validates incoming Hyperlane messages against the original lock hash
- Executes solver payout on successful validation
- Is non-custodial — only the signer's locked funds are ever used for payouts
Security Properties
| Property | Guarantee |
|---|---|
| Signer can't be drained | Only locked amounts (specified by signer) are ever paid out |
| Solver can't be stiffed | Payout is atomic with execution — if execution happened, payout happens |
| No bridge pool to hack | No centralized liquidity pool exists in the flow |
| Deterministic payout | Hyperlane message is constructed deterministically in preOp; solver address is bound at execution time |
| Full revert on failure | Any step failing reverts the entire bundle; no partial state |
Relationship to ERC-4337
Call Abstraction is built on ERC-4337 Account Abstraction. The CrossCall Paymaster is a standard ERC-4337 paymaster that extends the _validatePaymasterUserOp and _postOpHandler hooks to add crosschain payout logic.
This means CrossCall is composable with any ERC-4337-compatible smart contract wallet, EntryPoint deployment, or bundler infrastructure — with CrossCall-specific behavior activated only via the paymasterAndData field.