Skip to content

chore: modernize toolchain and improve the examples - #211

Draft
dfapel wants to merge 17 commits into
mainfrom
chore/modernization
Draft

dfapel wants to merge 17 commits into
mainfrom
chore/modernization

Conversation

@dfapel

@dfapel dfapel commented Jul 21, 2026

Copy link
Copy Markdown

Summary

Modernizes the examples end-to-end and makes them more suitable to fork: removes the disabled sendToken/deposit-address flows, moves the toolchain to pnpm + Node 24 + anvil, migrates the contracts to gmp-sdk 6.x, and improves the example contracts.

⚠️ Draft — blocked on @axelar-network/axelar-local-dev@3.0.0 npm publish

The local chains now run on the anvil-based axelar-local-dev v3. Until it is published, package.json targets ^3.0.0 while pnpm-workspace.yaml carries a temporary relative-path override (file:../axelar-local-dev/...) so pnpm install works locally. On publish: drop the override, regenerate pnpm-lock.yaml against the registry, and move this PR to Ready.

Changes

Toolchain modernization

  • pnpm workspace via Corepack (pinned pnpm@11.5.2), nodeLinker: hoisted so Hardhat resolves transitive Solidity deps, allowBuilds script allowlist, Node 24 via .nvmrc; npm package-lock dropped.
  • Consume anvil-based axelar-local-dev v3 (no ganache); gmp-sdk → ^6.2.0, Hardhat → ^2.22.0; declare the previously-phantom axelar-cgp-solidity dep.
  • Migrated cross-chain-lending off ganache-only APIs (ganacheOptions → anvilOptions, evm_setAccountBalance → anvil_setBalance).
  • Harness lazy-requires the cosmos/multiversx siblings; local env uses a fixed bridge fee instead of the live gas-estimation API.

Other improvements

  • Migrate deprecated flows to use new callContractWithToken flow: cross-chain-lending returns borrowed/redeemed tokens to the source-chain satellite; nft-auctionhouse routes refunds back via an action-tagged payload.
  • Known limitation (noted in the READMEs): the return/refund leg does not pay destination gas, so it is only auto-relayed locally; testnet needs an IAxelarGasService payment for that leg.
  • Added some best-practices and caveats, especially about source validation and express execution.

Docs

  • Setup instructions updated to the pnpm / Node 24 / anvil toolchain.

Testing

  • NODE_ENV=ci pnpm run test:evm — 7/7 green.
  • call-contract-gas-estimation is excluded from the local suite: its on-chain estimateGasFee() needs a gas service the local-dev doesn't yet provide (cgp 6.2.1 lacks it). Re-enable after the cgp-6.4 / gmp-sdk-6 / ITS-2 stack bump.

Breaking changes (for repo users, not a published package)

  • Requires Node 24 (.nvmrc), Corepack pnpm, and Foundry/anvil on PATH. All npm commands are replaced with pnpm.

Follow-ups

  • On axelar-local-dev@3.0.0 publish: drop the workspace override, regenerate the lockfile against the registry, move to Ready.

dfapel added 10 commits July 21, 2026 21:41
sendToken and the deposit-address flow have been disabled in all Axelar
environments and might cause loss of funds if used. Drop every usage and
endorsement, and migrate cross-chain-lending and nft-auctionhouse to
callContractWithToken for their return/refund legs.
Move to a pnpm workspace on Node 24 and consume the anvil-based
axelar-local-dev v3, dropping the ganache toolchain and npm lockfiles; also
bump gmp-sdk to 6.2.0. The gmp-sdk 6.x contract migration lands in the next
commit, since those files also carry security changes git cannot split.
Fix real vulnerabilities across the examples: an ungated addMinter,
payload-based sender impersonation in nonced-execution, arbitrary-calldata
execution in multichain-nft-mint, and a few smaller bugs. Where a proper fix
needs a source allowlist the demo cannot carry, add "authenticate your source"
caveats so forks do not ship the gap unknowingly. Also migrates the express
example to the gmp-sdk 6.x base (shared files).
Bump axelar-chains-config (^1.3.0) and mocha (^11, dev-only test runner).
test:evm is 7/7 green with these versions.
Its on-chain estimateGasFee() needs a gas service the local-dev does not yet
provide; re-enable after the cgp-6.4 / gmp-sdk-6 / ITS-2 stack bump. Also
updates the multichain-nft-mint README to the typed encode + safeMint flow.
Setup now reflects the pnpm / Node 24 / anvil toolchain. Adds a prominent
"example code only" disclaimer: these omit production concerns (notably
message-source authentication), must be reviewed and audited before use, and
should go testnet-first then mainnet with small-value transfers.
The call-contract-with-token examples used raw IERC20.transfer/transferFrom,
which revert for non-standard tokens like USDT that return no bool. Switch the
value-moving calls to the gmp-sdk SafeTokenTransfer libraries.
Make nft-auctionhouse USDT-safe (SafeTokenTransfer for the aUSDC moves) and
flag that its _executeWithToken drives shared auction state without
authenticating the source. Also corrects the call-contract-with-token caveat:
a pure pass-through needs no source authentication.
sendToken and the deposit-address flow are disabled in all Axelar environments
and might cause loss of funds if used. Update the cross-chain-lending and
nft-auctionhouse code comments and READMEs to that wording.
The npm->pnpm modernization removed package-lock.json, which broke the
npm-based verify-cli / verify-web workflows (setup-node npm cache and
`npm ci`). Port them to Corepack pnpm on Node 24 and drop the obsolete
package-lock fsevents hack. These jobs run the examples against the
axelar-local-dev harness, so they stay red until v3 is published to npm.

