Skip to content

Add USDT0 (LayerZero OFT) to the cross-chain and assets skills - #116

Open
kaankacar wants to merge 15 commits into
mainfrom
issue-agent/115-usdt0-layerzero
Open

Add USDT0 (LayerZero OFT) to the cross-chain and assets skills#116
kaankacar wants to merge 15 commits into
mainfrom
issue-agent/115-usdt0-layerzero

Conversation

@kaankacar

Copy link
Copy Markdown
Contributor

🤖 Automated message from Kaan's Automated Triage Bot.

Closes #115

This restores layerzero.md and anchors it on USDT0, the first production OFT on Stellar's endpoint. assets gains the SAC-admin half: USDT0 as the live example, and a read-only pre-listing recipe.

I re-ran the read-only checks on 2026-08-27 (mainnet, ledger 64151532):

  • Issuer GATISXX6…: auth_revocable and auth_clawback_enabled set, master key weight 0, no home_domain.
  • SAC CBSJZEIO… matches Horizon's own derivation for the asset.
  • OFT surface (SendParam, quote_oft, quote_send, send, OFTReceipt) matches LayerZero's Stellar crates and a live mainnet send.
  • Fees leave in XLM to the endpoint. A 0.5 USDT0 send to Arbitrum cost 4864058 stroops.
  • Inbound path is ExecutorHelper.execute -> lz_receive -> SAC manager (MINTER_ROLE) -> SAC mint.
  • OApp skeleton import paths and __lz_send argument order match monorepo-external at HEAD.

Two corrections to the issue:

  1. The testnet endpoint moved. The old file said CBQOTWFU…; the metadata API now says CALTBA5S…. Fixed.
  2. The pathway list is stale. It named 3 peers. Live traffic spans at least 8 EIDs, including Polygon (30109). I did not copy the list in. The file teaches peer(eid) instead.

Please do not merge this without two checks I could not run. Details in the issue thread, @kaankacar.

Restores layerzero.md, pulled in 631db31 because the rail could not be
verified end to end, and anchors it on USDT0 — now live on the mainnet
endpoint and the first production OFT on Stellar.

Every address and signature re-checked on 2026-08-27:

- Endpoint components from the LayerZero metadata API. The testnet
  endpoint moved since the original file (CBQOTWFU -> CALTBA5S), so the
  stale value is replaced.
- USDT0's classic asset, SAC, OFT, SAC manager and OneSig against
  Horizon, the ledger, and USDT0's deployments page.
- The OFT surface (SendParam, quote_oft, quote_send, send, OFTReceipt)
  against LayerZero's Stellar crates and a live mainnet send.
- The OApp skeleton's import paths and __lz_send argument order against
  monorepo-external at HEAD.

The pathway list from the issue is deliberately not copied in: mainnet
traffic already spans more EIDs than it names. The file teaches peer(eid)
instead, alongside is_paused, fee_bps and rate_limit_config as live reads.

assets gains the other half of the story: USDT0 as the live example of a
contract-administered classic asset, why the issuer must be locked for
that model to hold, that a missing stellar.toml is not a red flag, and a
read-only pre-listing recipe that verifies an asset from the ledger.
@github-actions

github-actions Bot commented Aug 27, 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-116/

Built to branch gh-pages at 2026-08-28 18:31 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 USDT0 and LayerZero guidance across Stellar’s cross-chain and asset skills.

Changes:

  • Adds LayerZero/OFT routing, pitfalls, and OApp guidance.
  • Adds USDT0 SAC administration and pre-listing checks.
  • Updates the site’s cross-chain skill card.

Reviewed changes

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

File Description
skills/cross-chain/SKILL.md Adds LayerZero and USDT0 routing.
skills/cross-chain/layerzero.md Documents LayerZero, USDT0, OFT, and OApps.
skills/assets/SKILL.md Adds USDT0 administration and validation guidance.
site/src/data/skills.ts Updates cross-chain card metadata.
Suppressed comments (1)

skills/cross-chain/layerzero.md:126

  • This recommends the testnet endpoint as a working rehearsal path, but the required send re-test was not run and the next bullet says sends were previously blocked by UnsupportedMessageLib. Re-test before merge and make this recommendation conditional or document the current failure if sending is still unsupported.
