Skip to content

Add official cross-chain skill: CCTP V2, Axelar GMP/ITS, NEAR Intents - #67

Open
kaankacar wants to merge 11 commits into
mainfrom
feat/cross-chain-skill
Open

Add official cross-chain skill: CCTP V2, Axelar GMP/ITS, NEAR Intents#67
kaankacar wants to merge 11 commits into
mainfrom
feat/cross-chain-skill

Conversation

@kaankacar

Copy link
Copy Markdown
Contributor

Closes #66.

What's in this PR

  • skills/cross-chain/SKILL.md (60 lines) — router: a rail-selection decision table (native USDC -> CCTP; contract calls -> Axelar GMP; multichain tokens -> Axelar ITS; intent-based swaps -> NEAR Intents), the five pitfalls every rail shares (address formats, decimals, trustlines, asynchrony, testnet-first), and the NEAR Intents section at routing depth with status-sensitive wording (including the Stellar MEMO-mode deposit requirement, where a missing memo means an uncredited deposit).
  • skills/cross-chain/cctp.md (207 lines) — contracts and addresses for domain 27 (mainnet + testnet), the fund-loss rule (CCTP carries raw 32-byte addresses, assumes mintRecipient is a contract, so Stellar recipients require CctpForwarder as both mintRecipient and destinationCaller with the real recipient in hook data), the hook byte layout with Circle's validated builder, 7-vs-6 decimal semantics in both directions with worked examples, full outbound and inbound flows including the verified deposit_for_burn argument order, the one-signature Soroban wrapper pattern, and Iris polling with its quirks (null address fields on Stellar legs, the base58-vs-hex hash normalization trap, insufficient_fee delays).
  • skills/cross-chain/axelar.md (141 lines) — GMP send (pay_gas then call_contract) and receive (Executable + validate_message, plus the allowlist caveat that Axelar authenticates the sender, not whether you should trust them), and ITS (new interchain tokens, canonical registration of existing Stellar tokens, interchain_transfer, flow limits, hub mode). Addresses deliberately resolve from Axelar's directory instead of being hardcoded.
  • Site: new "Cross-Chain" filter category (FilterType, FILTERS, the scss @each list) and an official SKILL_CARD_SOURCES entry.

Sources and verification

  • Content is grounded in the Circle CCTP Stellar reference + contracts reference, the developers.stellar.org cross-chain page, Axelar's Stellar GMP/ITS guides, and the NEAR Intents 1Click API docs.
  • CCTP testnet addresses cross-check exactly between Circle's reference and the tested demo config in ElliotFriend/stunning-octo-carnival; the demo is featured in cctp.md as the worked reference implementation (it runs both directions on testnet, and several snippets are adapted from it, including the verbatim wrapper contract).
  • All three files stay under the 500-line guidance from Align skills with Anthropic's 500-line guidance and add evals #42.
  • pnpm lint:ts, pnpm lint, and pnpm build pass; the card server-renders in out/index.html with data-category="Cross-Chain", and llms.txt picks up the router plus both companion files.

Heads-up

#49 and #55 also touch skills.ts, FILTERS, and styles.scss (Wallets and DeFi categories). Whichever lands last takes a small append conflict; happy to resolve on whichever side needs it.

New official skill following the smart-contracts router pattern: a
SKILL.md decision table plus shared pitfalls, with companion deep
dives for Circle CCTP V2 (domain 27, CctpForwarder requirement, hook
data layout, 6-vs-7 decimal semantics, Iris polling, both transfer
directions) and Axelar (GMP send/receive signatures, ITS deployment
and canonical registration, flow limits). NEAR Intents is covered at
the routing level with status-sensitive wording.

CCTP contract addresses are cross-checked between Circle's reference
and the tested demo at ElliotFriend/stunning-octo-carnival, which the
skill features as its worked reference implementation.

Site: new Cross-Chain filter category and official skill card.
Closes #66.
Copilot AI review requested due to automatic review settings July 20, 2026 15:46
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://stellar.github.io/stellar-dev-skill/pr/pr-67/

Built to branch gh-pages at 2026-08-04 16:39 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds official guidance for cross-chain development on Stellar.

Changes:

  • Adds routing guidance for CCTP, Axelar, and NEAR Intents.
  • Documents CCTP and Axelar integration flows.
  • Adds the Cross-Chain site category and skill card.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
skills/cross-chain/SKILL.md Adds rail selection and shared pitfalls.
skills/cross-chain/cctp.md Documents CCTP V2 flows and safety requirements.
skills/cross-chain/axelar.md Documents Axelar GMP and ITS APIs.
site/src/data/skills.ts Registers the category and skill card.
site/src/app/styles.scss Enables Cross-Chain filtering.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread skills/cross-chain/SKILL.md Outdated
Comment thread skills/cross-chain/cctp.md Outdated
Comment thread skills/cross-chain/axelar.md Outdated
Comment thread skills/cross-chain/axelar.md Outdated
Comment thread skills/cross-chain/axelar.md Outdated
Comment thread skills/cross-chain/axelar.md Outdated
Checked every signature against axelarnetwork/axelar-amplifier-stellar.
The docs site had drifted from the source in five places:

- deploy_remote_token does not exist; the real name is
  deploy_remote_interchain_token
- the receive pattern is the AxelarExecutable derive macro plus
  CustomAxelarExecutable (__gateway/__execute) — the source forbids
  implementing the executable interface manually, and validation runs
  before __execute, generated
- interchain_transfer's payload parameter is metadata, not data
- set_flow_limit takes a caller and is open to approved flow limiters,
  not operator-only
- deploy_interchain_token's first parameter is deployer, not caller

Also added the canonical-token naming quirk and the
InterchainTokenExecutable receiving hook, both from source.
The 7-decimal rule holds for classic assets and their SACs, not for
Soroban token contracts (ITS tokens declare their own decimals) —
scoped in both SKILL.md and the ITS pitfalls. Flow getters take
env: &Env per the ITS interface. The wrapper allowance prose now
states the real 50-99 ledger expiry range instead of ~50.
A demo contract built against the amplifier crates at current main
(wasm32v1-none and native) surfaced two requirements the example
didn't state: AxelarExecutableInterface must be imported alongside
CustomAxelarExecutable, and the contract's error enum needs a
NotApproved variant for the derive-generated execute to map
validation failures onto.
@kaankacar
kaankacar requested a review from ElliotFriend July 20, 2026 16:28
@oceans404

Copy link
Copy Markdown
Contributor

This is awesome! Can you also mention https://docs.layerzero.network/v2/developers/stellar/overview and how LayerZero works for building Omnichain Applications (OApps) and Omnichain Fungible Tokens (OFTs) that connect Stellar to any LayerZero-supported chain.

@kaankacar
kaankacar removed the request for review from ElliotFriend July 20, 2026 16:58
The Stellar endpoint went live on mainnet in July 2026 (EID 30600,
testnet 40600), making LayerZero the second message-passing rail.
New layerzero.md covers the endpoint addresses (pulled from
LayerZero's metadata API), the four live DVNs (LayerZero Labs,
Horizen, Nethermind, Canary, confirmed via the same API), the OApp
Soroban pattern with names verified against the omni-counter example
in LayerZero-Labs/monorepo-external, OFT at routing depth, and a
neutral Axelar-vs-LayerZero comparison. The router decision table
now offers both messaging rails, and the site card mentions all
three protocols.
@kaankacar

Copy link
Copy Markdown
Contributor Author

Scope addition: LayerZero V2 went live on Stellar mainnet in July (EID 30600), so the skill now covers it as the second message-passing rail (layerzero.md, 94 lines). Same verification bar as the rest: endpoint and DVN data pulled from LayerZero's own metadata API (four live DVNs: LayerZero Labs, Horizen, Nethermind, Canary), and the OApp pattern names checked against the omni-counter example in LayerZero-Labs/monorepo-external. The router's decision table now offers Axelar GMP and LayerZero OApp side by side with a neutral comparison, and the site card title mentions all three protocols. All files still under the 500-line guidance; tsc and build green.

…footgun

