Skip to content

feat(solidity): Axelar Hook and ISM (closes #2851)#8913

Draft
Utkarsh-Sinha0 wants to merge 2 commits into
hyperlane-xyz:mainfrom
Utkarsh-Sinha0:feat/axelar-hook-ism
Draft

feat(solidity): Axelar Hook and ISM (closes #2851)#8913
Utkarsh-Sinha0 wants to merge 2 commits into
hyperlane-xyz:mainfrom
Utkarsh-Sinha0:feat/axelar-hook-ism

Conversation

@Utkarsh-Sinha0

@Utkarsh-Sinha0 Utkarsh-Sinha0 commented Jun 17, 2026

Copy link
Copy Markdown

feat(solidity): Axelar Hook and ISM (closes #2851)

Summary

Adds a message Hook and Interchain Security Module that transport a
Hyperlane message ID over Axelar's General Message Passing (GMP).

  • AxelarHook (origin chain) — on postDispatch, pre-pays the Axelar Gas
    Service in native tokens and calls IAxelarGateway.callContract, carrying a
    payload that invokes preVerifyMessage(messageId, 0) on the destination ISM.
  • AxelarIsm (destination chain) — inherits AxelarExecutable; the
    gateway-validated execute entrypoint authorizes the delivery by Axelar
    source chain + source address, then records verification.

This mirrors the existing OPStackHook / OPStackIsm pair, reusing
AbstractMessageIdAuthHook and AbstractMessageIdAuthorizedIsm so it slots
into the standard hook/ISM machinery with no Mailbox changes.

Before / After

Condition — before this PR: Hyperlane had no way to transport a message ID
over Axelar GMP. A route that wanted Axelar-secured verification could not be
expressed: there was no Axelar hook to emit the cross-chain call on the origin,
and no Axelar ISM to attest the message on the destination. (Bridge hook/ISM
pairs existed for OP Stack, CCIP, etc. — but not Axelar.)

Condition — after this PR: Dispatching through AxelarHook pays Axelar gas
and emits gateway.callContract; Axelar relays it; AxelarIsm.execute verifies
the message on the destination, after which the Mailbox can process it. Axelar
becomes a usable transport for the standard message-ID auth hook/ISM flow.

Metric Before After
Axelar GMP transport for Hyperlane not available AxelarHook + AxelarIsm
New production contracts 0 2
New external/npm dependencies 0 (Axelar surface vendored in-tree)
New Solidity files (contracts + vendored + mock) 0 8
Test cases exercising the pair 0 ~39 (shared ExternalBridgeTest suite + Axelar-specific)
Target coverage on the 2 new contracts n/a 100% line + branch
Mailbox / SDK / registry changes 0 (strictly in scope of #2851)
Native value bridged to destination n/a 0 by design (Axelar GMP carries none; non-zero msgValue rejected)

Acceptance conditions (definition of done):

  • forge build compiles on main toolchain (solc 0.8.33 / cancun)
  • forge test --match-contract AxelarIsmTest all green
  • forge coverage shows 100% line + branch on AxelarHook and AxelarIsm
  • Testnet demo tx hashes attached (see Demo section)

Design

Gas accounting (per the maintainer's suggestion in #2851)

The exact Axelar GMP gas cost cannot be computed on-chain — it depends on
destination gas price and token exchange rates resolved by Axelar's off-chain
gas oracle. Following the maintainer's "over-pay and let Axelar refund" hint,
quoteDispatch returns 0 and the caller instead attaches native value when
dispatching. The hook forwards the entire attached value to
payNativeGasForContractCall (an over-payment), and Axelar refunds the surplus
off-chain to the metadata refund address. Under-payment does not lose funds — it
simply stalls relaying until more gas is added via the Axelar Gas Service. A
0 quote also keeps the hook compatible with the shared ExternalBridgeTest
harness (which, like OPStackHook, assumes a zero protocol-charged quote).

Authorization / trust model

AxelarExecutable.execute asks the gateway (validateContractCall) to confirm
the Axelar network approved this exact (commandId, sourceChain, sourceAddress, payloadHash) delivery. AxelarIsm._execute then enforces:

  1. sourceChain equals the configured trusted origin chain (originChainHash);
  2. the parsed sourceAddress equals the configured authorizedHook;
  3. the payload is a preVerifyMessage call (selector check, defense-in-depth).

Because authorization comes from Axelar's validated call arguments rather than
msg.sender, preVerifyMessage is reached via a self-call guarded by a
transient _verifying flag, so its _isAuthorized() check can only pass inside
a gateway-validated, source-authorized execute. Direct external calls to
preVerifyMessage revert. Source addresses are compared as parsed address
values (not raw strings), making authorization robust to hex casing.

No native-value bridging

Axelar GMP does not deliver native value to the destination contract. To avoid
silently stranding funds, the hook rejects any non-zero metadata.msgValue, and
the ISM always records msgValue == 0.

Dependencies

Minimal Axelar interfaces (IAxelarGateway, IAxelarGasService,
IAxelarExecutable), a small AxelarExecutable base, and the AddressString
library are vendored under contracts/interfaces/axelar/, consistent with
how this repo vendors other bridge interfaces (e.g. interfaces/optimism).
Function selectors are identical to the canonical
@axelar-network/axelar-gmp-sdk-solidity, so the contracts are ABI-compatible
with the deployed Axelar contracts. If maintainers prefer the upstream
dependency, the vendored files can be swapped for SDK imports with no change to
AxelarHook / AxelarIsm.

Files

File Purpose
contracts/hooks/AxelarHook.sol origin-chain hook
contracts/isms/hook/AxelarIsm.sol destination-chain ISM
contracts/interfaces/axelar/* vendored minimal Axelar surface
contracts/mock/MockAxelar.sol gateway + gas service test doubles
test/isms/AxelarIsm.t.sol full test suite (extends ExternalBridgeTest)
script/AxelarHookDeployer.s.sol testnet deploy helper
.changeset/axelar-hook-and-ism.md changeset

Testing

cd solidity
forge build
forge test  --match-contract AxelarIsmTest -vvv
forge coverage --match-contract AxelarIsmTest \
  --no-match-coverage "(test|mock|script)/"

The suite extends ExternalBridgeTest (the shared hook/ISM harness used by
OPStackIsm) and adds Axelar-specific tests covering: constructor validation
(hook + ISM), quoteDispatch/postDispatch value rejection, gas-service
pre-payment, gateway-not-approved, untrusted source chain/address, payload
validation (short payload + wrong selector), replay rejection, direct
preVerifyMessage rejection, and the AddressString round-trip + revert paths.
Target: 100% line/branch coverage on the new contracts.

Demo (testnet)

Filled in after running RUNBOOK_testnet_demo.md.

  • Origin dispatch tx: <hash>
  • AxelarScan GMP: <link>
  • Destination execute tx: <hash>

Checklist

  • Mirrors existing OPStack hook/ISM conventions
  • No changes outside the scope of Axelar Hook and ISM #2851
  • Changeset added (@hyperlane-xyz/core)
  • NatSpec on all public/external members
  • forge test + forge coverage green locally
  • Testnet demo tx hashes added above

Test results ✅

Toolchain: forge (Foundry) + solc 0.8.33 / evm cancun (per solidity/foundry.toml).

forge test --match-contract AxelarIsmTest
Suite result: ok. 44 passed; 0 failed; 0 skipped (44 total tests)

Coverage (FOUNDRY_PROFILE=coverage forge coverage --match-contract AxelarIsmTest --no-match-coverage "(test|mock|script)/"):

File % Lines % Statements % Branches % Funcs
contracts/hooks/AxelarHook.sol 100.00% (16/16) 100.00% (15/15) 100.00% (10/10) 100.00% (3/3)
contracts/isms/hook/AxelarIsm.sol 100.00% (13/13) 100.00% (12/12) 100.00% (8/8) 100.00% (3/3)
contracts/interfaces/axelar/AxelarExecutable.sol 100.00% (10/10) 100.00% (10/10) 100.00% (2/2) 100.00% (3/3)
contracts/interfaces/axelar/AddressString.sol 100.00% (26/26) 100.00% (35/35) 100.00% (7/7) 100.00% (2/2)

All new contracts are at 100% line/statement/branch/function coverage.

Demo (testnet tx hashes) — TODO

Issue #2851 requests a testnet/mainnet demo with tx hashes. These will be
captured via RUNBOOK_testnet_demo.md (needs funded testnet keys + RPC URLs)
and added here before the PR is marked ready for review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

Axelar Hook and ISM

1 participant