@joaoazevedo joaoazevedo left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CLI and web workflows install JavaScript dependencies and then start local chains without installing Foundry. Once the tarball blocker is removed, startup will fail with spawn anvil ENOENT on runners without it. Add the Foundry setup step to each affected job; it is absent from the runner’s installed-software inventory. CLI workflow, web workflow

The local chains run on anvil, which is not part of the GitHub runner
image. Without it, every job that calls start() fails with
spawn anvil ENOENT once dependencies resolve from the registry.

Add foundry-rs/foundry-toolchain to all three CLI jobs and the web job:
cosmos and multiversx also spin up local EVM chains, not just test:evm.
Groundwork for a Sui example. Inert until sui.enabled is turned on: the
flag defaults to false and no harness code reads it yet.

Fix deployOnAltChain while here. It read a hardcoded cosmos side-file
regardless of the path the example returned, and skipped the write when
that file was absent, so a non-cosmos alt chain silently lost its
deployment data and cosmos lost its first deploy on a fresh checkout.

HelloWorld.sol follows the post-audit conventions: gmp-sdk imports,
gateway() getter, commandId in the event, and abi.encode rather than
abi.encodePacked, which is not abi.decode-able as a string.
Sends a string in both directions between an EVM chain and a Sui Move
package, wired into the harness the same way cosmos and multiversx are:
a config flag, a lazy require in start, a side-file for the deployment,
an executeSuiExample, a test and a CI job.

The Move module carries the three audit verdicts this example needs.
Its execute and the Solidity _execute both get the standard demo-only
caveat, since a forged message rewrites state everyone reads.
register_transaction is deliberately permissionless and says why that is
safe - every value it registers is derived from the singleton itself.
set_remote_value carries a Sui-specific caveat with no EVM analogue: the
singleton is shared, so any account can send from its Channel, and a
destination allowlisting that Channel therefore trusts them all.

Both pnpm-workspace overrides resolve axelar-local-dev to the same path.
`networks` is module-level state there, so two copies meant SuiRelayer
iterated an empty network list and Sui to EVM silently did nothing.
Both pnpm-workspace overrides are tarballs again. Switching the core one
to link: took it out of pnpm's graph, so the override no longer reached
cosmos's and multiversx's transitive dependency and pnpm nested a second
copy for each. `networks` is module-level state in that package, so their
relayers iterated an empty network list and delivered nothing, silently -
the same failure the override exists to prevent, moved onto two other
chains. All four importers now share one copy.

The example polls for delivery in both directions and throws if either
does not arrive, replacing a fixed 15s sleep that asserted nothing. The
test's claim that delivery was covered by the sibling package was only
half true: its Sui to EVM assertion lives in the end-to-end suite that CI
excludes, so that direction was asserted nowhere. It is also faster, since
polling returns on arrival.

Harden the paths a user actually hits wrong: a missing side-file now says
to run start, a stale one says it survives --force-regenesis and to delete
it, and an undecodable payload no longer throws out of a getter.

Drop the CI job's sleep and its `sui client` setup - nothing in the stack
invokes `sui client`, and SuiNetwork.init already waits for the node - and
add a step timeout, cache restore-keys, and libpq-dev on every run rather
than only on a cache miss.

Document the exact-case trap for the Sui to EVM direction: a lowercase
chain name is approved on the EVM gateway and then never executed,
because the final lookup is case-sensitive.
pnpm updates the lockfile hash for a changed file: tarball but does not
re-extract over an existing directory, so the documented refresh command
silently kept serving the previous build.
pnpm update rewrote the declared dependency to the local tarball path.
The committed range is the real target; the workspace override is what
resolves it locally until the package is published.
axelar-local-dev v3 removed its MultiversX package, and Axelar has no
MultiversX deployment in any environment. Remove the examples, test,
CI job, config keys, script plumbing and dependencies. Also drop the
stale near-* allowBuilds entries; nothing in the lockfile uses them.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants