7Block Labs
Blockchain Development

ByAUJay

Cross-Chain Error Handling: The New Frontier in Intent UX Debugging

Meta description: Intents are shaking things up for cross-chain user experiences, but they've got their fair share of challenges too. This guide takes you on a journey through the specific error surfaces found across CCIP, LayerZero v2, Wormhole, Axelar, IBC, and Arbitrum. You’ll get the lowdown on how to spot, fix, and avoid these hiccups, featuring practical examples and fresh standards like ERC‑7683 and the Open Intents Framework.


Why error handling is now a strategic problem

Intent-Centric UX: Simplifying User Experience

Intent-centric UX streamlines everything by zeroing in on a single user goal--think “Swap X for Y on chain Z.” So, when something doesn't go as planned, your support tickets will say something straightforward like “my intent failed” rather than getting bogged down in techy language like “the CCIP destination revert reason was empty due to OOG.” That’s exactly why it’s crucial for decision-makers to establish a strong error architecture that:

  • standardizes failures across different protocols,
  • connects events to a single intentId across multiple chains and services,
  • offers reliable and auditable recovery options that users (or bots) can trigger,
  • and aligns with the latest standards (ERC‑7683, Open Intents Framework) to reduce the need for custom integration. (eips.ethereum.org)

Let's dive into the different failure surfaces and I'll guide you on how to set up an "intent-aware" debugging stack that you can use in production.


The failure map: where cross-chain intents break (and what to do)

Think in “Protocol Slices”

Every interoperability stack has its own set of failure modes and levers that are unique to it:

  • Chainlink CCIP (DON-signed commit + execution)

    • So, if you’re working with a destination gas cap (which is usually set to 90,000 by default), just a heads up: if you go over this limit or hit any unhandled exceptions, your message will automatically switch to Manual Execution. Once you’ve figured out the receiver issues, you can tweak the gas and try re-executing through the CCIP Explorer. To catch those pesky out-of-gas errors, keep an eye out for the ReceiverError with empty revert data (0x). For all the juicy details, check it out here: (docs.chain.link)
  • LayerZero v2 (DVNs + Executor; OApps)

    • If you find your channels aren’t set up just right (look for “LzDeadDVN” in the defaults), your quoteSend calls are going to keep tripping you up until you get those DVNs and confirmations dialed in on both sides. Also, don’t overlook non-blocking receive patterns, executor gas, and resume flows. To avoid those pesky dead DVNs before they start causing trouble, use LayerZero Scan’s Default Config Checker. Want to dive deeper? Check out the details here: (docs.sei.io)
  • Wormhole Relayer (delivery providers + redelivery)

    • If a delivery is running low on gas or just needs a little extra boost, you can easily call resend() with a higher gasLimit or switch to a different provider. The API provides you with the quoteEVMDeliveryPrice and a deliveryHash for your receiver to log. For all the specifics, take a look here: (wormhole.com)
  • Axelar GMP (validator network + relayers)

    • Keep an eye out for two common stuck states: things not being approved (usually due to some relay hiccups) and those pesky execution failures or running low on gas at the destination. The good news? You can sort both issues through the Axelarscan UI. Just use options like “Approve,” “Execute,” and “Add Gas.” If you’re into coding, you can also handle it programmatically with the AxelarGMPRecoveryAPI. For more details, check this out: (docs.axelar.dev)
  • IBC (light client proofs; ack/timeout semantics)

    • In IBC, whether you get a success or an error acknowledgment, they’re treated the same. Timeouts will kick off callbacks on the source side as well. So when you're building your apps, it's super important to ensure they can send out predictable error acknowledgments and properly handle OnTimeoutPacket for any refunds or reversals you might need. If you want to dive deeper, check this out: (evm.cosmos.network)
  • Arbitrum L1→L2 (retryable tickets)

    • Sometimes auto-redeeming can hit a snag because of those pesky fee spikes, but no worries! You’ve got a whole week (7 days) to redeem your tickets, and you can also choose to extend or cancel if you need to. It’s smart to set up some monitors for TicketCreated and RedeemScheduled events, and don’t forget to support manual redeem flows too. Check out all the details here: (docs.arbitrum.io)

