--- name: cross-crossd description: This skill should be used when the user asks to bridge tokens between BSC (56) and CROSS Chain (612055) via the CrossDefi backend (`bridge-api.crosstoken.io`). Any pair listed in `/bridge/pairs` is executable; the CROSSD product also publishes a canonical 8-pair UX surface (§1.1) that is surfaced as informational metadata (`canonical`, `canonicalKind`) but does NOT gate execution. Lists pairs, quotes with full fee + native-gas breakdown, polls bridge status, reads history, and **executes** the bridge with an accurate per-tx native-gas guard. Triggers on phrases like "BSC에서 CROSS로 브릿지", "CROSS 체인 브릿지", "USDT BSC→USDT CROSS", "USDT BSC→CROSSD", "CROSSD를 CROSS로 스왑", "crossdefi bridge", "list crossd pairs", "check bridge status", "permit-hash status", "execute bridge", "send bridge tx". version: 0.3.0 license: MIT --- # CrossDefi Swap-Bridge A distributable skill that lets Claude drive the **CrossDefi swap-bridge** backend across **BSC (chain id 56)** and **CROSS Chain (612055)** — the same backend that powers `https://www.crossdefi.io/swap-bridge`. Execution path is **EOA + viem + raw HTTPS** — no browser automation, no ERC-4337. > **Scope (v0.3.0):** Both **read-path** (`pairs`, `tokens`, `quote`, `status`, `history`) and **write-path** (`bridge`) ship. The authoritative gate is `/bridge/pairs` (the live CrossDefi backend); any pair listed there is executable. The CROSSD product also publishes a **canonical 8-pair UX surface** (§1.1) which is surfaced as informational metadata so callers can label requests, but it is NOT a hard gate. > > `bridge.mjs` auto-picks one of three on-chain sub-paths per the live frontend: > > - **native** — from-token is the chain's native asset. Encodes `bridgeToken(...)` calldata, sends the tx with `msg.value = value + gasFee + exFee`. Returns the on-chain tx hash. > - **permit** — from-token supports EIP-2612 (`nonces(address)` view succeeds). Signs EIP-712 typed data offline, POSTs to `/bridge/permit`. The backend submits the on-chain `permitBridgeToken(...)`; the user pays **no** source-chain gas. Returns the `permitId`. > - **approve** — non-permit ERC-20. Approves the bridge router (exact amount by default; `--max-approve` for unlimited), then sends `bridgeToken(...)` with `msg.value = 0`. Returns both tx hashes. > > Sub-path override: `--path=native|permit|approve`. > > **Authoritative native-gas guard** — before any signing, the skill builds the full source-chain tx plan, runs `eth_estimateGas` for each entry, multiplies by `eth_gasPrice` with a 1.2× headroom, adds any `msg.value`, and refuses with `insufficient_gas` if the wallet's native balance can't cover the total. The error envelope surfaces a per-tx breakdown (`gasShortage.perTx`) so the user sees exactly how much they need (e.g. "BNB short 0.0000388"). > > Deeper protocol details (chain map, endpoint table, native sentinel `0x…0001`, derivation provenance) live in `references/cross-crossd.md`. ## 1.1 Canonical UX routes (informational) The crossdefi.io product surfaces exactly these 8 directed pairs in its UI. Each request through `quote.mjs` / `bridge.mjs` is annotated with `canonical` and `canonicalKind` when it matches one of these rows. **This is informational only — non-canonical pairs that exist in `/bridge/pairs` (e.g. `USDT(BSC) → USDT(CROSS Chain)`) are fully executable.** | # | FROM | → | TO | canonicalKind | In /bridge/pairs | |---|---|---|---|---|---| | 1 | `612055-0x…0001` CROSS (CROSS Chain native) | → | `56-0x6bf6…a510` CROSS (BSC BEP20) | `bridge` | ✅ | | 2 | `612055-0x…0001` CROSS (CROSS Chain native) | → | `612055-0xff42…d452` CROSSD (CROSS Chain) | `swap` | ❌ (same-chain) | | 3 | `56-0x6bf6…a510` CROSS (BSC BEP20) | → | `612055-0x…0001` CROSS (CROSS Chain native) | `bridge` | ✅ | | 4 | `612055-0xff42…d452` CROSSD (CROSS Chain) | → | `612055-0x…0001` CROSS (CROSS Chain native) | `swap` | ❌ (same-chain) | | 5 | `612055-0xff42…d452` CROSSD (CROSS Chain) | → | `56-0x55d3…7955` USDT (BSC) | `swap-bridge` | ❌ (multi-step) | | 6 | `56-0x55d3…7955` USDT (BSC) | → | `612055-0xff42…d452` CROSSD (CROSS Chain) | `swap-bridge` | ❌ (multi-step) | | 7 | `612055-0x1c37…a85c` USDT (CROSS Chain) | → | `612055-0xff42…d452` CROSSD (CROSS Chain) | `swap` | ❌ (same-chain) | | 8 | `612055-0x1c37…a85c` USDT (CROSS Chain) | → | `56-0x55d3…7955` USDT (BSC) | `bridge` | ✅ | In `/bridge/pairs` but **not** in the canonical 8 (still executable; the skill will sign): | FROM | → | TO | notes | |---|---|---|---| | `56-0x55d3…7955` USDT (BSC) | → | `612055-0x1c37…a85c` USDT (CROSS Chain) | direct USDT cross-chain bridge — no swap leg | | `612055-0x1c37…a85c` USDT (CROSS Chain) | → | `56-0x55d3…7955` USDT (BSC) | (same as canonical #8; symmetric direction is also listed in /bridge/pairs) | Run `node scripts/pairs.mjs` to see the canonical list alongside the live `/bridge/pairs` catalog. --- ## 1. Activation Activate when the user wants to: - **List** every currently supported bridge pair (across BSC / CROSS / Klaytn) with its source-chain bridge-router address - **Resolve** ERC-20 / native-token metadata (symbol, decimals, image) on a specific chain — e.g. "what does `56-0x6bf6…a510` mean?" - **Quote** a swap-bridge: parsed intent + ETA seconds + bridge-router address + token-config-derived `valueWei`, `gasFeeWei`, `exFeeWei`, `totalPayableWei`, `minimumValueWei`, `belowMinimum` flag, and in-chain swap-pool routing - **Poll** a permitId via `/bridge/permit-hash/{id}` (single-shot or with `--watch `) — flips from `"pending"` to a terminal status - Read **bridge history** for the resolved EOA via `POST /bridge/history` - **Submit** a bridge tx via the auto-selected path (native / permit / approve+bridgeToken). Returns either an on-chain tx hash (native + approve paths) or a `permitId` (permit path) Trigger phrases (Korean + English, ≥ 6 each): - KR: - `"BSC에서 CROSS로 브릿지"` / `"BSC CROSS를 CROSS 체인으로 보내줘"` - `"CROSS 체인 → BSC로 보내"` / `"USDT BSC → CROSSD"` - `"USDT를 CROSSD로 바꿔줘"` / `"CROSSD를 CROSS로 스왑"` - `"CROSSD 견적"` / `"crossdefi 견적"` - `"내 브릿지 진행상황"` / `"브릿지 상태 확인"` - `"브릿지 페어 목록"` / `"CROSSD 지원 자산 목록"` - EN: - `"bridge BSC CROSS to CROSS Chain"` / `"bridge from BSC to CROSS"` - `"USDT BSC to CROSSD"` / `"swap CROSSD into CROSS"` - `"crossdefi swap"` / `"crossdefi bridge"` - `"list crossd supported pairs"` / `"show crossd pair catalog"` - `"check bridge status"` / `"bridge status "` - `"permit-hash status"` / `"poll permit hash"` If the user pastes a `crossdefi.io/swap-bridge?from=…&to=…` URL, extract the `from` and `to` fragments (they are already in `chainId-tokenAddress` form) and route them verbatim to `quote.mjs` / `bridge.mjs`. If they ask to execute (`"send the bridge tx"`, `"USDT BSC를 USDT CROSS로 보내줘"`): 1. Run `node scripts/quote.mjs ` first. 2. Inspect `executable`: - **`true`** — surface `fees.totalPayableHuman` + `etaSeconds` (and `canonical`/`canonicalKind` if the pair matches the documented UX), ask the user to confirm, then run `node scripts/bridge.mjs --confirm`. The sub-path (native / permit / approve) is auto-detected; pass `--path=…` only if the user asks to override. - **`false`** with `canonicalKind: "swap"` or `"swap-bridge"` — surface the quote envelope's `note` field. The route is in CrossDefi's UX but isn't a single `/bridge/pairs` entry (same-chain swap or multi-step). `bridge.mjs` will refuse with `unsupported_pair`. Tell the user the route needs the swap-router workstream (not yet shipped) and stop. 3. If the pair is not in `/bridge/pairs` at all (e.g. a Kaia route, an unknown token), the skill emits `unsupported_pair`. Suggest `node scripts/pairs.mjs` so the user can see the live catalog + the canonical 8. --- ## 2. Prerequisites — verify before doing anything else Run these checks in order. Stop and report to the user at the first failure. ```bash node --version # require >= 20 ``` Then ensure the script's deps are installed (one-time): ```bash SKILL_DIR="$HOME/.claude/skills/cross-crossd" [ -d "$SKILL_DIR/node_modules" ] || (cd "$SKILL_DIR" && npm install --silent) ``` Read-path commands (`pairs`, `tokens`, `quote`, `status`) work **without** signer configuration. Only `history` and `bridge` need local signer configuration. --- ## 3. Credential resolution — strict priority Resolve the EOA in this order. **Never echo wallet secrets back to the user, never write them into the conversation transcript, never log them, and never ask the user to paste them into chat.** 1. **`process.env.PRIVATE_KEY`** — inherited by the spawned process, not typed into the chat transcript or shell argv. 2. **`./.env` in the user's current working directory** — read `PRIVATE_KEY` and (optionally) `WALLET_ADDRESS`, `BSC_RPC_URL`, `CROSS_RPC_URL`, `MAX_BRIDGE_NOTIONAL`, `CONFIRM_THRESHOLD`. 3. **`$HOME/.claude/skills/cross-crossd/.env`** — same vars, used as the personal default. 4. **Missing signer config** — if all three sources lack `PRIVATE_KEY` and the requested subcommand needs it, stop. Tell the user to create `~/.claude/skills/cross-crossd/.env` locally with: ```bash PRIVATE_KEY=<0x-prefixed-64-hex-secret> MAX_BRIDGE_NOTIONAL=10 CONFIRM_THRESHOLD=1 ``` Then ask them to re-run the request. Do not collect the secret in chat, and do not pass it on the command line. For personal testing, the default `env` backend reads the key from local environment variables or a gitignored `.env` file. For team, hosted-agent, or production funds, prefer Vault Transit, KMS, or HSM-backed signing so the raw key is not exported to the agent runtime. Validation: the value must match `^0x[0-9a-fA-F]{64}$`. Reject otherwise without retrying silently. Read-path subcommands NEVER prompt for a PK. --- ## 4. Safety rails — apply every time Read-path scripts cannot lose funds. The rails below are enforced verbatim by `bridge.mjs` (via `_guard.mjs`) for every write path: 1. **Source-chain id check** — every signed tx aborts unless `eth_chainId` equals the `` chain id encoded in the URL fragment. 2. **`MAX_BRIDGE_NOTIONAL` cap** — env var; if set, abort if the token-unit amount exceeds it. 3. **`CONFIRM_THRESHOLD` + `--confirm` gate** — any bridge above this notional aborts with `{ok:false, error:"awaiting_confirm", parsedIntent}` exit 2 unless `--confirm` is passed. Default `1` token unit. 4. **Pair existence guard** — any pair not present in `/bridge/pairs` aborts with `unsupported_pair` BEFORE any signing. (The 8-pair canonical list in §1.1 is informational only — it does not gate.) 5. **Minimum-value guard** — `valueWei < cfg.minimumValue` (from `/bridge/token-config`) aborts with `below_minimum`. 6. **Accurate native-gas guard** (`enforceNativeGasForTxs`) — after path detection, the skill builds the full source-chain tx plan, runs `eth_estimateGas` for each entry, computes `Σ(estimatedGas × gasPrice) × 1.2 + Σ(msg.value)`, and aborts with `insufficient_gas` if the wallet's native balance can't cover the total. The envelope surfaces a per-tx breakdown (`gasShortage.perTx`) including labels (`approve`, `bridgeToken`), estimated gas per tx, and the exact shortfall in human units (e.g. "BNB short 0.0000388"). For the `permit` path there are zero source-chain txs and this guard is a no-op. 7. **Allowance hygiene (`approve` path)** — defaults to exact-amount approval; opt-in `--max-approve` flag for unlimited. 8. **`WALLET_ADDRESS` mismatch warning** — non-null `signerWarn` field when env-declared address ≠ PK-derived address. Surface to user before continuing. 9. **Never echo PK** — same as siblings. --- ## 5. Execution All subcommands run via Bash and emit a **single JSON object on stdout** (no decorative prose). Parse the envelope and report key fields back. Stderr stays empty unless `DEBUG=1`. ```bash cd "$HOME/.claude/skills/cross-crossd" node scripts/.mjs [args] ``` ### Argument syntax `` and `` use the **same `chainId-tokenAddress` syntax as the website URL**. Only the five tokens below appear in the whitelist (§1.1): - `612055-0x0000000000000000000000000000000000000001` — **CROSS (CROSS Chain native)** (sentinel address) - `56-0x6bf62ca91e397b5a7d1d6bce97d9092065d7a510` — **CROSS (BSC BEP20)** - `612055-0xff42c13d927a6b9265236619161accb227b6d452` — **CROSSD (CROSS Chain)** - `56-0x55d398326f99059ff775485246999027b3197955` — **USDT (BSC)** - `612055-0x1c37d4f44ed3ec86d96868001cfa89e97112a85c` — **USDT (CROSS Chain)** ### NL → subcommand map | User says (KR / EN) | Subcommand | Canonical row | |---|---|---| | "CROSSD 지원 자산 목록" / "list crossd pairs" | `node scripts/pairs.mjs` | — | | "BSC 토큰 보여줘" / "list BSC bridge tokens" | `node scripts/tokens.mjs --chain 56` | — | | "CROSS 체인 토큰" / "tokens on CROSS chain" | `node scripts/tokens.mjs --chain 612055` | — | | "내 진행상황 12345" / "permit-hash status 12345" | `node scripts/status.mjs 12345` | — | | "트랜잭션 0x… 추적" / "track tx 0x…" | `node scripts/status.mjs --tx 0x…` | — | | "내 history" / "show bridge history" | `node scripts/history.mjs` (PK from .env) | — | | "CROSS native 5개 BSC로 보내" / "bridge 5 native CROSS to BSC" | `node scripts/bridge.mjs 612055-0x0000…0001 56-0x6bf6…a510 5 --confirm` | #1 bridge (native) | | "BSC CROSS 5개 CROSS 체인으로" / "bridge 5 BSC-CROSS to CROSS Chain" | `node scripts/bridge.mjs 56-0x6bf6…a510 612055-0x0000…0001 5 --confirm` | #3 bridge (permit) | | "USDT CROSS 10개 BSC로" / "bridge 10 USDT from CROSS to BSC" | `node scripts/bridge.mjs 612055-0x1c37…a85c 56-0x55d3…7955 10 --confirm` | #8 bridge (approve) | | "USDT BSC 5개 CROSS 체인으로" / "bridge 5 USDT from BSC to CROSS" | `node scripts/bridge.mjs 56-0x55d3…7955 612055-0x1c37…a85c 5 --confirm` | off-canonical (in /bridge/pairs) | | "CROSS native → CROSSD 견적" / "quote CROSS to CROSSD" | `node scripts/quote.mjs 612055-0x0000…0001 612055-0xff42…d452 5` (quote only — same-chain swap) | #2 swap | | "CROSSD → CROSS 견적" / "quote CROSSD to CROSS" | `node scripts/quote.mjs 612055-0xff42…d452 612055-0x0000…0001 5` (quote only) | #4 swap | | "CROSSD → USDT BSC 견적" / "quote CROSSD to USDT BSC" | `node scripts/quote.mjs 612055-0xff42…d452 56-0x55d3…7955 5` (quote only) | #5 swap-bridge | | "USDT BSC → CROSSD 견적" / "quote USDT BSC to CROSSD" | `node scripts/quote.mjs 56-0x55d3…7955 612055-0xff42…d452 5` (quote only) | #6 swap-bridge | | "USDT CROSS → CROSSD 견적" / "quote USDT CROSS to CROSSD" | `node scripts/quote.mjs 612055-0x1c37…a85c 612055-0xff42…d452 5` (quote only) | #7 swap | ### Subcommand cheat-sheet - `pairs` — emit the **canonical 8-pair UX surface** plus the raw `/bridge/pairs` catalog. The canonical list is informational; the catalog is the executable gate. Read-only, no PK needed. - `tokens --chain ` — iterates the unique localToken set on `` from `/bridge/pairs` and resolves metadata via `wallet-server/v1/public/token/info`. Read-only. - `quote ` — canonical-route lookup + `/bridge/pairs` existence + `/bridge/tx-time` ETA + `/bridge/token-config` (query shape `{from,to,token}` returns `minimumValue`, `gasFee`, `exFeeRate`) + per-amount fee math + `/bridge/swap-pools` + token-info. Read-only. Emits `canonical`, `canonicalKind`, and `executable`. Pairs not in `/bridge/pairs` fail with `unsupported_pair`; canonical swap/swap-bridge rows succeed with `executable: false` and a `note`. - `status [--watch ] | --tx ` — `/bridge/permit-hash/{id}` (one-shot or polling, max 60 polls) OR `/bridge/check-cached-tx-hashes` for raw tx hashes. - `history [--owner <0x…>] [--page N] [--size N] [--sort desc] [--status ]` — `POST /bridge/history`. Owner defaults to PK-derived address. - `bridge [--confirm] [--max-approve] [--path=native|permit|approve]` — signs any pair in `/bridge/pairs`. Builds the full source-chain tx plan, runs accurate per-tx gas estimation (`enforceNativeGasForTxs`), and refuses with `insufficient_gas` (with `gasShortage` breakdown) if the wallet's native balance can't cover the total. Auto-detects the sub-path (native / permit / approve). Pairs not in `/bridge/pairs` (including canonical swap/swap-bridge rows) fail with `unsupported_pair`. --- ## 6. Reporting back After every action, surface to the user: - The parsed intent (so they can audit it) — echo the `parsedIntent` field - For `pairs`: `canonicalRoutesCount`, the canonical 8 rows, and the live `bridgeApiCatalog` chain count + total pairs - For `tokens`: count + first few `{symbol, address, decimals}` rows - For `quote`: `executable` boolean, `canonical`/`canonicalKind` (if present), `fees.totalPayableHuman` + `fees.belowMinimum`, `etaSeconds`, any `warnings`, the `note` field if present (for `executable: false` canonical swap/swap-bridge rows). - For `status`: the literal `status` string and whether it's `terminal`. - For `history`: count + per-row `{from, to, amount, status, txHash}`. - For `bridge`: the `mode` (native/permit/approve), `parsedIntent.path`, `parsedIntent.gasReport.estimatedGasCostHuman` + `balanceHuman` (so the user sees gas cost vs balance), and the `pollHint`. For `permit`, surface the `permitId`; for `native`/`approve`, surface `txHash`/`approveTxHash`+`bridgeTxHash` and the `explorerUrl(s)`. Never include the PK or full env contents in the report. If the envelope contains a non-null `signerWarn`, surface the mismatch to the user before declaring success. For `awaiting_confirm` errors (exit code 2), summarize the parsed intent, ask the user explicitly for "yes / 진행", and only re-invoke with `--confirm` on confirmation. For `insufficient_gas`, surface the `gasShortage` block in human units: `balanceHuman`, `requiredHuman`, `shortHuman`, plus `nativeSymbol` (e.g. "BNB"). Per-tx breakdown lives at `gasShortage.perTx` (labels: `approve`, `bridgeToken`). Tell the user exactly how much native gas they need to add. For `unsupported_pair`, suggest running `node scripts/pairs.mjs` so the user can see the canonical 8-pair UX list AND the live `/bridge/pairs` catalog. --- ## 7. Distribution This skill folder is the unit of distribution. Recipients: 1. Copy the whole `cross-crossd/` folder into `~/.claude/skills/` 2. Run `cd ~/.claude/skills/cross-crossd && npm install` once (or let the skill do it on first use) 3. (Optional, only for `history` / `bridge`) create `~/.claude/skills/cross-crossd/.env` from `.env.example`, or let the skill prompt them on first run Cross-link: deeper details (chain registry, endpoint table, native sentinel handling, derivation provenance) live in `references/cross-crossd.md`. Lazy-load it only when an endpoint returns an unfamiliar shape.