Compile verification against the monorepo crates caught a real bug:
the skeleton paired #[oapp(custom = [receiver])] with only an
LzReceiveInternal impl. custom = [receiver] skips generating the
receiver surface, so that contract compiles cleanly but exports no
lz_receive at all. The counter example pairs the flag with its own
OAppReceiver impl, which the condensation had dropped. The skeleton
now uses plain #[oapp] (verified: full receive surface exported in
the wasm) and a new bullet documents the footgun. Also fixed the
snippet imports: added the soroban_sdk types it needs, dropped the
two it doesn't.
@kaankacar

Copy link
Copy Markdown
Contributor Author

LayerZero got the same compile-verification treatment as Axelar: a fresh minimal OApp built from only what layerzero.md documents, compiled to wasm32v1-none against the monorepo crates (their omni-counter also built in place as a baseline), plus an independent re-check of the metadata API facts (EIDs, endpoint addresses, the shared ULN address, and exactly four live DVNs). One real bug surfaced and is fixed in the latest commit: the skeleton's custom = [receiver] flag suppressed the generated receive surface, producing a contract that compiles but exports no lz_receive; the skeleton now uses plain #[oapp] and the footgun is documented. LayerZero's public Stellar docs checked out clean against source, so no upstream fix was needed there.

@kaankacar
kaankacar requested a review from ElliotFriend July 20, 2026 18:27

@ElliotFriend ElliotFriend left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

these look REALLY good!! i left some notes and thoughts, especially on the CCTP skill. the layerzero and axelar ones i'm less familiar with, but the structures of those skills make sense to me, at least.

i don't think i'd say any of my comments are blockers, but i do think some of them are pretty useful things to include.

Comment thread skills/cross-chain/cctp.md Outdated
| `CctpForwarder` | `CA66Q2WFBND6V4UEB7RD4SAXSVIWMD6RA4X3U32ELVFGXV5PJK4T4VSZ` |
| USDC (SAC) | `CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA` (`USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5`) |

Derive the USDC SAC address for any network yourself rather than trusting a doc: `stellar contract id asset --asset USDC:<ISSUER> --network <net>`. Testnet USDC comes from [faucet.circle.com](https://faucet.circle.com) (pick "Stellar Testnet") — your account needs the trustline first.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

might also be worth noting that there's a bunch of liquidity on testnet, and you can often get a pretty decent test amount with a pathPayment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added! Fun fact: that's exactly how I funded the test account when stress-testing this file — a pathPaymentStrictReceive got 5 USDC for ~2.7 XLM, no faucet needed.

| `TokenMessengerMinter` | `CDNG7HXAPBWICI2E3AUBP3YZWZELJLYSB6F5CC7WLDTLTHVM74SLRTHP` |
| `MessageTransmitter` | `CBJ6MTCKKZG73PMDZCJMSFRD7DQEMI4FKDH7CGDSV4W6FHCRBCQAVVJY` |
| `CctpForwarder` | `CA66Q2WFBND6V4UEB7RD4SAXSVIWMD6RA4X3U32ELVFGXV5PJK4T4VSZ` |
| USDC (SAC) | `CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA` (`USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5`) |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the USDC SAC address might be nice to include for mainnet, as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good call — added the mainnet SAC + issuer to the table (derived it with stellar contract id asset to be safe).

Comment thread skills/cross-chain/cctp.md Outdated
When transferring **to** Stellar, on the source-chain burn:

- Set **both** `mintRecipient` **and** `destinationCaller` to the `CctpForwarder` address (decoded to 32 bytes).
- Put the real recipient's `strkey` (`G…`, `M…`, or `C…`) into **hook data** as `forwardRecipient`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the "as forwardRecipient part makes it read like the hook data should be an object or dictionary or something. in reality, there isn't a field, it's just an arrangement of bytes, and the hex-encoded StrKey is a part of those bytes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you, reworded — it now says the strkey goes into the hook data bytes, and that "forwardRecipient" is just the docs' name for that stretch of the byte layout, not a field.

Comment thread skills/cross-chain/cctp.md Outdated
- Wrong `destinationCaller` → the forwarder cannot complete the transfer.
- `mintRecipient` set to a user or muxed account → USDC never reaches the forwarder.

The forwarder flow is non-custodial: `mint_and_forward(message, attestation)` verifies the message, calls `receive_message` (minting to the forwarder), and pays out to `forwardRecipient` — all in one atomic Soroban invocation. Any failure reverts the whole thing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
The forwarder flow is non-custodial: `mint_and_forward(message, attestation)` verifies the message, calls `receive_message` (minting to the forwarder), and pays out to `forwardRecipient` — all in one atomic Soroban invocation. Any failure reverts the whole thing.
The forwarder flow is non-custodial: `mint_and_forward(message, attestation)` verifies the message, calls `receive_message` (minting to the forwarder), and pays out to `forwardRecipient` — all in one atomic contract invocation. Any failure reverts the whole thing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Applied, thanks!

Comment thread skills/cross-chain/cctp.md Outdated

| Bytes | Type | Data |
|---|---|---|
| 0–23 | `bytes24` | Magic, Circle-reserved; use all zero bytes |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

might be worthwhile to note that it's "use all zero bytes" for Stellar-inbound, specifically. there's the cctp-forward that you can put in there, when the transfer is destined for a chain that supports the forwarding service.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nice catch — scoped it to Stellar-inbound and noted that forwarding-service chains use a cctp-forward magic instead.

Address.fromString(USDC_SAC).toScVal(),
bytesN32(destinationCaller), // 32 zero bytes = anyone may complete the mint (permissionless)
nativeToScVal(maxFee, { type: "i128" }), // fee budget, 7-decimal subunits (100_000 ≈ $0.01)
nativeToScVal(finalityThreshold, { type: "u32" }), // 1000 = Fast, 2000 = Standard

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

it's probably a good idea to note that Stellar only supports standard transfer times on outbound transfers. because we already have fast finality, there's really no point to using fast transfers, and the CCTP transfers don't actually make use of any fast transferring things when it's a stellar-outbound transfer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Great point, added. I actually saw this live in a testnet run: requested 1000 and Iris came back with finalityThresholdExecuted: 2000, zero fee.

Comment thread skills/cross-chain/cctp.md Outdated

Poll until `status` is `"complete"`, then read `message` and `attestation` (both hex). Field-level gotchas, all verified against live behavior:

- **Stellar legs return `null` address fields.** In `decodedMessage`/`decodedMessageBody`, `sender`, `recipient`, `mintRecipient`, etc. are `null` when Stellar is involved — the API can't decode 32-byte Stellar payloads either. The transfer is fine; parse the raw `message` hex if you need the addresses.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

might be worth double-checking as of now. i think maybe some of those might've begun showing up?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right, some of them do show up now — verified against a live Stellar→Arc transfer: recipient/mintRecipient/destinationCaller decode fine, only the fields holding Stellar payloads are null. Rewrote the bullet to match (first of the two new commits).

Comment thread skills/cross-chain/cctp.md Outdated

## Worked reference implementation

The best way to see all of this run is [**ElliotFriend/stunning-octo-carnival**](https://github.com/ElliotFriend/stunning-octo-carnival) — a SvelteKit demo that bridges testnet USDC Stellar ↔ EVM (Arc, Base Sepolia) and Stellar ↔ Solana, making every step visible on one screen: burn, attestation, mint. It is the source several snippets above were adapted from, and it's tested end-to-end on testnet in both directions.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

again, the URL has changed for that repo

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated!

Comment thread skills/cross-chain/cctp.md Outdated

## Limitations and status notes

- **USDC only** on Stellar CCTP today — EURC is not confirmed. Verify current asset support in [Circle's docs](https://developers.circle.com/cctp) before promising it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i think EURC is explicitly not enabled for any blockchain? I think the only other CCTP token that's available is USYC and that's only on Ethereum and BNB.

edit: source

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the source! Fixed — EURC isn't a CCTP token anywhere; noted USDC on all domains except BNB, and USYC only on Ethereum/BNB.

Comment thread skills/cross-chain/SKILL.md Outdated
These bite regardless of which rail you pick. Each companion file adds rail-specific ones.

1. **Address formats do not translate.** Stellar addresses are `strkey` strings (`G…` accounts, `C…` contracts, `M…` muxed); EVM uses 20-byte hex; Solana uses base58. Every rail defines its own encoding for foreign addresses (CCTP: raw 32-byte payloads; Axelar: strings + bytes payloads). Never paste an address from one chain into a field meant for another — encode it the way the rail specifies, and validate with the SDK (`StrKey.isValidEd25519PublicKey` / `isValidContract`) before encoding.
2. **Decimals differ.** Classic Stellar assets and their SACs use 7 decimals, but other Soroban token contracts (ITS-deployed tokens included) declare their own — call `decimals()` instead of assuming. USDC is 6 on EVM chains and Solana; EVM tokens are commonly 18; CCTP messages are always 6-decimal. Convert at every boundary and test with amounts that exercise the last digit (see the worked decimal examples in [cctp.md](cctp.md#usdc-precision-7-decimals-vs-6)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
2. **Decimals differ.** Classic Stellar assets and their SACs use 7 decimals, but other Soroban token contracts (ITS-deployed tokens included) declare their own — call `decimals()` instead of assuming. USDC is 6 on EVM chains and Solana; EVM tokens are commonly 18; CCTP messages are always 6-decimal. Convert at every boundary and test with amounts that exercise the last digit (see the worked decimal examples in [cctp.md](cctp.md#usdc-precision-7-decimals-vs-6)).
2. **Decimals differ.** Classic Stellar assets and their SACs use 7 decimals, but other Soroban token contracts (ITS-deployed tokens included) declare their own — call `decimals()` instead of assuming. USDC is 6 on every supported chain except Stelllar (which uses 7 decimal places); EVM tokens are commonly 18; CCTP messages are always 6-decimal. Convert at every boundary and test with amounts that exercise the last digit (see the worked decimal examples in [cctp.md](cctp.md#usdc-precision-7-decimals-vs-6)).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Applied — with the "Stelllar" typo ironed out :)

Stress-tested the file by having a fresh agent bridge 2 USDC from
Stellar testnet to Arc with only this skill as documentation. Every
Stellar-side instruction survived contact with the live network; the
gaps were all on the destination side, closed here:

- destination contract reference: MessageTransmitterV2/TokenMessengerV2
  are address-uniform across EVM chains, so list both (testnet +
  mainnet) with the receiveMessage(bytes,bytes) call and a link to
  Circle's contract-addresses page
- domain examples now include Arc (26) and link the full domain table
- document the Iris fee-schedule endpoint (/v2/burn/USDC/fees/...) so
  maxFee can be chosen instead of guessed, and the minted amount
  predicted exactly
- scope the Iris null-fields note to fields that actually hold Stellar
  payloads: on an outbound leg the EVM-side fields decode fine
- mainnet USDC SAC address (+ issuer) in the mainnet table
- note testnet DEX liquidity as a faucet alternative
- hook data: clarify forwardRecipient is a byte range, not a field;
  scope the all-zero magic to Stellar-inbound (forwarding-service
  chains use a cctp-forward magic); state the uint32 fields are
  big-endian per Circle's reference builder
- decimals: the seventh digit stays in the source account; SKILL.md
  pitfall now says 6 decimals everywhere except Stellar
- approve step: spell out the most basic version (exact amount, short
  expiration), when to skip it, and the wrapper alternative
- annotate the caller arg; note Stellar-outbound always executes at
  Standard finality, no fast fee
- Solana consolidates messenger+minter into one program, like Stellar
- inbound: complete the depositForBurnWithHook arg list (burnToken,
  minFinalityThreshold), link the contractStrkeyToBytes32 helper, fix
  the hook-layout link text
- tokens: EURC is not a CCTP token anywhere; USDC on all domains
  except BNB, USYC only Ethereum/BNB
- update links to the renamed demo repo (stellar-cctp-demo)
Verified the file end-to-end on testnet: a bidirectional GMP round
trip Stellar <-> Base Sepolia (Soroban AxelarExecutable receiver +
minimal EVM counterpart), 46s outbound, ~27min inbound. Every
documented signature and both line-82 compile gotchas held exactly as
written. What follows closes the gaps that had to be learned from
outside the file:

- name the gas/status APIs: the Axelarscan GMP API endpoints and the
  estimateGasFee / searchGMP methods
- Cargo feature flags without which the documented pattern doesn't
  compile: stellar-axelar-std 'derive', gateway/gas-service 'library'
- pay_gas sender-vs-spender semantics (sender must be the address
  that makes the follow-up call_contract)
- gas is XLM via the native-asset SAC; overpayment refundable but not
  promptly automatic
- delivery-time asymmetry: sub-minute outbound vs ~25-30min inbound
  behind L2->L1 finality
- point address resolution at axelar-chains-config/info/*.json and
  extend the don't-hardcode warning to chain names, which are also
  deployment-versioned (testnet Stellar is 'stellar-2026-q1-2')
Live verification on Stellar testnet isn't possible right now: the
deployment's required DVN doesn't support the endpoint's only
registered message library, so every send reverts (#1213
UnsupportedMessageLib), and the pathway set is still in flux while
the integration lands. Mainnet works but has only exchanged messages
with two chains so far.

Rather than ship a file we can't verify end-to-end the way cctp.md
and axelar.md were, LayerZero gets its own PR once the testnet
deployment stabilizes. The OApp skeleton and its compile-verified
notes (incl. the custom=[receiver] footgun) are preserved in the
branch history for that follow-up.
@kaankacar

Copy link
Copy Markdown
Contributor Author

Two updates after putting the remaining rails through the same live testnet verification cctp.md got:

axelar.md — verified end-to-end, small additions (b14ce47). Ran a full bidirectional GMP round trip on testnet: a Soroban AxelarExecutable receiver + a minimal EVM counterpart on Base Sepolia — Stellar→Base in 46s, Base→Stellar in ~27min (Sepolia L1 finality, expected). Every documented signature and both of the line-82 compile gotchas held exactly as written. The commit adds the operational facts the run had to learn from outside the file: the Axelarscan GMP API (estimateGasFee/searchGMP), the Cargo feature flags (derive/library) without which the pattern doesn't compile, pay_gas sender-vs-spender semantics, gas = XLM native SAC, the delivery-time asymmetry, and that chain names are deployment-versioned too (testnet Stellar is stellar-2026-q1-2).

layerzero.md — pulled out of this PR, follow-up coming (631db31). The same verification isn't possible on LayerZero right now: Stellar testnet's deployment currently can't send at all (the required DVN doesn't support the endpoint's only registered message library — every send reverts with #1213 UnsupportedMessageLib), and the integration is still actively landing. Mainnet works but has only exchanged messages with two chains so far (Avalanche and Arbitrum Nova). Rather than ship a file we can't stand behind end-to-end the way cctp.md and axelar.md now are, I'll re-add LayerZero in its own PR once the testnet deployment stabilizes — the compile-verified OApp skeleton and the custom = [receiver] footgun note are preserved in branch history and will come back with a proper live round trip behind them.

Net: this PR is now CCTP + Axelar GMP/ITS + NEAR Intents routing, all live-verified.

Probed the API without moving funds: XLM and USDC are the two Stellar
entries in /v0/tokens; a 20 XLM -> Base USDC quote priced at ~0.4%
spread with a 27s ETA; and a non-dry quote returned the
depositAddress + depositMemo pair exactly as the MEMO-mode note
promised.

- name the enforcement: Stellar-origin quotes must set
  depositMode: "MEMO" or /v0/quote rejects outright — the concept
  was documented, the required parameter wasn't
- document dry quotes (pricing/ETA with no deposit commitment) and
  GET /v0/tokens as the support probe
- state plainly that NEAR Intents has no testnet, and carve it out of
  the shared 'every rail has a testnet' pitfall accordingly
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.

Add official cross-chain skill (CCTP, Axelar GMP/ITS, NEAR Intents)

4 participants