--- title: EVM contract description: "The escrow, the solver, and the router: entry points, events, and revert reasons." --- import { ROUTER_ARBITRUM, ESCROW_ARBITRUM, SOLVER_ARBITRUM } from "/snippets/vars.mdx"; Three contracts share the EVM side. `HularEscrow` receives every deposit and pays refunds; its funds can only leave toward the solver or a refund recipient. `HularSolver` holds the operator float and is the contract that pays fulfillments, gas drops, and vouchers; its executors can pull settled deposits out of the escrow with `pullFromEscrow`. `HularRouter` carries every entry point that swaps or takes a signature (aggregator deposits, permit and permit2 variants, gasless witness deposits, same-chain swaps, partner funding) and forwards the resulting funds into the escrow. The router is UUPS-upgradeable by the owner; the escrow and solver are not upgradeable. | Chain | Escrow | Solver | Router | | --- | --- | --- | --- | | `arbitrum` | {ESCROW_ARBITRUM} | {SOLVER_ARBITRUM} | {ROUTER_ARBITRUM} | Solana is covered separately in [SVM program](/api-reference/svm-program). Prefer the `router_address` from the quote over these constants. A plain cross-chain deposit binds to the escrow, a swap deposit is sent to the router returned by `GetSwapInstructions`, and a same-chain swap binds to the router. ## Escrow deposit entry points ```solidity function depositNative(bytes32 quoteHash) external payable; function depositToken(bytes32 quoteHash, address token, uint256 amount) external; ``` Both emit `Deposit` and leave the funds in the escrow. ## Router deposit entry points ```solidity function depositTokenWithPermit( bytes32 quoteHash, address token, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function depositTokenWithPermit2( bytes32 quoteHash, address token, uint256 amount, uint256 nonce, uint256 deadline, bytes calldata signature ) external; function depositWithSwap( bytes32 quoteHash, address srcToken, uint256 amountIn, address aggregator, bytes calldata swapData, address bridgeToken, uint256 minBridgeOut ) external payable; ``` `depositWithSwapAndPermit` and `depositWithSwapAndPermit2` take the same arguments plus the respective signature parameters. Permit variants attempt the permit and ignore its failure, so an already-consumed permit does not revert the deposit. Token pulls credit what was actually received, so fee-on-transfer amounts are accounted at the escrow, not assumed. Two witness variants back [gasless orders](/api-reference/gasless): `depositTokenWithPermit2Witness` and `depositWithSwapAndPermit2Witness` take an `owner` plus a permit2 `SignatureTransfer` witness signature binding the quote hash (and, for swaps, the bridge token and floor), so an operator wallet can submit the deposit on the signer's behalf. They are called by the operator's relayer, not by integrators. ## Same-chain swap ```solidity function swapAndForward( bytes32 quoteHash, address srcToken, uint256 amountIn, address aggregator, bytes calldata swapData, address dstToken, uint256 minAmountOut, uint256 amountOut, uint16 referrerBps, address recipient ) external payable; ``` Swaps and pays the recipient in one transaction, on the router. Delivery is floored at `minAmountOut` and capped at `amountOut`; the referrer share and any surplus are forwarded to the solver. `swapAndForwardWithPermit` and `swapAndForwardWithPermit2` add the signature parameters. See [Same-chain swaps](/integration/same-chain-swaps). ## Partner funding ```solidity function fundPartner(bytes32 fundingId, address token, uint256 amount) external payable; ``` Deposits an allowlisted funding token against a funding id issued by `BuildPartnerFunding`, emitting `PartnerFunding`. The indexer credits the partner's balance from the event. See [PartnerApi funding](/api-reference/partner#funding). ## Voucher redemption ```solidity function redeemVoucher( bytes32 id, address token, address referrer, uint256 amount, bytes calldata signature ) external; ``` On the solver, callable by anyone. The EIP-712 signature is verified against the current voucher-signer set, the id is marked used so it cannot be redeemed twice, and the amount is paid to `referrer` from the solver's balance. See [Referral program](/integration/referrals). ## Operator functions Restricted to the owner or registered executors, listed for completeness. | Function | Contract | Caller | | --- | --- | --- | | `fulfillToken`, `payToken` | Solver | Executors. Pay out fulfillments and gas drops. | | `refund` | Escrow | Executors. Pays refunds from the escrow's balance. | | `pullFromEscrow` | Solver | Executors. Sweeps settled deposits from the escrow into the solver. | | `release` | Escrow | The solver contract only, from `pullFromEscrow`. | | `fulfillWithSwap` | Router | Executors. Fulfillments that swap into the destination token. | | `setExecutor`, `setVoucherSigner`, `revokeVoucherSigners`, `setEscrow` | Solver | Owner. `setEscrow` is one-shot. | | `setAggregator`, `setFundingToken` | Router | Owner. Allowlist management. | | `withdraw` | All three | Owner. Moves float out. | | `pause`, `unpause` | Solver | Owner. Halts deposits everywhere; the escrow and router read the solver's pause state. | | `transferOwnership`, `acceptOwnership` | Solver | Owner, two-step. The escrow and router read the solver's owner. | ## Events | Event | Emitted by | | --- | --- | | `Deposit(bytes32 quoteHash, address token, uint256 amount, address from)` | Escrow: `depositToken`, `depositNative`, and router deposits forwarding in | | `Refunded(bytes32 quoteHash, address token, address recipient, uint256 amount)` | Escrow: `refund` | | `Released(address token, uint256 amount)` | Escrow: `release` | | `DepositWithSwap(bytes32 quoteHash, address from, address srcToken, uint256 amountIn)` | Router swap deposits | | `SameChainSwap(...)` | Router: `swapAndForward` | | `Fulfilled(...)` | Solver `fulfillToken`, router `fulfillWithSwap` | | `PartnerFunding(bytes32 fundingId, address token, uint256 amount, address from)` | Router: `fundPartner` | | `VoucherRedeemed(bytes32 id, address token, address referrer, uint256 amount)` | Solver: `redeemVoucher` | | `AggregatorSet`, `FundingTokenSet` | Router allowlist changes | | `ExecutorSet`, `VoucherSignerSet`, `VoucherSignersRevoked`, `EscrowSet` | Solver allowlist changes | | `Paused`, `Unpaused`, `OwnershipTransferStarted`, `OwnershipTransferred` | Ownership and pause | The indexer matches `Deposit` and `SameChainSwap` against stored quotes by `quoteHash`. ## Reverts | Error | Cause | | --- | --- | | `InsufficientOutput` | A swap or fulfillment delivered less than the committed minimum. | | `AggregatorNotAllowed` | The aggregator is not allowlisted. Refetch swap instructions. | | `SwapFailed` | The aggregator call failed, usually stale calldata. | | `InvalidMsgValue` | Transaction value does not match the call. | | `InvalidReferrerBps` | `referrerBps` above the contract cap. | | `FundingTokenNotAllowed` | `fundPartner` with a token outside the allowlist. | | `GasDropNotAllowed` | Gas drop requested where it is not permitted. | | `EnforcedPause` | The contract is paused. | | `VoucherUsed` | Voucher id already redeemed. | | `InvalidVoucherSignature` | Signature does not match a registered signer. | | `Unauthorized`, `NotOwner` | Caller is not an executor or the owner. | | `TransferFailed`, `ApproveFailed`, `NativeForwardFailed` | Underlying token or native transfer failed. |