Practical debugging playbooks (copy/paste ready)

1) CCIP: Diagnose OOG vs logic bugs, then override gas and replay

  • Symptom: If CCIP Explorer is saying “Ready for manual execution” and you see a revert reason of 0x, that usually means you’ve run out of gas (OOG). Try bumping up the gas limit and then manually kick off the execution in the Explorer. If there’s a hiccup in your receiver logic, just tweak what you need, deploy the fix, and hit replay. You can dive deeper into this topic here: (docs.chain.link)
  • Defensive receiver: It’s a good idea to keep track of any failures so you can retry them later; make sure you’ve got a retry function that’s managed either by the owner or through governance.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/CCIPReceiver.sol";
import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol";

contract DefensiveCcipReceiver is CCIPReceiver {
    error OnlyRouter();
    mapping(bytes32 => Client.Any2EVMMessage) public failed;
    event MessageFailed(bytes32 indexed messageId, bytes reason);
    event MessageProcessed(bytes32 indexed messageId);

    constructor(address router) CCIPReceiver(router) {}

    function _ccipReceive(Client.Any2EVMMessage memory m) internal override {
        try this.handle(m) {
            emit MessageProcessed(m.messageId);
        } catch (bytes memory reason) {
            failed[m.messageId] = m;
            emit MessageFailed(m.messageId, reason);
            // no revert: let CCIP mark as failed and eligible for manual execution
        }
    }

    function handle(Client.Any2EVMMessage memory m) external {
        if (msg.sender != address(this)) revert OnlyRouter();
        // ...do work; require adequate gasLimit on destination or this will OOG...
    }

    function retry(bytes32 messageId) external {
        Client.Any2EVMMessage memory m = failed[messageId];
        require(m.messageId != bytes32(0), "no such");
        delete failed[messageId];
        this.handle(m); // will revert if still bad
    }
}
  • Operational tip: When you're dealing with token transfers and handling receiver logic, it's best to stick to CCIP’s default gas limit of 90k. If you suspect you’ll need a bit more, feel free to set a higher gas limit when you send the transaction. Otherwise, you might find yourself having to trigger a manual execution. For more info, check it out here.

2) LayerZero v2 OApps: Fix dead DVNs, make receives non-blocking, tune executor gas

  • Symptom A (wiring): If you're encountering quoteSend reverts and your Default Config Checker highlights LzDeadDVN, don’t sweat it! Simply configure your DVNs/confirmations in layerzero.config.ts and try the wiring again. You can find all the nitty-gritty details here.
// layerzero.config.ts (excerpt)
const EVM_ENFORCED_OPTIONS = [
  { msgType: 1, optionType: ExecutorOptionType.LZ_RECEIVE, gas: 150_000, value: 0 }
];
const pathways: TwoWayConfig[] = [[
  optimismContract,
  currentContract,
  [['LayerZero Labs'], []], // required DVNs, optional DVNs
  [1, 1],                    // confirmations in each direction
  [EVM_ENFORCED_OPTIONS, EVM_ENFORCED_OPTIONS],
]];
  • Symptom B (destination OOG / reverts): Consider using a non-blocking receive to store any payloads that didn’t go through. This keeps things flowing smoothly and allows you to continue from where you left off without stalling the channel.