- **No USDT0 testnet deployment.** USDT0's deployments page lists no Stellar testnet entry (checked 2026-08-27), so a testnet round trip of USDT0 itself is not available. Rehearse instead with LayerZero's own testnet endpoint (EID `40600`) and your own OApp or OFT, and keep mainnet USDT0 work to read-only simulation until the flow is proven.
- **Testnet sends were previously blocked** by `#1213 UnsupportedMessageLib` (the required DVN did not support the endpoint's only registered message library, August 2026). The testnet endpoint has been redeployed since, so re-check current status rather than treating that error as permanent.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread skills/cross-chain/layerzero.md Outdated
--send_param '{"dst_eid":30101,"to":"<32-byte hex>","amount_ld":"10000000","min_amount_ld":"9950000","extra_options":"","compose_msg":"","oft_cmd":""}'
```

**Fees are paid in XLM.** `quote_send` returns a `MessagingFee`; `native_fee` is XLM in stroops, and `send` transfers it to the endpoint through the native SAC. A recent mainnet send of `0.5` USDT0 to Arbitrum cost `4864058` stroops (about `0.486` XLM) — quote it, never assume it. `refund_address` receives any excess.
Comment thread skills/cross-chain/layerzero.md Outdated

Source of truth: [LayerZero-Labs/monorepo-external](https://github.com/LayerZero-Labs/monorepo-external) — the protocol contracts (`contracts/protocol/stellar/`), the OApp packages (`apps/oapp-app/contracts/stellar/`), the OFT and SAC-manager contracts (`apps/oft-app/contracts/stellar/`), and the worked reference at `apps/project-types/omni-counter-app/contracts/stellar/`. There is no Stellar package in the public `LayerZero-v2` repo and no LayerZero Stellar crate on crates.io — work from this monorepo.

The skeleton below compiles to `wasm32v1-none` with the full receive surface exported, and every import path and argument order in it was re-checked against the monorepo on 2026-08-27.
Comment thread skills/cross-chain/SKILL.md Outdated
3. **Classic Stellar recipients need a trustline first.** A `G…` account cannot receive an issued asset (USDC included) without a trustline to that asset. Bridged funds destined for an account without one will not land. Check and provision before starting the transfer — see `../assets/SKILL.md`.
4. **Cross-chain is asynchronous.** Every rail has a wait: CCTP waits for finality plus Circle's attestation (seconds to ~15 minutes depending on chain and finality threshold), Axelar waits for validator confirmation, intents wait for a market maker. Build UIs and agents around polling a status, never around "submit and assume".
5. **Testnet first, always.** Every rail here except NEAR Intents has a testnet deployment (intents are filled by real market makers — mainnet only; rehearse with dry quotes and a dust-sized swap instead). Do the full round-trip on testnet before touching mainnet — cross-chain mistakes are frequently unrecoverable by design (burns are final, and some misencodings permanently strand funds).
5. **Testnet first, always.** Do the full round-trip on testnet before touching mainnet — cross-chain mistakes are frequently unrecoverable by design (burns are final, and some misencodings permanently strand funds). Two rails cannot be rehearsed that way: NEAR Intents is filled by real market makers, and **USDT0 publishes no testnet deployment** even though LayerZero's testnet endpoint exists. For both, the rehearsal path is read-only quotes (`quote_oft` and `quote_send` for USDT0, dry quotes for intents) followed by a dust-sized real transfer — and for LayerZero specifically, you can still exercise your own OApp or OFT on testnet EID `40600` before trusting the mainnet path.
Comment thread skills/assets/SKILL.md Outdated
Comment on lines +525 to +527
- If the asset is bridged, read the bridge contract too — for a LayerZero
OFT: `token`, `shared_decimals`, `oft_type`, `endpoint`, and `is_paused`.
See `../cross-chain/layerzero.md`.
Remove the observed mainnet fee amount. The issue keeps fee numbers out
of the file on purpose, and a single quote anchors estimates.

Say what was actually checked against monorepo-external at HEAD (import
paths, argument order, trait signatures) instead of claiming the skeleton
compiles. The compile is the reader's step.

Make the testnet rehearsal conditional. Testnet sends failed with #1213
UnsupportedMessageLib in August 2026 and no successful send is recorded
since the endpoint was redeployed.

Add the read-only OFT invocations to the pre-listing recipe, so step 5
runs instead of only naming the methods.

List all three cross-chain companion files in the README tree.
Copilot AI review requested due to automatic review settings August 27, 2026 18:14
@kaankacar

Copy link
Copy Markdown
Contributor Author

🤖 Automated message from Kaan's Automated Triage Bot.

Copilot reviewed this PR. All four points were fair. Fixed in 9028152:

  • Removed the observed mainnet fee (4864058 stroops). The issue keeps fee numbers out on purpose.
  • The skeleton no longer claims to compile. It now states what was checked: import paths, argument order, and trait signatures against monorepo-external at HEAD.
  • The testnet rehearsal is now conditional. The file says to confirm one delivered testnet send first.
  • Step 5 of the pre-listing recipe now runs. It defines OFT= and invokes token, shared_decimals, oft_type, endpoint, and is_paused, all read-only.

I also re-verified the facts independently today, at this head:

  • Every endpoint, ULN, executor, and helper address matches the LayerZero metadata API, both networks.
  • Issuer flags, locked master key, no home_domain, and the SAC derivation match Horizon.
  • SendParam, the three call signatures, and every function name in the live-state table match the LayerZero Stellar crates.
  • The SAC ledger entry confirms admin CA3GUWLO…, and the OFT entry confirms MintBurn with a decimals difference of 1.

Two checks still need you, @kaankacar:

  1. Compile the skeleton to wasm32v1-none. This sandbox has no Rust toolchain.
  2. Send one testnet message and confirm delivery. That needs signing keys.

I am not merging this. The file teaches transaction signing, so a human merges it. I will keep tracking this PR.

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

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

Suppressed comments (1)

skills/assets/SKILL.md:544

  • oft_type() does not tell you every party that can mint; MintBurn(<address>) only identifies the mintable adapter the OFT calls. For the role-gated SAC manager, the owner/authorizer and role admins can change MINTER_ROLE, and its members are independently enumerable. Stopping at this result leaves the pre-listing check unable to answer its stated trust question.
- **Step 5 tells you who can mint.** `oft_type` returning `MintBurn(<address>)`
  means that address mints on credit, so it must be the SAC admin from step 4
  or a role holder on it. A `shared_decimals` below the SAC's 7 also means
  sends drop the extra digits. See `../cross-chain/layerzero.md` for that rail.

Comment thread skills/cross-chain/layerzero.md Outdated
Comment on lines +50 to +51
- Send `1.0000001` USDT0 (`amount_ld` = `10000001`): `amount_sent_ld` is `10000000` and the trailing `0.0000001` **stays in your account**. It is not lost, but it is not sent either.
- Quote first and compare. `quote_oft` returns an `OFTReceipt` whose `amount_sent_ld` and `amount_received_ld` are already dust-adjusted — treat those, not your input, as the truth.
Comment thread skills/cross-chain/layerzero.md Outdated
--send_param '{"dst_eid":30101,"to":"<32-byte hex>","amount_ld":"10000000","min_amount_ld":"9950000","extra_options":"","compose_msg":"","oft_cmd":""}'
```

**Fees are paid in XLM.** `quote_send` returns a `MessagingFee`; `native_fee` is XLM in stroops, and `send` transfers it to the endpoint through the native SAC. The fee tracks the destination route, the DVN set, and executor pricing, so quote every send and never reuse a number from a previous one. `refund_address` receives any excess.
Comment thread skills/cross-chain/layerzero.md Outdated

Source of truth: [LayerZero-Labs/monorepo-external](https://github.com/LayerZero-Labs/monorepo-external) — the protocol contracts (`contracts/protocol/stellar/`), the OApp packages (`apps/oapp-app/contracts/stellar/`), the OFT and SAC-manager contracts (`apps/oft-app/contracts/stellar/`), and the worked reference at `apps/project-types/omni-counter-app/contracts/stellar/`. There is no Stellar package in the public `LayerZero-v2` repo and no LayerZero Stellar crate on crates.io — work from this monorepo.

Every import path, argument order, and trait signature below was checked against the monorepo at HEAD on 2026-08-27. It is a skeleton, not a compiled artifact: build it yourself for `wasm32v1-none` before you trust it, and confirm `lz_receive` appears in the exported interface.
Comment thread skills/assets/SKILL.md Outdated
Comment on lines +490 to +491
# Look for: signers all weight 0 (locked), auth_revocable,
# auth_clawback_enabled, auth_immutable, and whether home_domain exists.
Copilot's second pass found three real defects.

The dust example only held while the route charged no OFT fee. The
Stellar OFT keeps the remainder with the sender when effective_fee_bps
is 0, and debits the full amount_ld into the fee when it is not, so the
example is now conditional.

"Fees are paid in XLM" covered only the LayerZero messaging fee. The OFT
fee is separate and denominated in the token, so a sender can need both.

The pre-listing recipe read the signer list to decide whether an issuer
is locked. The ledger entry keeps the master key weight in thresholds
and never lists it under signers, so that test passed an issuer with a
live master key.

Also pin the monorepo revision and toolchain behind the OApp skeleton,
and stop claiming oft_type enumerates every minter.
Copilot AI review requested due to automatic review settings August 28, 2026 17:03
@kaankacar

Copy link
Copy Markdown
Contributor Author

🤖 Automated message from Kaan's Automated Triage Bot.

Copilot reviewed this PR again. Three of the points were real defects. Fixed in 525add7:

  • The dust example was only half true. The Stellar OFT keeps the remainder with the sender when effective_fee_bps(dst_eid) is 0. When the rate is above 0, it debits the full amount_ld and absorbs the remainder into the fee. The example is now conditional on the route.
  • "Fees are paid in XLM" was incomplete. The LayerZero messaging fee is XLM. The OFT fee is separate and charged in USDT0 itself. A sender can need both.
  • The pre-listing check tested the wrong field. It read the signer list to decide if an issuer is locked. The ledger entry keeps the master key weight in thresholds and never lists that key under signers. An issuer with a live master key passed the old test.

Two smaller fixes in the same commit:

  • The OApp source is now pinned to commit 3f1cf3a with its Rust 1.90.0 and wasm32v1-none toolchain.
  • oft_type no longer claims to name every minter. The SAC manager owner can grant MINTER_ROLE to others.

Sources: oft.rs __debit_view and oft_fee.rs in LayerZero-Labs/monorepo-external at 3f1cf3a, and the Thresholds type in stellar/rs-stellar-xdr.

I did not compile the skeleton. This sandbox has no Rust toolchain, so I recorded the pinned revision instead of a build result. That compile and one delivered testnet send still need you, @kaankacar.

I am not merging this. The file teaches transaction signing, so a human merges it. I will keep tracking the PR.

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

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

Suppressed comments (3)

skills/cross-chain/layerzero.md:131

  • The required testnet check remains unresolved: this status describes the failure before the endpoint redeployment and explicitly records no successful send afterward. The PR description says not to merge until the redeployed endpoint is retested. Run that send and update this note with the current result.
- **A testnet rehearsal is not guaranteed.** You can deploy your own OApp or OFT against the testnet endpoint (EID `40600`), but testnet sends failed with `#1213 UnsupportedMessageLib` in August 2026 — the required DVN did not support the endpoint's only registered message library. That endpoint has been redeployed since, and this file does not record a successful testnet send after it. Send one small testnet message and confirm delivery before you treat testnet as a rehearsal path.

skills/cross-chain/layerzero.md:141

  • This confirms that the restored OApp skeleton has not passed the mandatory compile check from the PR description. Signature inspection cannot validate macro expansion or the exported interface. Compile this exact example for wasm32v1-none and verify that lz_receive is exported before publishing it as a copyable pattern.
It is a skeleton, not a compiled artifact. Signature comparison does not catch macro expansion, feature, dependency, or target errors, and no build was run against this snippet. Compile it yourself for `wasm32v1-none` before you trust it, and confirm `lz_receive` appears in the exported interface.

skills/assets/SKILL.md:619

  • This omits the issuer-lock check and therefore contradicts the pre-listing guidance above (skills/assets/SKILL.md:549-550). A contract SAC admin does not neutralize an unlocked classic issuer: the issuer can still mint, freeze, or claw back directly. Require both verification of the issuer lock and inspection of the contract's role holders.
- **When `AUTH_REVOCABLE` and `AUTH_CLAWBACK_ENABLED` are both set, find out
  who holds the admin role before listing the asset.** Those flags mean
  balances can be frozen or clawed back, and if the SAC admin is a contract
  the real authority is whoever holds its roles — not the issuer account

| Move **native USDC** between Stellar and an EVM chain or Solana (no wrapped assets, no liquidity pools) | Circle CCTP V2 | [cctp.md](cctp.md) |
| Have a Stellar contract **call a contract on another chain**, or receive calls from one (arbitrary payloads) | Axelar GMP | [axelar.md](axelar.md) |
| Make a token — new or an existing Stellar asset — **exist on multiple chains** | Axelar ITS | [axelar.md](axelar.md) |
| Move **native USDT** between Stellar and an EVM chain | USDT0, a LayerZero OFT | [layerzero.md](layerzero.md#usdt0-native-usdt-on-stellar) |
Comment thread skills/cross-chain/layerzero.md Outdated
Comment on lines +93 to +98
```bash
# Read-only. --send=no simulates and never signs or submits.
stellar contract invoke --id CBOWOLFSDM5PZXNFIVDMP5NZ7U2GSIHED6H6R446QOHF266XINKUMMF6 \
--source-account alice --network mainnet --send=no \
-- quote_send --from <G_SENDER> --pay_in_zro false \
--send_param '{"dst_eid":30101,"to":"<32-byte hex>","amount_ld":"10000000","min_amount_ld":"9950000","extra_options":"","compose_msg":"","oft_cmd":""}'
Copilot AI review requested due to automatic review settings August 28, 2026 17:07
Copilot's third pass caught two gaps against the repo's own rules.

The contributing guide asks for a matching scenario under evals/ when a
change touches what a skill teaches, and cross-chain had no scenarios at
all. Add three for the USDT0 rail (inbound routing, the dust question,
the testnet rehearsal) and one cross-skill scenario for the collateral
question, then correct both READMEs, which still described cross-chain
as uncovered.

The outbound section showed a command shape for quote_send only. Add
quote_oft and send, all three simulated with --send=no, and replace the
<G_SENDER> placeholder: inside a bash fence that is a stdin redirect,
not an argument.

Also require the issuer-lock check alongside the role check in the
security list. A contract admin does not contain an issuer that can
still sign.
@kaankacar

Copy link
Copy Markdown
Contributor Author

🤖 Automated message from Kaan's Automated Triage Bot.

Copilot reviewed the fix commit. Two more points were correct. Fixed in aef2fd8:

  • This PR broke a contributing rule. README.md asks for a matching scenario under evals/ whenever a change touches what a skill teaches. cross-chain had none at all. I added three scenarios for the USDT0 rail and one cross-skill scenario for the collateral question. Both READMEs no longer call cross-chain uncovered.
  • Only quote_send had a command shape. quote_oft and send now have one too, all three simulated with --send=no. I also replaced --from <G_SENDER>. Inside a bash fence that is a stdin redirect, not an argument.

One suppressed point was fair too. The security list asked for the admin roles but not the issuer lock. A contract admin does not contain an issuer that can still sign. Both checks are now required.

I rejected two suppressed points. They ask for the testnet send and the Rust compile. Those are the two checks this sandbox cannot run, and they are already with you.

I also merged main into the branch. main gained pnpm check:ecosystem-links after this branch was cut, which turned the preview build red for a reason unrelated to this PR. The build is green again at aef2fd8: install, lint, lint:ts, check:ecosystem-links, test:ecosystem-links, build.

The merge is still yours, @kaankacar. The file teaches transaction signing. I will keep tracking this PR.

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

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Suppressed comments (2)

skills/cross-chain/layerzero.md:58

  • This repeats the account-only trustline requirement as if it applied to every recipient. Contract (C…) recipients can hold SAC balances without trustlines, so condition this step on the destination being a G… account.
1. The recipient's trustline must exist first (rule 1 above).

skills/cross-chain/layerzero.md:141

  • The PR's required compile verification remains unfinished here. Signature inspection cannot catch macro expansion, feature, dependency, target, or exported-interface failures, and the PR description explicitly says not to merge without this check. Compile this exact skeleton against the pinned revision for wasm32v1-none, confirm lz_receive is exported, and update the snippet or verification note with the result.
Storage keys mirror these names (`Peer(eid)`, `FeeBps(eid)`, `RateLimit(direction, eid)`, `EnforcedOptions(eid, msg_type)`), so `stellar ledger entry fetch contract-data --contract "$OFT" --key-xdr "$KEY"` reads them without any invocation at all.

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

## USDT0: native USDT on Stellar

USDT0 is USDT moved by burn-and-mint over LayerZero's OFT standard. There is no wrapped token and no pool: the OFT burns on the source chain and mints on the destination. On Stellar it is a **classic asset** with a locked issuer whose SAC admin is a contract.
Comment thread skills/cross-chain/layerzero.md Outdated

### The rules that save funds

1. **The recipient needs a USDT0 trustline before anything inbound lands.** A `G…` account cannot hold an issued asset without one. This is the single most common inbound failure.
Comment thread skills/cross-chain/SKILL.md Outdated
| **Swap any asset cross-chain** (BTC, ETH, SOL, … → XLM or Stellar USDC) without integrating a bridge yourself | NEAR Intents | [below](#near-intents-intent-based-swaps) |

Rules of thumb: if the asset is USDC and both ends are CCTP chains, CCTP is the cheapest and most direct (it burns and mints Circle-native USDC — nothing wrapped, nothing pooled). If you need logic, not just value, on the far chain, that is message passing — Axelar GMP is the rail for it. If you control a token and want it multichain, that is ITS. If the user just wants "turn my X on chain A into Y on Stellar" and you don't want bridge plumbing at all, quote it through NEAR Intents.
Rules of thumb: if the asset is USDC and both ends are CCTP chains, CCTP is the cheapest and most direct (it burns and mints Circle-native USDC — nothing wrapped, nothing pooled). If the asset is USDT, the answer is USDT0 over LayerZero OFT — same burn-and-mint shape, different rail. If you need logic, not just value, on the far chain, that is message passing — two rails do it, and the [comparison at the end of layerzero.md](layerzero.md#choosing-between-axelar-gmp-and-layerzero-oapp) helps you pick between Axelar's shared validator security and LayerZero's app-configured DVN sets. If you control a token and want it multichain, that is ITS or OFT on the same split. If the user just wants "turn my X on chain A into Y on Stellar" and you don't want bridge plumbing at all, quote it through NEAR Intents.
### Limitations and status notes

- **No USDT0 testnet deployment.** USDT0's deployments page lists no Stellar testnet entry (checked 2026-08-27), so a testnet round trip of USDT0 itself is not available. Keep mainnet USDT0 work to read-only simulation until the flow is proven.
- **A testnet rehearsal is not guaranteed.** You can deploy your own OApp or OFT against the testnet endpoint (EID `40600`), but testnet sends failed with `#1213 UnsupportedMessageLib` in August 2026 — the required DVN did not support the endpoint's only registered message library. That endpoint has been redeployed since, and this file does not record a successful testnet send after it. Send one small testnet message and confirm delivery before you treat testnet as a rehearsal path.
Copilot AI review requested due to automatic review settings August 28, 2026 17:16
The Ethereum leg is an OFT Adapter over canonical USDT, so it locks and
unlocks rather than burning and minting. Contract recipients hold SAC
balances in contract storage and need no trustline.

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

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

skills/assets/SKILL.md:493

  • stellar ledger entry fetch account serializes the XDR flags field as a numeric u32; it does not emit the named booleans listed here. As written, the pre-listing recipe does not tell readers how to determine the required authorization risks. Document the bit values (USDT0 should yield 10) so the check is reproducible.
# 1. Is the issuer locked, and which flags are set?
#    Read: thresholds and signers (see "Step 1" below — the signer list
#    alone does not answer this), auth_revocable, auth_clawback_enabled,
#    auth_immutable, and whether home_domain exists.
stellar ledger entry fetch account --account $ISSUER --network mainnet

skills/cross-chain/layerzero.md:152

  • The second explicit merge gate is still outstanding: the issue thread confirms no post-redeployment testnet send was run. Because this endpoint moved and the previous deployment failed with UnsupportedMessageLib, retain the warning but do not merge until a signed test message is delivered (or the current failure is reproduced) and the dated result is recorded here.

Comment thread skills/cross-chain/layerzero.md Outdated
### Inbound: EVM → Stellar

1. The recipient's trustline must exist first (rule 1 above).
2. Send on the source chain against USDT0's OFT there, with Stellar's EID `30600` and the recipient encoded as a 32-byte value.
Comment thread skills/cross-chain/layerzero.md Outdated

### Limitations and status notes

- **No USDT0 testnet deployment.** USDT0's deployments page lists no Stellar testnet entry (checked 2026-08-27), so a testnet round trip of USDT0 itself is not available. Keep mainnet USDT0 work to read-only simulation until the flow is proven.
Comment thread skills/assets/SKILL.md Outdated
Comment on lines +551 to +555
- **Step 5 names the bridge's minter, not every minter.** `oft_type` returning
`MintBurn(<address>)` means that address mints on credit, so it must be the
SAC admin from step 4 or a role holder on it. It does not enumerate the
others: on LayerZero's SAC manager the owner grants and revokes
`MINTER_ROLE`, so finish the trust question by listing the role holders and

Every import path, argument order, and trait signature below was read out of that monorepo at commit `3f1cf3adadca88aa7a4ee5a7ee251c8b7fefcf2f` (2026-08-26), whose `rust-toolchain.toml` pins Rust `1.90.0` and the `wasm32v1-none` target. Pin the same revision when you copy it, because these crates are not versioned on crates.io.

It is a skeleton, not a compiled artifact. Signature comparison does not catch macro expansion, feature, dependency, or target errors, and no build was run against this snippet. Compile it yourself for `wasm32v1-none` before you trust it, and confirm `lz_receive` appears in the exported interface.
Copilot AI review requested due to automatic review settings August 28, 2026 17:24
@kaankacar

Copy link
Copy Markdown
Contributor Author

🤖 Automated message from Kaan's Automated Triage Bot.

Copilot reviewed the last commit. Two points were real defects. Fixed in 38325cd:

  • "Burn-and-mint" was not true for every leg. Ethereum's USDT0 is an OFT Adapter over canonical Tether USDT. That leg locks and unlocks a reserve. Stellar's deployment is MintBurn. layerzero.md and the router now state the mode per leg and tell the reader to check the route.
  • The trustline rule was too broad. It applies to G… recipients only. A C… recipient holds the SAC balance in contract storage, so it needs no trustline. Rule 1 and the inbound step are now scoped.

Sources for both:

  • USDT0's deployments page lists Ethereum as OFT Adapter and the other native chains as OFT.
  • token() on the adapter 0x6C96… returns 0xdAC17F95…, canonical USDT, and the adapter holds the locked reserve.
  • resolve_address in LayerZero's oft-core/src/utils.rs returns a contract address when that contract exists, and a G… account otherwise.

The evals moved with the teaching. Scenario 01 now scopes the trustline, and a new 04-usdt0-stellar-to-ethereum.json covers the per-leg mode.

I rejected the repeated testnet-send point. That check needs signing keys, which this sandbox does not have. It is already with you.

The build is green at 38325cd: install, lint, lint:ts, ecosystem link check, tests, build.

The merge is still yours, @kaankacar. The file teaches transaction signing. I will keep tracking this PR.

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

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

skills/cross-chain/layerzero.md:221

  • This is only true when pay_in_zro is false. The documented API exposes ZRO payment, and __lz_send transfers zro_fee through the endpoint's configured ZRO token; presenting every OApp fee as native-token-only can make callers mishandle a ZRO quote.
- **Fees are quoted, then paid in the chain's native token** (XLM on Stellar). Quote with `__quote` into a `MessagingFee` and pass it to `__lz_send`; underquoting fails the send.

skills/assets/SKILL.md:537

  • Stellar multisig authorization sums signer weights. Checking whether one remaining signer reaches a threshold can classify an account as locked even when several signers collectively authorize payments or SetOptions; require that no combination can meet the medium or high threshold.
  signers has an empty `signers` list. Locked means the master weight is `0`
  *and* no remaining signer can reach the medium or high threshold. USDT0's
  issuer reads `thresholds` `00000000` with no extra signers. (Horizon differs:

Comment thread skills/assets/SKILL.md Outdated
Comment on lines +418 to +421
> One hard prerequisite: **lock the issuer** (master key weight `0`) before
> handing administration to a contract. Payments from a classic issuer are
> minting, so an unlocked issuer can mint outside the contract and bypass
> the role model entirely.

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

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Suppressed comments (3)

skills/cross-chain/layerzero.md:164

  • Issue #115 requires the restored OApp skeleton to be compiled for wasm32v1-none, but this line confirms that verification has not been performed. Signature comparison cannot catch macro expansion or exported-interface failures, so please compile the pinned snippet and record the verified revision/result before merging.
It is a skeleton, not a compiled artifact. Signature comparison does not catch macro expansion, feature, dependency, or target errors, and no build was run against this snippet. Compile it yourself for `wasm32v1-none` before you trust it, and confirm `lz_receive` appears in the exported interface.

skills/cross-chain/layerzero.md:154

  • Issue #115 explicitly requires re-testing a testnet send after the endpoint redeployment, while this status still relies on the pre-redeployment failure and says no current send was confirmed. Re-run a small message against EID 40600 and update this note with the dated result before merge; otherwise the documented operational status may already be stale.
- **A testnet rehearsal is not guaranteed.** You can deploy your own OApp or OFT against the testnet endpoint (EID `40600`), but testnet sends failed with `#1213 UnsupportedMessageLib` in August 2026 — the required DVN did not support the endpoint's only registered message library. That endpoint has been redeployed since, and this file does not record a successful testnet send after it. Send one small testnet message and confirm delivery before you treat testnet as a rehearsal path.

skills/assets/SKILL.md:564

  • Checking only direct role holders and the owner misses indirect privilege. LayerZero's RBAC also lets any holder of the role returned by get_role_admin(role) grant or revoke that role, so a collateral review must enumerate those admin-role holders as well; otherwise an account able to grant itself MINTER_ROLE is omitted.
  others: on LayerZero's SAC manager the owner grants and revokes
  `MINTER_ROLE`, so finish the trust question by listing the role holders and

Comment thread skills/cross-chain/layerzero.md Outdated
- The `#[oapp]` macro generates the public surface (`OAppCore`, sender internals, the `lz_receive` entrypoint, options handling). The generated `lz_receive` does peer validation and `endpoint.clear()` **before** dispatching to your `__lz_receive` — don't reimplement either.
- **`custom = [receiver]` is a footgun.** Passing `#[oapp(custom = [receiver])]` tells the macro to *skip* generating the receiver surface; unless you then supply your own `#[contract_impl(contracttrait)] impl OAppReceiver` (as the counter example does, to customize `next_nonce`), the contract **compiles cleanly but exports no `lz_receive` at all** — an OApp that silently cannot receive. Use plain `#[oapp]` unless you're deliberately taking that surface over.
- **Peers must be set on both sides.** `set_peer(&dst_eid, &Some(remote_oapp_bytes32), &caller)` on Stellar, and the mirror call on the destination OApp. A message from an unset peer never reaches `__lz_receive`.
- **Fees are quoted, then paid — in XLM, or in ZRO.** Quote with `__quote(dst_eid, message, options, pay_in_zro)` into a `MessagingFee { native_fee, zro_fee }` and pass that value to `__lz_send`; underquoting fails the send. `__lz_send` pays ZRO whenever `fee.zro_fee` is not `0`. The endpoint rejects that payment unless a ZRO token is set on it (`zro()` → `Option<Address>`, error `ZroUnavailable`). Pass `pay_in_zro = false` unless you read a ZRO token on the endpoint you use.
"expected_behavior": [
"Quotes with `__quote` and passes the returned `MessagingFee` to `__lz_send`, rather than guessing an amount",
"Names both fee fields: `native_fee` in XLM and `zro_fee` in the ZRO token",
"Says `__lz_send` pays ZRO whenever `zro_fee` is not 0, and that the endpoint fails with `ZroUnavailable` when no ZRO token is set on it",
__quote with pay_in_zro = true reaches the endpoint and panics
EndpointError::ZroUnavailable. __lz_send never gets that far: __pay_zro
runs first and panics OAppError::ZroTokenUnavailable. Naming one error
for both makes a correct handler look wrong.

The inbound recipe said "a 32-byte value", which is ambiguous on the one
step that loses funds. Spell out the decode: the Ed25519 public key for
a G address, the contract ID hash for a C address, no version byte and
no checksum. Point away from the CCTP hook-data pattern, which is the
opposite shape.

Also finish the pre-listing recipe. Step 5 told the reader to list the
admin contract's role holders without showing how. Add step 6 with the
read-only RBAC calls, and record that the owner and any role-admin
holder can grant a role, so an empty role is not a safe role.

Align the testnet note with the router and the rehearsal scenario: the
proving step is a dust-sized mainnet transfer, not an open condition.
Copilot AI review requested due to automatic review settings August 28, 2026 17:51
An M strkey decodes to 40 bytes and the OFT reads 32, so the muxed id is
dropped and a custodian loses the sub-account it routes on. Say that,
give the two workarounds, and note that CCTP hook data takes M directly
so the two rails are not confused.
@kaankacar

Copy link
Copy Markdown
Contributor Author

🤖 Automated message from Kaan's Automated Triage Bot.

Copilot's last review found two real defects. Reading back through the thread, I also found three earlier points I never answered. All six fixes are in e8a0fd7b03d077.

The two new ones:

  • Two different no-ZRO failures were named as one. __quote with pay_in_zro = true reaches the endpoint and panics EndpointError::ZroUnavailable. __lz_send never gets there. __pay_zro runs first and panics OAppError::ZroTokenUnavailable. The file and eval 05 now separate them.
  • Eval 05 asserted the wrong error, so a correct answer would have failed it.

The three I had missed:

  • "A 32-byte value" was too vague for the inbound recipient. The OFT passes those bytes straight to resolve_address. The file now says to decode the strkey and send only the payload: the Ed25519 key for G…, the contract ID hash for C…. No version byte, no checksum. It also warns against copying the CCTP shape, which carries the strkey as UTF-8 hook data. An M… address does not fit at all.
  • Step 5 of the pre-listing recipe asked for the role holders but never showed how. New step 6 adds the read-only calls: owner, get_existing_roles, get_role_admin, get_role_member_count, get_role_member. An empty role is not a safe role: the owner, and any holder of a role's admin role, can fill it in one transaction.
  • The testnet note gave no way to prove the flow. It now matches the router and eval 03: quotes, then a dust-sized mainnet transfer.

Sources: oapp_sender.rs, endpoint_v2.rs, oft-core/src/utils.rs and common-utils/src/rbac.rs at 3f1cf3a. I also read the live SAC manager storage: one role (MINTER_ROLE), one member (the OFT), no role admin, owner is the OneSig contract.

I rejected the repeated compile and testnet-send points again. This sandbox has no Rust toolchain and no signing keys.

The build is green at b03d077: install, lint, lint:ts, ecosystem link check, tests, build.

Two checks still need you, @kaankacar:

  1. Compile the skeleton for wasm32v1-none.
  2. Send one testnet message and confirm delivery.

The merge is still yours. The file teaches transaction signing. I will keep tracking this PR.

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

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Comment thread skills/assets/SKILL.md Outdated
Comment on lines +595 to +598
USDT0 on 2026-08-28: `get_existing_roles` returns `MINTER_ROLE` only, its one
member is the OFT, it has no admin role, and the owner is the OneSig contract
`CBCZ5CET…`. So the OneSig signers are the real authority over minting,
clawback and blacklisting. Re-read it — this is live state.
Comment thread skills/cross-chain/layerzero.md Outdated

### The rules that save funds

1. **An account (`G…`) recipient needs a USDT0 trustline before anything inbound lands.** A `G…` account cannot hold an issued asset without one. This is the single most common inbound failure. A contract (`C…`) recipient needs none: SAC balances for contracts live in contract storage, not in a trustline. The OFT decides which one you get from the 32-byte recipient — it resolves to a contract address when a contract with that ID exists, and to a `G…` account otherwise.
Copilot AI review requested due to automatic review settings August 28, 2026 17:58

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

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Suppressed comments (3)

Previously missed (1) — in code that hasn't changed since the last review.

skills/assets/SKILL.md:550

  • get_existing_roles returns only roles with at least one member, so iterating that list skips empty privileged roles. Those roles may still have an admin role whose members can grant them later; for example, this recipe never checks the admin of an empty CLAWBACK_ROLE or ADMIN_MANAGER_ROLE. Query all four SAC-manager roles explicitly, then enumerate each role's members and admin role.
# Then, for every role that list returns:
ROLE=MINTER_ROLE
stellar contract invoke --id $MANAGER --source-account alice \
  --network mainnet --send=no -- get_role_admin --role $ROLE
stellar contract invoke --id $MANAGER --source-account alice \
  --network mainnet --send=no -- get_role_member_count --role $ROLE
stellar contract invoke --id $MANAGER --source-account alice \
  --network mainnet --send=no -- get_role_member --role $ROLE --index 0

skills/cross-chain/layerzero.md:184

  • The issue's verification plan requires this skeleton to compile for wasm32v1-none before shipping, and the PR description explicitly asks not to merge without that check. Leaving the snippet knowingly uncompiled does not satisfy that acceptance criterion; run the pinned workspace build and update this status only after the exported lz_receive is verified.
It is a skeleton, not a compiled artifact. Signature comparison does not catch macro expansion, feature, dependency, or target errors, and no build was run against this snippet. Compile it yourself for `wasm32v1-none` before you trust it, and confirm `lz_receive` appears in the exported interface.

skills/cross-chain/layerzero.md:174

  • This status note still describes a failure observed before the testnet endpoint was redeployed. The issue's verification plan and PR description require a new signed send against the current CALTBA5S… endpoint before merge; record that result and date, retaining #1213 only if it still reproduces.
- **A testnet rehearsal is not guaranteed.** You can deploy your own OApp or OFT against the testnet endpoint (EID `40600`), but testnet sends failed with `#1213 UnsupportedMessageLib` in August 2026 — the required DVN did not support the endpoint's only registered message library. That endpoint has been redeployed since, and this file does not record a successful testnet send after it. Send one small testnet message and confirm delivery before you treat testnet as a rehearsal path.

"Tests the issuer lock by the master key weight (the first byte of the account entry's thresholds), not by the signer list alone, and treats the extra signers' combined weight against the thresholds",
"Reports auth_revocable and auth_clawback_enabled as risks to model: balances can be frozen or clawed back",
"Derives the SAC from the asset rather than trusting a published address, and does not disqualify the asset for having no stellar.toml",
"Goes past oft_type's MintBurn address and enumerates the SAC admin contract's authority: get_existing_roles, then get_role_member for each role, plus get_role_admin and owner",
Comment thread skills/cross-chain/layerzero.md Outdated
SENDER=$(stellar keys address alice) # the account that pays and signs
EVM_TO=0x1234...abcd # the EVM recipient, 20-byte hex
TO=000000000000000000000000${EVM_TO#0x} # left-padded to 32 bytes
PARAM='{"dst_eid":30101,"to":"'"$TO"'","amount_ld":"10000000","min_amount_ld":"9950000","extra_options":"","compose_msg":"","oft_cmd":""}'
A C… recipient only skips the trustline while that contract exists. If it is
not deployed at delivery, resolve_address reads the same 32 bytes as an
Ed25519 account, and that account can never sign a changeTrust.

The pre-listing recipe stopped at 'the owner is a OneSig contract', which
names a governance layer rather than an authority. Step 7 reads the owner's
own quorum, and says to mark ultimate control unresolved when it cannot be
read.
Copilot AI review requested due to automatic review settings August 28, 2026 18:10
Both quotes call __debit_view, which asserts amount_received_ld >=
min_amount_ld. A discovery quote carrying a real floor therefore panics
SlippageExceeded on any route that costs more, and hides the receipt.

get_existing_roles returns only roles that have a member today. A role's
admin role is stored separately and outlives an empty role, so iterating
that list alone misses whoever can grant CLAWBACK_ROLE.

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

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

Suppressed comments (3)

Previously missed (1) — in code that hasn't changed since the last review.

skills/assets/SKILL.md:500

  • ledger entry fetch account emits the XDR flags field as a numeric u32, not these named booleans. As written, the recipe does not explain how to determine the authorization risks it requires. Document the bitmask values (USDT0's 10 means AUTH_REVOCABLE | AUTH_CLAWBACK_ENABLED).
#    Read: thresholds and signers (see "Step 1" below — the signer list
#    alone does not answer this), auth_revocable, auth_clawback_enabled,
#    auth_immutable, and whether home_domain exists.
stellar ledger entry fetch account --account $ISSUER --network mainnet

evals/scenarios/routing/04-usdt0-collateral.json:13

  • This evaluation codifies the incomplete get_existing_roles workflow: it cannot discover an admin assignment on an empty privileged role. Require explicit checks of all four SAC-manager roles and the members of each configured admin role, or the eval can pass while omitting an account able to grant itself sensitive authority.
    "Goes past oft_type's MintBurn address and enumerates the SAC admin contract's authority: get_existing_roles, then get_role_member for each role, plus get_role_admin and owner",
    "Does not treat get_existing_roles as the role list: it returns only roles with a member today, so the answer queries MINTER_ROLE, CLAWBACK_ROLE, BLACKLISTER_ROLE and ADMIN_MANAGER_ROLE by name and reports each one's admin role, because an empty role's admin can still grant it",

skills/assets/SKILL.md:546

  • get_existing_roles returns only roles that currently have members, so this loop skips empty privileged roles. An omitted role can still have a configured admin role whose members may grant it, causing the pre-listing check to miss indirect mint/clawback/admin authority. Query all four SAC-manager roles explicitly and enumerate each returned admin role too.
# get_existing_roles lists only roles that have a member today. Ask about
# the empty ones by name as well, and add anything new the call returned.
# Both views below are safe on an empty role: None, and 0.
for ROLE in MINTER_ROLE CLAWBACK_ROLE BLACKLISTER_ROLE ADMIN_MANAGER_ROLE; do

Copilot AI review requested due to automatic review settings August 28, 2026 18:16
The raw account entry carries flags as a u32 bitmask, not the four named
booleans Horizon returns, so the recipe now decodes it: USDT0's issuer
reads 10.

An admin role's members can grant the role they administer, so they belong
in the answer next to the role's own members.

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

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Suppressed comments (4)

Previously missed (1) — in code that hasn't changed since the last review.

evals/scenarios/routing/04-usdt0-collateral.json:4

  • This scenario expects assets to load, but that skill’s frontmatter still advertises only issuance, trustlines, and SAC bridging—not listing, due diligence, collateral, or asset-risk review. The exact collateral query can therefore route only to cross-chain, leaving the new pre-listing workflow undiscovered. Add these new intents to the assets frontmatter description so the trigger expectation is supported.
  "skills": [
    "assets",
    "cross-chain"

skills/cross-chain/layerzero.md:206

  • The PR’s required verification explicitly says not to merge until this skeleton builds for wasm32v1-none and exports lz_receive, but this note confirms that check was not run. Build this exact snippet against the pinned revision and record the result before merging; signature comparison does not verify macro expansion or target compatibility.
It is a skeleton, not a compiled artifact. Signature comparison does not catch macro expansion, feature, dependency, or target errors, and no build was run against this snippet. Compile it yourself for `wasm32v1-none` before you trust it, and confirm `lz_receive` appears in the exported interface.

skills/cross-chain/layerzero.md:196

  • The PR description identifies a post-redeployment testnet send as the second mandatory pre-merge check, but this still records only the earlier failure and no result against the current endpoint. Re-run one testnet delivery and update this status with either the successful transaction or the currently reproduced failure before merging.
- **A testnet rehearsal is not guaranteed.** You can deploy your own OApp or OFT against the testnet endpoint (EID `40600`), but testnet sends failed with `#1213 UnsupportedMessageLib` in August 2026 — the required DVN did not support the endpoint's only registered message library. That endpoint has been redeployed since, and this file does not record a successful testnet send after it. Send one small testnet message and confirm delivery before you treat testnet as a rehearsal path.

skills/assets/SKILL.md:571

  • This promises an ABI-free read of both values, but the command only fetches the instance entry containing Threshold; it never fetches the separate persistent Signers entry. As written, a reader cannot perform the advertised signer verification without the ABI call above.
stellar contract invoke --id $ONESIG --source-account alice \
  --network mainnet --send=no -- get_signers   # 20-byte secp256k1 addresses
stellar contract invoke --id $ONESIG --source-account alice \
  --network mainnet --send=no -- threshold     # signatures needed to act

Comment thread skills/cross-chain/layerzero.md Outdated
--output json-formatted --network mainnet
```

Existence is read when the message is delivered, not when you send it. Re-check right before the send, and never point a route at a contract you have not deployed yet.
Comment thread skills/cross-chain/layerzero.md Outdated

# 2. Show the user amount_received_ld, get their floor, then rebuild the
# parameter with it. This is the value you send with.
read -r MIN_AMOUNT # the minimum the user accepts, in stroops
Comment thread skills/cross-chain/layerzero.md Outdated
Comment on lines +148 to +155
# 3. What does the message cost? Returns MessagingFee { native_fee, zro_fee }.
stellar contract invoke --id "$OFT" --source-account alice \
--network mainnet --send=no \
-- quote_send --from "$SENDER" --pay_in_zro false --send_param "$PARAM"

# 4. The send itself. NATIVE_FEE is the stroop figure step 3 returned; quote
# it every time. Drop --send=no only when the user agreed to sign.
read -r NATIVE_FEE # paste the native_fee from step 3
Comment thread skills/assets/SKILL.md Outdated
Comment on lines +483 to +487
### Pre-Listing Check (read-only)

Before you list an asset, display it, or accept it as collateral, answer seven
questions from the ledger itself. Everything below **simulates only** —
`--send=no` never signs or submits, and no step needs a key.
Copilot AI review requested due to automatic review settings August 28, 2026 18:23

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

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

Suppressed comments (5)

Previously missed (1) — in code that hasn't changed since the last review.

skills/assets/SKILL.md:487

  • The recipe is not key-free as written: every contract invocation uses the undeclared local identity alice, so it fails on a fresh installation unless that identity has first been configured. Use an existing public G-address such as $ISSUER for --source-account throughout so the read-only checks require no local identity or secret.
Before you list an asset, display it, or accept it as collateral, answer seven
questions from the ledger itself. Everything below **simulates only** —
`--send=no` never signs or submits, and no step needs a key.

skills/cross-chain/layerzero.md:196

  • This still reports only the pre-redeployment testnet failure. The PR verification plan requires re-testing the current EID 40600 endpoint and, if it still fails, documenting the new attempt; run that check and update this status with the current result before merge.
- **A testnet rehearsal is not guaranteed.** You can deploy your own OApp or OFT against the testnet endpoint (EID `40600`), but testnet sends failed with `#1213 UnsupportedMessageLib` in August 2026 — the required DVN did not support the endpoint's only registered message library. That endpoint has been redeployed since, and this file does not record a successful testnet send after it. Send one small testnet message and confirm delivery before you treat testnet as a rehearsal path.

skills/cross-chain/layerzero.md:206

  • The PR's verification plan requires this OApp skeleton to compile for wasm32v1-none, but this paragraph explicitly says no build was run. Signature comparison cannot validate macro expansion or exported methods, so the requested compile check must be completed and the verified revision/build result recorded before merge.
It is a skeleton, not a compiled artifact. Signature comparison does not catch macro expansion, feature, dependency, or target errors, and no build was run against this snippet. Compile it yourself for `wasm32v1-none` before you trust it, and confirm `lz_receive` appears in the exported interface.

skills/assets/SKILL.md:483

  • This deep dive adds roughly 160 lines to assets/SKILL.md, taking it past 700 lines. That conflicts with the repository rule to keep SKILL.md bodies under ~500 lines and move deep dives into routed companion files (README.md:90,116); move the pre-listing recipe and result interpretation into an assets companion and leave a short router entry here.
### Pre-Listing Check (read-only)

skills/cross-chain/layerzero.md:145

  • min_amount_ld is a USDT0 token amount, not an XLM amount, so calling its units “stroops” is misleading. Describe it as 7-decimal USDT0 subunits; reserve stroops for the native XLM fee below.
read -r MIN_AMOUNT                     # the minimum the user accepts, in stroops

README asks for a SKILL.md under ~500 lines with deep dives in companion
files. The pre-listing recipe pushed assets/SKILL.md to 709 lines, so it
now lives in assets/pre-listing.md behind a short routed summary.

Also from review: re-quote quote_oft with the accepted floor before
pricing the message, name min_amount_ld's units (USDT0 7-decimal, not
stroops), and check the recipient contract's TTL, because an instance
archived before delivery takes the same G-address fallback.
Copilot AI review requested due to automatic review settings August 28, 2026 18:30
@kaankacar

Copy link
Copy Markdown
Contributor Author

🤖 Automated message from Kaan's Automated Triage Bot.

Copilot found more real defects after my last update. All are fixed across 46b9a0554485c0:

  • A C… recipient is trustline-free only while that contract exists. If it is not deployed at delivery, resolve_address reads the same 32 bytes as a G… account. Nobody holds that key, so its trustline can never be created, and the tokens are already gone on the source chain. The file now requires a deployment and TTL check before the send.
  • The pre-listing recipe stopped at "the owner is a OneSig contract". That names a governance layer, not an authority. New step 7 reads the owner's own quorum: 5 signers, threshold 3, secp256k1 keys that can replace themselves.
  • get_existing_roles is not the role list. It returns only roles with a member today. An empty role's admin role can still grant it, so the recipe now asks for all four roles by name and walks each admin role's members.
  • flags is a bitmask in the ledger entry, not the named booleans Horizon returns. USDT0's issuer reads 10.
  • The discovery quote carried a real slippage floor. Both quotes assert min_amount_ld and panic SlippageExceeded, which hides the receipt. Discovery now uses 0, then re-quotes with the user's floor.
  • assets/SKILL.md broke the ~500-line rule in README.md. The recipe moved to skills/assets/pre-listing.md, behind a short summary.

Sources: oft.rs, oft-core/src/utils.rs, rbac.rs and multisig.rs in LayerZero-Labs/monorepo-external at 3f1cf3a. I also read the live OneSig storage and Horizon.

The evals moved with the teaching: cross-chain 01 and 04, and the collateral scenario.

The build is green at 54485c0: install, lint, lint:ts, ecosystem link check, tests, build.

One review point is now yours, @kaankacar. I reached my 10-round fix limit on this PR. Copilot's last point is fair. The recipe says no step needs a key, but every command uses --source-account alice. That identity does not exist on a fresh machine. Replacing alice with a public G… address makes the claim true.

Two checks are unchanged and still need you:

  1. Compile the skeleton for wasm32v1-none.
  2. Send one testnet message and confirm delivery.

The merge is still yours. The file teaches transaction signing. I will keep tracking this PR.

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

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

Suppressed comments (4)

Previously missed (1) — in code that hasn't changed since the last review.

skills/assets/pre-listing.md:37

  • alice is a local identity that does not exist on a fresh machine, so this supposedly keyless recipe fails before simulation. Use the issuer's known public G… address as the source account instead; no private key is needed with --send=no.

This issue also appears in the following locations of the same file:

  • line 48
  • line 63
  • line 90
stellar contract invoke --id $SAC --source-account alice \

skills/assets/pre-listing.md:48

  • These read-only OFT calls still depend on an undefined local alice identity, contradicting the no-key recipe. Reuse the issuer public key as the simulation source.
stellar contract invoke --id $OFT --source-account alice \

skills/assets/pre-listing.md:90

  • The final OneSig reads have the same hidden alice prerequisite. Point them at the existing issuer public key so a fresh machine can run the promised keyless workflow.
stellar contract invoke --id $ONESIG --source-account alice \

skills/assets/pre-listing.md:63

  • The manager-role checks also fail unless the reader already configured an alice identity. Use the public issuer address consistently so this section remains read-only and portable.
stellar contract invoke --id $MANAGER --source-account alice \

@kaankacar

Copy link
Copy Markdown
Contributor Author

🤖 Automated message from Kaan's Automated Triage Bot.

Copilot re-reviewed 54485c0 and raised no new active points. Its one suppressed point repeats the alice item from my last comment. I checked that point against the CLI source. The defect it describes is not real.

  • --source-account parses into an UnresolvedMuxedAccount. An unknown name becomes AliasOrSecret("alice") and stays unresolved.
  • With --send=no, contract invoke simulates against a zero-filled default account. It returns the result before it ever reads --source-account.
  • So the recipe runs on a fresh machine, and "no step needs a key" is true as written.

Source: config/address.rs FromStr, and should_send_tx, simulate and default_account_entry in commands/contract/invoke.rs, stellar/stellar-cli at main.

I withdraw the wording of my last comment on this one point. I repeated Copilot's reason without checking it.

The suggestion is still a small improvement. $ISSUER in place of alice removes an implied setup step. It is one word on 15 lines of skills/assets/pre-listing.md. I reached my fix-round cap on this PR, so I am not pushing it.

The build is green at 54485c0: preview, CodeQL and Socket all pass.

Two checks are unchanged and still need you, @kaankacar:

  1. Compile the skeleton for wasm32v1-none.
  2. Send one testnet message and confirm delivery.

The merge is still yours. The file teaches transaction signing. I will keep tracking this PR.

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 USDT0 (LayerZero OFT) to the cross-chain and assets skills

2 participants