Axelar GMP (Hedera)
GMP pattern: encode a payload on the source chain, pay Axelar gas in native token, call the Gateway, then decode and act on the destination behind an allowlisted source.
Source app → BridgeSender (pay gas + gateway.callContract)
→ Axelar relayers
→ AxelarExecutable._execute → Handler
On Hedera, orchestration often pairs GMP with HSS (0x16b) so a contract can self-reschedule and dispatch each cycle. Keep bridge transport behind a thin sender interface so the orchestrator stays bridge-agnostic.
Quick Reference
| Piece | Role |
|---|---|
IAxelarGateway |
callContract(destChain, destAddress, payload) |
IAxelarGasService |
payNativeGasForContractCall{value} before the gateway call |
AxelarExecutable |
Destination base; override _execute(...) |
| Chain names | Axelar string IDs — e.g. "hedera", "ethereum-sepolia" (not numeric chain IDs) |
| Addresses | EVM string form of the peer contract (checksummed 0x…) |
See references/hedera-axelar.md for chain-name conventions and SDK imports. See references/examples.md for sender / receiver sketches.
Critical: Gas Then Gateway
Always pay gas before callContract. Use msg.value > 0 for relay fees; refund address should be the funded orchestrator (or plan owner), not a random EOA.
gasService.payNativeGasForContractCall{ value: msg.value }(
address(this), // sender contract
destinationChain, // Axelar name string
destinationAddress, // receiver 0x… as string
payload,
refundAddress // usually authorizedCaller / orchestrator
);
gateway.callContract(destinationChain, destinationAddress, payload);
Critical: Authorize And Allowlist
| Side | Guard |
|---|---|
| Source sender | Only authorizedCaller (orchestrator) may send |
| Destination receiver | Validate srcChain + srcAddress (case-insensitive) before handling |
| Destination handler | Only the receiver may call business logic (authorizedCaller) |
Compare Axelar chain/address strings with a shared lowercase hash — Axelar may vary casing.
require(
keccak256(bytes(_toLower(srcChain))) == keccak256(bytes(_toLower(expectedSourceChain))),
"invalid source chain"
);
require(
keccak256(bytes(_toLower(srcAddress))) == keccak256(bytes(_toLower(expectedSourceAddress))),
"invalid source address"
);
Architecture: Split Transport From Business Logic
Source chain Destination chain
───────────────────────────── ─────────────────────────────
Orchestrator ──send──► MessageReceiver (_execute)
│ BridgeSender iface │ Handler iface
BridgeSender Handler (swap / settle / …)
- Deploy handler first (no bridge knowledge).
- Deploy receiver with gateway + expected source + handler.
- Deploy sender with gateway + gas service + destination chain/address.
- Deploy orchestrator with sender address; authorize orchestrator on sender.
- Wire after both deploys: set destination on sender; set expected source on receiver.
- Fund orchestrator (native for Axelar gas) and handler (tokens/capital for destination work).
Payload Contract
Agree a single ABI encode/decode on both sides. Keep payloads versionable if you evolve fields — mismatched decode bricks the route.
bytes memory payload = abi.encode(/* agreed fields… */);
// receiver:
(/* fields… */) = abi.decode(payload, (/* matching types… */));
Hedera Orchestration Hook (HSS)
When the source app schedules itself via HSS (0x16b), each cycle should:
- Enforce interval / plan active
- Call the bridge sender with native value for Axelar gas
- Reschedule next expiry with
hasScheduleCapacity+scheduleCall(SUCCESS =22)
If scheduling fails, mark a recovery flag and expose a manual reschedule for the plan owner.
Checklist
- Gas paid with native value before
callContract - Destination chain name matches Axelar’s ID (string, not numeric chain ID)
- Peer addresses set after both contracts deploy (wire step)
- Source allowlist + authorized callers on both ends
- Payload encode/decode identical
- Orchestrator funded for relay gas; destination handler funded for work
- Demo vs production: clarify who owns destination proceeds
References
- references/examples.md — sender / receiver / allowlist sketches
- references/hedera-axelar.md — chain names vs IDs, SDK imports
- Axelar GMP overview
- axelar-gmp-sdk-solidity