// Nonblocking pattern (v2-compatible idea; simplified)
abstract contract NonblockingLzAppUpgradeable /* is LzAppUpgradeable */ {
    event MessageFailed(uint16 srcEid, bytes srcAddr, uint64 nonce, bytes payload, bytes reason);
    mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) internal failed;
    function _blockingLzReceive(
        uint16 srcEid, bytes memory srcAddr, uint64 nonce, bytes memory payload
    ) internal virtual /* override */ {
        (bool ok, bytes memory reason) = address(this).call(
            abi.encodeWithSignature("nonblockingLzReceive(uint16,bytes,uint64,bytes)", srcEid, srcAddr, nonce, payload)
        );
        if (!ok) {
            failed[srcEid][srcAddr][nonce] = keccak256(payload);
            emit MessageFailed(srcEid, srcAddr, nonce, payload, reason);
        }
    }
    function retryPayload(uint16 srcEid, bytes calldata srcAddr, uint64 nonce, bytes calldata payload) external {
        require(failed[srcEid][srcAddr][nonce] == keccak256(payload), "no failed");
        delete failed[srcEid][srcAddr][nonce];
        this.nonblockingLzReceive(srcEid, srcAddr, nonce, payload);
    }
    function nonblockingLzReceive(uint16,bytes memory,uint64,bytes memory) public virtual;
}
  • Risk Note: Just a quick heads up--if your gas provision is too low, it can really throw a wrench in your channels. Audits have revealed that attackers can use minimal gas to trigger stored payloads unless you're using non-blocking patterns and have a solid minimum gas requirement in place. So, always ensure your executor options are set high enough to tackle those worst-case situations. For more info, check it out here.
  • Tooling: If you need some useful tools, LayerZero Scan is here for you! They offer default configurations and APIs perfect for runtime diagnostics. Plus, their CLI includes a command, lz:oapp:config:get, which makes it easy to identify any mismatches. Check it out here.

3) Wormhole Relayer: Redelivery with more gas/value

  • Symptom: You’re noticing some destination calls that are either out of gas (OOG) or they need a little extra value for the receiver.
  • Fix: Simply call resend() with a new gasLimit or switch up your delivery provider. To figure out the nativePriceQuote, make sure to use quoteEVMDeliveryPrice. And hey, don’t forget to log the deliveryHash in receiveWormholeMessages for easier tracking!
IWormholeRelayer relayer = IWormholeRelayer(RELAYER);
(bytes32 digest, /*...*/) = abi.decode(payload, (bytes32, /*...*/));

function redeliver(VaaKey memory key, uint16 dstChain, uint256 gasLimit) external payable {
    (uint256 price,) = relayer.quoteEVMDeliveryPrice(dstChain, 0, gasLimit);
    relayer.resend{value: price}(
        key, dstChain, TargetNative.wrap(0),
        encodeEvmExecutionParamsV1(EvmExecutionParamsV1(Gas.wrap(uint(gasLimit)))),
        address(0) // default provider
    );
}
  • Take a look at resend(), quoteEVMDeliveryPrice, and the IWormholeReceiver's receiveWormholeMessages signature (and make sure to remember the deliveryHash). You can find all the details here!

4) Axelar GMP: Query status, add gas, or manually execute

  • Symptom: Your transaction is stuck--it’s either unapproved or failed at the destination. This usually happens because of gas issues or some sort of logic glitch.
  • Fix: Swing by the UI where AxelarScan gives you some handy options like “APPROVE,” “Execute,” and “Add gas.” If you’re feeling adventurous and want to jump into the code, check out AxelarGMPRecoveryAPI. It’s got some useful features like queryTransactionStatus, manualRelayToDestChain, and you can even top up your gas. For more details, you can check it out here.
import { AxelarGMPRecoveryAPI, Environment } from "@axelar-network/axelarjs-sdk";

const recovery = new AxelarGMPRecoveryAPI({ environment: Environment.MAINNET });

const s = await recovery.queryTransactionStatus(srcTxHash);
if (s.status === "GAS_PAID_NOT_ENOUGH_GAS") {
  await recovery.addNativeGas({ txHash: srcTxHash, amount: "0.02" }); // chain-dependent
}
if (s.status === "APPROVAL_REQUIRED") {
  await recovery.manualRelayToDestChain({ txHash: srcTxHash });
}
if (s.executionInfo?.status === "ERROR_EXECUTION") {
  // fix destination logic, then:
  await recovery.executeOnDestChain({ txHash: srcTxHash, overrideGas: 500000 });
}
  • Error taxonomy: Let’s dive into the difference between “Insufficient gas” and “destination contract error," plus I’ve got some handy tips for using Tenderly to track those pesky traces. Don’t miss out; check it out here: (docs.axelar.dev)

