tesseract

Implementation guide

Integrating Tesseract Into a Multi-Chain Vault: An Implementation Walkthrough

Step-by-step technical guide to adding Tesseract atomic swaps to a DeFi yield vault that rebalances across Ethereum L2s — with code, gotchas, and operational checklist.

May 26, 2026 · The Tesseract Team ·
integrationtutorialvaultsdefiyield

Multi-chain yield vaults — products that automatically move capital across rollups chasing the best risk-adjusted yield — have a structural problem: rebalancing. Every move from a lending market on Arbitrum to a lending market on Base used to involve a bridge transaction, a wait, a swap, and another wait, often eating 50–80% of the yield improvement in the process. The vaults that have figured out fast, atomic, MEV-protected rebalancing in 2026 are the ones outperforming.

This is a walkthrough of how to add Tesseract atomic swaps to a vault’s rebalancing logic. We’ll cover the data model, the contract integration, the operational flow, and the specific operational pitfalls that we’ve seen burn integrators.

If you’ve read the arbitrage guide, some of this will be familiar — the underlying contracts are the same. The vault use case differs in two important ways: you’re moving treasury-scale balances (not opportunistic trade-sized), and you need to integrate with whatever vault accounting framework you’re already using (typically ERC-4626 or a derivative). Both differences matter for how you structure the calls.

The vault model we’re assuming

For concreteness, imagine an ERC-4626 vault on each of three rollups (Arbitrum, Optimism, Base) that all hold USDC and route it to whichever local lending market currently has the best supply APY. Users deposit USDC on their preferred chain. A vault keeper periodically queries supply APYs across the three chains and rebalances when the spread is wide enough to justify the gas + slippage cost.

The vault has a Treasury contract on each chain that:

  • Holds the chain-local USDC balance.
  • Knows which lending market it’s currently deployed into.
  • Has an executeRebalance function the keeper calls.

The rebalancing decision is off-chain. The execution must be atomic across chains.

Why naive rebalancing doesn’t work

A naive flow:

  1. Keeper withdraws 1M USDC from Aave on Arbitrum.
  2. Keeper bridges 1M USDC to Base (10–20 minute wait via canonical bridge).
  3. Keeper deposits 1M USDC into Moonwell on Base.

Problems:

  • Window of weakness. Between steps 1 and 3, capital is sitting in transit earning nothing. On 1M USDC at a 5% APY differential, 15 minutes of idle capital costs about $14.
  • Bridge risk. Steps 2 holds capital in a bridge contract. That is a different risk profile than holding it in a lending market.
  • Slippage from MEV. If you’re swapping anywhere in this flow, the swap is public the moment it hits a mempool. For 1M USDC, that’s a slippage budget a searcher will happily eat into.
  • Non-atomic failure. If step 3 fails (Moonwell paused for an emergency, RPC issue, etc.), you’re holding 1M USDC on Base with no plan B. The treasury is mid-flight.

A Tesseract-based flow flips the problem: instead of moving the asset from chain to chain, the vault makes a synthetic swap on each chain, atomically.

The Tesseract-based flow

Conceptually:

  1. On Arbitrum, the vault swaps 1M USDC of vault treasury for 1M USDC owed by a counterparty on Base.
  2. On Base, the same swap settles in reverse: the counterparty delivers 1M USDC to the vault’s Base treasury.
  3. Both legs are bound by a shared swap_group_id. They both resolve, or both refund.

The counterparty here is whoever has the inverse imbalance — typically another vault, a market maker, or in the open market case, anyone who needs USDC on Arbitrum and has USDC on Base. The AtomicSwapCoordinator order book is where these counterparties meet.

The vault never holds capital in transit. The capital on Arbitrum simply changes hands (to a counterparty) while capital on Base simultaneously changes hands (from a counterparty). Atomicity is enforced by the protocol.

The integration: contract changes

Your vault’s treasury contract needs:

  1. Approval flow for TesseractBuffer to pull USDC when a swap resolves.
  2. A callback to handle the post-swap state update (the lending market deposit).
  3. Refund handling for the unhappy path.