5) IBC apps: Treat acks and timeouts as first-class error channels

  • Key idea: Don’t forget to have your app’s OnRecvPacket send back an Acknowledgement, no matter if it’s a success or an error. The source chain will later trigger OnAcknowledgementPacket or OnTimeoutPacket, so make sure you’ve got both of those set up to manage compensation logic and user refunds. (evm.cosmos.network)

Implementation Notes:

  • Set Up the Environment: Make sure you've got all the software you need up and running--this means Node.js and npm. Double-check that you're using the right versions for the project.
  • Clone the Repository: Go ahead and snag a copy of the code by running:

    git clone https://github.com/your-repo/project.git
  • Install Dependencies: Head over to your project directory and grab the necessary packages:

    cd project
    npm install
  • Configuration: If you don't already have a configuration file, go ahead and create one and fill in all the required details. You can check out the sample file included in the repo for some guidance.
  • Run the Application: To kick things off, start the server by using:

    npm start

You can check it out running at http://localhost:3000!

  • Testing: Make sure to run your tests to confirm that everything's functioning properly:

    npm test
  • Deployment: Once you’re all set to deploy, don’t forget to check out the guidelines in the README.md. It’s got some great best practices and any specific tweaks you might need for your setup.

Additional Resources:

  • Dive into the documentation for a deeper look at everything we offer.
  • If you're running into issues, head over to our FAQ page or jump into the community forum for some help.

Reminder:

Make sure to keep your local branch synced up with the main branch to steer clear of any merge conflicts later on. Happy coding!

  • Go ahead and whip up some middleware to take care of cross-cutting concerns like fees, using ICS‑29 and callbacks. Just remember to wrap WriteAcknowledgement so that any error payloads are all nice and standardized. If you need the details, check this out: (docs.cosmos.network).
  • It's also wise to stick with the suggested Acknowledgement envelope (that's result|error), and definitely include the intentId along with some deterministic error codes. This way, your indexer can easily line up the user-visible status. For more on that, take a look here: (docs.cosmos.network).

6) Arbitrum retryables: Monitor, redeem, and set refund addresses safely

  • Lifecycle: First, kick things off with createRetryableTicket (L1). After that, try to auto-redeem it at L2. If that doesn’t pan out, you can still redeem it manually if necessary. Just remember, you’ve got about 7 days before it expires, but you can always extend that. Keep an eye on TicketCreated and RedeemScheduled, and don’t forget to include a manual redeem option in your UI. (docs.arbitrum.io)

Operational Cautions and Mitigations:

When it comes to tackling operational risks, keeping an eye out for potential pitfalls is crucial. Take a look at these important cautions and some handy strategies you can employ to steer clear of issues:

1. Identify Risks Early

  • Keep an eye out for any potential risks. The sooner you notice them, the easier they’ll be to deal with.
  • Make it a habit to regularly check your processes and systems. This way, you can catch any red flags before they become a problem.

2. Engage the Team

  • Bring your team into the mix! They've got some great insights and can help spot risks that you might’ve missed.
  • Foster a culture of open communication, making sure everyone feels at ease when sharing their concerns.

3. Develop a Response Plan

  • Seriously, don’t wait for a crisis to come knocking at your door. Having a solid response plan all set can save you a whole lot of stress down the line.
  • It’s super important that the plan is easy to understand and that everyone knows their role if things take a turn for the worse.

4. Monitor Performance

  • Stay updated on what's happening. Use metrics to monitor performance and catch any drops that might indicate trouble.
  • Regular check-ins can help you nip potential issues in the bud before they blow up.

5. Train for Success

  • Put some resources into training your team. When they’re well-prepared to tackle challenges, your operations will run a lot more smoothly.
  • Organize regular training sessions and updates to keep everyone’s skills fresh.

6. Review and Adjust

  • Once an incident occurs, make sure to take a moment to reflect on what transpired. What went well? What might you improve next time?
  • Use those insights to tweak your processes and response plans for the future.

If you keep these tips in mind and put these strategies into action, you'll be set up for smooth sailing and will reduce those pesky risks.

  • Attackers can really stir things up by canceling retryables or cutting off gas. To keep this from happening, make sure you set the callValueRefundAddress and admin correctly. Also, it's smart to avoid any patterns that might give griefers an advantage. Creating playbooks for handling cancel and extend flows is a solid move, too. For more details, take a look here: (code4rena.com)

Make failures “intent-native”: a uniform correlation model

Normalize Every Cross-Chain Hop into a Single Intent Timeline:

When you're navigating through the world of cross-chain interactions, keeping track of everything can feel like a maze. So, let's break it down and get every single hop lined up neatly on one timeline. Here’s how you can do that:

  1. Identify Your Start Point: Know where your assets or data are coming from. This is your jumping-off point.
  2. Map Out Each Hop: For every chain you're interacting with, list out the hops. This includes:

    • The chain you're starting on
    • The destination chain
    • Any intermediary chains or layers involved
  3. Timestamp Each Event: Assign a timestamp to each event or action taken during these hops. It’s like making a mark on the timeline for every significant moment.
  4. Create a Visual Timeline: Use tools or platforms that allow you to visualize these hops. This can be as simple as a flowchart or a more complex Gantt chart, depending on what you’re comfortable with.
  5. Document Outcomes: After completing each hop, jot down the results. Did everything transfer smoothly? Were there any hiccups? This can help you refine the process for next time.

By normalizing every cross-chain hop into this single intent timeline, you'll have a clearer picture of your journey and can optimize your strategy for future transactions. Keep it organized, and you’ll make cross-chain interactions a breeze!

  • Primary key: intentId

    • If you're working with ERC‑7683, you can create your intentId using intentId = keccak256(ResolvedCrossChainOrder). Just remember to hang on to it for both the origin and destination events. You can find more details over here: (eips.ethereum.org).
  • Attach protocol breadcrumbs:

    • CCIP: Make sure to snag the ccipMessageId and the transaction hash from the receiving chain. Don’t forget to jot down “manualExecutionNeeded” and the suggested gas amount. If you need more details, check out the docs here: (docs.chain.link).
    • LayerZero: To get started, make sure you have your source and destination eids, nonce, and the endpoint message hash ready. If you're using a non-blocking stored setup, don’t forget to tick off the “stored payload” flag. Oh, and it’s super handy to note the DVN set and confirmations when you send it out--trust me, you'll thank yourself later! Check out the details here: (docs.layerzero.network).
  • Wormhole: Make sure to note down the sequence, emitter, and deliveryHash. Don't forget to jot down the provider address and gasLimit too, just in case you need to handle any redelivery scenarios. For more info, check it out here: (github.com).

    • Axelar: Be sure to jot down the source transaction hash, if the call got the green light, the gas fees, the execution status, and any refunds you received. Also, keep track of any recovery actions you took. For more details, check this out: (docs.axelar.dev).
    • IBC: Make sure to jot down the port, channel, and sequence, plus the ack.error or the timeout proof height. You can find more details over here: (evm.cosmos.network).
    • Arbitrum: Make sure to jot down the ticketId, the outcome of the autoRedeem (was it a hit or a miss?), any manual redeem transactions, and the expiry timestamp. You can grab all the details right here: (docs.arbitrum.io).

When you're gearing up for an event, having a solid plan can really make a difference in how everything goes down. That's where the Event Schema steps in to help! Here’s a simple overview of the schema we recommend using.

Basic Structure

Here’s a simple breakdown of how your event schema should be structured:

{
  "@context": "https://schema.org",
  "@type": "Event",
  "name": "Sample Event Name",
  "startDate": "2023-10-10T19:00",
  "endDate": "2023-10-10T21:00",
  "eventAttendanceMode": "https://schema.org/OnlineEventAttendanceMode",
  "eventStatus": "https://schema.org/EventScheduled",
  "location": {
    "@type": "Place",
    "name": "Sample Location",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "123 Main St",
      "addressLocality": "Anytown",
      "addressRegion": "CA",
      "postalCode": "90210",
      "addressCountry": "US"
    }
  },
  "image": "https://example.com/image.jpg",
  "description": "A brief description of the event goes here.",
  "offers": {
    "@type": "Offer",
    "url": "https://example.com/tickets",
    "price": "10.00",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock",
    "validFrom": "2023-09-01T12:00"
  }
}

Key Properties

  • @context: This sets up the context for your schema. It’s a good idea to use https://schema.org for this.
  • @type: Make sure to always use Event here. This tells everyone that you're dealing with event data.
  • name: Come up with a fun and catchy name for your event - it’s the first impression attendees will get!
  • startDate & endDate: Make sure to specify when your event begins and when it comes to a close.
  • eventAttendanceMode: This is super handy for telling apart in-person events from those happening online. If you're dealing with an online event, just go with OnlineEventAttendanceMode.
  • eventStatus: Here’s where you can choose the status of your event--whether it’s on the calendar or if you’ve had to cancel it.
  • location: Share the specifics on where the event is taking place. If it’s an online event, you might want to give a little info about the platform being used.
  • image: Use a vibrant and relevant image to grab attention! It should really pop and connect with your audience.
  • description: Share a brief overview of what this event entails.
  • offers: Make sure to add the ticket or registration details, plus the pricing info.

Final Thoughts

Using this schema is a great way to help search engines get a clearer picture of your event. It can really boost your online visibility! Plus, it makes sure everyone’s on the same page with the details, cutting down on confusion and making your event more accessible. Good luck with planning your event!

event IntentTrace(
  bytes32 indexed intentId,
  bytes32 protocolMsgId, // e.g., CCIP messageId, Wormhole deliveryHash, Axelar GMP id
  uint256 stage,         // enum: SENT, APPROVED, EXECUTED, MANUAL_NEEDED, ERROR_ACK, TIMEOUT
  bytes   data           // ABI-encoded protocol-specific context (gasLimit, provider, ack.error, etc.)
);

This lets your ops console show a single timeline for every user intent, regardless of the protocol they're using.


Observability that actually works in production

  • Explorers you should check out:

    • Don’t miss the LayerZero Scan Tools/API for quick defaults and message lookups. Be sure to add the Default Configs by Chain view to your internal runbooks. You can find it at layerzeroscan.com.
    • Make sure to integrate Axelarscan links into your app for self-service options like “Approve / Execute / Add Gas.” You’ll find more details at docs.axelar.dev.
    • Check out the Wormhole docs to get the scoop on quoting delivery prices and redelivery info. Consider adding a “Bump gas & redeliver” button that utilizes your own pricing hints. Dive in at wormhole.com.
  • Tracing:

    • Don’t forget to generate an intentId for every on-chain event and make sure to log it in your off-chain solver logs. It’s a good idea to use OpenTelemetry baggage to pass the intentId from your API all the way to the solver and relayer.
  • Health SLOs (our commitments to clients):

    • We target a P95 time from “intent opened → destination executed” for every route.
    • Our aim is to hit a P99 redelivery success rate after the first failure, thanks to the automated gas bump (shoutout to Wormhole/CCIP!).
    • We’re working to keep manual user actions under 0.1% of retail flows; everything else will be managed through enterprise dashboards.

Design patterns that prevent incidents (not just fix them)

Key Recommendations

  • Make Sure It's Idempotent

    • Use intentId gating on the destination receivers to prevent any double execution when redelivering or retrying.
  • Always Support “Operator-Triggered” Recovery

    • CCIP: Don’t forget to expose a runbook for Manual Execution that includes gas overrides. And, make sure you keep track of precomputed overrideGas by path. You can read more about it here.
    • Axelar: Make sure to integrate AxelarGMPRecoveryAPI in your back office and utilize manualRelayToDestChain for any classes that are known to get stuck. For more details, check it out here.
    • Wormhole: You’ll want to implement a resend() function that picks a new provider or bumps up the gas based on recent block gas. You can find all the details here.
  • Non-Blocking by Default on LayerZero

    • Take advantage of non-blocking receive scaffolds and make sure to set a minimum executor gas to avoid any griefing issues. And hey, remember to monitor those DVN sets in your config drift trackers. You can find more details over on Sepolia Etherscan.
  • IBC Acknowledgements as Product Surface

    • Make sure error acknowledgements pop up for users along with helpful messages they can act on. Also, set up an automatic refund process that includes receipts when there's a timeout. For more details, check it out here.
  • Arbitrum Retryables

    • Think about setting up some automated redeemer bots. If you're still facing issues as the 6-day mark approaches, make sure to contact SRE to get an extension. Also, it's a good idea to have a plan ready to cancel any malicious tickets safely. You can find more info here.

Standards you should adopt now

  • ERC‑7683 (Cross-Chain Intents)
    This proposal focuses on creating a standard for order structures, the Open event, and settlement interfaces. What does this mean for you? You’ll end up with a tidy intentId plus shared filler/solver semantics. If you’ve got some order data lying around, now’s the perfect time to start mapping it to ResolvedCrossChainOrder. You can dive into the details here.
  • Open Intents Framework (OIF)
    This is an exciting collaborative project launched by the Ethereum Foundation that’s all about giving you a modular approach. It includes reference solvers and settlement modules that work seamlessly with ERC‑7683, plus it's supported by some of the big players in the Layer 2 space. Think of it as your go-to framework for exploring new intent features. Want to dive deeper? Check it out here.

What This Gets You Right Away:

  • Instant Access: Jump right into all the exciting stuff without any delays.
  • Exclusive Content: Score content that’s not available to just anyone.
  • Early Updates: Be in the loop first about new features and any changes coming your way.
  • Direct Support: Got questions? You’ll enjoy priority assistance whenever you need it.
  • We’ve cut down on the custom glue that connects solvers and bridges,
  • Introduced a universal language for error states,
  • You can now swap out verification backends (DVN-based, optimistic, light-client) for each route--no more fiddling with your app surface! (blog.ethereum.org)

Brief, in‑depth examples across stacks

  • “CCIP stablecoin + call” flow

    • Just a heads-up: make sure to set your gasLimit nice and high for the token pool’s releaseOrMint and receiver functions. If you’re still hitting those pesky out-of-gas errors on those busy chains, you can always run it manually through the Explorer with some overridden gas settings (just for operators). And don’t forget to adjust your business logic inside ccipReceive to keep everything under the steady-state cap. For all the details, check it out here.
  • “LayerZero OFT send → dead DVN”

    • If you’re running into a quoteSend error, it usually means your DVNs are a bit out of sync. No worries! We can sort that out. Just hop into layerzero.config.ts, re-wire everything, double-check your peers, re-quote, and don’t forget to increase the executor LZ_RECEIVE gas to cover any unexpected bumps along the way. If you need more details, check out the full guide here.
  • “Wormhole delivery underpriced”

    • If you’re dealing with underpriced delivery, a good move is to redeliver with a higher gasLimit using resend(). Just make sure to track the deliveryHash and your intentId so your dashboards can display “redelivery #n.” You can find all the details here.
  • “Axelar callContractWithToken underfunded”

    • You have a couple of options: you can either add gas via Axelarscan or the SDK, or just hit Execute on the destination when things settle down. Only reach out for a contract fix if there’s a logic error in the executionReason. For more info, check it out here.
  • “IBC Timeout Refund”

    • When OnTimeoutPacket kicks in, that's your cue to set off the refund process. Don’t forget to publish an IntentTrace featuring the TIMEOUT stage. It's a good idea to include info like height and sequence so your users are well informed. You can find more details here.
  • “Arbitrum auto-redeem failed”

    • If you’re running into issues with the redeemer, it’s worth giving it another shot. But if you keep getting those pesky reverts, you might need to update the L2 target and give the redeem another go. If the expiry is creeping up on you, go ahead and extend it. Just don’t forget to check the TicketCreated and RedeemScheduled receipts. You can find more info here.

What “great” looks like in 2025

  • Unified Intent Console

    • Take a look at the streamlined timeline that's organized by intentId. It comes packed with useful deep links like LayerZero Scan, Axelarscan, CCIP Explorer, your IBC channel view, and the Arbitrum retryable panel. You can check it out here.
  • Automated Remediations

    • Our smart bots are always on patrol, keeping an eye out for familiar signatures like CCIP ReceiverError 0x, Axelar GAS_PAID_NOT_ENOUGH_GAS, and Wormhole revert. When they spot an issue, they spring into action with preapproved fixes, such as gas bumps or switching providers--all under the watchful eye of circuit breakers. Curious for more info? You can dive into the details here.
  • Standards-First Development

    • For new flows, we suggest going with ERC‑7683; it’s really straightforward to integrate with the OIF reference solver. Also, it's a smart move to keep your protocol adapters light and easy to swap out. You can find more details here.

7Block Labs’ quick-start checklist

  • Let's go ahead and adopt ERC‑7683 order structs and make sure we’re emitting intentId everywhere. You can check out the specifics here.
  • Next up is LayerZero v2. We need to wire up DVNs explicitly, verify Default Configs by Chain, and roll out non-blocking receives while ensuring we keep up with LZ_RECEIVE gas. More details can be found here.
  • When it comes to CCIP, let’s budget some gas for those worst-case receiver paths. Plus, we should document our Manual Execution runbook with the Explorer. Get the scoop here.
  • For Wormhole, we need to integrate resend() and quoteEVMDeliveryPrice. And definitely remember to log deliveryHash for better tracking. You can find all the details here.
  • With Axelar, let’s embed AxelarGMPRecoveryAPI in our operations and allow users to self-serve through Axelarscan. You can check it out here.
  • Moving on to IBC, we should formalize those error ack envelopes and put deterministic refunds in place for any timeouts. Relevant info is available here.
  • Last but not least, for Arbitrum, we need to deploy those redeemer bots, keep track of ticket lifecycles, and secure those refund addresses. More info can be found here.

If you're on the hunt for a dependable intent console, some handy recovery automations, or want to get on the same page with ERC‑7683/OIF, you’ve come to the right place! 7Block Labs offers tailored reference implementations and SRE runbooks that are designed specifically for your stack.


References and further reading

  • ERC‑7683: Cross‑Chain Intents (spec + site). Give it a look here: (eips.ethereum.org)
  • Open Intents Framework (announcement and site). You can check out more details here: (theblock.co)
  • Chainlink CCIP manual execution and receiver error semantics. Get into the nitty-gritty here: (docs.chain.link)
  • LayerZero v2 wiring, DVNs, and troubleshooting; plus, a few handy scanning tools. Scoop up more info here: (docs.sei.io)
  • Wormhole Relayer resend and delivery pricing; take a peek at the receiver interface. More details here: (wormhole.com)
  • Axelar transaction recovery and error debugging. Find what you’re looking for here: (docs.axelar.dev)
  • IBC packet lifecycle, including acks and timeouts. Learn all about it here: (evm.cosmos.network)
  • Arbitrum retryable ticket lifecycle and manual redeem. Uncover the details here: (docs.arbitrum.io)

Like what you're reading? Let's build together.

Get a free 30-minute consultation with our engineering team.

7BlockLabs

Full-stack blockchain product studio: DeFi, dApps, audits, integrations.

7Block Labs is a trading name of JAYANTH TECHNOLOGIES LIMITED.

Registered in England and Wales (Company No. 16589283).

Registered Office address: Office 13536, 182-184 High Street North, East Ham, London, E6 2JA.

© 2026 7BlockLabs. All rights reserved.