A sketch of the additions:

// In your Treasury contract on each chain
contract Treasury {
    address public tesseractBuffer;
    address public coordinator;
    IERC20  public usdc;

    // Called by the vault keeper to initiate a cross-chain rebalance
    function initiateRebalance(
        bytes32 swapGroupId,
        bytes32 txId,
        uint256 amount,
        address targetCounterparty,
        uint256 deadline,
        bytes32 commitment
    ) external onlyKeeper {
        usdc.approve(tesseractBuffer, amount);
        ITesseractBuffer(tesseractBuffer).buffer_transaction_with_commitment(
            txId,
            address(this),
            targetCounterparty,
            commitment,
            bytes32(0),
            deadline,
            swapGroupId,
            address(this) // refund recipient
        );
        emit RebalanceInitiated(swapGroupId, txId, amount);
    }

    // Called when a swap resolves successfully — the buffer transfers USDC in
    function onSwapResolved(bytes32 txId, uint256 amount) external {
        require(msg.sender == tesseractBuffer, "only buffer");
        _redeployIntoBestMarket(amount);
        emit RebalanceResolved(txId, amount);
    }

    // Called when a swap refunds (deadline expired)
    function onSwapRefunded(bytes32 txId, uint256 amount) external {
        require(msg.sender == tesseractBuffer, "only buffer");
        // Capital came back; either retry or redeploy locally
        emit RebalanceRefunded(txId, amount);
    }
}

The vault keeper coordinates between treasuries on different chains. The atomicity is enforced at the buffer layer.

The keeper flow

A condensed pseudo-flow for the off-chain keeper:

# 1. Decide to rebalance
arb_apy  = get_apy("arbitrum", "aave",  "usdc")
base_apy = get_apy("base",     "moonwell", "usdc")
if (base_apy - arb_apy) * AMOUNT * TIME_HORIZON < REBALANCE_THRESHOLD:
    return  # not worth it

# 2. Find counterparty via AtomicSwapCoordinator
order = coordinator.find_complementary_order(
    chain_from="arbitrum",
    chain_to="base",
    asset="usdc",
    amount=AMOUNT,
)
if order is None:
    return  # no matching counterparty right now

# 3. Build atomic swap group
group_id = keccak(os.urandom(32))
deadline = int(time.time()) + 180

# Leg A: arbitrum treasury -> counterparty
leg_a_payload    = encode_swap_payload("arbitrum", AMOUNT, order.counterparty_arb)
leg_a_secret     = os.urandom(32)
leg_a_commitment = keccak(leg_a_payload + leg_a_secret)
leg_a_tx_id      = keccak(b"leg_a_" + group_id)

# Leg B: counterparty on base -> base treasury
leg_b_payload    = encode_swap_payload("base", AMOUNT, base_treasury_address)
leg_b_secret     = os.urandom(32)
leg_b_commitment = keccak(leg_b_payload + leg_b_secret)
leg_b_tx_id      = keccak(b"leg_b_" + group_id)

# 4. Submit commitments in parallel
fut_a = thread.submit(arb_treasury.initiateRebalance,  group_id, leg_a_tx_id, AMOUNT, ..., deadline, leg_a_commitment)
fut_b = thread.submit(base_treasury.initiateRebalance, group_id, leg_b_tx_id, AMOUNT, ..., deadline, leg_b_commitment)
wait_all([fut_a, fut_b])

# 5. Reveal once both commits confirmed
arb_buffer.reveal_transaction(leg_a_tx_id, leg_a_payload, leg_a_secret)
base_buffer.reveal_transaction(leg_b_tx_id, leg_b_payload, leg_b_secret)

# 6. Wait for resolution; relayers handle it
wait_for_resolution(group_id, deadline)

Operational gotchas (the part that bites people)

1. Approval revocation. The Treasury contract has to approve TesseractBuffer for the swap amount. If you leave that approval at infinity for convenience, you’ve granted the buffer the right to drain your treasury under any future bug. Approve per-swap, revoke after resolution.

2. Reentrancy on the resolve callback. The onSwapResolved callback runs while the buffer’s state is mid-update. Use OpenZeppelin’s ReentrancyGuard or the equivalent. The Vyper buffer itself is reentrancy-safe but your Solidity treasury might not be.

3. Counterparty selection logic. The AtomicSwapCoordinator gives you an order book; the vault keeper has to pick a counterparty. If you blindly accept the first match, you’re vulnerable to a counterparty who repeatedly posts and cancels orders to learn your behaviour. Use rotating counterparty preference, randomised timing on order matching, and treat the order book as adversarial input.

4. Deadline tuning. Too short and benign network congestion causes refunds. Too long and you’re holding capital out of yield-bearing positions longer than necessary. We’ve found 120–180 seconds works well for L2-to-L2 swaps in normal conditions; expand to 300 during known congestion events.

5. Failed reveal handling. If your reveal transaction fails for any reason (insufficient gas, RPC hiccup, wrong payload encoding), the commit will time out and refund. Your vault should handle the refund as an expected outcome of the system, not an exceptional one. Build the refund flow first.

6. Accounting on refund. When a swap refunds, your vault’s USDC balance on the source chain is unchanged but its position state may have been pre-adjusted in anticipation of the swap. Make sure your accounting only commits state changes on the resolve callback, not on the initiate call.

7. Gas budgeting. Each leg costs ~330k gas on the source chain. On Arbitrum at the time of writing that’s roughly 30 cents per leg. For a 1M USDC rebalance that’s a rounding error; for a 1k USDC rebalance you’re losing money on the gas. Have a minimum-trade-size guard.

8. Per-chain RPC redundancy. A single RPC provider hiccuping during the reveal phase can cost you a swap. Use at least two providers per chain with auto-failover.

What you get when this is wired up correctly

Once it’s working, the vault gets a few properties that meaningfully change its economics:

  • Sub-minute rebalancing. From decision to resolved-on-destination is typically 60–120 seconds. That’s 10x faster than canonical-bridge-based rebalancing.
  • No capital in transit. The vault’s treasury is always either in a lending market or in a buffered swap. There is no point at which it’s just sitting in a bridge.
  • No MEV leakage on rebalances. Commit-reveal means the rebalance amount and direction are hidden until ordering is fixed. Searchers can’t front-run.
  • Atomic failure handling. If anything goes wrong, the vault gets its capital back at the deadline. There’s no partial-rebalance state to clean up.
  • Frequent rebalancing becomes economic. Because the per-rebalance cost is roughly halved, you can afford to rebalance on smaller APY differentials. That compounds.

In aggregate, vaults integrating atomic swap protocols for rebalancing are reporting net APYs 80–150 bps above non-atomic equivalents on the same underlying strategies. That’s the difference between a competitive product and an also-ran.

Checklist before shipping

  • Treasury contracts deployed on all target chains with TesseractBuffer integration.
  • Keeper service running with at least two RPC providers per chain.
  • Order book monitoring for counterparty selection.
  • Per-swap approval pattern, not infinite approval.
  • Reentrancy guards on resolve/refund callbacks.
  • Refund flow tested under deliberate timeout conditions.
  • Accounting only commits on resolve, not on initiate.
  • Minimum trade size guard.
  • Per-chain finality awareness in deadline calculation.
  • Monitoring + alerting on refund rate (sustained refunds are a signal something is misconfigured).
  • Kill switch on the keeper.

Closing

Building a multi-chain vault without atomic settlement was a viable strategy in 2022 because nothing better existed. In 2026 it leaves money on the table — both directly (rebalancing inefficiency) and indirectly (MEV leakage, bridge risk). The integration cost of moving to a protocol like Tesseract is real but bounded; the operational improvement compounds for the life of the product.

For the contract reference, see TesseractBuffer.vy and AtomicSwapCoordinator.vy. For broader architectural context, The State of Cross-Rollup Interoperability in 2026 is a good follow-on.