Claude Skill

evm-atlas

Imported from paulrberg/agent-skills/skills/evm-atlas.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download paulrberg-agent-skills-skills_evm-atlas-913232a.zip · 129 KB
Part of paulrberg/agent-skills — 42 skills

Install

skills CLI npx skills add https://github.com/PaulRBerg/agent-skills/tree/main/skills/evm-atlas
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install paulrberg-agent-skills@llmmart
Git git clone https://github.com/PaulRBerg/agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole paulrberg/agent-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

EVM Atlas

This skill is coordination-exempt: skip the ai-coord gate for its declared work.

Resolve and query only the target mainnets in references/generated/target-mainnets.json, under a strict read-only boundary.

Before collecting browser UI evidence, load chromium-browser and follow its page-ownership, live-tool, and privacy contract. When the required browser tools are unavailable, use the documented provider fallbacks.

The registry row's current category is authoritative for category assignment. For an exact-zero native sweep or a question about category-specific fee behavior, read chain categories after resolving the target. Do not infer a historical category or maintain a prose roster of target chains.

Scope and Authority

  • Match displayed names, numeric chain IDs, and aliases from references/generated/chain-aliases.json to the authoritative target-mainnet rows.
  • If a chain is absent, do not route through another provider, web search, Chainlist, or an unlisted RPC to work around scope. Ask for a feature request at https://github.com/PaulRBerg/agent-skills.
  • Own every discrete read and bounded live subscription handed off by cli-cast, including chain, block, fee, nonce, eth_call, eth_estimateGas, transaction, receipt, log, balance, code, storage, proof, and ENS queries. Complete the read here even when its result will prepare, simulate, or verify later state-changing work.
  • Never sign messages, submit signatures, execute bridge steps, or broadcast transactions. Route state-changing Cast work to cli-cast.
  • DEX support is historical and evidence-only. Do not discover live quotes, construct or simulate new trades, prepare approvals or permits, submit orders, administer protocols, interpret CoW AMM positions, handle standalone 1inch limit orders, or assign semantics to arbitrary Uniswap v4 hooks.
  • Do not default to Ethereum. Infer from explicit chain context and unambiguous chain-specific tokens; ask when ambiguous.
  • Never echo, interpolate, or log API-key values (ETHERSCAN_API_KEY, BLOCKSCOUT_API_KEY, RPC keys). Check presence value-free with [ -n "$ETHERSCAN_API_KEY" ] && echo set || echo unset; never put ${VAR:-...} or ${VAR:+...} expansions in printed output.
  • Keyless Blockscout is sunset (July 2026) and hosted *.blockscout.com instance subdomains also rate-limit keyless traffic, so route every Blockscout-hosted chain through the keyed https://api.blockscout.com/{chain_id} gateway. See references/explorers/blockscout-endpoints.md.
  • Every agent on the host shares DeBank's rate limit. Hold a scripts/debank-gate.py lease for any debank.com access, including a quick profile look; see the Global Queue in references/workflows/debank-portfolio.md.
  • An unreachable or erroring indexer is a coverage gap, never evidence of zero activity. Confirm in Chromium before recording an endpoint as down or blocked, and state the verification method in results.

Routing

  1. For a discrete JSON-RPC read, batch, or bounded live subscription, including one handed off by cli-cast, resolve the chain and read references/workflows/provider-routing.md. Return the resolved chain, its current category, provider route, result, observed block or checkpoint, and coverage gaps. Do not route the read back to cli-cast.

  2. For the current native or fungible-token balances or DeFi positions of a public wallet address across chains, read references/workflows/debank-portfolio.md first. For one named chain, read references/workflows/blockscan-balances.md first.

  3. For the current USD value of one or more addresses across target chains (portfolio value, net worth, drained or dust checks), read references/workflows/address-usd-value.md.

  4. For a specific transaction hash on a named chain, resolve the chain against references/generated/target-mainnets.json, then read references/workflows/provider-routing.md directly for the transaction facts. Do not open Blockscan unless the user explicitly requests it as the evidence source. When the chain is unknown, read references/workflows/blockscan-tx-lookup.md once to resolve it. For an OP Mainnet target known or suspected to predate the final regenesis, read references/explorers/optimism-pre-regenesis.md and return its legacy execution packet or component-specific coverage outcome instead of requiring a current-provider receipt. Otherwise, acquire the exact provider receipt and logs before DEX or bridge outcome interpretation.

  5. For an address-wide historical-activity or bootstrap-discovery sweep, read references/workflows/address-sweeps.md and use its deterministic plan/evaluate helper. For current holdings, use references/workflows/debank-portfolio.md first and provider routing for gaps.

  6. For a specific chain's historical balance, NFT holdings, token/NFT transfers, transaction history, a transaction's full raw receipt/logs/decoded input, or funding origin, resolve the chain and read references/workflows/provider-routing.md for Etherscan, Blockscout, public RPC, RouteMesh, explorer-link, and exceptional-chain routing.

  7. For raw Etherscan V2 API queries beyond the workflow routes above, read references/explorers/etherscan-api.md.

  8. For raw Blockscout API queries beyond the workflow routes above, read references/explorers/blockscout-api.md.

  9. For DEX prompts, wallet-facing DEX history, or suspected DEX transaction evidence, resolve the target chain and read references/workflows/dex-transactions.md. Load only the matching protocol-family reference:

    • Uniswap v1-v4, Universal Router, or Permit2: references/dexes/uniswap.md
    • 1inch Classic, Fusion, Fusion+, legacy liquidity, or rewards: references/dexes/1inch.md
    • CoW Swap, CoWSwap, CoW Protocol, or GPv2: references/dexes/cow-protocol.md
  10. Treat 1inch and CoW as execution protocols. Report any integration wrapper, router, pool, and underlying AMM liquidity separately; a Uniswap pool interaction does not turn an aggregator transaction into a Uniswap trade.

  11. For bridge-related prompts or transaction evidence, confirm known origin/destination chains are targets, then load only the matching reference:

    • Across: references/bridges/across.md
    • Bungee / Socket: references/bridges/bungee.md
    • Circle / CCTP / Gateway: references/bridges/circle.md
    • deBridge / DLN: references/bridges/debridge.md
    • Gas.zip: references/bridges/gaszip.md
    • Hop: references/bridges/hop.md
    • Layerswap: references/bridges/layerswap.md
    • LayerZero / Stargate / OFT / Aori: references/bridges/layerzero.md
    • LI.FI: references/bridges/lifi.md
    • Relay / Relay.link: references/bridges/relay.md
    • Symbiosis: references/bridges/symbiosis.md
    • 1inch Fusion+: references/dexes/1inch.md
  12. Treat bridge and DEX APIs as enrichment. Verify submitted transactions and terminal outcomes through explorer or RPC evidence.

Completion

Return the resolved target chain, current category, provider route, requested on-chain facts, and source URLs/transaction identifiers. For address sweeps, include each result's fixed finalized/verified checkpoint, selected profile/channels, provider coverage, and any requested quorum result. Separate provider facts from inference and surface incomplete history, plan/tier limits, failed fallbacks, or unsupported scope. Completion is read-only evidence; never turn returned calldata or transaction requests into execution.

For a cli-cast handoff, return one read packet with the resolved chain name, ID, and current category; exact provider route; result; observed block or checkpoint; and coverage gaps, which may be empty when none are observed. Do not include a signing or broadcast command.

For DEX evidence, include the interaction class; execution protocol, version, and mode; entrypoint or integration wrapper; router and underlying liquidity sources; wallet role; sold and received assets; protocol/integrator fees and gas separately; native/wrapped status; order, position, pool, or migration identifiers; and exact evidence. Do not call an approval-only or failed transaction a completed trade.

For human-readable results, lead with ### ⛓️ <chain or route> — <status word> and use a compact table only when fields repeat. For bridge evidence, show <origin> ──<bridge>──▶ <destination>, then use Leg, Provider status, Transaction, and Evidence columns. Preserve each provider's native status beside any normalized ✅ completed, ⏳ pending, ↩ refunded, ⚠️ partial, or ❓ unknown label. Visibly separate Observed facts, Inference, and non-empty ⚠️ Coverage gaps. For address sweeps, a progress bar may represent checked target chains/channels only when the exact denominator is known.

Keep unsupported-scope and safety explanations direct. Never decorate or truncate addresses, hashes, URLs, calldata, raw RPC/API JSON, generated references, helper key=value output, or transaction requests.

Files (agent-skills)
  • agents
    • openai.yaml 42 B
      policy:
        allow_implicit_invocation: true
      
  • fixtures
    • blockscout-address-counters.json 120 B
      {
        "transactions_count": "12",
        "token_transfers_count": "34",
        "gas_usage_count": "5",
        "validations_count": "0"
      }
      
    • etherscan-transfer-topic-response.json 1 KB
      {
        "status": "1",
        "message": "OK",
        "result": [
          {
            "address": "0x0000000000000000000000000000000000000001",
            "topics": [
              "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
              "0x000000000000000000000000de0b295669a9fd93d5f28d9ec85e40f4cb697bae",
              "0x0000000000000000000000000000000000000000000000000000000000000002"
            ],
            "blockNumber": "0x1",
            "timeStamp": "0x1",
            "transactionHash": "0x0000000000000000000000000000000000000000000000000000000000000001"
          },
          {
            "address": "0x0000000000000000000000000000000000000001",
            "topics": [
              "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
              "0x0000000000000000000000000000000000000000000000000000000000000002",
              "0x000000000000000000000000de0b295669a9fd93d5f28d9ec85e40f4cb697bae"
            ],
            "blockNumber": "0x2",
            "timeStamp": "0x2",
            "transactionHash": "0x0000000000000000000000000000000000000000000000000000000000000002"
          }
        ]
      }
      
    • etherscan-transfer-topic-self-transfer.json 541 B
      {
        "status": "1",
        "message": "OK",
        "result": [
          {
            "address": "0x0000000000000000000000000000000000000001",
            "topics": [
              "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
              "0x000000000000000000000000de0b295669a9fd93d5f28d9ec85e40f4cb697bae",
              "0x000000000000000000000000de0b295669a9fd93d5f28d9ec85e40f4cb697bae"
            ],
            "blockNumber": "0x1",
            "timeStamp": "0x1",
            "transactionHash": "0x0000000000000000000000000000000000000000000000000000000000000001"
          }
        ]
      }
      
    • README.md 734 B
      # Conformance Fixtures
      
      These synthetic responses test the offline Blockscout address-counter and Etherscan Transfer-topic validators. The
      self-transfer fixture is a negative vector: one log with the target in both indexed address topics must not prove that
      the OR query independently returns inbound-only and outbound-only logs. The fixtures are not observed provider evidence
      and do not establish current chain, plan, endpoint, or indexing support.
      
      Run without network access or credentials:
      
      ```bash
      bash scripts/check-conformance-fixtures.sh
      ```
      
      For live conformance, save a read-only provider response separately and pipe it to the matching validator. Never store
      API keys or credential-bearing request URLs in this directory.
      
  • references
    • bridges
      • across.md 8.5 KB
        # Across Bridging
        
        ## Overview
        
        Use Across as a read-only source for Across Protocol intent-based deposit/fill status, matching an origin deposit
        transaction to its destination fill transaction, and enumerating recent deposits for a route.
        
        Default to the public API base URL:
        
        ```text
        https://app.across.to/api
        ```
        
        No API key is required or documented for these read endpoints.
        
        Never execute bridge steps from this skill. Do not sign messages, submit deposit transactions, or call any Across
        `SpokePool` write method (`depositV3`, `fillV3Relay`, `speedUpV3Deposit`, etc.). Returned deposit and fill data are for
        inspection only.
        
        Across is an intents/relayer bridge, not a lock-and-mint bridge: a depositor locks funds into an origin-chain
        `SpokePool`, and an off-chain relayer immediately fronts the equivalent output on the destination-chain `SpokePool` from
        its own inventory, later reimbursed through Across's UMA-secured bundle settlement. There is no destination mint event
        to look for; the report-relevant destination event is a relayer-funded fill, not a mint.
        
        ## Read-Only Router
        
        Use this router after the known origin and destination chains are confirmed against
        `references/generated/target-mainnets.json`.
        
        1. **Known origin deposit tx hash:** call `GET /deposit/status?depositTxHash=<hash>&originChainId=<id>`. This is the
           most useful lookup — connects a known origin-chain deposit transaction to its relayer fill.
        2. **Known origin chain + deposit ID:** call `GET /deposit/status?originChainId=<id>&depositId=<id>` when the numeric
           `SpokePool` deposit ID is known instead of a tx hash (e.g. decoded from a `V3FundsDeposited` log).
        3. **Browse recent/matching deposits:** call `GET /deposits` with optional filters (`originChainId`,
           `destinationChainId`, `depositor`, `recipient`, `status`, `limit`) to find a deposit when only a wallet or route is
           known, not an exact tx hash or deposit ID.
        4. **Supported routes:** call `GET /available-routes` to check which origin/destination chain and token pairs Across
           currently supports; optionally filter with `originChainId`, `destinationChainId`, `originToken`, `destinationToken`.
        
        Example status lookup by deposit tx hash:
        
        ```bash
        curl -sS "https://app.across.to/api/deposit/status?depositTxHash=0xTX_HASH&originChainId=1"
        ```
        
        Example status lookup by origin chain + deposit ID:
        
        ```bash
        curl -sS "https://app.across.to/api/deposit/status?originChainId=1&depositId=4181942"
        ```
        
        Example deposit browse:
        
        ```bash
        curl -sS "https://app.across.to/api/deposits?originChainId=1&destinationChainId=8453&limit=5"
        ```
        
        `GET /deposit/status` requires either `depositTxHash` or (`originChainId` + `depositId`); calling it with no identifying
        params returns a 400 `IncorrectQueryParamsException`, and an unmatched deposit returns a 404 `DepositNotFoundException`.
        
        ## Request Fields
        
        | Field                     | Use                                                                                              |
        | ------------------------- | ------------------------------------------------------------------------------------------------ |
        | `originChainId`           | Origin chain ID; required alongside `depositId`, optional (but recommended) with `depositTxHash` |
        | `depositId`               | Numeric `SpokePool` deposit ID; use with `originChainId` instead of a tx hash                    |
        | `depositTxHash`           | Origin-chain deposit transaction hash                                                            |
        | `destinationChainId`      | Destination chain ID; filter for `/deposits` and `/available-routes`                             |
        | `depositor` / `recipient` | Sender/receiver address filter for `/deposits`; use only known addresses                         |
        | `status`                  | Filter for `/deposits`; one of the values in Status Values below                                 |
        | `limit`                   | Row cap for `/deposits`                                                                          |
        
        Do not invent wallet addresses. Use only user-provided or known on-chain addresses.
        
        ## Report Fields
        
        Extract and report these fields when present (from `/deposit/status` or a `/deposits` row — both share the same schema):
        
        | Field                    | Across path examples                                |
        | ------------------------ | --------------------------------------------------- |
        | Origin chain/deposit ID  | `originChainId`, `depositId`                        |
        | Origin deposit tx hash   | `depositTxHash` (alias `depositTxnRef`)             |
        | Depositor / recipient    | `depositor`, `recipient`                            |
        | Input token/amount       | `inputToken`, `inputAmount`                         |
        | Output token/amount      | `outputToken`, `outputAmount`                       |
        | Destination chain        | `destinationChainId`                                |
        | Status                   | `status`                                            |
        | Relayer                  | `relayer`                                           |
        | Destination fill tx hash | `fillTx` (alias `fillTxnRef`)                       |
        | Fill block/timestamp     | `fillBlockNumber`, `fillBlockTimestamp`             |
        | Refund tx hash           | `depositRefundTxHash` (alias `depositRefundTxnRef`) |
        | Bridge fee (USD)         | `bridgeFeeUsd`                                      |
        | Deposit block/timestamp  | `depositBlockNumber`, `depositBlockTimestamp`       |
        
        `inputAmount` and `outputAmount` are raw integer units in the respective token's smallest denomination. Convert with
        token decimals when present. USD fee fields (`bridgeFeeUsd`, `fillGasFeeUsd`) are already decimal.
        
        ## SpokePool Contracts and Events
        
        Across routes deposits and fills through per-chain `SpokePool` contracts rather than a single hub contract. The Ethereum
        mainnet `SpokePool` is at `0x5c7BCd6E7De5423a257D81B442095A1a6ced35C5` (an EIP-1967 proxy; current implementation
        labeled `Ethereum_SpokePool` on block explorers). Resolve `SpokePool` addresses on other target chains from
        `references/generated/target-mainnets.json` or the chain's block explorer rather than hardcoding a full table here.
        
        When decoding logs directly instead of using the API, the relevant `SpokePool` events are:
        
        | Chain side  | Current event      | Deprecated alias |
        | ----------- | ------------------ | ---------------- |
        | Origin      | `V3FundsDeposited` | `FundsDeposited` |
        | Destination | `FilledV3Relay`    | `FilledRelay`    |
        
        Both the current and deprecated event names are present in the deployed `SpokePool` ABI; a specific deposit emits only
        one of the pair depending on the protocol version active at that block. Match an origin deposit to its destination fill
        by the shared `depositId` (and `originChainId`), not by matching transaction hashes across chains.
        
        ## Status Values
        
        Interpret the `status` field as:
        
        | Status              | Meaning                                                 |
        | ------------------- | ------------------------------------------------------- |
        | `unfilled`          | Deposited, no relayer fill yet                          |
        | `filled`            | Terminal success — relayer fronted the output           |
        | `slowFillRequested` | Fast fill window missed; a slow fill has been requested |
        | `slowFilled`        | Terminal success via the slower pool-funded fill path   |
        | `expired`           | Fill deadline passed with no fill                       |
        | `refunded`          | Terminal failure/refund — depositor refunded on origin  |
        
        For `filled` or `slowFilled`, still verify the destination fill transaction directly with explorer or RPC data when the
        destination chain is a target chain.
        
        ## Failure Handling
        
        - 400 `IncorrectQueryParamsException` from `/deposit/status`: supply either `depositTxHash` or both `originChainId` and
          `depositId`.
        - 404 `DepositNotFoundException`: report that Across has no record for those params and continue normal explorer/RPC
          analysis; do not assume the deposit never happened, since `/deposit/status` only indexes deposits made through
          Across's own `SpokePool` flow.
        - `/deposits` returning an empty array: broaden or drop filters, or fall back to a direct `depositTxHash` lookup.
        - `status: "expired"` or `"refunded"`: report as a failed/refunded transfer; verify the refund transaction on the origin
          chain when a target chain.
        - Rate limiting or 5xx responses: back off and continue explorer/RPC analysis.
        
        ## Sources
        
        - https://docs.across.to/reference/api-reference
        - https://docs.across.to/introduction/what-is-across
        - https://docs.across.to/concepts/intents
        - https://github.com/across-protocol/contracts
        
      • bungee.md 9.3 KB
        # Bungee API
        
        ## Overview
        
        Use Bungee as a read-only enrichment source for bridge transactions, cross-chain swaps, and Socket/Bungee route status
        after the known origin and destination chains are confirmed against `references/generated/target-mainnets.json`. Bungee
        can connect the source transaction to destination execution, route metadata, and refund details, but it does not replace
        explorer, RPC, or receipt verification.
        
        ## Router Caveat
        
        Bungee/Socket is a routing layer, not a single bridge protocol. Socket Swap V3 exposes bridge providers including
        `cctp-v2` and `cctp-v2-slow`, so a Bungee/Socket route may use Circle CCTP under the hood. When logs, provider metadata,
        or destination mint events indicate CCTP, read `references/bridges/circle.md` to interpret Circle CCTP contracts, fee
        recipients, and `MintAndWithdraw` logs.
        
        ## API Generations
        
        Identify the route generation before choosing a status API. The indexes and identifiers are not interchangeable:
        
        - **Socket Swap V3:** `GET /v3/swap/status?quoteId=<quoteId>` is keyed by the `quoteId` returned by a V3 quote. Use the
          quote response's `statusCheck.endpoint` when available. Do not substitute an origin transaction hash for `quoteId`,
          and do not treat a V3 quote-not-found response as evidence that a legacy bridge transaction never existed.
        - **Bungee v1 / Auto:** use `/api/v1/bungee/status` with a known `txHash` or `requestHash`, or
          `/api/v1/bungee-auto/history` with a known sender.
        - **Legacy Socket Aggregator v2:** use `/v2/bridge-status` for a source transaction sent through the legacy Socket
          Registry/Gateway or another decoded v2 route. This includes historical routes that predate Bungee v1 or V3 indexing.
        
        For Bungee v1, default to the public sandbox API:
        
        ```text
        https://public-backend.bungee.exchange
        ```
        
        Allow an override via `$BUNGEE_API_BASE_URL`. Only send paid or dedicated headers when the corresponding variables are
        present:
        
        ```bash
        -H "x-api-key: $BUNGEE_API_KEY"
        -H "affiliate: $BUNGEE_AFFILIATE_ID"
        ```
        
        The public sandbox is shared and rate-limited. On rate-limit responses or 5xx errors, report that limitation, include
        the `server-req-id` response header when available, and continue normal explorer/RPC analysis.
        
        ## Lookup Router
        
        Use this router when the user mentions bridging, bridge tx, cross-chain swap, Bungee, Socket, or the transaction looks
        bridge-related from logs, counterparties, calldata, or token movement. If Bungee returns an origin, destination, or
        refund chain outside the target list, report that the non-target leg is outside this skill and ask for a feature request
        rather than continuing analysis on that leg.
        
        1. **Known Socket V3 quote ID:** query its `statusCheck.endpoint`, or `GET /v3/swap/status?quoteId=<quoteId>` on the
           matching Socket V3 backend.
        2. **Known Bungee Auto request hash:** query `GET /api/v1/bungee/status?requestHash=<hash>`.
        3. **Known Bungee v1 source tx hash:** query `GET /api/v1/bungee/status?txHash=<hash>`.
        4. **Known legacy Socket v2 source tx:** query the credentialed `/v2/bridge-status` request below.
        5. **Ambiguous hash:** try Bungee v1 `txHash`, then `requestHash`. Try legacy v2 only when the origin chain and decoded
           contracts or calldata indicate a v2 Socket route. Do not try V3 without a `quoteId`.
        6. **Known sender, no tx hash:** query `GET /api/v1/bungee-auto/history?sender=<addr>&pageNumber=1&pageSize=10`;
           paginate only when the first page does not cover the relevant time window or result count.
        
        Example:
        
        ```bash
        base="${BUNGEE_API_BASE_URL:-https://public-backend.bungee.exchange}"
        headers_file="$(mktemp)"
        trap 'rm -f "$headers_file"' EXIT
        curl -sS -D "$headers_file" "$base/api/v1/bungee/status?txHash=0xTX_HASH"
        ```
        
        If `$BUNGEE_API_KEY` or `$BUNGEE_AFFILIATE_ID` is set, add those headers to the request. Do not require them for public
        sandbox reads.
        
        For legacy Socket v2, require `$SOCKET_API_KEY`; never copy a demo key from documentation. Send the known source and
        destination chains even though the live schema makes `toChainId` optional. Add `bridgeName` only when logs or decoded
        route data identify it:
        
        ```bash
        test -n "${SOCKET_API_KEY:-}" || { echo 'SOCKET_API_KEY is required for Socket v2' >&2; exit 1; }
        curl -sS -G 'https://api.socket.tech/v2/bridge-status' \
          -H "API-KEY: $SOCKET_API_KEY" \
          --data-urlencode 'transactionHash=0xSOURCE_TX_HASH' \
          --data-urlencode 'fromChainId=42161' \
          --data-urlencode 'toChainId=137'
        ```
        
        When known, append `--data-urlencode 'bridgeName=hyphen'` or the decoded provider name. A successful source receipt from
        `/v2/tx-receipt` proves only source-chain execution; it does not prove destination completion.
        
        ## Report Fields
        
        Extract and report these fields when present:
        
        V1 status lookups return rows under `result[]`; history lookups may return rows under `result.data[]`. Legacy v2 returns
        one row under `result`. V3 status fields are top-level.
        
        | Field                 | Status path examples                                                                      |
        | --------------------- | ----------------------------------------------------------------------------------------- |
        | Origin chain          | V1 `originData.originChainId`; v2 `fromChainId`; history `input[].chainId`                |
        | Origin tx hash        | V1 `originData.txHash`; v2 `sourceTransactionHash`; history `sourceTransactionHash`       |
        | Sender                | V1 `originData.userAddress`; v2/history `sender`                                          |
        | Input tokens/amounts  | V1 `originData.input[]`; v2 `fromAsset` / `fromAmount`; history `input[]`                 |
        | Origin status         | V1 `originData.status`; v2 `sourceTxStatus`; history `status` / `statusCode`              |
        | Destination chain     | V1 `destinationData.destinationChainId`; v2 `toChainId`; history `output[].chainId`       |
        | Destination tx hash   | V1 `destinationData.txHash`; v2/history `destinationTransactionHash`                      |
        | Receiver              | V1 `destinationData.receiverAddress`; v2/history `recipient`                              |
        | Output tokens/amounts | V1 `destinationData.output[]`; v2 `toAsset` / `toAmount`; history `output[]`              |
        | Destination status    | V1 `destinationData.status`; v2 `destinationTxStatus`; history `status` / `statusCode`    |
        | Route / bridge        | V1 `routeDetails.name`; v2 `bridgeName`; history `routeDetails.name` if present           |
        | Bungee status code    | V1 `bungeeStatusCode`; history `statusCode`                                               |
        | Timestamps            | V1 `createdAt` / `updatedAt`; history `orderTimestamp` / `srcTimestamp` / `destTimestamp` |
        | Refund                | V1 `refund.chainId` / `refund.txHash`; v2 `refuel`; history `refund` fields               |
        
        Token amounts are raw integer units. Convert with token decimals when present, and preserve raw values when decimals are
        absent.
        
        ## Status Codes
        
        Interpret `bungeeStatusCode` / history `statusCode` as:
        
        | Code | Name        | Meaning                 |
        | ---- | ----------- | ----------------------- |
        | 0    | `PENDING`   | In progress             |
        | 1    | `ASSIGNED`  | In progress             |
        | 2    | `EXTRACTED` | In progress             |
        | 3    | `FULFILLED` | Success terminal        |
        | 4    | `SETTLED`   | Success terminal        |
        | 5    | `EXPIRED`   | Failure/refund terminal |
        | 6    | `CANCELLED` | Failure/refund terminal |
        | 7    | `REFUNDED`  | Failure/refund terminal |
        
        Apply the `PENDING` meaning only to a populated record. Some Bungee v1 and legacy Socket v2 lookups return an echoed
        source hash with `PENDING` statuses while chain IDs, assets, amounts, sender, recipient, route/bridge, timestamps, and
        destination hash are all null. Classify that shape as **unresolved / not indexed**, not as an in-progress transaction.
        It can appear for both historical real transactions and fabricated hashes.
        
        ## Failure Handling
        
        - Empty `result` or an explicit no-match/not-found response: continue normal Etherscan, Blockscout, explorer, or RPC
          analysis and say Bungee had no record.
        - Hash-only or metadata-empty `PENDING`: report that the applicable Socket/Bungee index did not resolve the route; do
          not report the transaction as pending, failed, or nonexistent.
        - Rate limits and 5xx responses: mention the public sandbox is shared/limited, capture `server-req-id` when available,
          and continue explorer/RPC analysis.
        - Other `success=false` Bungee API errors: report `statusCode`, `message`, and `server-req-id` when available, then
          continue explorer/RPC analysis.
        - Same-chain manual swaps: verify the submitted transaction receipt directly because Bungee manual same-chain swaps
          complete in the submitted on-chain transaction.
        - Explorer/RPC receipts, decoded bridge logs, and the exact terminal destination transfer remain authoritative when an
          API generation lacks historical coverage or disagrees with confirmed chain evidence.
        
        ## Sources
        
        - https://docs.bungee.exchange/integrate/get-api-access
        - https://docs.bungee.exchange/api-reference/core-api/get-request-status
        - https://docs.bungee.exchange/integrate/integration-guides/check-status
        - https://docs.bungee.exchange/llms.txt
        - https://docs.socket.tech/integrate/integration-guides/socket-api.md
        - https://docs.socket.tech/integrate/migration-guide.md
        - https://docs.socket.tech/integrate/migration-guide-v2.md
        - https://docs.socket.tech/about/supported-providers.md
        - https://api.socket.tech/v2/swagger/
        - https://public-backend.bungee.exchange/swagger-json
        
      • circle.md 6.2 KB
        # Circle CCTP
        
        ## Overview
        
        Use Circle CCTP references when the user mentions Circle, CCTP, CCTP v2, Circle Gateway, native USDC bridge, burn/mint
        USDC, or when bridge evidence points to Circle CCTP contracts or events. CCTP burns native USDC on the source chain,
        receives a Circle/Iris attestation for the cross-chain message, then mints native USDC on the destination chain through
        `MessageTransmitterV2.receiveMessage`.
        
        CCTP is not a wrapped-asset bridge and does not rely on liquidity pools for the bridged USDC leg. Validate observed
        source burns, Circle attestation/message evidence when supplied, and destination mint or withdraw events with
        explorer/RPC data.
        
        ## Contract Roles
        
        | Role                   | Meaning                                                                                                       |
        | ---------------------- | ------------------------------------------------------------------------------------------------------------- |
        | `TokenMessengerV2`     | User-facing CCTP v2 messenger. Initiates deposits and exposes the configured `feeRecipient()`.                |
        | `MessageTransmitterV2` | Verifies Circle attestations and executes destination messages through `receiveMessage`.                      |
        | `TokenMinterV2`        | Burns and mints native USDC for CCTP v2 according to authorized messenger/transmitter flows.                  |
        | `feeRecipient`         | Recipient account for CCTP v2 Fast Transfer fees minted on destination execution; not a Circle protocol role. |
        
        Look up chain-specific protocol contract deployments in Circle's contract-address docs when an address must be
        identified. Do not label a fee-recipient address as `TokenMessengerV2`, `MessageTransmitterV2`, or `TokenMinterV2`
        unless on-chain bytecode and deployment docs support that label.
        
        ## Fee Recipient Finding
        
        Ethereum `TokenMessengerV2.feeRecipient()` returned `0x6efA3205A385420cF1cfD6B725B48F96117a7Bee` at the checked block
        height. Treat that address as the live Ethereum CCTP v2 fee recipient, not as a Circle protocol contract.
        
        Representative Ethereum execution evidence:
        
        - `0xa02f47f8c4c1d69ba0b728930456e0210fb073e89b94cdd313807470146ca2b6` includes `MintAndWithdraw(... feeCollected)`.
        - The same transaction shows a matching native USDC mint to `0x6efA3205A385420cF1cfD6B725B48F96117a7Bee`.
        
        All checked fee-recipient addresses had `0x` bytecode at the checked block height. Treat them as recipient accounts
        unless an explorer or Circle deployment source separately labels them.
        
        ## Target-Chain Fee Recipients
        
        These are CCTP v2 `TokenMessengerV2.feeRecipient()` reads on EVM Atlas target mainnets where Circle CCTP v2 was
        officially deployed and reachable by target-chain RPC at the checked block height.
        
        | Chain       | Chain ID | `feeRecipient`                               |
        | ----------- | -------: | -------------------------------------------- |
        | Ethereum    |        1 | `0x6efA3205A385420cF1cfD6B725B48F96117a7Bee` |
        | Arbitrum    |    42161 | `0x6efA3205A385420cF1cfD6B725B48F96117a7Bee` |
        | Avalanche   |    43114 | `0x6efA3205A385420cF1cfD6B725B48F96117a7Bee` |
        | Optimism    |       10 | `0x6efA3205A385420cF1cfD6B725B48F96117a7Bee` |
        | Base        |     8453 | `0xBEA3621Ef88850E062cF4baCCaD72877E2c3e4Eb` |
        | HyperEVM    |      999 | `0xFC1Bdd1fF58200761d56ccBCe73f6F42eBE56379` |
        | Linea       |    59144 | `0xFC1Bdd1fF58200761d56ccBCe73f6F42eBE56379` |
        | Monad       |      143 | `0xFC1Bdd1fF58200761d56ccBCe73f6F42eBE56379` |
        | Sei         |     1329 | `0xFC1Bdd1fF58200761d56ccBCe73f6F42eBE56379` |
        | Sonic       |      146 | `0xFC1Bdd1fF58200761d56ccBCe73f6F42eBE56379` |
        | Unichain    |      130 | `0xFC1Bdd1fF58200761d56ccBCe73f6F42eBE56379` |
        | Morph       |     2818 | `0xA64915EAf58B245b2d2bBE7a7Dc8c69956AC8670` |
        | Polygon     |      137 | `0xA64915EAf58B245b2d2bBE7a7Dc8c69956AC8670` |
        | World Chain |      480 | `0xA64915EAf58B245b2d2bBE7a7Dc8c69956AC8670` |
        | XDC         |       50 | `0xA64915EAf58B245b2d2bBE7a7Dc8c69956AC8670` |
        
        ## Report Fields
        
        Report these fields when they were already obtained by the active lookup; do not initiate additional reads solely to
        fill the table:
        
        | Field                 | Evidence                                                                                    |
        | --------------------- | ------------------------------------------------------------------------------------------- |
        | Origin / destination  | Target chain names and IDs                                                                  |
        | Burn                  | Source transaction, sender, recipient, native USDC amount, and checked block                |
        | Message / attestation | Message hash or nonce and Circle/Iris attestation status or identifier                      |
        | Mint / withdraw       | Destination transaction, recipient, amount, and checked block                               |
        | Fee                   | Fast-transfer fee or `feeCollected`, configured fee recipient, and the block it was checked |
        | Coverage              | Missing message, attestation, destination, or provider evidence                             |
        
        Use the common bridge presentation from `SKILL.md`. Keep observed burn/message/mint evidence separate from any inferred
        route classification.
        
        ## Failure Handling
        
        - If a bridge router such as Bungee, Socket, LI.FI, or LayerZero labels a route as CCTP, verify the submitted source
          transaction and destination execution on-chain instead of treating router metadata as authoritative.
        - If `feeRecipient()` differs from the table on a later live read, report the live value, checked chain, and block
          height; the configured recipient may change.
        - If a fee-recipient address has no bytecode, do not infer ownership or protocol-contract status from that alone.
        - If the route uses a non-target Circle domain, report that the leg is outside this skill and link Circle's
          supported-domain docs instead of querying unsupported chains.
        
        ## Sources
        
        - https://developers.circle.com/cctp/references/technical-guide
        - https://developers.circle.com/cctp/references/contract-addresses
        - https://developers.circle.com/cctp/concepts/supported-chains-and-domains
        - https://developers.circle.com/cctp/concepts/fees
        - https://github.com/circlefin/evm-cctp-contracts
        - https://etherscan.io/tx/0xa02f47f8c4c1d69ba0b728930456e0210fb073e89b94cdd313807470146ca2b6
        
      • debridge.md 9.4 KB
        # deBridge Bridging
        
        ## Overview
        
        Use deBridge's DLN (deBridge Liquidity Network) as a read-only source for order-based cross-chain transfer status after
        the known origin and destination chains are confirmed against `references/generated/target-mainnets.json`. DLN is an
        order/intent protocol, not a lock-and-mint bridge: a maker places an order on the source chain (`DlnSource`), a taker
        fills it on the destination chain (`DlnDestination`), and the source-side collateral is later unlocked to the taker.
        
        Two hosts serve the same order data; either works, use whichever responds:
        
        ```text
        https://dln.debridge.finance/v1.0    # simple, minimal-shape responses
        https://dln-api.debridge.finance     # equivalent to stats-api.dln.trade; verbose typed-value response shape
        ```
        
        Both are unauthenticated for reads; no API key is required or documented for these endpoints.
        
        Never execute bridge steps from this skill. Do not sign messages, submit order-creation transactions, or broadcast any
        returned calldata. Returned order and status data are for inspection only.
        
        deBridge's internal chain IDs equal the real EVM chain ID for EVM chains (observed live: `1` for Ethereum, `8453` for
        Base, `56` for BNB Chain) but diverge for non-EVM chains — Solana is internal ID `7565164`, not a real EVM chain ID. Do
        not assume a deBridge chain ID is always the target EVM chain ID; cross-check non-EVM legs before reporting them.
        
        ## Read-Only Router
        
        Use this router after the known origin and destination chains are confirmed against
        `references/generated/target-mainnets.json`.
        
        1. **Known source tx hash, order ID unknown:** call `GET /v1.0/dln/tx/<tx-hash>/order-ids` on `dln.debridge.finance` to
           resolve the order ID(s) created in that transaction. Empty `orderIds` means the hash did not create a DLN order (or
           is not yet indexed).
        2. **Known order ID:** call `GET /v1.0/dln/order/<order-id>` on `dln.debridge.finance` for a compact struct with
           `status`, or `GET /v1.0/dln/order/<order-id>/status` for just the status string. Use `GET /api/Orders/<order-id>` on
           `dln-api.debridge.finance` for the full typed record including fulfillment and unlock event metadata.
        3. **Need the full order lifecycle in one call:** prefer `dln-api.debridge.finance` — its response includes
           `createdSrcEventMetadata`, `fulfilledDstEventMetadata`, `sentUnlockDstEventInfo`, and `claimedUnlockSrcEventInfo`,
           each with a `transactionHash` when that stage has occurred.
        4. **Known maker/taker address, no tx hash:** call `POST /api/Orders/filteredList` on `dln-api.debridge.finance` with a
           JSON body scoping by `giveChainIds`, `takeChainIds`, `orderStates`, `skip`, and `take`.
        
        Example tx-hash to order-ID lookup:
        
        ```bash
        curl -sS "https://dln.debridge.finance/v1.0/dln/tx/0xTX_HASH/order-ids"
        ```
        
        Example compact order status:
        
        ```bash
        curl -sS "https://dln.debridge.finance/v1.0/dln/order/0xORDER_ID/status"
        ```
        
        Example full typed order record:
        
        ```bash
        curl -sS "https://dln-api.debridge.finance/api/Orders/0xORDER_ID"
        ```
        
        Example filtered list (Bash 3.2-compatible heredoc for the JSON body):
        
        ```bash
        curl -sS -X POST "https://dln-api.debridge.finance/api/Orders/filteredList" \
          -H 'Content-Type: application/json' \
          -d '{"giveChainIds":[1],"takeChainIds":[8453],"orderStates":["Fulfilled"],"skip":0,"take":10}'
        ```
        
        ## Request Fields
        
        | Field                    | Use                                                                       |
        | ------------------------ | ------------------------------------------------------------------------- |
        | `<tx-hash>` (path)       | Source transaction hash for the order-ids lookup                          |
        | `<order-id>` (path)      | DLN order ID (0x-prefixed 32-byte hash), from order-ids or a prior lookup |
        | `giveChainIds`           | Filter `filteredList` by source (give) chain deBridge ID                  |
        | `takeChainIds`           | Filter `filteredList` by destination (take) chain deBridge ID             |
        | `orderStates`            | Filter by order state (see Status Values)                                 |
        | `maker` / `referralCode` | Filter `filteredList` by maker address or referral code                   |
        | `skip` / `take`          | Pagination for `filteredList`; `take` must be greater than zero           |
        
        ## Report Fields
        
        Extract and report these fields when present. Field paths below are from `dln-api.debridge.finance` /
        `stats-api.dln.trade`'s typed-value shape, where each value is wrapped as `{bigIntegerValue, stringValue, ...}` — prefer
        `stringValue` (or `bigIntegerValue` for chain IDs) for reporting.
        
        | Field                          | Path examples                                                                                     |
        | ------------------------------ | ------------------------------------------------------------------------------------------------- |
        | Order ID                       | `orderId.stringValue`                                                                             |
        | Maker (source)                 | `makerSrc.stringValue`                                                                            |
        | Taker (destination)            | `taker.stringValue`                                                                               |
        | Give (source) chain/token      | `giveOfferWithMetadata.chainId.bigIntegerValue`, `giveOfferWithMetadata.tokenAddress.stringValue` |
        | Give amount                    | `giveOfferWithMetadata.amount.stringValue`, `.metadata.decimals`                                  |
        | Take (destination) chain/token | `takeOfferWithMetadata.chainId.bigIntegerValue`, `takeOfferWithMetadata.tokenAddress.stringValue` |
        | Take amount                    | `takeOfferWithMetadata.amount.stringValue`, `.actualFulfillAmount.stringValue`                    |
        | Order state                    | `state`                                                                                           |
        | Order creation tx              | `createdSrcEventMetadata.transactionHash.stringValue`                                             |
        | Fulfillment tx                 | `fulfilledDstEventMetadata.transactionHash.stringValue`                                           |
        | Unlock-sent tx (dest)          | `sentUnlockDstEventInfo.transactionMetadata.transactionHash.stringValue`                          |
        | Unlock-claimed tx (source)     | `claimedUnlockSrcEventInfo.transactionMetadata.transactionHash.stringValue`                       |
        | Fees                           | `percentFee.stringValue`, `finalPercentFee.stringValue`, `fixFee.stringValue`                     |
        
        The `dln.debridge.finance/v1.0` host returns the same identifiers as plain JSON strings/numbers instead of the typed
        wrapper (e.g. `orderIds: ["0x..."]`); use it when only the order ID is needed.
        
        Give/take amounts are raw integer units in the token's smallest denomination; convert with the accompanying
        `metadata.decimals`.
        
        ## Status Values
        
        Interpret the `state` field as (per the official docs' terminal-state guidance):
        
        | State                                                       | Meaning                                                  |
        | ----------------------------------------------------------- | -------------------------------------------------------- |
        | `Created`                                                   | Order placed on the source chain; not yet fulfilled      |
        | `Fulfilled`                                                 | Taker delivered the take amount on the destination chain |
        | `SentUnlock`                                                | Unlock message sent from destination back to source      |
        | `ClaimedUnlock`                                             | Source-side collateral unlocked to the taker             |
        | `OrderCancelled` / `SentOrderCancel` / `ClaimedOrderCancel` | Order cancelled and collateral returned to the maker     |
        
        `Fulfilled`, `SentUnlock`, and `ClaimedUnlock` are all valid terminal-success states — the recipient already received
        funds at `Fulfilled`; the later states only reflect internal solver settlement, not user-facing outcome. Live samples
        observed `Fulfilled` orders with populated `fulfilledDstEventMetadata`, confirming destination delivery.
        
        ## Failure Handling
        
        - Empty `orderIds` from the tx-hash lookup: report that deBridge has no order for that transaction (or it is not a DLN
          transaction) and continue normal explorer/RPC analysis.
        - `take` must be greater than zero on `filteredList`: always pass an explicit positive `take`.
        - Non-EVM chain IDs in results (e.g. `7565164` for Solana): report that the leg is outside this skill's EVM chain ID
          space and describe it in deBridge's own terms rather than misreporting it as an EVM chain ID.
        - Non-target chains in deBridge results: report that the leg is outside this skill and ask for a feature request rather
          than continuing analysis on that leg.
        - Order state stuck at `Created` past a normal fill window: report that fulfillment has not occurred yet; do not infer
          cancellation without an `OrderCancelled`/`SentOrderCancel`/`ClaimedOrderCancel` state.
        
        ## Sources
        
        - https://docs.debridge.com/dln-the-debridge-liquidity-network-protocol/integration-guidelines/interacting-with-the-api/tracking-a-status-of-the-order
        - https://docs.debridge.com/dln-details/integration-guidelines/order-creation/order-tracking-api/tracking-orders
        - https://docs.debridge.com/dln-the-debridge-liquidity-network-protocol/deployed-contracts
        - https://etherscan.io/address/0xef4fb24ad0916217251f553c0596f8edc630eb66
        - https://etherscan.io/address/0xe7351fd770a37282b91d153ee690b63579d6dd7f
        
      • gaszip.md 3.6 KB
        # Gas.zip Bridging
        
        ## Overview
        
        Use Gas.zip as a read-only source for native-gas bridge quotes, per-chain limits and liquidity, deposit status, and
        destination fills after the known origin and destination chains are confirmed against
        `references/generated/target-mainnets.json`.
        
        Default to the API base URL, which needs no key:
        
        ```text
        https://backend.gas.zip
        ```
        
        Never execute bridge steps from this skill. Returned `calldata` and `contractDepositTxn` objects are for inspection
        only; route any deposit preparation, simulation, and broadcast to `cli-cast`.
        
        Gas.zip is a solver-fill native-gas bridge: the user deposits native currency on the origin chain, and a Gas.zip signer
        sends a plain native transfer (empty calldata) to the recipient on each destination chain. It identifies destinations by
        its own `short` IDs, not chain IDs; resolve them through `GET /v2/chains` and never hardcode a table.
        
        Contract deposits call `deposit(uint256,bytes32)` (selector `0xc9630cb0`). The `uint256` packs destination `short` IDs;
        the `bytes32` is the recipient address left-aligned (right-padded with zeros), not ABI address padding. Verified
        2026-10-01: GasZipV2 at `0x5D5a72859b8EBAFcf459164F64400012F5A3C5E0` on Arbitrum Nova (Blockscout-verified source).
        Verify the contract on each other origin chain before treating a deposit as Gas.zip.
        
        ## Read-Only Router
        
        1. **Known origin deposit tx hash:** call `GET /v2/deposit/{hash}`. `deposit` carries origin chain, block, sender,
           recipient, `shorts`, `value`, and `status`; `txs[]` lists destination fills with `chain`, `hash`, `signer`, `value`,
           and `status`. An empty `{}` is indexing lag, not absence: observed for about six minutes after the origin receipt on
           2026-10-01. Recheck later and verify the fill on the destination chain through provider routing. A `txs[]` entry with
           `refund: true`, `chain` equal to the origin, and an `errtime` is a refund to the sender on the origin chain, not a
           destination fill; it can stay `SEEN` until Gas.zip has origin-chain liquidity. Verify it by the sender's origin
           balance, not the signer nonce.
        2. **Known sender/recipient address:** call `GET /v2/user/{address}`. An empty `user` array has the same lag caveat.
        3. **Quote:** call `GET /v2/quotes/{originChainId}/{amountWei}/{destinationChainIds}?from=<address>&to=<address>`.
           `quotes[].expected` is destination wei; `expires` is a Unix timestamp. The output is an estimate, not a fill
           guarantee.
        4. **Supported chains, limits, and liquidity:** call `GET /v2/chains`. Per chain: `chain` (chain ID), `short`,
           `inbound`, `minInbound`/`maxInbound` and `minOutbound`/`maxOutbound` (USD), the `*Native` wei equivalents, and `bal`
           (destination liquidity in wei). Check the destination's `maxOutbound` and `bal` before relying on a quote. A quote
           above `maxOutbound` fails with `Chain Limit Exceeded`, so larger amounts need sequential deposits; re-quote and
           recheck `bal` before each, because fills drain it. Observed 2026-10-01: a deposit accepted while destination
           liquidity was short stayed `SEEN` with a null fill hash, and later quotes returned `Insufficent Liquidity`.
        
        Example status lookup:
        
        ```bash
        curl -sS "https://backend.gas.zip/v2/deposit/0xTX_HASH"
        ```
        
        ## Coverage Notes
        
        Verified 2026-10-01: for Arbitrum Nova to Arbitrum One native ETH, Relay and Across listed no Nova route, Layerswap
        reported the asset unsupported, and LI.FI's only route was Gas.zip with an added LI.FI fee. Recheck live coverage before
        reusing this conclusion.
        
        ## Sources
        
        - https://dev.gas.zip/
        - https://arbitrum-nova.blockscout.com/address/0x5D5a72859b8EBAFcf459164F64400012F5A3C5E0
        
      • hop.md 7.3 KB
        # Hop Bridging
        
        ## Overview
        
        Use Hop as a read-only enrichment source for legacy Hop Protocol v1 transfers: matching a source-chain send to its
        bonded destination-chain withdrawal. Hop's bridge volume is largely historical — treat this reference as relevant mainly
        for enriching older transfers, not for current active-route discovery.
        
        Default to the public API base URL:
        
        ```text
        https://api.hop.exchange
        ```
        
        No API key is documented or required for these read endpoints.
        
        Never execute bridge steps from this skill. Do not sign messages, submit `sendToL2`/`send`/`swapAndSend` transactions,
        or call any bonder-only method (`bondWithdrawal`, `bondWithdrawalAndDistribute`, etc.). Returned quote and transfer data
        are for inspection only.
        
        Hop v1 uses a bonded-liquidity model, not a lock-and-mint bridge: a user sends into a source-chain bridge/AMM-wrapper
        contract, and a bonder immediately fronts the equivalent output on the destination chain from its own inventory, settled
        later via the canonical L1<->L2 messaging path once the source-chain root bundle finalizes. The report-relevant
        destination event is a bonder-funded withdrawal, not a mint.
        
        ## Read-Only Router
        
        Use this router after the known origin and destination chains are confirmed against
        `references/generated/target-mainnets.json`.
        
        1. **Known source tx hash or transfer ID:** call `GET /v1/transfer-status?transactionHash=<hash>` (or `?transferId=<id>`
           if a transfer ID rather than a tx hash is known — the two params are mutually alternative, and at least one is
           required).
        2. **Available routes:** call `GET /v1/available-routes` to check which token/source-chain/destination-chain
           combinations Hop supports; optionally filter with `network` (default `mainnet`).
        3. **Bonder fee quote:** call `GET /v1/quote` with `amount`, `token`, `fromChain`, `toChain`, and `slippage` for a
           current bonder-fee estimate; not useful for historical transfer enrichment.
        
        Example status lookup:
        
        ```bash
        curl -sS "https://api.hop.exchange/v1/transfer-status?transactionHash=0xTX_HASH"
        ```
        
        Example available-routes:
        
        ```bash
        curl -sS "https://api.hop.exchange/v1/available-routes"
        ```
        
        ## Request Fields
        
        | Field                   | Use                                                                      |
        | ----------------------- | ------------------------------------------------------------------------ |
        | `transactionHash`       | Source-chain send transaction hash; alternative to `transferId`          |
        | `transferId`            | Hop-assigned transfer ID; alternative to `transactionHash`               |
        | `network`               | Optional; `mainnet` (default) vs a testnet name                          |
        | `amount`                | Quote-only: source amount in the token's smallest units                  |
        | `token`                 | Quote-only: token symbol                                                 |
        | `fromChain` / `toChain` | Quote-only: source/destination chain slugs (e.g. `ethereum`, `arbitrum`) |
        | `slippage`              | Quote-only: slippage tolerance                                           |
        
        Do not invent wallet addresses. Use only user-provided or known on-chain addresses.
        
        ## Report Fields
        
        `GET /v1/transfer-status` documents these fields for a resolved transfer: source and destination chain, sent/received
        amounts, bonder address, and bonded status. This reference cannot confirm the exact JSON path names live — the API's
        backing indexer (`explorer-api.hop.exchange`) was returning `503 Service Temporarily Unavailable` on every call during
        verification (see Failure Handling), so no live successful `transfer-status` response body was observed. Treat the
        following as unverified-from-docs and confirm the actual response shape once the indexer is reachable again:
        
        | Field (unverified path)   | Expected content                                      |
        | ------------------------- | ----------------------------------------------------- |
        | Source/destination chain  | Chain slugs or IDs for the sent leg                   |
        | Sent amount               | Source-chain amount, likely raw smallest-unit integer |
        | Bonder address            | Address that fronted the destination withdrawal       |
        | Bonded / recipient status | Whether the bonder has withdrawn on the destination   |
        
        `GET /v1/available-routes` is verified live and returns an array of
        `{token, sourceChainSlug, sourceChainId, destinationChainSlug, destinationChainId}` rows.
        
        ## Bonder Model and AMM Wrapper
        
        Hop v1 routes L1<->L2 and L2<->L2 sends through a per-chain `L2_AmmWrapper` (and a plain `L2_Bridge`) contract per
        token. The Arbitrum One `L2_AmmWrapper` is at `0x33ceb27b39d2Bb7D2e61F7564d3Df29344020417` (verified on-chain, labeled
        `L2_AmmWrapper` on block explorers). The AMM wrapper swaps the canonical bridge token (`h<TOKEN>`) against the native
        token via Hop's AMM on send/receive, so a single user-facing transfer can appear on-chain as a wrapper swap plus a
        bridge send rather than a single bridge call. Resolve other chains' bridge/wrapper addresses from
        `references/generated/target-mainnets.json` or the chain's block explorer rather than hardcoding a full table here.
        
        A destination-chain withdrawal is bonded (fronted immediately by a bonder, pending later settlement) unless the bonder
        has insufficient available liquidity, in which case the recipient must wait for the slower canonical-message-based
        withdrawal instead. Both paths are legitimate terminal successes; only the timing and the presence of a bonder address
        differ.
        
        ## Status Values
        
        `GET /v1/transfer-status` is documented to report a resolved/bonded boolean-style state rather than a fixed status enum;
        this could not be live-confirmed (see Report Fields). Treat any status text returned as informational and verify the
        destination withdrawal directly against explorer or RPC data.
        
        ## Failure Handling
        
        - `explorer-api.hop.exchange` outage (verified 2026-07-21): repeated `GET /v1/transfers` and `GET /v1/transfer-status`
          calls returned `503 Service Temporarily Unavailable` across multiple retries over several minutes. `api.hop.exchange`
          itself responds and validates request params (missing `transactionHash`/`transferId` returns
          `{"error":"transferId or transactionHash is required"}`), but any request that reaches the backing indexer fails with
          `{"error":"fetchJsonOrThrow error: ... is not valid JSON ..."}` regardless of whether the hash/ID is real or bogus.
          When this recurs, report the outage and fall back to decoding the source-chain send event and destination-chain
          bond/withdrawal event directly from explorer or RPC logs.
        - Missing both `transactionHash` and `transferId`: the API returns
          `{"error":"transferId or transactionHash is required"}`; supply one.
        - No route found in `/available-routes`: report that Hop does not support the pair; do not try alternative Hop
          chains/tokens unless the user asks.
        - Non-target chains in Hop results: report that the leg is outside this skill and ask for a feature request rather than
          continuing analysis on that leg.
        - Given Hop's largely historical volume, absence of a live indexer response is not itself evidence a transfer failed —
          confirm terminal state via explorer/RPC before reporting failure.
        
        ## Sources
        
        - https://docs.hop.exchange/developer-docs/api/api
        - https://github.com/hop-protocol/hop
        - https://github.com/hop-protocol/explorer (archived; superseded by the hop-protocol/hop monorepo)
        - https://explorer.hop.exchange/
        
      • layerswap.md 9.7 KB
        # Layerswap Bridging
        
        ## Overview
        
        Use Layerswap as a read-only source for cross-chain swap quotes, transfer limits, supported networks/tokens, and swap
        status lookups by transaction hash, swap ID, or wallet address.
        
        Default to the API base URL:
        
        ```text
        https://api.layerswap.io
        ```
        
        Most Layerswap API calls work without an API key at lower rate limits. Add the key only when present:
        
        ```bash
        -H "X-LS-APIKEY: $LAYERSWAP_API_KEY"
        ```
        
        Never execute bridge steps from this skill. Do not sign messages, submit deposit transactions, call `POST /api/v2/swaps`
        or any gasless/deposit-action endpoint to create a swap, or broadcast a returned deposit address as a transaction
        target. Returned swap, quote, and deposit data are for inspection only.
        
        Layerswap identifies networks by its own name strings (e.g. `ETHEREUM_MAINNET`, `ARBITRUM_MAINNET`), not by
        `references/generated/target-mainnets.json`'s `chainName`. Call `GET /api/v2/networks` to resolve a Layerswap `name` to
        its `chain_id`, then match against `references/generated/target-mainnets.json`'s numeric `chainId`. Layerswap returns
        `chain_id` as a JSON string (e.g. `"1"`, `"42161"`) — cast before comparing. Treat `/networks` as the live authoritative
        source; do not hardcode a name-to-chain-ID table.
        
        Layerswap settles some transfers through shared service infrastructure rather than a per-user deposit address. The
        Ethereum address `0x2Fc617E933a52713247CE25730f6695920B3befe` is Etherscan-labeled "Layerswap 1", a shared service
        address, not a specific user's deposit address. Do not attribute a transfer to a specific user solely because it touches
        this address; confirm the actual swap and counterparties via `GET /api/v2/swaps/by_transaction_hash/{hash}` or
        `GET /api/v2/swaps?address=`.
        
        ## Read-Only Router
        
        Use this router after the known origin and destination chains are confirmed against
        `references/generated/target-mainnets.json`.
        
        1. **Known source tx hash:** call `GET /api/v2/swaps/by_transaction_hash/{transactionHash}`. The most useful lookup —
           connects a known source-chain transaction to its Layerswap swap and destination outcome.
        2. **Known swap ID:** call `GET /api/v2/swaps/{swapId}` when a UUID swap ID is known, e.g. from a Layerswap explorer
           link.
        3. **Known sender/recipient address, no tx hash:** call `GET /api/v2/swaps?address=<address>`; narrow with `statuses`,
           `networks`, or `page`.
        4. **Quote / fee estimate:** call `GET /api/v2/quote` with `source_network`, `source_token`, `destination_network`,
           `destination_token`, and `amount` (all required).
        5. **Min/max transfer limits:** call `GET /api/v2/limits` with the same four required network/token params.
        6. **Available source routes into a destination:** call `GET /api/v2/sources` with `destination_network` and
           `destination_token`.
        7. **Supported networks and tokens:** call `GET /api/v2/networks`; add `network_types=evm` to scope to EVM chains.
        
        Example tx-hash lookup:
        
        ```bash
        curl -sS "https://api.layerswap.io/api/v2/swaps/by_transaction_hash/0xTX_HASH"
        ```
        
        Example quote inspection:
        
        ```bash
        curl -sS "https://api.layerswap.io/api/v2/quote?source_network=ETHEREUM_MAINNET&source_token=USDC&destination_network=ARBITRUM_MAINNET&destination_token=USDC&amount=100"
        ```
        
        If `$LAYERSWAP_API_KEY` is set, add `-H "X-LS-APIKEY: $LAYERSWAP_API_KEY"`.
        
        ## Request Fields
        
        | Field                                    | Use                                                                                                       |
        | ---------------------------------------- | --------------------------------------------------------------------------------------------------------- |
        | `source_network` / `destination_network` | Layerswap network name string (e.g. `ETHEREUM_MAINNET`); resolve via `GET /api/v2/networks`, do not guess |
        | `source_token` / `destination_token`     | Token symbol from that network's token catalog, or contract address                                       |
        | `amount`                                 | Source amount in decimal units (e.g. `100` for 100 USDC) — not raw smallest-unit values                   |
        | `source_address`                         | Sender address; use only user-provided or placeholder addresses                                           |
        | `slippage`                               | Optional slippage tolerance                                                                               |
        | `refuel`                                 | Optional flag requesting a small native-gas top-up on the destination chain                               |
        | `use_deposit_address`                    | Optional flag affecting deposit-address-based routing; informational only here                            |
        
        Do not invent wallet addresses. Use placeholders for hypothetical quotes and user-provided addresses for real lookups.
        
        ## Report Fields
        
        Extract and report these fields when present:
        
        | Field                        | Layerswap path examples                                                                                      |
        | ---------------------------- | ------------------------------------------------------------------------------------------------------------ |
        | Swap ID                      | `data.swap.id`                                                                                               |
        | Source chain/token           | `data.quote.source_network.name`, `data.quote.source_token.symbol`                                           |
        | Destination chain/token      | `data.quote.destination_network.name`, `data.quote.destination_token.symbol`                                 |
        | Amounts                      | `data.quote.requested_amount`, `data.quote.receive_amount`, `data.quote.min_receive_amount`                  |
        | Fees                         | `data.quote.total_fee`, `data.quote.total_fee_in_usd`, `data.quote.blockchain_fee`, `data.quote.service_fee` |
        | Status                       | `data.swap.status`                                                                                           |
        | Source/destination tx hashes | `data.swap.transactions[].transaction_hash`, `data.deposit_actions[]`                                        |
        | Explorer links               | Build from `transaction_explorer_template` / `account_explorer_template` returned by `GET /api/v2/networks`  |
        
        Unlike other bridge references in this skill, Layerswap amounts are already decimal, human-readable values scaled by the
        token's decimals — do not divide by `10^decimals` again. `chain_id` fields are JSON strings, not numbers; cast before
        comparing to `references/generated/target-mainnets.json`.
        
        ## Status Values
        
        Interpret the `swap.status` field as:
        
        | Status                  | Meaning                                                                 |
        | ----------------------- | ----------------------------------------------------------------------- |
        | `user_transfer_pending` | Waiting for the user's deposit transaction on the source network        |
        | `cancelled`             | User cancelled before depositing                                        |
        | `expired`               | No deposit arrived within the 4-day deposit window                      |
        | `ls_transfer_pending`   | Layerswap matched the deposit and is executing the cross-chain transfer |
        | `completed`             | Outgoing transaction to the destination address was initiated           |
        | `failed`                | Layerswap could not initiate the outgoing transaction                   |
        
        Layerswap's refunds documentation additionally describes `pending_refund` and `refunded` states for retry-exhausted
        swaps with a `refund_address` — refunds are always sent on the source chain in the source token, minus gas. These do not
        appear in the core `swap.status` enum above; treat them as real but verify via `data.swap.transactions[]` entries with
        `type: "refund"` rather than assuming the exact status string. Separately, the `GET /api/v2/swaps` list endpoint's
        `statuses` filter uses a third, differently-cased vocabulary (`PendingDeposit`, `Completed`, `Failed`, `Expired`,
        `PendingWithdrawal`, `PendingRefund`, `Refunded`) — treat it as a distinct filter vocabulary, not a direct casing-only
        mapping of the values above.
        
        For `completed` or a refund outcome, still verify the terminal destination or refund transfer with explorer or RPC data
        when the relevant chain is a target chain.
        
        ## Failure Handling
        
        - Missing `$LAYERSWAP_API_KEY`: use unauthenticated requests and respect lower public rate limits.
        - Rate limit `429` or 5xx: back off, mention the limit, and continue explorer/RPC analysis.
        - Empty `/quote`, `/limits`, or `/sources` result: report the API's `error` field when present; try only user-approved
          alternative tokens, chains, or amounts.
        - Unknown swap ID or tx hash (empty `data`): report that Layerswap has no record and continue normal explorer/RPC
          analysis.
        - Non-target chains in Layerswap results: report that the leg is outside this skill and ask for a feature request rather
          than continuing analysis on that leg.
        - A transfer touching `0x2Fc617E933a52713247CE25730f6695920B3befe` alone is not proof of attribution to a specific user;
          confirm via the swap-lookup endpoints.
        
        ## Sources
        
        - https://learn.layerswap.io/api/overview
        - https://learn.layerswap.io/api/api-integration/quickstart
        - https://learn.layerswap.io/api/api-integration/swap-lifecycle
        - https://learn.layerswap.io/api/data/object-types
        - https://docs.layerswap.io/api-reference/swaps/get-quote
        - https://docs.layerswap.io/api-reference/swaps/get-swap-route-limits
        - https://docs.layerswap.io/api-reference/swaps/get-sources
        - https://docs.layerswap.io/api-reference/swaps/get-swap-details
        - https://docs.layerswap.io/api-reference/swaps/get-swap-by-transaction-hash
        - https://docs.layerswap.io/api-reference/swaps/get-all-swaps
        - https://docs.layerswap.io/api-reference/swaps/get-networks
        
      • layerzero.md 4.3 KB
        # LayerZero Bridging
        
        ## Overview
        
        Use LayerZero as a read-only source for bridge route discovery, supported-token discovery, and deployment metadata on
        target chains. This reference covers unauthenticated LayerZero Value Transfer API (VTA) discovery endpoints only.
        
        Default to the VTA base URL:
        
        ```text
        https://transfer.layerzero-api.com/v1
        ```
        
        Do not use authenticated VTA transfer endpoints from this skill. Do not request API keys, build user steps, submit
        signatures, approve spenders, or broadcast transactions. If the user provides transaction hashes or LayerZeroScan links,
        verify them with explorer/RPC data instead.
        
        ## Legacy Stargate API
        
        The Stargate API at `https://stargate.finance/api/v1` is deprecated in favor of LayerZero VTA. Use Stargate docs only as
        legacy context when analyzing older integrations or URLs.
        
        Legacy discovery endpoints:
        
        | Endpoint      | Use                                           |
        | ------------- | --------------------------------------------- |
        | `GET /chains` | List legacy Stargate chain keys and chain IDs |
        | `GET /tokens` | List legacy Stargate bridgeable tokens        |
        
        Do not prefer legacy Stargate endpoints for new data unless the user specifically asks about Stargate API behavior.
        
        ## Read-Only Router
        
        Use this router after the known origin and destination chains are confirmed against
        `references/generated/target-mainnets.json`.
        
        1. **Supported chains:** call `GET /chains`.
        2. **Supported tokens or destinations:** call `GET /tokens`. Use `transferrableFromChainKey` and
           `transferrableFromTokenAddress` together to find valid destination tokens from a source token.
        3. **Deployment metadata:** call `GET /metadata` when contract addresses or deployment details matter.
        4. **Known transaction hash or LayerZeroScan link:** verify the referenced transaction on-chain with explorer/RPC data.
        
        Example discovery:
        
        ```bash
        curl -sS "https://transfer.layerzero-api.com/v1/chains"
        curl -sS "https://transfer.layerzero-api.com/v1/tokens?transferrableFromChainKey=base&transferrableFromTokenAddress=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
        curl -sS "https://transfer.layerzero-api.com/v1/metadata?chainKey=base"
        ```
        
        ## Report Fields
        
        Extract and report these fields when present:
        
        | Field          | VTA path examples                                             |
        | -------------- | ------------------------------------------------------------- |
        | Chain identity | `chains[].chainKey`, `chains[].chainId`, `chains[].chainType` |
        | Token identity | `tokens[].chainKey`, `tokens[].address`, `symbol`, `decimals` |
        | Token routes   | `tokens[].transferableTo[]`, token route metadata             |
        | Deployments    | metadata contract addresses and deployment labels             |
        
        Token amounts, if present in supplied external data, are raw integer units. Convert with token decimals when present,
        and preserve raw values when decimals are absent.
        
        ## Route Types
        
        Common LayerZero route types and integrations to recognize in logs, explorer labels, or supplied payloads:
        
        | Type               | Meaning                         |
        | ------------------ | ------------------------------- |
        | `OFT`              | Omnichain Fungible Token route  |
        | `STARGATE_V2_TAXI` | Stargate V2 instant route       |
        | `STARGATE_V2_BUS`  | Stargate V2 batched route       |
        | `CCTP`             | Circle native USDC burn/mint    |
        | `AORI`             | Intent-based route through Aori |
        
        ## Failure Handling
        
        - Empty discovery results: report the missing chain, token, or deployment metadata; do not infer bridge support from
          token symbols alone.
        - Rate limits and 5xx responses: report the API limitation and continue normal explorer/RPC analysis.
        - Non-target chains in VTA results: report that the leg is outside this skill and ask for a feature request rather than
          continuing analysis on that leg.
        
        ## Sources
        
        - https://docs.stargate.finance/developers/api-docs/overview
        - https://docs.layerzero.network/v2/developers/value-transfer-api/start
        - https://docs.layerzero.network/v2/developers/value-transfer-api/overview
        - https://docs.layerzero.network/v2/developers/value-transfer-api/api-reference/overview
        - https://docs.layerzero.network/v2/developers/value-transfer-api/api-reference/chains
        - https://docs.layerzero.network/v2/developers/value-transfer-api/api-reference/tokens
        - https://docs.layerzero.network/v2/developers/value-transfer-api/api-reference/metadata
        
      • lifi.md 6.7 KB
        # LI.FI Bridging
        
        ## Overview
        
        Use LI.FI as a read-only source for bridge and cross-chain swap quotes, route options, supported chains/tokens/tools,
        and transfer status on target chains. Prefer the REST API for agent use. The SDK docs are useful for object names and
        route semantics, but this skill must not execute SDK routes or returned transaction requests.
        
        Default to the REST API base URL:
        
        ```text
        https://li.quest/v1
        ```
        
        Most LI.FI API calls work without an API key at lower rate limits. Add the key only when present:
        
        ```bash
        -H "x-lifi-api-key: $LIFI_API_KEY"
        ```
        
        Never execute bridge steps from this skill. Do not sign messages, submit transactions, execute SDK routes, call
        `executeRoute`, call `getStepTransaction` for execution, or broadcast any returned `transactionRequest`. Returned
        transaction data is for inspection only.
        
        ## Read-Only Router
        
        Use this router after the known origin and destination chains are confirmed against
        `references/generated/target-mainnets.json`.
        
        1. **Simple quote:** call `GET /quote` when the user wants one best route or a specific bridge/swap estimate.
        2. **Multiple routes:** call `POST /advanced/routes` when the user needs route comparison, multiple options, or complex
           multi-step paths.
        3. **Known source tx hash:** call `GET /status?txHash=<hash>`. Add `fromChain`, `toChain`, and `bridge` when known for
           faster, more precise results.
        4. **Supported chains:** call `GET /chains`; use `chainTypes=EVM` when only EVM chains are relevant.
        5. **Supported tokens:** call `GET /tokens?chains=<chainIds>` for chain-specific token catalogs.
        6. **Available bridges and exchanges:** call `GET /tools`.
        7. **Possible token connections:** call `GET /connections` when validating whether a pair can be swapped or bridged.
        
        Example quote inspection:
        
        ```bash
        curl -sS "https://li.quest/v1/quote?fromChain=1&toChain=42161&fromToken=USDC&toToken=USDC&fromAmount=1000000&fromAddress=0xWALLET"
        ```
        
        Example status lookup:
        
        ```bash
        curl -sS "https://li.quest/v1/status?txHash=0xTX_HASH&fromChain=1&toChain=42161&bridge=stargateV2"
        ```
        
        If `$LIFI_API_KEY` is set, add `-H "x-lifi-api-key: $LIFI_API_KEY"`.
        
        ## Request Fields
        
        For quote or route inspection, use explicit chain IDs from `references/generated/target-mainnets.json`.
        
        | Field                          | Use                                                                    |
        | ------------------------------ | ---------------------------------------------------------------------- |
        | `fromChain`                    | Source chain ID                                                        |
        | `toChain`                      | Destination chain ID                                                   |
        | `fromToken`                    | Source token symbol or token contract address                          |
        | `toToken`                      | Destination token symbol or token contract address                     |
        | `fromAmount`                   | Source amount in smallest units                                        |
        | `toAmount`                     | Destination amount in smallest units; do not combine with `fromAmount` |
        | `fromAddress`                  | Sender address; use only user-provided or placeholder addresses        |
        | `toAddress`                    | Destination receiver; defaults may differ by endpoint                  |
        | `slippage`                     | Optional slippage tolerance; default is API-defined                    |
        | `allowBridges` / `denyBridges` | Restrict route bridge set when the user requests it                    |
        
        Do not invent wallet addresses. Use placeholders for hypothetical quotes and user-provided addresses for real lookups.
        
        ## Report Fields
        
        Extract and report these fields when present:
        
        | Field                   | LI.FI path examples                                                |
        | ----------------------- | ------------------------------------------------------------------ |
        | Route or quote ID       | `id`, `routes[].id`                                                |
        | Tool / bridge           | `tool`, `toolDetails`, `includedSteps[].tool`, `steps[].tool`      |
        | Source chain/token      | `action.fromChainId`, `action.fromToken`, `estimate.fromAmount`    |
        | Destination chain/token | `action.toChainId`, `action.toToken`, `estimate.toAmount`          |
        | Amounts                 | `estimate.fromAmount`, `estimate.toAmount`, `estimate.toAmountMin` |
        | Gas and fees            | `estimate.gasCosts[]`, `estimate.feeCosts[]`                       |
        | Execution artifacts     | `transactionRequest`, step transaction fields for inspection only  |
        | Status                  | `status`, `substatus`, `substatusMessage`                          |
        | Source tx hash          | `sending.txHash`, `transactionHash`, request `txHash`              |
        | Destination tx hash     | `receiving.txHash`                                                 |
        | Explorer links          | `sending.txLink`, `receiving.txLink`                               |
        
        Token amounts are raw integer units. Convert with token decimals when present, and preserve raw values when decimals are
        absent.
        
        ## Status Values
        
        Interpret LI.FI status as:
        
        | Status      | Meaning                 |
        | ----------- | ----------------------- |
        | `NOT_FOUND` | LI.FI has no record yet |
        | `PENDING`   | In progress             |
        | `DONE`      | Terminal success        |
        | `FAILED`    | Terminal failure        |
        
        Important substatuses:
        
        | Substatus   | Meaning                                        |
        | ----------- | ---------------------------------------------- |
        | `COMPLETED` | Transfer completed as expected                 |
        | `PARTIAL`   | User received a different token; still success |
        | `REFUNDED`  | Tokens were returned to the sender             |
        
        For `DONE`, still verify terminal destination activity with explorer or RPC data when the destination chain is a target
        chain.
        
        ## Failure Handling
        
        - Missing `$LIFI_API_KEY`: use unauthenticated requests and respect lower public rate limits.
        - Rate limit `429`: back off, mention the limit, and continue explorer/RPC analysis.
        - No route found: report the API reason when present; try only user-approved alternative tokens, chains, amounts, or
          bridge filters.
        - Insufficient balance or gas: report the quoted requirement; do not attempt remediation transactions.
        - Slippage errors: report the issue; do not change slippage unless the user explicitly asks for a new quote.
        - Non-target chains in LI.FI results: report that the leg is outside this skill and ask for a feature request rather
          than continuing analysis on that leg.
        
        ## Sources
        
        - https://docs.li.fi/sdk/overview
        - https://docs.li.fi/api-reference/introduction
        - https://docs.li.fi/agents/overview
        - https://docs.li.fi/sdk/request-routes
        - https://docs.li.fi/sdk/execute-routes
        - https://docs.li.fi/sdk/chains-tools
        
      • relay.md 8.7 KB
        # Relay Bridging
        
        ## Overview
        
        Use Relay (relay.link) as a read-only source for cross-chain request status, solver-fill details, and route metadata
        after the known origin and destination chains are confirmed against `references/generated/target-mainnets.json`.
        
        Default to the API base URL:
        
        ```text
        https://api.relay.link
        ```
        
        Relay's public API works without a key at standard rate limits; there is no documented API-key header for read
        endpoints.
        
        Never execute bridge steps from this skill. Do not sign messages, submit deposit transactions, or broadcast a returned
        `depositAddress` or `inTxs[].data` as a transaction target. Returned request, deposit, and fill data are for inspection
        only.
        
        Relay uses a solver-fill model, not a lock-and-mint bridge: the user sends funds to a per-request deposit address (or an
        existing shared receiver contract) on the origin chain, and a Relay solver fills the equivalent amount on the
        destination chain, later reimbursed from the deposit. Two addresses recur across requests and are not user-specific:
        
        - `0xf70da97812CB96acDF810712Aa562db8dfA3dbEF` — Etherscan-labeled "Relay: Solver", Relay's primary solver wallet that
          executes destination-side fills across many chains. It appears as `protocol.solver.address` in request records.
        - `0xa5F565650890fBA1824Ee0F21EbBbF660a179934` — Etherscan-labeled "Reservoir: Relay Receiver", a shared RelayReceiver
          contract deployed at the same address on multiple chains that forwards deposits and can execute calls on a solver's
          behalf. Do not attribute a transfer to a specific user solely because it touches this address.
        
        Per-request deposit addresses (`protocol.deposit.origin.depository`) are distinct from the shared receiver above and are
        typically unique per request; do not assume they are stable across requests.
        
        ## Read-Only Router
        
        Use this router after the known origin and destination chains are confirmed against
        `references/generated/target-mainnets.json`.
        
        1. **Known source or destination tx hash:** call `GET /requests/v2?hash=<tx-hash>`. Relay indexes this against both
           `inTxs[].hash` (origin deposit) and `outTxs[].hash` (destination fill).
        2. **Known sender/recipient address, no tx hash:** call `GET /requests/v2?user=<address>&limit=20`; narrow with
           `chainId` when only one leg's chain is known.
        3. **Known request or order ID:** call `GET /requests/v2?id=<request-id>` or `GET /requests/v2?orderId=<order-id>` when
           known, e.g. from a Relay explorer link. `id` is a Relay-internal request identifier distinct from both the deposit tx
           hash and `protocol.orderId`; do not assume it equals either.
        4. **Scoped browsing:** call `GET /requests/v2?chainId=<chainId>&limit=<n>` (or `originChainId=`/ `destinationChainId=`)
           to list recent requests on one chain when no hash or address is known yet. Add `status=<value>` to filter by
           lifecycle stage, and `sortBy=createdAt&sortDirection=desc` for recency ordering.
        
        Example hash lookup:
        
        ```bash
        curl -sS "https://api.relay.link/requests/v2?hash=0xTX_HASH"
        ```
        
        Example user-scoped lookup:
        
        ```bash
        curl -sS "https://api.relay.link/requests/v2?user=0xADDRESS&limit=20"
        ```
        
        ## Request Fields
        
        | Field                                              | Use                                                              |
        | -------------------------------------------------- | ---------------------------------------------------------------- |
        | `hash`                                             | Origin or destination transaction hash to look up a request      |
        | `id`                                               | Relay-internal request ID (from a prior lookup or explorer link) |
        | `orderId`                                          | `protocol.orderId` value from a prior lookup                     |
        | `user`                                             | Depositor/sender address; scopes results to that user's requests |
        | `depositAddress`                                   | Per-request deposit address; scopes results to that address      |
        | `chainId` / `originChainId` / `destinationChainId` | Restrict results to requests touching a given chain              |
        | `status`                                           | Filter by lifecycle status (see Status Values)                   |
        | `startTimestamp` / `endTimestamp`                  | Bound results by Unix timestamp                                  |
        | `limit`                                            | Page size, default 20, max 50                                    |
        | `apiKey` (query) / `x-api-key` (header)            | Optional API key; not required for standard-rate reads           |
        
        ## Report Fields
        
        Extract and report these fields when present, under the top-level `requests[]` array:
        
        | Field                  | Relay path examples                                                                      |
        | ---------------------- | ---------------------------------------------------------------------------------------- |
        | Request ID             | `id`                                                                                     |
        | Status                 | `status`                                                                                 |
        | Depositor / recipient  | `user`, `recipient`, `protocol.deposit.origin.depositor`                                 |
        | Origin chain / tx      | `data.inTxs[].chainId`, `data.inTxs[].hash`, `protocol.deposit.origin.chainId`           |
        | Destination chain / tx | `data.outTxs[].chainId`, `data.outTxs[].hash`, `protocol.settlement.destination.fills[]` |
        | Deposit address        | `protocol.deposit.origin.depository`                                                     |
        | Solver                 | `protocol.solver.address`, `protocol.solver.chainId`                                     |
        | Order ID               | `protocol.orderId`                                                                       |
        | Currency in / out      | `data.metadata.currencyIn`, `data.metadata.currencyOut`                                  |
        | Amounts                | `data.metadata.currencyIn.amount`, `data.metadata.currencyOut.amount`                    |
        | USD values             | `data.metadata.currencyIn.amountUsd`, `data.metadata.currencyOut.amountUsd`              |
        | Fees                   | `data.fees`, `data.feesUsd`, `data.appFees[]`                                            |
        | Timestamps             | `createdAt`, `updatedAt`, `data.inTxs[].timestamp`, `data.outTxs[].timestamp`            |
        | Refund                 | `data.failReason`, `data.refundFailReason`                                               |
        
        Amounts under `data.metadata.currencyIn`/`currencyOut` and `data.fees`/`data.feesUsd` are raw integer units in the
        token's smallest denomination; convert with the accompanying `currency.decimals` (or `amountFormatted`, which is already
        decimal, when present). Do not divide `amountFormatted` again.
        
        ## Status Values
        
        Interpret the top-level `status` field as (per the official API reference):
        
        | Status       | Meaning                                                |
        | ------------ | ------------------------------------------------------ |
        | `depositing` | Deposit transaction submitted but not yet confirmed    |
        | `pending`    | Deposit confirmed; awaiting solver fill                |
        | `success`    | Destination fill completed                             |
        | `failure`    | Request failed; check `data.failReason`                |
        | `refund`     | Deposit was refunded to the sender on the origin chain |
        
        Live samples observed only `pending` and `success` (see Sources); `depositing`, `failure`, and `refund` are documented
        but not directly observed in this pass.
        
        For `success`, still verify the destination fill transaction with explorer or RPC data when the destination chain is a
        target chain; `data.outTxs[].stateChanges[]` lists the observed balance deltas as an additional cross-check.
        
        ## Failure Handling
        
        - Empty `requests` array: report that Relay has no record for the hash/user/ID and continue normal explorer/RPC
          analysis.
        - Rate limit `429` or 5xx: back off and continue explorer/RPC analysis.
        - `status: "failure"`: report `data.failReason` when present; do not attempt remediation transactions.
        - Non-target chains in Relay results: report that the leg is outside this skill and ask for a feature request rather
          than continuing analysis on that leg.
        - A transfer touching `0xa5F565650890fBA1824Ee0F21EbBbF660a179934` or `0xf70da97812CB96acDF810712Aa562db8dfA3dbEF` alone
          is not proof of attribution to a specific user; confirm via a request lookup keyed on the actual hash or user address.
        
        ## Sources
        
        - https://docs.relay.link/what-is-relay
        - https://docs.relay.link/references/api/get-requests
        - https://etherscan.io/address/0xf70da97812cb96acdf810712aa562db8dfa3dbef
        - https://etherscan.io/address/0xa5f565650890fba1824ee0f21ebbbf660a179934
        
      • symbiosis.md 7.9 KB
        # Symbiosis Bridging
        
        ## Overview
        
        Use Symbiosis Finance's public explorer API as a read-only source for cross-chain swap status after the known origin and
        destination chains are confirmed against `references/generated/target-mainnets.json`. Symbiosis is a liquidity-network
        aggregator: it does not move tokens directly between arbitrary chains but routes every cross-chain swap through
        synthesized-token liquidity pools ("Octopools") on its own host chain, converting a source token to a synthetic
        representation, moving that synthetic across the host chain, then converting to the requested destination token. A swap
        record therefore has three legs: `from` (origin chain), `join` (host-chain leg), and `to` (destination chain).
        
        Default to the explorer API base URL:
        
        ```text
        https://api-v2.symbiosis.finance/explorer/v1
        ```
        
        This is an unauthenticated public read API; no API key is documented or required.
        
        Never execute bridge steps from this skill. Do not sign messages or submit swap transactions. Returned transaction and
        route data are for inspection only.
        
        ## Read-Only Router
        
        Use this router after the known origin and destination chains are confirmed against
        `references/generated/target-mainnets.json`.
        
        1. **Known source tx hash and its origin chain ID:** call `GET /transactions/<originChainId>/<txHash>` for the single
           matching record.
        2. **Known source tx hash, chain ID unknown:** call `GET /transactions?search=<txHash>`; the search endpoint matches the
           hash regardless of leg or chain.
        3. **Scoped browsing:** call `GET /transactions?limit=<n>` to page recent swaps; results are not filtered by chain or
           address in this form — narrow further only with parameters confirmed via a `search` match first.
        
        Example direct chain/hash lookup:
        
        ```bash
        curl -sS "https://api-v2.symbiosis.finance/explorer/v1/transactions/1/0xTX_HASH"
        ```
        
        Example hash search:
        
        ```bash
        curl -sS "https://api-v2.symbiosis.finance/explorer/v1/transactions?search=0xTX_HASH"
        ```
        
        ## Request Fields
        
        | Field                                 | Use                                                            |
        | ------------------------------------- | -------------------------------------------------------------- |
        | `<originChainId>` / `<txHash>` (path) | Direct lookup: origin chain ID and the origin transaction hash |
        | `search`                              | Free-text match against a transaction hash                     |
        | `limit`                               | Page size for list-style queries                               |
        
        ## Report Fields
        
        Extract and report these fields when present, either from a single object (direct lookup) or `records[]` (search /
        list):
        
        | Field                        | Symbiosis path examples                                                                  |
        | ---------------------------- | ---------------------------------------------------------------------------------------- |
        | Internal record ID           | `id`                                                                                     |
        | Origin chain / tx            | `from_chain_id`, `from_tx_hash`                                                          |
        | Host-chain (join) chain / tx | `join_chain_id`, `join_tx_hash`                                                          |
        | Destination chain / tx       | `to_chain_id`, `to_tx_hash`                                                              |
        | Sender / recipient           | `from_address`, `from_sender`, `to_address`, `to_sender`                                 |
        | Tokens                       | `tokens[].symbol`, `tokens[].address`, `tokens[].decimals`                               |
        | Route legs                   | `from_route[]`, `to_route[]` (each with `chain_id`, `amount`, `token`)                   |
        | Amounts                      | `amounts[]` (parallel to `tokens[]`/route arrays)                                        |
        | USD values                   | `from_amount_usd`, `to_amount_usd`                                                       |
        | Timestamps                   | `created_at`, `mined_at`, `success_at`                                                   |
        | Client/integrator            | `from_client_id` (e.g. `"symbiosis-app"`, or an aggregator name when routed through one) |
        | Stuck / retry signal         | `state_stuck_reason`, `retry_active`                                                     |
        | Lost-leg flags               | `from_is_lost`, `join_is_lost`, `to_is_lost`                                             |
        
        Amounts in `amounts[]` and `from_route[]`/`to_route[]` are raw integer units in the token's smallest denomination;
        convert with the accompanying `token.decimals`.
        
        `from_client_id` shows the integrating frontend or aggregator that submitted the swap (observed live values include
        `"symbiosis-app"` and third-party aggregator names such as `"lifi"`) — a Symbiosis-routed leg can appear while
        investigating a different aggregator's transaction; check this field before assuming the immediate frontend is Symbiosis
        itself.
        
        ## Status Values
        
        The `state` field is an integer whose exact enum is not published in developer docs; live sampling was inconclusive
        enough to assert a firm 1:1 mapping (records with the same `state` value showed a mix of populated and null
        `success_at`, and some `state=2` records still eventually recorded a `success_at`). Prefer these directly observable
        signals over the raw `state` code:
        
        | Signal                                    | Meaning                                                                                                         |
        | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
        | `to_tx_hash` present and `success_at` set | Destination leg completed                                                                                       |
        | `to_tx_hash` null and `success_at` null   | Swap has not completed the destination leg yet (in progress or stuck)                                           |
        | `state_stuck_reason` non-empty            | The swap hit an execution error (e.g. gas estimation failure, below-minimum deposit); read the message directly |
        | `retry_active` true                       | Symbiosis is actively retrying a failed step                                                                    |
        
        Symbiosis's own user-facing documentation describes swap lifecycle states as In progress, Success, Success*,
        Interrupted, and Reverted, and states that swaps stuck for too long are automatically reverted (tokens returned to the
        sender) — this vocabulary is unverified against the explorer API's integer `state` field; treat it as background
        context, not a field mapping to implement against.
        
        ## Failure Handling
        
        - Empty `records` from `search`, or 404 from the direct chain/hash lookup: report that Symbiosis has no record for that
          hash and continue normal explorer/RPC analysis.
        - `state_stuck_reason` non-empty: report the raw reason string; do not infer a specific remediation.
        - `to_is_lost` / `join_is_lost` / `from_is_lost` true: report that Symbiosis itself flags that leg as unresolved; treat
          this as stronger signal than the raw `state` code.
        - Non-target chains in `from_chain_id`/`to_chain_id`: report that the leg is outside this skill and ask for a feature
          request rather than continuing analysis on that leg.
        - `join_chain_id` values reflect Symbiosis's internal host chain, not necessarily a chain tracked in
          `references/generated/target-mainnets.json`; do not treat it as a target chain requiring its own explorer
          verification.
        
        ## Sources
        
        - https://docs.symbiosis.finance/developer-tools/symbiosis-api
        - https://docs.symbiosis.finance/user-guide-webapp/symbiosis-explorer
        - https://docs.symbiosis.finance/user-guide-webapp/where-are-my-tokens
        - https://docs.symbiosis.finance/user-guide-webapp/stuck-transactions
        - https://docs.symbiosis.finance/crosschain-liquidity-engine/symbiosis-octopools
        - https://docs.symbiosis.finance/main-concepts/symbiosis-cross-chain-swaps
        
    • dexes
      • 1inch.md 7.5 KB
        # 1inch Evidence
        
        Use this reference after `references/workflows/dex-transactions.md` for 1inch Classic swaps, same-chain Fusion orders,
        Fusion+ cross-chain orders, and historical 1inch liquidity/reward interactions. It is evidence-only.
        
        Do not call live Classic `/quote` or `/swap` endpoints, request a Fusion/Fusion+ quote, construct or submit an order, or
        cover standalone Orderbook/Limit Order workflows. Fusion's use of the Limit Order Protocol does not expand that scope.
        
        ## Attribution and Deployment
        
        Treat 1inch as the execution or aggregation protocol. Report the integration wrapper, 1inch router/settlement, and each
        underlying liquidity source separately. A Classic or Fusion execution that calls a Uniswap pool remains a 1inch
        execution using Uniswap liquidity.
        
        For historical and current routers, settlement contracts, escrow factories, and legacy pools:
        
        1. Resolve the target chain.
        2. Consult the current 1inch API deployment endpoint where documented, official tagged contract repositories and their
           `deployments/` artifacts, then verified explorer source.
        3. Compare runtime bytecode, proxy implementation, and the ABI for the transaction's block-era contract.
        4. Report the observed router/contract version. Do not infer that a historical transaction used today's API or router
           version, and do not maintain a copied address list.
        
        ## Classic Swaps
        
        - Recognize historical aggregation contracts and current Aggregation Router variants from verified code, not address
          shape or explorer labels.
        - Decode the exact router ABI and calldata. Historical `swap`, split-route, direct-pool, and optimized selector families
          differ; a decoded selector name from an unrelated ABI is not evidence.
        - Use traces, pool events, and token/native transfers to attribute underlying liquidity. Router calldata or an API route
          from a different time cannot reconstruct an opaque executor's historical path.
        - Compute the wallet's sold, received, refunded, and wrapped/native legs from the receipt and trace. Report the wallet
          gas payer separately.
        - Classify ERC-20 approvals and permit calls separately from the swap. Verify the spender against the block-era router
          or settlement deployment; an approval to an obsolete router is still an approval, not evidence of a later trade.
        - List integrator or infrastructure fees only when calldata, transfers, or verified contract accounting proves them. Do
          not back-calculate a fee from a live quote or current documentation.
        
        ## Same-Chain Fusion
        
        Fusion is an intent order filled by a resolver, not a maker-submitted swap transaction.
        
        - Require a resolved chain before calling the chain-scoped Fusion Orders API for a known order hash or maker address.
          Use the current documented API version and configured credential. Missing credentials are a coverage gap, not a reason
          to call quote, relayer, or submission endpoints.
        - Preserve the API-native status and fill array. Sum only fills tied to verified settlement transactions; distinguish
          pending, partial, filled, expired, and cancelled states.
        - Verify every reported fill through its receipt, 1inch settlement/router evidence, transfers, and underlying liquidity
          calls. The API can associate an order hash with fills but does not replace on-chain execution evidence.
        - Report maker, receiver, maker/taker assets, filled amounts, resolver/settlement, gas payer, and order hash. The
          resolver normally pays settlement gas; a native-order escrow creation may still produce a separate maker transaction.
        - Decode any order extension carrying an ERC-20 permit or Permit2 authorization. Report it as consumed only when the
          verified settlement call, event, or allowance state proves consumption; signature data alone is intent.
        - Treat a signed or API-accepted order without a verified fill as an order lifecycle record, not a completed trade.
          Expiry, invalid signature, insufficient allowance, and cancellation are not zero-value swaps.
        
        ## Fusion+ Cross-Chain
        
        Route Fusion+ here from both DEX and bridge prompts.
        
        1. Resolve and validate both source and destination chains. If either is outside the target set, stop that leg and
           report partial coverage.
        2. Query the current Fusion+ Orders status endpoint only for a known order hash. Preserve `srcChainId`, `dstChainId`,
           order version, receiver, fill status, and every source/destination escrow event returned.
        3. Verify the source fill and escrow creation on the source chain and the destination escrow funding/withdrawal on the
           destination chain. Correlate order hash, escrow immutables/factory provenance, hashlock, amounts, and fill index
           where available.
        4. Report source and destination asset changes, resolver safety deposits, withdrawal recipient, and gas payer per leg.
           Do not present resolver deposits as user bridge fees.
        5. For partial fills, keep each fill and escrow pair separate. Do not merge secrets, fill amounts, or settlement
           transactions across resolvers.
        6. For cancellation, recovery, rescue, or refund, require the applicable escrow call/event and actual return transfer.
           An API `refunding`, `refunded`, `cancelled`, or `executed` status alone is enrichment.
        
        Fusion+ uses linked source/destination escrows rather than proving a conventional lock-and-mint bridge route. Describe
        the execution mode as Fusion+ and the observed escrow lifecycle; do not invent a bridge provider or messaging leg.
        
        ## Legacy Liquidity, Claims, and Rewards
        
        - The historical 1inch Liquidity Protocol/Mooniswap is obsolete. Verify the pool through the archived official
          repository, deployment artifacts, factory provenance, and code before using the 1inch label.
        - Classify pool `deposit`/`withdraw` and swap calls with LP-token mint/burn/transfers and underlying wallet deltas.
          Preserve native-token legs and referral-fee LP minting when evidenced.
        - Identify farms, staking, and reward distributors separately from the liquidity pool. A stake/unstake, claim, or reward
          transfer is not a swap or LP withdrawal.
        - For legacy reward claims, report the verified distributor, claimant, reward asset, and received amount. A generic
          `claim` selector or token transfer without contract provenance is insufficient.
        
        ## Failure and Coverage
        
        - Approval-only: report token, owner, spender, amount, and receipt; do not report a trade.
        - Failed Classic or settlement transaction: describe decoded intent as attempted and report no surviving trade events or
          wallet asset changes other than gas.
        - Unverified router, pool, fork, or executor: retain observed transfers and selector bytes, but leave protocol/version
          or route attribution unknown.
        - Unavailable traces: report wallet net changes and proven pool events; mark split percentages and hidden liquidity
          sources unknown.
        - Missing or stale API data: keep on-chain evidence authoritative and show the indexing/authentication gap.
        
        ## Sources
        
        - https://business.1inch.com/portal/documentation/apis/swap/swap
        - https://business.1inch.com/portal/documentation/apis/swap/classic-swap/introduction
        - https://business.1inch.com/portal/documentation/apis/swap/intent-swap/introduction
        - https://business.1inch.com/portal/documentation/apis/swap/intent-swap/orders/v2.0/1/order/status/method/post
        - https://business.1inch.com/portal/documentation/apis/swap/cross-chain-swap/introduction
        - https://business.1inch.com/portal/documentation/apis/swap/cross-chain-swap/orders/v1.2/order/status/orderHash/method/get
        - https://github.com/1inch/limit-order-protocol
        - https://github.com/1inch/cross-chain-swap
        - https://github.com/1inch/fusion-sdk
        - https://github.com/1inch/liquidity-protocol
        
      • cow-protocol.md 7.1 KB
        # CoW Protocol Evidence
        
        Use this reference after `references/workflows/dex-transactions.md` for CoW Swap, CoWSwap, CoW Protocol, and GPv2
        orders, approvals, cancellations, EthFlow refunds, fills, and settlements. CoW Swap is an interface; CoW Protocol is the
        execution protocol and retains historical GPv2 contract names.
        
        Do not create, sign, submit, or cancel orders. Do not add CoW AMM liquidity-position interpretation; a CoW AMM
        interaction observed inside a settlement may be named only as an underlying liquidity source.
        
        ## Deployment and Identity
        
        Resolve the chain, then use CoW's current contract reference/deployment feed and verified explorer source for
        `GPv2Settlement`, `GPv2VaultRelayer`, and EthFlow. Verify runtime code and proxy/implementation state where applicable.
        Do not copy a deployment table into this reference.
        
        An order UID is 56 bytes: order digest, owner, and `validTo`. Preserve it in full. Verify its digest against the
        chain-specific EIP-712 domain and exact order fields when those fields and signature are available. Supported signature
        schemes include EIP-712, `eth_sign`, ERC-1271, and pre-signature; report the observed scheme rather than assuming an EOA
        signature.
        
        ## Read-Only Order Book API
        
        Use the public chain-specific Order Book API only when the user supplies or on-chain evidence resolves a known order
        UID, settlement transaction hash, or owner address:
        
        - `GET /api/v1/orders/{UID}` and `/api/v1/orders/{UID}/status` for one known order.
        - `GET /api/v1/trades` or `/api/v2/trades` filtered to the known UID for fills.
        - `GET /api/v1/transactions/{txHash}/orders` for orders in a known settlement.
        - `GET /api/v1/account/{owner}/orders` only for an explicitly requested, chain-scoped owner history.
        - `GET /api/v1/app_data/{app_data_hash}` for known `appData`.
        
        Do not call quote, auction-discovery, order-creation, cancellation, or app-data registration endpoints. If the chain is
        missing or the identifier cannot select a chain-specific API, ask; do not default or query every API deployment.
        
        Preserve API-native status and executed amounts. Verify fulfilled, cancelled, expired, partially filled, and
        EthFlow-refund claims against finalized on-chain evidence where such evidence exists. An API-only off-chain cancellation
        remains a provider fact with an on-chain coverage gap.
        
        ## Orders, Approvals, and Lifecycle
        
        - Record sell/buy tokens and amounts, receiver, `validTo`, kind, partial-fill flag, balance source/destination,
          `feeAmount`, signature scheme, owner, UID, and `appData` only from the order/API/signature evidence.
        - A CoW order signature and API submission are off-chain. Do not invent a maker transaction or maker-paid settlement
          gas.
        - Direct ERC-20 approvals normally target `GPv2VaultRelayer`, not `GPv2Settlement`. Report the actual spender and
          distinguish ERC-20 allowance from Balancer external/internal-balance authorization.
        - An approval, pre-signature, or signature revocation is not a fill. Use `PreSignature` and exact state/call evidence.
        - On-chain `invalidateOrder` and `OrderInvalidated` prove protocol invalidation. API-side signed cancellation can
          explain Order Book status but has no transaction receipt. Expiry requires the signed `validTo`, a finalized timestamp
          after it, and no later fill evidence.
        - For partially fillable orders, report each `Trade` and cumulative executed amount. A cancelled or expired order may
          still have earlier partial fills.
        
        Do not use `filledAmounts` as sole historical proof: storage may be cleared after expiry. Use receipt logs and indexed
        trades.
        
        ## Settlement Evidence
        
        `GPv2Settlement.settle` can execute many orders in one transaction and arbitrary interactions with underlying liquidity.
        
        1. Decode the verified settlement ABI, including token list, clearing prices, trades, and pre/intra/post interactions.
        2. Match the target UID/digest/owner to its `Trade` event and API trade record. Do not assign all settlement transfers
           or gas to one wallet.
        3. Use the `Settlement` event and transaction sender for solver evidence. Use `Interaction` events, calldata, traces,
           and downstream pool events for underlying AMMs.
        4. Report execution protocol as CoW Protocol, integration wrapper if any, and underlying liquidity separately.
        5. Compute the target wallet's sold/received assets from its trade and transfers. Report solver-paid transaction gas
           separately.
        
        If an explorer oddly decodes the settlement selector, recompute the first four bytes from the official ABI, verify the
        calldata target and runtime implementation, and decode locally. Keep the explorer name as a hint and the raw selector as
        observed evidence.
        
        ## EthFlow
        
        EthFlow wraps a user's native ETH into WETH and creates an ERC-1271 CoW order through an intermediary contract.
        
        - For creation, require a successful `createOrder`, native value, `OrderPlacement`, stored owner/validity, and the
          derived contract order. Report the user intent (ETH) and settlement intent (WETH) without collapsing them.
        - The EthFlow contract is the order signer/owner while the user is the native depositor and buy-token receiver.
        - For fills, connect the EthFlow order to the settlement `Trade` and recipient transfer.
        - For invalidation/refund, require `invalidateOrder` evidence and the actual unmatched native return. Expiry or API
          status alone does not prove refund.
        
        ## Fees, Surplus, and appData
        
        - Separate signed `feeAmount`, executed protocol/partner fee fields, solver gas, and wallet asset deltas. Do not infer a
          fee from the spread between current market price and execution.
        - Report surplus or price improvement only when the Order Book/solver-competition data or clearing-price accounting for
          the exact settlement proves it. Preserve the provider and formula.
        - Resolve `appData` by its hash through the public read endpoint or IPFS/schema evidence. Treat app code, partner fee,
          referral, hooks, and metadata as declared data unless on-chain effects independently confirm them.
        
        ## Failure and Coverage
        
        - Missing API record: continue with receipt/log evidence; do not conclude the order never existed.
        - API `fulfilled` without a resolvable finalized `Trade`: report the API status and an on-chain verification gap.
        - Failed settlement: no orders in it filled and no interactions survived; report only attempted calldata and gas.
        - Multi-order settlement with unavailable traces: report the target `Trade` and wallet deltas, but leave underlying
          liquidity and interaction semantics incomplete.
        - Unverified settlement, relayer, EthFlow deployment, or fork: retain raw evidence and leave canonical attribution
          unknown.
        
        ## Sources
        
        - https://api.cow.fi/docs/
        - https://docs.cow.fi/cow-protocol/reference/apis/orderbook
        - https://docs.cow.fi/cow-protocol/reference/contracts/core
        - https://docs.cow.fi/cow-protocol/reference/contracts/core/settlement
        - https://docs.cow.fi/cow-protocol/reference/contracts/core/vault-relayer
        - https://docs.cow.fi/cow-protocol/reference/contracts/periphery/eth-flow
        - https://docs.cow.fi/cow-protocol/reference/core/intents
        - https://docs.cow.fi/cow-protocol/reference/core/intents/app-data
        - https://docs.cow.fi/cow-protocol/reference/core/signing-schemes
        - https://github.com/cowprotocol/services
        
      • uniswap.md 8.7 KB
        # Uniswap Evidence
        
        Use this reference after `references/workflows/dex-transactions.md` for canonical Uniswap v1, v2, v3, or v4 evidence,
        including Universal Router, Permit2, position management, wrapper-driven liquidity, and v1-to-v2 migration. This
        reference does not add live quoting/execution or UniswapX order interpretation.
        
        ## Establish Canonical Deployment and Version
        
        Resolve the chain first. At observation time, use Uniswap's current deployment pages or official repository deployment
        artifacts, then compare the target's runtime bytecode, verified source, proxy implementation where applicable, factory,
        and ABI. Do not keep or infer a local address encyclopedia.
        
        If official sources omit the chain or conflict with the explorer, report the conflict. Treat an independently deployed
        fork as non-canonical even when it preserves Uniswap selectors, events, or bytecode.
        
        Use the architecture, entrypoint, and logs together:
        
        | Family | Core identity and high-signal evidence                                                                     |
        | ------ | ---------------------------------------------------------------------------------------------------------- |
        | v1     | Factory-created exchange per ERC-20; ETH/token exchange, LP-token transfers, and exchange events           |
        | v2     | Factory-created pair per token pair; pair `Mint`, `Burn`, `Swap`, `Sync`, and ERC-20 LP-token transfers    |
        | v3     | Factory-created pool per token pair/fee; pool events plus router or `NonfungiblePositionManager` evidence  |
        | v4     | Singleton `PoolManager`; `PoolKey`/`PoolId`, core events, periphery commands, flash-accounting settlements |
        
        The transaction's root target may instead be an integration wrapper, smart account, migrator, position manager, or
        Universal Router. Report that entrypoint separately.
        
        ## Uniswap v1
        
        - Resolve the token's exchange through the canonical v1 factory or prove the historical factory provenance. v1 exchange
          contracts are also the fungible LP tokens.
        - Decode the exact exchange method to distinguish ETH-to-token, token-to-ETH, and token-to-token swaps and exact-input
          from exact-output intent. Confirm execution with `TokenPurchase`/`EthPurchase`, transfers, native value, and receipt
          status.
        - For token-to-token swaps, preserve both exchange legs. Do not report a single-pool route if the intermediate ETH leg
          is missing from the available trace.
        - Use `AddLiquidity`/`RemoveLiquidity`, LP-token mint/burn transfers, and wallet deltas for liquidity actions.
        - Identify the official v1-to-v2 migrator only after verifying its deployment and code. A migration removes the wallet's
          v1 liquidity, adds the resulting token/ETH to v2, mints v2 LP tokens to the recipient, and may refund unused token or
          ETH. Report the two liquidity legs and refund separately; a coincidental remove-plus-add sequence is not enough.
        
        ## Uniswap v2
        
        - Prove the pair through its canonical factory and token ordering. Pair events establish pool activity; router calldata
          establishes user intent and recipient.
        - LP positions are fungible pair tokens. Use pair `Mint`/`Burn` events together with LP-token `Transfer` mint/burn
          evidence and underlying token transfers.
        - Distinguish standalone LP-token `approve`/`permit`, transfer, add/remove liquidity, and swap actions. A permit can be
          consumed inside a remove-liquidity call and need not be a separate user transaction.
        - For fee-on-transfer router methods, wallet and pair deltas outrank nominal calldata amounts.
        - Treat zaps, vaults, farms, and third-party migrators as integration wrappers. If a wrapper receives one asset, swaps
          part, and adds liquidity, report one wrapper-driven zap with its swap and LP legs; do not claim the root transaction
          was sent to Uniswap.
        - Treat staking/mining deposits, withdrawals, and reward claims as separate incentive-contract actions. Verify the
          incentive contract and reward transfer; pair LP-token provenance alone does not make a farm canonical Uniswap.
        
        ## Uniswap v3
        
        - Identify a pool by canonical factory provenance, token pair, and fee tier. Pool `Swap`, `Mint`, `Burn`, and `Collect`
          events describe core activity; periphery events identify the wallet-facing action.
        - Decode router `exactInput*`/`exactOutput*` calls and multicalls in order. A multicall is a container, not an
          interaction class.
        - Track the complete position-NFT lifecycle through `NonfungiblePositionManager`: ERC-721 mint/transfer/approval,
          `IncreaseLiquidity`, `DecreaseLiquidity`, `Collect`, and burn. Report the token ID, owner, operator/recipient, pool,
          ticks, liquidity change, and collected assets when evidenced.
        - `DecreaseLiquidity` records newly owed principal from the liquidity reduction; `Collect` can transfer that principal
          together with previously or newly accrued fees. In a decrease-plus-collect multicall, treat the decrease amounts as
          principal. Compute fees as collected minus included principal only when pre-transaction owed amounts, fee-growth
          accounting, command order, and full collection prove the allocation. Otherwise report collected total and the
          unresolved principal/fee split.
        - Burning the position NFT is distinct from decreasing liquidity and collecting. Require its ERC-721 burn evidence.
        
        ## Uniswap v4
        
        - Identify the canonical singleton `PoolManager` and relevant periphery deployment. A v4 pool is not a contract address:
          derive or decode its `PoolKey` (`currency0`, `currency1`, fee, tick spacing, hooks) and corresponding `PoolId`.
        - Use `Initialize`, `Swap`, and `ModifyLiquidity` events with the exact `PoolId`. Resolve currencies and preserve native
          ETH, which v4 can use without WETH.
        - Decode Universal Router v4 commands or `PositionManager` action sequences. Position actions include mint, increase,
          decrease, and burn. Fee collection is a zero-liquidity decrease paired with `TAKE_PAIR`; there is no `COLLECT` action.
          Preserve the settle, take, and close actions that resolve deltas.
        - Flash accounting nets intermediate balance changes inside `PoolManager`. Do not require an ERC-20 transfer per hop or
          infer a missing hop from transfer logs; use commands, pool events, balance deltas, and traces.
        - Record the hook address and verified permissions. Interpret custom fees, curves, deltas, rewards, or access rules only
          from verified hook source/ABI and observed evidence. Otherwise label the hook opaque and keep its semantic effect as a
          coverage gap.
        - A `ModifyLiquidity` event proves core liquidity changed, not who ultimately owned a wrapper-managed position. Use the
          periphery command, token/position transfer, and recipient evidence for wallet attribution.
        
        ## Universal Router and Permit2
        
        - Decode `execute` command bytes and their paired inputs in sequence. Attribute each v2, v3, or v4 swap command to its
          version and list wrap, unwrap, sweep/refund, transfer, and balance-check commands as secondary legs.
        - Do not label the entire Universal Router transaction with one Uniswap version when commands cross versions.
        - Distinguish an ERC-20 allowance granted to Permit2, a Permit2 allowance or signature-transfer authorization, and the
          later router spend. Report owner, token, spender, amount, nonce, and expiration only when evidenced.
        - A Permit2 or token approval without a successful consuming swap is approval-only. A failed consuming transaction does
          not undo an approval from an earlier transaction.
        
        ## Fees and Asset Results
        
        Use wallet net changes from the shared workflow. Report pool fee tier or dynamic-fee evidence separately from gas,
        wrapper/integrator fees, and explicit rewards. Do not calculate historical LP fees, price impact, or slippage from
        current reserves or a present-day quote.
        
        For v4, hook accounting can alter pool and wallet deltas. For v2/v3, protocol or wrapper transfers may also make simple
        `amountIn - amountOut` fee arithmetic wrong. State only the fee components directly supported by calldata, events,
        state, or traces.
        
        ## Sources
        
        - https://developers.uniswap.org/docs/protocols/overview
        - https://developers.uniswap.org/docs/protocols/v2/deployments
        - https://developers.uniswap.org/docs/protocols/v3/deployments
        - https://developers.uniswap.org/docs/protocols/v4/deployments
        - https://developers.uniswap.org/docs/protocols/v4/concepts/architecture
        - https://developers.uniswap.org/docs/protocols/v4/guides/position-manager
        - https://developers.uniswap.org/docs/protocols/v4/guides/managing-liquidity/collect-fees
        - https://developers.uniswap.org/docs/protocols/universal-router/overview
        - https://developers.uniswap.org/docs/protocols/permit2/overview
        - https://github.com/Uniswap/v1-contracts
        - https://github.com/Uniswap/v2-periphery/blob/master/contracts/UniswapV2Migrator.sol
        - https://github.com/Uniswap/v3-periphery
        - https://github.com/Uniswap/v4-core
        - https://github.com/Uniswap/v4-periphery
        
    • explorers
      • blockscout-api.md 16.2 KB
        # Blockscout API
        
        ## Overview
        
        Query target-mainnet data that Blockscout indexes. Blockscout exposes three compatible surfaces:
        
        - **Native REST API v2** (`/api/v2/...`) — rich JSON, the recommended surface. Returns balances, full token holdings,
          transactions, and transfers with embedded token/exchange-rate metadata.
        - **Etherscan-compatible RPC** (`/api?module=...&action=...`) — legacy `{status,message,result}` shape. Useful for
          porting existing Etherscan code; superseded by v2.
        - **Unified PRO API** (`https://api.blockscout.com/...`) — a single keyed host fronting both of the above across major
          chains, selected by `chain_id`.
        
        This skill covers read-only account/address queries: native balance, ERC-20/721/1155 holdings and transfers, transaction
        history, and first-funding tracing.
        
        **Relationship to Etherscan (`references/explorers/etherscan-api.md`):** Same problem space, different explorer. Prefer
        Blockscout when the target chain is **not** on Etherscan, when a paid Etherscan target chain needs free-tier data, when
        you want full token holdings on the free tier, or when the user names Blockscout/Chainscout. The two surfaces are
        interchangeable for native-balance and transfer queries.
        
        ## Prerequisites
        
        ### API Key
        
        A free Blockscout PRO key (`proapi_…`) is expected in `$BLOCKSCOUT_API_KEY`:
        
        ```bash
        if [ -z "$BLOCKSCOUT_API_KEY" ]; then
          echo "Error: BLOCKSCOUT_API_KEY is not set."
          echo "Get a free key at: https://dev.blockscout.com/"
          exit 1
        fi
        ```
        
        The key is required for the unified PRO host (`api.blockscout.com`), which returns `401 {"error":"Unauthorized"}`
        without it. Keyless per-instance hosts are an exception only for a self-hosted or third-party target instance that the
        gateway does not serve; see [Per-Instance Exception](#per-instance-exception).
        
        ### Plan & Credit Detection
        
        Run once per session and cache the result. It reads rate-limit/credit headers returned on every PRO response:
        
        ```bash
        scripts/blockscout-detect-plan.sh
        ```
        
        Output (key=value lines):
        
        ```
        plan=free
        rate_limit_rps=5
        rate_limit_remaining=3
        rate_limit_reset=441
        credits_remaining=99880
        ```
        
        `x-ratelimit-limit` maps directly to plan tier; see the Plans and Credit Costs tables in
        `references/explorers/blockscout-endpoints.md`.
        
        At the default 20 credits/call, the free 100K/day tier ≈ 5,000 calls/day. `blockscout-detect-plan.sh` itself costs ~20
        credits — do not re-run mid-session.
        
        Per-instance public hosts are not credit-metered but are rate-limited per IP by instance configuration; the Blockscout
        backend default is **300 requests per minute** (`API_RATE_LIMIT_BY_IP`), and operators may change it.
        
        ## Choosing an Endpoint
        
        Decide per query:
        
        | Situation                                                              | Use                                                                                 |
        | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
        | Chain is on the PRO host (eth, OP, Polygon, Base, Arbitrum, Gnosis, …) | **Unified PRO** `https://api.blockscout.com/{chain_id}/api/v2/...` + key            |
        | Porting existing Etherscan **V2** code (minimal diff)                  | **Etherscan-V2 alias** `https://api.blockscout.com/v2/api?chain_id={id}&module=...` |
        | Gateway does not serve a self-hosted or third-party target instance    | **Per-instance** `https://{instance}/api/v2/...` (no key) — resolve via Chainscout  |
        
        For Blockscout-hosted targets, keep using the keyed gateway after a `401`, `429`, or transient error; a per-instance
        host is not a fallback for those conditions. If the gateway does not serve a target's self-hosted or third-party
        instance, resolve that instance through `scripts/resolve-chain.sh`. If the target chain is absent from Chainscout, use
        Etherscan (`references/explorers/etherscan-api.md`) or the `primaryPublicRpc` from
        `references/generated/target-mainnets.json`. If the requested chain is not in
        `references/generated/target-mainnets.json`, stop and ask the user to file a feature request in
        <https://github.com/PaulRBerg/agent-skills>.
        
        ## Chain Resolution
        
        Do **not** default to Ethereum Mainnet. Infer the chain from the prompt first (same rules as
        `references/explorers/etherscan-api.md`: explicit chain mention, chain-specific tokens like POL→137 / ARB→42161, testnet
        keywords). If ambiguous, ask.
        
        Two-step resolution:
        
        1. **Name → `chain_id`** — use `references/generated/target-mainnets.json` and
           `references/generated/chain-aliases.json`.
        2. **`chain_id` -> instance URL** (only needed for the per-instance route) — use the target-gated Chainscout helper:
        
        ```bash
        scripts/resolve-chain.sh 100
        ```
        
        ```
        chain_id=100
        name=Gnosis
        native_currency=XDAI
        instance_url=https://gnosis.blockscout.com/
        api_url=https://gnosis.blockscout.com/api
        hosted_by=blockscout
        is_testnet=false
        layer=1
        rollup_type=
        ```
        
        `hosted_by=blockscout` indicates the chain is a candidate for the PRO host; community-hosted chains (`hosted_by` other
        than `blockscout`) are per-instance only. Chainscout indexes many networks, but this skill only uses target chains — see
        `references/generated/blockscout-chains.md`.
        
        ## Authentication
        
        On the PRO host, pass the key either way:
        
        ```bash
        # Query parameter
        curl -s "https://api.blockscout.com/1/api/v2/addresses/0xADDR?apikey=$BLOCKSCOUT_API_KEY"
        # Authorization header (preferred — keeps the key out of URLs/logs)
        curl -s -H "authorization: Bearer $BLOCKSCOUT_API_KEY" "https://api.blockscout.com/1/api/v2/addresses/0xADDR"
        ```
        
        ## Native REST API v2
        
        The recommended surface is the keyed `https://api.blockscout.com/{chain_id}/api/v2` gateway. Use
        `https://{instance}/api/v2` only for the per-instance exception described below. Examples use the keyed gateway.
        
        ### Address Overview (native balance + metadata)
        
        ```bash
        curl -s -H "authorization: Bearer $BLOCKSCOUT_API_KEY" \
          "https://api.blockscout.com/1/api/v2/addresses/0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe"
        ```
        
        ```json
        {
          "coin_balance": "9774452722498812330011",
          "exchange_rate": "1974.4",
          "is_contract": true,
          "ens_domain_name": null,
          "creation_transaction_hash": "0x9c81…",
          "creator_address_hash": "0x5AbF…",
          "has_tokens": true,
          "has_token_transfers": true
        }
        ```
        
        `coin_balance` is the indexed native balance in wei and can lag chain state; use RPC `eth_getBalance` when the amount
        decides anything. See [Unit Conversion](#unit-conversion).
        
        ### Token Holdings
        
        ```bash
        # Full holdings in one call (array) — no PRO gating, unlike Etherscan's addresstokenbalance
        curl -s -H "authorization: Bearer $BLOCKSCOUT_API_KEY" \
          "https://api.blockscout.com/1/api/v2/addresses/0xADDR/token-balances"
        
        # Paginated + filterable variant
        curl -s -H "authorization: Bearer $BLOCKSCOUT_API_KEY" \
          "https://api.blockscout.com/1/api/v2/addresses/0xADDR/tokens?type=ERC-20,ERC-721,ERC-1155"
        ```
        
        Each entry embeds full token metadata and balance:
        
        ```json
        [
          {
            "token": {
              "address_hash": "0xC02aaA39…",
              "name": "WETH",
              "symbol": "WETH",
              "decimals": "18",
              "type": "ERC-20",
              "exchange_rate": "1977.19"
            },
            "value": "214140968121599991968",
            "token_id": null,
            "token_instance": null
          }
        ]
        ```
        
        For ERC-721/1155, `token_id` and `token_instance` are populated. Divide `value` by `10^decimals` per token. Indexed
        `value` can be stale (a listed USDT balance has read zero on-chain); treat these endpoints as token discovery and
        confirm amounts with RPC `balanceOf`.
        
        ### Transaction History
        
        ```bash
        # Normal transactions (filter=to|from to restrict direction)
        curl -s -H "authorization: Bearer $BLOCKSCOUT_API_KEY" \
          "https://api.blockscout.com/1/api/v2/addresses/0xADDR/transactions"
        
        # Internal transactions
        curl -s -H "authorization: Bearer $BLOCKSCOUT_API_KEY" \
          "https://api.blockscout.com/1/api/v2/addresses/0xADDR/internal-transactions"
        ```
        
        ### Token Transfers (ERC-20 / 721 / 1155)
        
        One endpoint, filtered by `type`:
        
        ```bash
        curl -s -H "authorization: Bearer $BLOCKSCOUT_API_KEY" \
          "https://api.blockscout.com/1/api/v2/addresses/0xADDR/token-transfers?type=ERC-20"
        ```
        
        `type` accepts `ERC-20`, `ERC-721`, or `ERC-1155`. Each item carries `block_number`, `timestamp` (ISO-8601 UTC), `from`,
        `to`, `total` (`value`/`decimals` for fungible; `token_id` for NFTs), and embedded `token` metadata. Derive mint/burn
        from `from`/`to` being the zero address.
        
        ### Pagination (keyset)
        
        v2 returns 50 items plus a `next_page_params` object. To fetch the next page, append those fields as query params:
        
        ```json
        { "items": [ … ], "next_page_params": { "block_number": 25103884, "index": 1275, "items_count": 50 } }
        ```
        
        ```bash
        curl -s -H "authorization: Bearer $BLOCKSCOUT_API_KEY" \
          "https://api.blockscout.com/1/api/v2/addresses/0xADDR/token-transfers?type=ERC-20&block_number=25103884&index=1275&items_count=50"
        ```
        
        When `next_page_params` is `null`, the last page was reached. There is no `sort` parameter — v2 returns newest-first.
        
        ## Etherscan-Compatible Layer
        
        For porting existing Etherscan V2 code (`references/explorers/etherscan-api.md`) with minimal changes, use the
        **Etherscan-V2 alias**. It returns the familiar `{status,message,result}` shape:
        
        ```bash
        curl -s "https://api.blockscout.com/v2/api?chain_id=1&module=account&action=balance&address=0xADDR&apikey=$BLOCKSCOUT_API_KEY"
        # → {"message":"OK","result":"9774452722498812330011","status":"1"}
        ```
        
        Porting checklist from Etherscan V2: change host `api.etherscan.io` → `api.blockscout.com`, use `chain_id` (canonical;
        `chainid` is tolerated), and swap the key var. Action → v2 mapping:
        
        | Need                 | Etherscan action            | Native v2 (preferred)                       | Compat action              |
        | -------------------- | --------------------------- | ------------------------------------------- | -------------------------- |
        | Native balance       | `balance`                   | `addresses/{h}` → `coin_balance`            | `balance`                  |
        | Multi balance        | `balancemulti`              | —                                           | `balancemulti`             |
        | Single token balance | `tokenbalance`              | —                                           | `tokenbalance`             |
        | **All holdings**     | `addresstokenbalance` (PRO) | `addresses/{h}/token-balances` (**free**)   | `tokenlist`                |
        | Normal txs           | `txlist`                    | `addresses/{h}/transactions`                | `txlist`                   |
        | Internal txs         | `txlistinternal`            | `addresses/{h}/internal-transactions`       | `txlistinternal`           |
        | ERC-20 transfers     | `tokentx`                   | `addresses/{h}/token-transfers?type=ERC-20` | `tokentx`                  |
        | ERC-721 transfers    | `tokennfttx`                | `…token-transfers?type=ERC-721`             | `tokennfttx`               |
        | ERC-1155 transfers   | `token1155tx`               | `…token-transfers?type=ERC-1155`            | `token1155tx`              |
        | Logs                 | `getLogs`                   | —                                           | `getLogs`                  |
        | ABI / source         | `getabi` / `getsourcecode`  | `smart-contracts/{h}`                       | `getabi` / `getsourcecode` |
        
        Blockscout's compat layer does not implement every Etherscan action; when one is missing, use the native v2 equivalent.
        Full endpoint catalog: `references/explorers/blockscout-endpoints.md`.
        
        ## First Funding Transaction
        
        Blockscout has **no `fundedby` equivalent**. Use the compat `txlist`/`txlistinternal` with ascending sort (the native v2
        surface only sorts newest-first, which is awkward for "earliest"):
        
        ```bash
        curl -s "https://api.blockscout.com/v2/api?chain_id=1&module=account&action=txlist&address=0xADDR&sort=asc&page=1&offset=10&apikey=$BLOCKSCOUT_API_KEY"
        curl -s "https://api.blockscout.com/v2/api?chain_id=1&module=account&action=txlistinternal&address=0xADDR&sort=asc&page=1&offset=10&apikey=$BLOCKSCOUT_API_KEY"
        ```
        
        Pick the earliest entry where `to == address` (lowercased), `value > 0`, and (normal txs) `isError == "0"`. The funding
        tx is the lower `blockNumber` across both lists. Check both because addresses are often funded internally (CEX
        router/proxy withdrawals). Genesis-allocated balances appear in neither list — report explicitly.
        
        ## Per-Instance Exception
        
        For a target chain whose resolved instance is self-hosted or third-party and the keyed gateway does not serve it, query
        that instance directly without a key:
        
        ```bash
        # 1. Resolve the API base for a self-hosted instance
        api_url="$(scripts/resolve-chain.sh 2818 | sed -n 's/^api_url=//p')"
        
        # 2. Hit native v2 on that host (no key)
        curl -s "${api_url}/v2/addresses/0xADDR/token-balances"
        
        # Or the Etherscan-compatible layer on that host
        curl -s "${api_url}?module=account&action=balance&address=0xADDR"
        ```
        
        Use the helper's `api_url` for API requests; `instance_url` is the page host. An explicit `explorerApiUrl` in
        `target-mainnets.json` takes precedence over a Chainscout URL. Morph (`2818`) uses
        `https://explorer-api.morph.network/api` for its API and `https://explorer.morph.network` for pages, verified in
        Chromium and through the API on 2026-09-15.
        
        Superseed (`5330`) is not a usable Blockscout instance despite its stale Chainscout entry. Chromium verified on
        2026-09-15 that `https://explorer.superseed.xyz` serves Conduit Explorer and explicitly lacks historical transactions,
        holdings, and transfers. Use the target RPC for state facts; preserve indexed-history coverage as unknown.
        
        Per-instance hosts are community-operated for many chains, so uptime and indexing depth vary. Do not use one to bypass
        missing credentials, rate limits, or transient errors on a Blockscout-hosted target.
        
        ## Unit Conversion
        
        Native balances and token `value`s are in the smallest unit. Divide by `10^decimals` (18 for native and most tokens;
        USDC/USDT 6; WBTC 8):
        
        ```bash
        echo "scale=18; 9774452722498812330011 / 1000000000000000000" | bc
        # 9774.452722498812330011
        ```
        
        ## Output Formatting
        
        Use the completion format in `SKILL.md`: preserve full identifiers and use a compact table only when fields repeat.
        
        ## Error Handling
        
        | Symptom                                    | Cause / Action                                                                                                    |
        | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
        | `401 {"error":"Unauthorized"}`             | Missing/invalid key on the gateway. Report the coverage gap; do not substitute a hosted per-instance route.       |
        | `402` "requires Builder/Business/Pro plan" | Chain is plan-gated on the gateway (e.g. Polygon `137`). Coverage gap for this route; do not retry.               |
        | `404` on `api.blockscout.com/{id}/…`       | Resolve the target through Chainscout; use its per-instance route only when it qualifies for the exception above. |
        | `429` / `x-ratelimit-remaining: 0`         | Rate limited. Back off until `x-ratelimit-reset` (seconds); retain the keyed gateway route.                       |
        | `503`                                      | Transient gateway error. Retry within the bounded policy; otherwise report a coverage gap.                        |
        | `403` HTML "Just a moment..." page         | Bot challenge on a hosted `*.blockscout.com` instance. Use the keyed gateway, not repeated scripted retries.      |
        | Compat `{"status":"0", …}`                 | Etherscan-shaped error (`No transactions found`, bad address, etc.).                                              |
        
        ## Reference Files
        
        - **`references/generated/blockscout-chains.md`** — Target-gated Chainscout registry usage and target-chain
          observations.
        - **`references/explorers/blockscout-endpoints.md`** — full native v2 endpoint catalog, compat action list, and
          per-endpoint credit costs.
        - **`scripts/blockscout-detect-plan.sh`** — header-based plan/credit detection (run once per session).
        - **`scripts/resolve-chain.sh`** — `chain_id` → Blockscout instance URL via Chainscout.
        
        ## Fallback Documentation
        
        For features beyond this skill (blocks, smart contracts, search, stats, NFT instances):
        
        - AI-friendly docs index: `https://docs.blockscout.com/llms.txt`
        - Per-instance interactive schema: `https://{instance}/api-docs`
        - PRO OpenAPI spec: `https://docs.blockscout.com/openapi-specs/pro-api.yaml`
        
      • blockscout-endpoints.md 6.9 KB
        # Endpoints, Credits & Limits
        
        Bases:
        
        - Unified PRO: `https://api.blockscout.com/{chain_id}/api/v2/...` (key required)
        - Etherscan-V2 alias: `https://api.blockscout.com/v2/api?chain_id={id}&module=...&action=...` (key required;
          `{status,message,result}` shape)
        - Per-instance: `https://{instance}/api/v2/...` and `https://{instance}/api?module=...` (keyless traffic is
          rate-limited; see below)
        
        Keyless Blockscout was sunset in July 2026. Hosted `*.blockscout.com` instance subdomains enforce keyless rate limits
        and return `429` under sweep-shaped traffic, so the keyed `https://api.blockscout.com/{chain_id}` gateway is the correct
        route — and the correct fallback after a `429` — for every Blockscout-hosted chain. Reserve per-instance hosts for
        self-hosted or third-party instances the gateway does not serve.
        
        ## Native REST v2 — Endpoint Catalog
        
        Address (the core of this skill):
        
        | Path                                                   | Returns                                      |
        | ------------------------------------------------------ | -------------------------------------------- |
        | `addresses/{hash}`                                     | Native balance, metadata, creation info      |
        | `addresses/{hash}/token-balances`                      | Full holdings (array, single call)           |
        | `addresses/{hash}/tokens?type=ERC-20,ERC-721,ERC-1155` | Paginated/filtered holdings                  |
        | `addresses/{hash}/transactions?filter=to\|from`        | Normal transactions                          |
        | `addresses/{hash}/internal-transactions`               | Internal transactions                        |
        | `addresses/{hash}/token-transfers?type=ERC-20`         | Token transfers (also `ERC-721`, `ERC-1155`) |
        | `addresses/{hash}/coin-balance-history`                | Native balance over time                     |
        | `addresses/{hash}/logs`                                | Logs emitted by the address                  |
        | `addresses/{hash}/nft?type=ERC-721,ERC-1155`           | Owned NFT instances                          |
        
        Beyond address (use the fallback docs for full schemas):
        
        | Path                                        | Returns                  |
        | ------------------------------------------- | ------------------------ |
        | `transactions/{hash}`                       | Transaction detail       |
        | `transactions/{hash}/token-transfers`       | Transfers within a tx    |
        | `transactions/{hash}/logs`                  | Logs within a tx         |
        | `transactions/{hash}/internal-transactions` | Internal txs within a tx |
        | `blocks/{number_or_hash}`                   | Block detail             |
        | `tokens/{hash}`                             | Token metadata           |
        | `tokens/{hash}/holders`                     | Token holder list        |
        | `smart-contracts/{hash}`                    | ABI + verified source    |
        | `search?q=...`                              | Unified search           |
        | `stats`                                     | Chain-level stats        |
        
        Pagination is keyset: responses include `next_page_params` (50/page); append those fields as query params for the next
        page. `null` means last page. No `sort` param — newest-first.
        
        ## Etherscan-Compatible Actions
        
        Available on both `/{chain_id}/api?module=...` and the `/v2/api?chain_id=...` alias. `module=account` actions:
        `balance`, `balancemulti`, `tokenbalance`, `tokenlist`, `txlist`, `txlistinternal`, `tokentx`, `tokennfttx`,
        `token1155tx`. Other modules: `logs/getLogs`, `contract/getabi`, `contract/getsourcecode`, `block/*`, `stats/*`,
        `token/*`. The compat layer is legacy and does not implement every Etherscan action — prefer native v2; it supports
        `page`/`offset`/`sort` (asc/desc), which native v2 does not.
        
        ## Credit Costs (PRO host)
        
        Default **20 credits** per call. Exceptions:
        
        | Endpoint                                           | Credits |
        | -------------------------------------------------- | ------- |
        | (default — all unlisted)                           | 20      |
        | `api/v2/search/quick`                              | 25      |
        | `api/v2/tokens`                                    | 30      |
        | `api/v2/tokens/{hash}/transfers`                   | 30      |
        | `api/v2/transactions/{hash}/logs`                  | 30      |
        | `api/v2/transactions/{hash}/token-transfers`       | 30      |
        | `api/v2/transactions/{hash}/state-changes`         | 30      |
        | `api/v2/addresses/{hash}/token-transfers`          | 30      |
        | `api/v2/addresses/{hash}/logs`                     | 30      |
        | `api/v2/transactions/{hash}/internal-transactions` | 40      |
        | `api/v2/addresses/{hash}/internal-transactions`    | 40      |
        | `api/v2/smart-contracts/verification/config`       | 40      |
        | `api/v2/transactions/{hash}/summary`               | 50      |
        | `api/v2/transactions/{hash}/raw-trace`             | 50      |
        | `api/v2/addresses/{hash}/coin-balance-history`     | 50      |
        
        ## Plans
        
        | Plan         | Price   | Credits      | Rate limit (`x-ratelimit-limit`) |
        | ------------ | ------- | ------------ | -------------------------------- |
        | **Free**     | $0      | 100K / day   | 5 rps                            |
        | **Builder**  | $49/mo  | 100M / month | 15 rps                           |
        | **Pro**      | $199/mo | 500M / month | 30 rps                           |
        | **Business** | $999/mo | 3B / month   | 50 rps                           |
        
        Some chains are plan-gated on the keyed gateway: plans below Builder get HTTP `402` "requires Builder/Business/Pro plan"
        for at least Polygon PoS (`137`), Base (`8453`), and ZKsync Era (`324`). Treat `402` as a coverage gap for that route
        and fall through to the next one; do not retry. The gateway's bot protection also rejects Python's default `urllib`
        user-agent; scripted requests must send an explicit `User-Agent` header.
        
        Public per-instance hosts are not credit-metered but throttle keyless traffic per IP, including hosted
        `*.blockscout.com` subdomains. The backend default is **300 requests per minute** (`API_RATE_LIMIT_BY_IP` over a `1m`
        window); operators may change it, and exceeding it returns `429`. Their bot protection can also return `403` with an
        HTML "Just a moment..." challenge instead of JSON. Switch to the keyed gateway rather than backing off repeatedly.
        
        ## Response Headers (PRO host)
        
        Returned on every PRO call — read them instead of guessing tier or remaining budget:
        
        | Header                  | Meaning                                |
        | ----------------------- | -------------------------------------- |
        | `x-ratelimit-limit`     | Requests/sec for the plan (5/15/30/50) |
        | `x-ratelimit-remaining` | Requests left in the current second    |
        | `x-ratelimit-reset`     | Seconds until the window resets        |
        | `x-credits-remaining`   | Credits left in the current window     |
        
        ## Authoritative Docs
        
        - Index: <https://docs.blockscout.com/llms.txt>
        - PRO routes & credits: <https://docs.blockscout.com/devs/pro-api-responses-and-routes>
        - Per-instance schema: `https://{instance}/api-docs`
        - OpenAPI: <https://docs.blockscout.com/openapi-specs/pro-api.yaml>
        
      • etherscan-api.md 28 KB
        # Etherscan API V2
        
        ## Overview
        
        Query blockchain data using Etherscan's unified API V2. This skill covers:
        
        - Native ETH balance queries
        - ERC-20 token balance queries (single contract on every plan; full holdings on PRO)
        - Transaction history queries (normal, internal, ERC-20/ERC-721/ERC-1155 transfers)
        - First-funding lookup for an address (PRO `fundedby` with a 2-call free-tier fallback)
        - Target-chain support via the `chainid` parameter
        - Auto-detection of Free vs Lite vs PRO so paid-only chains and PRO-only endpoints are used when available
        - Token reputation availability: Etherscan exposes this only through paid metadata surfaces, not Lite
        
        **Scope:** Read-only account queries. For other Etherscan API features, consult the fallback documentation.
        
        ## Prerequisites
        
        ### API Key Validation
        
        Before making any API call, verify the `ETHERSCAN_API_KEY` environment variable is set:
        
        ```bash
        if [ -z "$ETHERSCAN_API_KEY" ]; then
          echo "Error: ETHERSCAN_API_KEY environment variable is not set."
          echo "Get a free API key at: https://etherscan.io/myapikey"
          exit 1
        fi
        ```
        
        If the environment variable is missing, inform the user and halt execution. Never print the key itself: presence checks
        must stay value-free, so do not echo `$ETHERSCAN_API_KEY` or use `${ETHERSCAN_API_KEY:+...}` /
        `${ETHERSCAN_API_KEY:-...}` expansions in any command whose output reaches the transcript.
        
        ### Plan Detection
        
        Run the detection helper **once per session** and cache the result. It maps `getapilimit` → plan tier and probes a Base
        balance call to disambiguate Free from Lite:
        
        ```bash
        scripts/etherscan-detect-plan.sh
        ```
        
        Output (key=value lines):
        
        ```
        plan=lite
        credit_limit=100000
        credits_used=4
        credits_available=99996
        limit_interval=daily
        interval_expiry=14:38:10
        pro_endpoints=false
        paid_chains=true
        ```
        
        `plan` is one of `free`, `lite`, `standard`, `advanced`, `professional`, `pro_plus`, `enterprise`, `unknown`. Two
        boolean fields gate behavior:
        
        - `paid_chains=true` — paid-only chains (Base, OP, Avalanche, BNB) are queryable. True for Lite and all higher tiers.
        - `pro_endpoints=true` — PRO-only actions (`addresstokenbalance`, `balancehistory`, `tokenholderlist`, `fundedby`,
          daily-stats endpoints, etc.) are callable. True for Standard and higher; **false on Lite**.
        
        **Manual detection** (if the script is unavailable):
        
        ```bash
        curl -s "https://api.etherscan.io/v2/api?chainid=1&module=getapilimit&action=getapilimit&apikey=$ETHERSCAN_API_KEY"
        # → {"status":"1","message":"OK","result":{"creditsUsed":1,"creditsAvailable":99999,"creditLimit":100000,"limitInterval":"daily","intervalExpiryTimespan":"07:20:05"}}
        ```
        
        | `creditLimit` | Plan         | Paid-only chains | PRO endpoints |
        | ------------- | ------------ | ---------------- | ------------- |
        | 100,000       | Free or Lite | Probe to confirm | No            |
        | 200,000       | Standard     | Yes              | Yes           |
        | 500,000       | Advanced     | Yes              | Yes           |
        | 1,000,000     | Professional | Yes              | Yes           |
        | 1,500,000     | Pro Plus     | Yes              | Yes           |
        | > 1,500,000   | Enterprise   | Yes              | Yes           |
        
        Free and Lite both report `creditLimit: 100000`. Lite ($49/mo) raises rate-limit-per-second (5 vs 3) **and unlocks every
        supported chain** (Base, OP, Avalanche, BNB), but does **not** add PRO endpoints — those start at Standard. To
        disambiguate, attempt a paid-chain balance call (e.g., `chainid=8453`): status=1 → Lite, status=0 → Free. To probe PRO
        instead, the failure response is `"Sorry, it looks like you are trying to access an API Pro endpoint."`.
        
        `getapilimit` itself consumes 1 credit (plus 1 more for the paid-chain probe), so do not re-run mid-session.
        
        ## Chain Inference
        
        Do not default to Ethereum Mainnet. Always infer the chain from the user's prompt before making any API call.
        
        ### Inference Rules
        
        1. **Explicit chain mention** — If the user mentions a chain name (e.g., "on Polygon", "Arbitrum balance", "Base
           chain"), use that chain.
        2. **Chain-specific tokens** — Some tokens exist primarily on specific chains:
           - POL → Polygon (137)
           - ARB → Arbitrum One (42161)
           - OP → OP Mainnet (10)
           - AVAX → Avalanche C-Chain (43114)
           - BNB → BNB Smart Chain (56)
           - SONIC → Sonic (146)
           - SEI → Sei (1329)
           - MON → Monad (143)
        3. **Contract address patterns** — If the user provides a contract address, consider asking which chain it's deployed on
           (many contracts exist on multiple chains).
        4. **Testnet keywords** — Testnets are outside this skill's target list. Ask the user to file a feature request instead
           of querying them.
        5. **Ambiguous cases** — If the chain cannot be inferred, **ask the user** before proceeding. Do not assume Ethereum
           Mainnet.
        
        ### Unsupported Chains
        
        If the user references a chain that is not in `references/generated/target-mainnets.json`, halt and ask them to file a
        feature request in <https://github.com/PaulRBerg/agent-skills>. Do not query Etherscan, Blockscout, Bungee, Chainlist,
        web search, or public RPCs for non-target chains.
        
        If the user references a **target EVM chain** that Etherscan API V2 does not cover, do **not** halt. Prefer Blockscout
        (`references/explorers/blockscout-api.md`) before direct RPC. If Blockscout doesn't index the target chain either, fall
        back to direct RPC calls against the target chain's default public RPC:
        
        1. Resolve the chain via `references/generated/target-mainnets.json` and `references/generated/chain-aliases.json` to
           get the default public RPC, chain ID, native currency symbol, and explorer URL.
        2. Issue bounded direct HTTP JSON-RPC calls (e.g., `eth_getBalance`, `eth_getLogs`, `eth_getTransactionByHash`) against
           that RPC. Do not hand the read to `cli-cast`.
        3. Note in the response that the data came from the chain's public RPC, not Etherscan, so PRO-style aggregations (full
           token holdings, first-funding lookup) are unavailable and must be derived manually from logs/transactions if needed.
        
        If the user references a **non-EVM chain**, do not use this skill:
        
        ```
        The chain "[chain name]" is outside the evm-atlas target list.
        Please file a feature request in https://github.com/PaulRBerg/agent-skills.
        ```
        
        For the target-filtered list of Etherscan-supported chains and their IDs, see
        `references/generated/etherscan-chains.md`.
        
        ## API Base URL
        
        All requests use the unified V2 endpoint:
        
        ```
        https://api.etherscan.io/v2/api
        ```
        
        The `chainid` parameter determines which blockchain to query.
        
        ## ETH Balance Query
        
        Query native ETH (or native token) balance for an address.
        
        ### Endpoint Parameters
        
        | Parameter | Required | Default  | Description                                                                                                                                           |
        | --------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
        | `chainid` | No       | `1`      | Chain ID (see etherscan-chains.md)                                                                                                                    |
        | `module`  | Yes      | -        | Set to `account`                                                                                                                                      |
        | `action`  | Yes      | -        | Set to `balance`                                                                                                                                      |
        | `address` | Yes      | -        | Wallet address (supports up to 20 comma-separated)                                                                                                    |
        | `tag`     | No       | `latest` | `latest` or hex block number. On free/Lite, only the last 128 blocks are queryable; older history needs the `balancehistory` PRO endpoint (Standard+) |
        | `apikey`  | Yes      | -        | API key from `$ETHERSCAN_API_KEY`                                                                                                                     |
        
        ### Single Address Query
        
        ```bash
        curl -s "https://api.etherscan.io/v2/api?chainid=1&module=account&action=balance&address=0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe&tag=latest&apikey=$ETHERSCAN_API_KEY"
        ```
        
        ### Multi-Address Query (up to 20)
        
        ```bash
        curl -s "https://api.etherscan.io/v2/api?chainid=1&module=account&action=balancemulti&address=0xaddress1,0xaddress2,0xaddress3&tag=latest&apikey=$ETHERSCAN_API_KEY"
        ```
        
        ### Response Format
        
        **Single address:**
        
        ```json
        {
          "status": "1",
          "message": "OK",
          "result": "172774397764084972158218"
        }
        ```
        
        **Multi-address:**
        
        ```json
        {
          "status": "1",
          "message": "OK",
          "result": [
            { "account": "0xaddress1", "balance": "1000000000000000000" },
            { "account": "0xaddress2", "balance": "2500000000000000000" }
          ]
        }
        ```
        
        ## ERC-20 Token Balance Query
        
        Query ERC-20 token balance for an address.
        
        ### Endpoint Parameters
        
        | Parameter         | Required | Default  | Description                        |
        | ----------------- | -------- | -------- | ---------------------------------- |
        | `chainid`         | No       | `1`      | Chain ID (see etherscan-chains.md) |
        | `module`          | Yes      | -        | Set to `account`                   |
        | `action`          | Yes      | -        | Set to `tokenbalance`              |
        | `contractaddress` | Yes      | -        | ERC-20 token contract address      |
        | `address`         | Yes      | -        | Wallet address to query            |
        | `tag`             | No       | `latest` | Block tag                          |
        | `apikey`          | Yes      | -        | API key from `$ETHERSCAN_API_KEY`  |
        
        ### Example Query
        
        ```bash
        curl -s "https://api.etherscan.io/v2/api?chainid=1&module=account&action=tokenbalance&contractaddress=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&address=0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe&tag=latest&apikey=$ETHERSCAN_API_KEY"
        ```
        
        ### Response Format
        
        ```json
        {
          "status": "1",
          "message": "OK",
          "result": "135499000000"
        }
        ```
        
        ### Full Holdings
        
        `tokenbalance` returns the balance for **one** ERC-20 contract at a time. To list **every** token an address holds:
        
        | Action                   | Returns                                                    |
        | ------------------------ | ---------------------------------------------------------- |
        | `addresstokenbalance`    | All ERC-20 holdings (token, quantity, decimals, USD price) |
        | `addresstokennftbalance` | All ERC-721 collection holdings and counts                 |
        
        **Use only when `pro_endpoints=true`** from plan detection. Both require Standard plan or higher and are throttled to
        **2 calls/second** regardless of tier.
        
        ```bash
        curl -s "https://api.etherscan.io/v2/api?chainid=1&module=account&action=addresstokenbalance&address=0x...&page=1&offset=100&apikey=$ETHERSCAN_API_KEY"
        ```
        
        When `pro_endpoints=false`, fall back to looping `tokenbalance` over a known token contract list.
        
        ## Transaction History Queries
        
        Query an address's transaction history. Five actions are available under `module=account`:
        
        | Action           | Returns                                    |
        | ---------------- | ------------------------------------------ |
        | `txlist`         | Normal (external) transactions             |
        | `txlistinternal` | Internal transactions (contract-initiated) |
        | `tokentx`        | ERC-20 token transfer events               |
        | `tokennfttx`     | ERC-721 (NFT) token transfer events        |
        | `token1155tx`    | ERC-1155 token transfer events             |
        
        ### Endpoint Parameters
        
        | Parameter         | Required | Default     | Description                                                  |
        | ----------------- | -------- | ----------- | ------------------------------------------------------------ |
        | `chainid`         | No       | `1`         | Chain ID (see etherscan-chains.md)                           |
        | `module`          | Yes      | -           | Set to `account`                                             |
        | `action`          | Yes      | -           | One of the actions above                                     |
        | `address`         | Yes      | -           | Wallet address                                               |
        | `contractaddress` | No       | -           | Token contract filter (`tokentx`/`tokennfttx`/`token1155tx`) |
        | `startblock`      | No       | `0`         | Starting block number                                        |
        | `endblock`        | No       | `999999999` | Ending block number                                          |
        | `page`            | No       | `1`         | Page number for pagination                                   |
        | `offset`          | No       | `100`       | Results per page (see free-tier limit note below)            |
        | `sort`            | No       | `asc`       | `asc` or `desc` by block number                              |
        | `apikey`          | Yes      | -           | API key from `$ETHERSCAN_API_KEY`                            |
        
        > **Pagination cap by plan (effective July 1, 2026):** `offset` maximum is `1000` for free-tier accounts and `10000` for
        > paid tiers (Lite included) on `txlist`, `txlistinternal`, `tokentx`, `tokennfttx`, `token1155tx`, and other list
        > endpoints. When `plan=free`, paginate in batches ≤ 1,000.
        
        ### Example Query
        
        ```bash
        curl -s "https://api.etherscan.io/v2/api?chainid=1&module=account&action=txlist&address=0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe&startblock=0&endblock=999999999&page=1&offset=100&sort=desc&apikey=$ETHERSCAN_API_KEY"
        ```
        
        ### Response Format
        
        `result` is an array of transaction objects. Each contains a Unix `timeStamp` (seconds, as a string) and chain-specific
        fields (`hash`, `from`, `to`, `value`, `gasUsed`, etc.).
        
        ```json
        {
          "status": "1",
          "message": "OK",
          "result": [
            {
              "blockNumber": "18000000",
              "timeStamp": "1693526400",
              "hash": "0x...",
              "from": "0x...",
              "to": "0x...",
              "value": "1000000000000000000",
              "gasUsed": "21000"
            }
          ]
        }
        ```
        
        ### Timestamp Conversion
        
        `timeStamp` is a Unix epoch in seconds. Always produce **timezone-aware UTC datetimes**.
        
        ```python
        from datetime import datetime, timezone
        
        dt = datetime.fromtimestamp(int(tx["timeStamp"]), tz=timezone.utc)
        ```
        
        Do **not** use `datetime.utcfromtimestamp()` — it returns a naive datetime and is deprecated in Python 3.12+.
        
        ```bash
        # Shell equivalent (GNU date)
        date -u -d "@1693526400" --iso-8601=seconds
        # macOS / BSD date
        date -u -r 1693526400 +"%Y-%m-%dT%H:%M:%SZ"
        ```
        
        ## NFT Transfer History
        
        Fetch historical ERC-721 or ERC-1155 transfers for an address. Both actions share the parameter table in the previous
        section; pass `contractaddress` to filter by collection. Pagination caps (1,000 free / 10,000 paid) and the
        `startblock`/`endblock`/`page`/`offset`/`sort` semantics are identical to `txlist`.
        
        ### ERC-721 Transfers (`tokennfttx`)
        
        ```bash
        curl -s "https://api.etherscan.io/v2/api?chainid=1&module=account&action=tokennfttx&address=0x6975be450864c02b4613023c2152ee0743572325&contractaddress=0x06012c8cf97bead5deae237070f9587f8e7a266d&startblock=0&endblock=999999999&page=1&offset=100&sort=asc&apikey=$ETHERSCAN_API_KEY"
        ```
        
        Response entry (one per `Transfer` event involving the address):
        
        ```json
        {
          "blockNumber": "4708120",
          "timeStamp": "1512907118",
          "hash": "0x031e6968...",
          "nonce": "0",
          "blockHash": "0x4be19c27...",
          "from": "0xb1690c08e213a35ed9bab7b318de14420fb57d8c",
          "contractAddress": "0x06012c8cf97bead5deae237070f9587f8e7a266d",
          "to": "0x6975be450864c02b4613023c2152ee0743572325",
          "tokenID": "202106",
          "tokenName": "CryptoKitties",
          "tokenSymbol": "CK",
          "tokenDecimal": "0",
          "transactionIndex": "81",
          "gas": "158820",
          "gasPrice": "40000000000",
          "gasUsed": "60508",
          "cumulativeGasUsed": "4880352",
          "input": "deprecated",
          "methodId": "0x454a2ab3",
          "functionName": "bid(uint256 _tokenId)",
          "confirmations": "18759540"
        }
        ```
        
        NFT-specific fields: `contractAddress` (collection), `tokenID` (per-NFT identifier), `tokenName`, `tokenSymbol`,
        `tokenDecimal` (always `"0"` for ERC-721).
        
        ### ERC-1155 Transfers (`token1155tx`)
        
        Same parameter shape — swap `action=token1155tx`. ERC-1155 differs from ERC-721 in two response fields:
        
        - **`tokenValue`** (string) — quantity transferred for this `tokenID`. Required because ERC-1155 is semi-fungible; a
          single transfer can move N copies of one ID. **Not present in ERC-721 responses.**
        - **`tokenDecimal`** is omitted (ERC-1155 has no decimals concept).
        
        ```json
        {
          "blockNumber": "...",
          "timeStamp": "...",
          "hash": "...",
          "from": "...",
          "to": "...",
          "contractAddress": "0x76be3b62873462d2142405439777e971754e8e77",
          "tokenID": "10371",
          "tokenValue": "1",
          "tokenName": "...",
          "tokenSymbol": "...",
          "...": "(other tx-level fields identical to tokennfttx)"
        }
        ```
        
        `TransferBatch` events (multiple IDs in one tx) appear as **multiple result entries sharing the same `hash`** — one per
        `(tokenID, tokenValue)` pair. Group by `hash` to reconstruct the batch.
        
        ### Filtering by Collection or Token ID
        
        - **By collection** — pass `contractaddress=<collection>`. The API filters server-side; omit to fetch transfers across
          all collections.
        - **By token ID** — no server-side filter exists. Fetch the collection's transfers and filter `result[].tokenID == <id>`
          client-side. For high-volume collections, narrow with `startblock`/`endblock` first.
        - **Mint vs burn vs transfer** — derive from `from`/`to`:
          - `from == 0x0000...0000` → mint
          - `to == 0x0000...0000` → burn
          - otherwise → transfer
        
        ### Cost & Limits
        
        Standard list-endpoint pricing — 1 credit per call, same rate-limit tier as `txlist`. Not a PRO endpoint; available on
        Free and Lite for Etherscan-supported target chains (paid-chain restriction still applies to Base/OP/Avalanche/BNB).
        
        ## First Funding Transaction
        
        Identify the earliest transaction that sent native value to an address — useful for fund-origin tracing, provenance, or
        compliance checks. Cost is **1 API call** (PRO) or **2 API calls** (fallback).
        
        ### Preferred: `fundedby` (PRO endpoint)
        
        Returns the address, tx hash, block, timestamp, and value of the transaction that first funded an EOA. Single call,
        structured response.
        
        | Parameter | Required | Default | Description                         |
        | --------- | -------- | ------- | ----------------------------------- |
        | `chainid` | No       | `1`     | Chain ID (see etherscan-chains.md)  |
        | `module`  | Yes      | -       | Set to `account`                    |
        | `action`  | Yes      | -       | Set to `fundedby`                   |
        | `address` | Yes      | -       | EOA address (contracts unsupported) |
        | `apikey`  | Yes      | -       | API key from `$ETHERSCAN_API_KEY`   |
        
        ```bash
        curl -s "https://api.etherscan.io/v2/api?chainid=1&module=account&action=fundedby&address=0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97&apikey=$ETHERSCAN_API_KEY"
        ```
        
        Response:
        
        ```json
        {
          "status": "1",
          "message": "OK",
          "result": {
            "block": 53708500,
            "timeStamp": "1708349932",
            "fundingAddress": "0x6969174fd72466430a46e18234d0b530c9fd5f49",
            "fundingTxn": "0xbc0ca4a67eb1555920552246409626cd60df01314dd2bcdb99718b506d9c9946",
            "value": "1000000000000000"
          }
        }
        ```
        
        **Requirements & limits:**
        
        - PRO endpoint — requires Standard plan or higher (`pro_endpoints=true` from plan detection).
        - Throttled to **2 calls/second** regardless of paid tier.
        - **EOA only.** Contract addresses return an error; use the fallback below.
        
        ### Fallback: scan ASC normal + internal transactions
        
        When `pro_endpoints=false` (free/Lite) or the address is a contract, scan both transaction lists ascending and pick the
        earliest qualifying incoming entry. Two API calls per address.
        
        ```bash
        # Earliest normal txs involving the address
        curl -s "https://api.etherscan.io/v2/api?chainid=1&module=account&action=txlist&address=0x...&startblock=0&endblock=999999999&page=1&offset=10&sort=asc&apikey=$ETHERSCAN_API_KEY"
        
        # Earliest internal txs involving the address
        curl -s "https://api.etherscan.io/v2/api?chainid=1&module=account&action=txlistinternal&address=0x...&startblock=0&endblock=999999999&page=1&offset=10&sort=asc&apikey=$ETHERSCAN_API_KEY"
        ```
        
        For each response, pick the first entry where **all** of the following hold:
        
        - `to.toLowerCase() == address.toLowerCase()` — incoming, not outgoing.
        - `value` (in wei) is greater than `0` — actual funding, not a zero-value call.
        - `isError == "0"` (omit this filter for internal txs, which use `isError` differently or not at all).
        
        The funding tx is whichever match has the lower `blockNumber`; break ties by `transactionIndex` (normal txs) or by list
        order (internal txs).
        
        **Why both lists:** An address may be funded externally (normal tx) or internally (contract sent ETH — common for CEX
        withdrawals routed through proxy/router contracts, contract deployments with non-zero `msg.value`, or SELFDESTRUCT
        refunds). Checking only `txlist` will miss internally-funded addresses.
        
        **Why `offset=10`, not `1`:** A `txlist` query returns every tx involving the address, including outgoing ones. The very
        first entry is occasionally outgoing (e.g., the address was internally pre-funded), so fetch a small window and scan for
        the first incoming match.
        
        **Edge cases:**
        
        - **No qualifying entry in the first 10** — extend with `offset=100` and `page=1`, or paginate further. In practice, >
          10 outgoing-before-incoming is exceedingly rare.
        - **Genesis allocation** — pre-mined balances do not appear in either list. The address shows a balance with no funding
          tx; report this explicitly.
        - **Token-only funding** — `fundedby` and this fallback only consider native value. If the address was bootstrapped with
          ERC-20 transfers alone (rare for EOAs since gas is needed), repeat the fallback against `tokentx`.
        
        ## Multi-Chain Usage
        
        Specify the `chainid` parameter to query different blockchains.
        
        ### Target Chain IDs (Free Tier)
        
        See `references/generated/etherscan-chains.md` for the free-tier target chain list with chain IDs.
        
        ### Example: Polygon Query
        
        ```bash
        curl -s "https://api.etherscan.io/v2/api?chainid=137&module=account&action=balance&address=0x...&tag=latest&apikey=$ETHERSCAN_API_KEY"
        ```
        
        ## Wei to Human-Readable Conversion
        
        API responses return balances in the smallest unit (wei for ETH, smallest decimals for tokens).
        
        ### ETH Conversion
        
        Divide by 10^18:
        
        ```bash
        # Using bc for precision
        echo "scale=18; 172774397764084972158218 / 1000000000000000000" | bc
        # Result: 172774.397764084972158218
        ```
        
        ### ERC-20 Conversion
        
        Divide by 10^decimals (typically 18, but varies per token):
        
        | Token       | Decimals |
        | ----------- | -------- |
        | Most tokens | 18       |
        | USDC, USDT  | 6        |
        | WBTC        | 8        |
        
        ```bash
        # USDC example (6 decimals)
        echo "scale=6; 135499000000 / 1000000" | bc
        # Result: 135499.000000
        ```
        
        ## Output Formatting
        
        Use the completion format in `SKILL.md`: preserve full identifiers and use a compact table only when fields repeat.
        
        ## Plan-Gated Capabilities
        
        Decisions in this section depend on the cached output of `scripts/etherscan-detect-plan.sh`.
        
        ### Paid-Only Chains
        
        Four target mainnets require any paid Etherscan plan. **Lite ($49/mo) is sufficient** — it grants access to every
        Etherscan-supported target chain at the same 100,000 daily-credit limit as Free. Data endpoints (balance, txlist, logs,
        etc.) fail only when `plan=free` (i.e., `paid_chains=false`). See `references/generated/etherscan-chains.md` for the
        paid-plan target chain list with chain IDs.
        
        **Exception:** `module=contract` endpoints (`getsourcecode`, `getabi`, etc.) work on **all** chains for every plan
        including free. The paid-plan requirement applies only to data endpoints.
        
        If `paid_chains=false` (i.e., `plan=free`) and the user requests a data query on the chains above, route to Blockscout
        (`references/explorers/blockscout-api.md`) before direct RPC. Only mention upgrading to Lite or higher if the user
        specifically needs Etherscan as the source.
        
        ### PRO-Only Endpoints
        
        When `pro_endpoints=true`, the following actions become available (non-exhaustive — see
        `https://docs.etherscan.io/api-pro/api-pro` for the full list):
        
        | Module       | Action(s)                                                                     | Use case                                                 |
        | ------------ | ----------------------------------------------------------------------------- | -------------------------------------------------------- |
        | `account`    | `addresstokenbalance`, `addresstokennftbalance`, `balancehistory`, `fundedby` | Full holdings, historical balances, first-funding lookup |
        | `token`      | `tokenholderlist`, `tokeninfo`, `tokensupplyhistory`, `tokenbalancehistory`   | Token analytics                                          |
        | `block`      | `dailyavgblocksize`, `dailyblkcount`, `dailyblockrewards`, etc.               | Daily block stats                                        |
        | `stats`      | `dailytxnfee`, `dailynewaddress`, `dailynetutilization`, etc.                 | Network-wide daily metrics                               |
        | `gastracker` | `dailyavggaslimit`, `dailygasused`, `dailyavggasprice`                        | Daily gas metrics                                        |
        
        When `pro_endpoints=false` (free or Lite), prefer the non-PRO equivalents listed in this skill or fall back to per-token
        loops.
        
        ### Token Reputation / Metadata
        
        Etherscan's token reputation badges are **not available on Lite**. The documented API surfaces are:
        
        | Surface              | Endpoint/action                           | Minimum plan | Notes                                                                                            |
        | -------------------- | ----------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------ |
        | Address Metadata API | `module=nametag&action=getaddresstag`     | Pro Plus     | Query the token contract address; response includes numeric `reputation` and `other_attributes`. |
        | Metadata CSV export  | `module=nametag&action=exportaddresstags` | Enterprise   | Bulk export; `other_attributes` can include `TR` token reputation values.                        |
        | Token info           | `module=token&action=tokeninfo`           | Standard     | Returns project/social metadata and `blueCheckmark`, but not the token reputation badge.         |
        
        Do not tell Lite users they can fetch token reputation from Etherscan API. Lite only unlocks paid Etherscan target
        chains and higher community rate limits; it does not unlock API Pro endpoints, Pro Plus address metadata, or Enterprise
        metadata CSV exports.
        
        ### All Plans
        
        All other Etherscan-supported target chains are available on every plan including Free. On Lite and higher, the
        paid-only target chains above also become available. See `references/generated/etherscan-chains.md` for the
        target-filtered list with chain IDs.
        
        ## Error Handling
        
        ### Common Error Responses
        
        | Status | Message                  | Cause                           |
        | ------ | ------------------------ | ------------------------------- |
        | `0`    | `NOTOK`                  | Invalid API key or rate limited |
        | `0`    | `Invalid address format` | Malformed address               |
        | `0`    | `No transactions found`  | Address has no activity         |
        
        ### Rate Limits by Plan
        
        | Plan         | Calls/second | Daily calls |
        | ------------ | ------------ | ----------- |
        | Free         | 3            | 100,000     |
        | Lite         | 5            | 100,000     |
        | Standard     | 10           | 200,000     |
        | Advanced     | 20           | 500,000     |
        | Professional | 30           | 1,000,000   |
        | Pro Plus     | 30           | 1,500,000   |
        | Enterprise   | custom       | unmetered   |
        
        PRO endpoints (`addresstokenbalance`, etc.) are throttled to **2 calls/second** regardless of tier. See
        `https://docs.etherscan.io/resources/rate-limits` for the authoritative schedule.
        
        If rate limited, wait briefly and retry.
        
        ## Reference Files
        
        - **`references/generated/etherscan-chains.md`** - Target-filtered list of supported chains with chain IDs
        - **`scripts/etherscan-detect-plan.sh`** - Plan-tier detection helper (run once per session)
        
        ## Fallback Documentation
        
        For read-only use cases not covered by this skill (gas estimates, block or network stats, etc.), fetch the AI-friendly
        documentation:
        
        ```
        https://docs.etherscan.io/llms.txt
        ```
        
      • explorer-paths.json 534 B
        {
          "paths": [
            {
              "resource": "address",
              "path": "/address/<addr>",
              "example": "https://arbiscan.io/address/0xabc..."
            },
            {
              "resource": "transaction",
              "path": "/tx/<hash>",
              "example": "https://etherscan.io/tx/0x123..."
            },
            {
              "resource": "block",
              "path": "/block/<number>",
              "example": "https://basescan.org/block/12345678"
            },
            {
              "resource": "token",
              "path": "/token/<addr>",
              "example": "https://polygonscan.com/token/0xdef..."
            }
          ]
        }
        
      • fantom-opera.md 6.8 KB
        # Fantom Opera Explorer and GraphQL
        
        ## Route
        
        Use this reference for Fantom Opera (`chain_id=250`) account history:
        
        - Human explorer: <https://explorer.fantom.network/>
        - Official read-only GraphQL endpoint: `POST https://xapi.fantom.network/`
        
        Chainscout still lists the self-hosted FTMScout instance at <https://ftmscout.com/>, but its frontend can return HTTP
        200 while its `/api/v2/*` data routes return HTTP 500. The atlas overlay therefore marks that Blockscout route unsafe;
        do not use it for evidence or bypass the generated resolver's refusal.
        
        The GraphQL schema also exposes mutations, including raw transaction submission. Never invoke a mutation. EVM Atlas is
        strictly read-only.
        
        ## Evidence Boundary
        
        Treat Opera GraphQL account history as a **partial positive-evidence route**, not a complete historical index.
        Conformance probes have found existing EOA activity missing from all account lists, and the public schema declares
        neither a genesis start nor the MongoDB scanner's last processed block. `block` and `state.blocks` report the connected
        Opera node's chain head; they do not prove that the aggregated account index has processed every block through that
        head.
        
        Consequences:
        
        - A returned row can establish candidate activity after its transaction, block hash, status, parties, and transfer log
          are confirmed by checkpoint-bound RPC evidence.
        - An empty list, `totalCount: "0x0"`, or `hasNext: false` proves only that this GraphQL index returned no rows. It is
          not a historical negative.
        - For the `bootstrap-discovery` profile, an exact `ethereum-eoa` zero nonce and zero native balance may still omit
          normal and internal history under the profile invariant, but GraphQL empties do not complete ERC-20 or ERC-721
          coverage. Require an independent genesis-complete index or exhaustive checkpoint-bounded logs for those channels.
        - For general or nonzero-state sweeps, GraphQL also lacks account-wide internal-transaction history. Preserve that as an
          explicit coverage gap or use a complete tracing/indexed-history route.
        
        ## Account Channels
        
        The live schema exposes these cursor-paginated `Account` fields:
        
        | GraphQL field   | EVM Atlas channel | Returned evidence                                                         |
        | --------------- | ----------------- | ------------------------------------------------------------------------- |
        | `txList`        | `txlist`          | Normal transaction hash, parties, value, status, and block identity       |
        | `erc20TxList`   | `tokentx`         | ERC-20 transfer parties, amount, log index, transaction, and timestamp    |
        | `erc721TxList`  | `tokennfttx`      | ERC-721 transfer parties, token ID, log index, transaction, and timestamp |
        | `erc1155TxList` | `token1155tx`     | ERC-1155 transfer parties, token ID, amount, log index, and transaction   |
        
        `txCount` and `balance` are current account state, not historical-list completeness signals. Acquire nonce and native
        balance at the fixed checkpoint through JSON-RPC as specified in `references/workflows/provider-routing.md`.
        
        The account schema has no `txlistinternal` equivalent. Do not relabel `txList` as internal history, and do not infer
        native inbound or trace completeness from it.
        
        ## Query and Pagination
        
        Use independent cursors for every list. The endpoint accepts at most 250 edges per request; use a smaller positive count
        when needed. With the cursor omitted, a positive count starts at the most recent edge. Continue from that list's
        `pageInfo.last` while `hasNext` is true, reject repeated cursors, and preserve the provider's hexadecimal scalar values.
        
        ```graphql
        query AccountHistory(
          $address: Address!
          $checkpoint: Long!
          $txCursor: Cursor
          $erc20Cursor: Cursor
          $erc721Cursor: Cursor
          $erc1155Cursor: Cursor
          $count: Int!
        ) {
          providerHead: block {
            number
            hash
            timestamp
          }
          checkpoint: block(number: $checkpoint) {
            number
            hash
            timestamp
          }
          account(address: $address) {
            txList(cursor: $txCursor, count: $count) {
              totalCount
              pageInfo {
                first
                last
                hasNext
                hasPrevious
              }
              edges {
                cursor
                transaction {
                  hash
                  from
                  to
                  value
                  status
                  blockNumber
                  blockHash
                }
              }
            }
            erc20TxList(cursor: $erc20Cursor, count: $count) {
              totalCount
              pageInfo {
                first
                last
                hasNext
                hasPrevious
              }
              edges {
                cursor
                trx {
                  trxHash
                  trxIndex
                  trxType
                  sender
                  recipient
                  amount
                  timeStamp
                  transaction {
                    status
                    blockNumber
                    blockHash
                  }
                }
              }
            }
            erc721TxList(cursor: $erc721Cursor, count: $count) {
              totalCount
              pageInfo {
                first
                last
                hasNext
                hasPrevious
              }
              edges {
                cursor
                trx {
                  trxHash
                  trxIndex
                  trxType
                  sender
                  recipient
                  amount
                  tokenId
                  timeStamp
                  transaction {
                    status
                    blockNumber
                    blockHash
                  }
                }
              }
            }
            erc1155TxList(cursor: $erc1155Cursor, count: $count) {
              totalCount
              pageInfo {
                first
                last
                hasNext
                hasPrevious
              }
              edges {
                cursor
                trx {
                  trxHash
                  trxIndex
                  trxType
                  sender
                  recipient
                  amount
                  tokenId
                  timeStamp
                  transaction {
                    status
                    blockNumber
                    blockHash
                  }
                }
              }
            }
          }
        }
        ```
        
        Set `checkpoint` to the fixed decimal block number and compare the returned checkpoint hash and timestamp with the RPC
        checkpoint. Require `providerHead.number >= checkpoint.number`. This verifies chain access and row bounds only. Because
        the schema exposes no account-index head, never convert that comparison into a negative-history claim.
        
        For positive evidence, ignore post-checkpoint rows, apply the selected profile's predicates, and independently confirm
        the earliest qualifying transaction and logs through RPC. For a negative, replace this partial route with independent
        genesis-complete coverage; do not combine two partial empty responses and call them complete.
        
        ## Sources
        
        - Fantom Opera public endpoints: <https://docs.fantom.foundation/build-on-opera/api/public-endpoints>
        - GraphQL getting started: <https://docs.fantom.foundation/build-on-opera/api/graphql/getting-started>
        - GraphQL schema and account lists: <https://docs.fantom.foundation/build-on-opera/api/graphql/schema-structure>
        - Cursor pagination: <https://docs.fantom.foundation/build-on-opera/api/graphql/schema-basics>
        - API implementation: <https://github.com/Fantom-foundation/fantom-api-graphql>
        
      • optimism-pre-regenesis.md 9.9 KB
        # OP Mainnet Pre-Regenesis History
        
        ## Evidence Boundary
        
        Use this reference for OP Mainnet (`chain_id=10`) queries that target activity before the final regenesis on
        `2021-11-11`.
        
        Current OP Mainnet JSON-RPC cannot return a canonical `eth_getTransactionReceipt` response for an OVM1 transaction
        removed by the final regenesis. A current-RPC `null`, provider archive failure, or explorer omission proves only native
        receipt unavailability; it is not negative evidence that the historical transaction occurred.
        
        Dune's `optimism_legacy_ovm1` transaction, log, and trace rows are historical execution evidence. When the required
        components agree, reconstruct a **legacy execution packet**. Never call that packet a receipt: it is neither an
        authenticated JSON-RPC receipt nor a historical state-trie proof. Preserve its Dune provenance and every missing
        component.
        
        ## Exact-Transaction Workflow
        
        1. Establish that the target is on OP Mainnet and predates the final regenesis. Use supplied context, another dated
           source, or the exact Dune transaction row. Do not infer nonexistence when current OP Mainnet routes cannot resolve
           the hash.
        2. Query all three legacy tables by the exact hash. Replace `<TX_HASH_HEX>` with the 64 hexadecimal digits while
           retaining DuneSQL's `0x` varbinary prefix:
        
           ```sql
           SELECT *
           FROM optimism_legacy_ovm1.transactions
           WHERE hash = 0x<TX_HASH_HEX>;
        
           SELECT *
           FROM optimism_legacy_ovm1.logs
           WHERE tx_hash = 0x<TX_HASH_HEX>
           ORDER BY "index";
        
           SELECT *
           FROM optimism_legacy_ovm1.traces
           WHERE tx_hash = 0x<TX_HASH_HEX>
           ORDER BY trace_address;
           ```
        
        3. Require exactly one transaction row. Preserve zero rows or duplicate rows as a coverage outcome; do not choose a row
           heuristically.
        4. Verify the transaction hash, block number, block hash, and block time agree across every returned component. Verify
           transaction `from`/`to` against each log's `tx_from`/`tx_to`; verify transaction `success` against each trace's
           `tx_success`. A trace's own `from`, `to`, and `success` describe that call, so reconcile them through the call
           hierarchy rather than forcing them to equal the root transaction fields.
        5. Preserve log indices exactly in ascending order. Preserve trace-address arrays in ascending hierarchy order,
           including the root when present. Record duplicate, missing, or contradictory ordering metadata as a coverage gap;
           never renumber rows.
        6. Use the address dashboard only to discover candidate hashes for an address-wide request. An exact-hash request uses
           the table workflow above.
        
        ## Legacy Execution Packet
        
        Return exact values; do not truncate identifiers or normalize away nulls.
        
        | Component   | Dune provenance                                | Required packet fields                                                                                                   |
        | ----------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
        | Transaction | `optimism_legacy_ovm1.transactions`            | `success`, `from`, `to`, `value`, `data`, gas fields, `block_number`, `block_hash`, `block_time`, transaction index/hash |
        | Logs        | `optimism_legacy_ovm1.logs`, ordered by index  | `contract_address`, topics, `data`, `index`, transaction index/hash, block fields, `tx_from`, `tx_to`                    |
        | Traces      | `optimism_legacy_ovm1.traces`, ordered by path | call `from`/`to`, `type`, `call_type`, `input`, `output`, `value`, `success`, `error`, `tx_success`, `trace_address`     |
        
        Attach the namespace and table name to each component, plus an explicit `coverage_gaps` list. A packet is complete only
        for the requested determination; complete execution rows do not independently prove historical contract state,
        deployment identity, wallet ownership, or an LP principal/fee split.
        
        ## Coverage Outcomes
        
        Record native-receipt availability separately from packet coverage:
        
        | Outcome                              | Record when                                                                                               |
        | ------------------------------------ | --------------------------------------------------------------------------------------------------------- |
        | Native receipt unavailable           | Current RPC returns `null`, an archive route fails, or the current explorer omits the transaction.        |
        | Historical execution evidence absent | The exact transaction row is absent. This is absence from Dune, not proof the transaction never existed.  |
        | Legacy execution packet partial      | The transaction exists but zero log rows return and non-emission is not otherwise proven.                 |
        | Legacy execution packet partial      | Transaction and logs exist but traces are absent, contradictory, or structurally incomplete.              |
        | Legacy execution packet partial      | Optimism's known January-July 2021 execution-effects loss may cover a required component.                 |
        | Execution packet complete, state gap | Execution is reconstructable, but required historical contract, wallet, or position state is unavailable. |
        | Complete enough for determination    | Transaction, ordering, logs, traces, identity, state, and wallet deltas needed by the request are proven. |
        
        Zero logs can be a legitimate execution result. Promote that outcome beyond partial only when verified calldata, traces,
        and contract semantics show that no relevant event should exist. Likewise, trace rows are not complete merely because at
        least one row returned; reconcile their hierarchy, transaction status, errors, and required internal value movement.
        
        ## Concentrated-Liquidity Reconstruction
        
        When the accounting result depends on a concentrated-liquidity position:
        
        1. Identify the position token and prove the exact position-manager deployment used historically. Do not substitute a
           current explorer label or a same-interface deployment.
        2. Retrieve the full prior lifecycle for that position, from creation through the target transaction. Include every
           position transfer, liquidity increase/decrease, collection, and other state-changing action supported by the exact
           deployment's events and calls.
        3. Reconstruct the last pre-transaction liquidity state and whether principal or fees were already owed. Never infer
           historical state from a current position lookup. Treat an unproven zero balance as unknown, not zero.
        4. Decode the target's exact command order and maximum collect bounds. Bounds and calldata express intent; ordered logs,
           traces, and wallet movements establish execution.
        5. Reconcile decrease, collect, unwrap, sweep, refund, and final transfer evidence using exact integer amounts. Keep
           native and wrapped legs distinct.
        6. Verify a principal/fee split only when the complete lifecycle and target execution support the residual. Otherwise
           report the combined collection and name the missing pre-state or lifecycle evidence.
        
        ## Browser and Persistence Boundary
        
        Chrome DevTools may run bounded, read-only Dune queries. Executing a query may consume Dune credits. Leave queries
        unsaved by default; saving, publishing, scheduling, or otherwise externally persisting one requires explicit authority.
        Never put a private hash, wallet, position identifier, or financial value into this installable reference.
        
        As of `2026-09-10`, Dune's Free plan is view-only: public dashboards, query source, and data collections remain
        viewable, but exact-hash table lookups and parameterized address-dashboard results require query execution on a paid
        plan. Affected existing accounts received temporary Plus access through `2026-09-24`, with 2,500 trial credits. The
        unsaved browser workflow needs only query execution, so Dune advertises Analyst as sufficient; Plus is not required for
        this route. Never purchase or upgrade without explicit authority.
        
        Treat a missing execution entitlement or exhausted credit balance as a provider-plan coverage gap, not historical
        absence. Chromium does not bypass server-side plan enforcement, and viewing or copying a public query does not produce
        fresh address-parameterized or exact-hash results. Dune's older pricing FAQ and the OVM1 dashboard copy may still
        mention free executions or free credits; resolve entitlement from the live pricing page and the credentialed account.
        
        Separately, Dune's Application Service Addendum directs programmatic interaction to its API service. Chrome DevTools
        automation may be considered programmatic even when it drives the graphical site. Do not represent a paid browser
        workflow as terms-cleared; prefer a supported API or MCP route for repeated automation, or obtain Dune clarification.
        
        ## Deeper Fallback
        
        For the January-July 2021 period, Optimism documents partial loss of transaction execution effects, including emitted
        events and success state. When Dune genuinely lacks the exact transaction or a required lifecycle event, the raw inputs
        published through Ethereum's `CanonicalTransactionChain` are a deeper reconstruction path. Re-execution is laborious,
        costly, and may remain incomplete. Do not require it when the Dune packet already answers the requested question.
        
        ## Sources
        
        - Optimism, "Accessing pre-regenesis history":
          `https://docs.optimism.io/op-mainnet/pre-bedrock-history/regenesis-history`
        - Optimism, "Lost pre-regenesis data": `https://docs.optimism.io/op-mainnet/pre-bedrock-history/lost-pre-regenesis-data`
        - Dune catalog: `https://dune.com/data/optimism_legacy_ovm1.transactions`,
          `https://dune.com/data/optimism_legacy_ovm1.logs`, and `https://dune.com/data/optimism_legacy_ovm1.traces`
        - DuneSQL varbinary literals: `https://docs.dune.com/query-engine/datatypes#varbinary`
        - Dune query execution and credit behavior: `https://docs.dune.com/query-engine/query-executions`
        - OVM1 address dashboard: `https://dune.com/optimismfnd/OVM1.0-User-Address-Transactions`
        - Dune pricing: `https://dune.com/pricing`
        - Dune Application Service Addendum: `https://dune.com/application-terms`
        
    • generated
      • blockscout-chains.md 10.5 KB
        # Target Chains & Chainscout
        
        <!-- Generated by scripts/generate-evm-atlas.ts. Registry metadata comes from crypto-registry data/chains.json; edit skills/evm-atlas/references/atlas-overlays.json for Atlas-only fields. -->
        
        Blockscout and Chainscout index many EVM networks, but this skill only uses them for chains in
        `references/generated/target-mainnets.json`. Do not use Chainscout to expand scope. If a requested chain is not in that
        JSON file, ask the user to file a feature request in <https://github.com/PaulRBerg/agent-skills>.
        
        ## Chainscout API
        
        No API key required. Live UI: <https://chains.blockscout.com/>
        
        | Endpoint                                            | Returns                                               |
        | --------------------------------------------------- | ----------------------------------------------------- |
        | `GET https://chains.blockscout.com/api/chains`      | Object keyed by `chain_id` -> metadata (all networks) |
        | `GET https://chains.blockscout.com/api/chains/{id}` | Metadata for one chain                                |
        
        Single-chain response shape:
        
        ```json
        {
          "name": "Gnosis",
          "native_currency": "XDAI",
          "isTestnet": false,
          "layer": 1,
          "rollupType": null,
          "ecosystem": "Ethereum",
          "explorers": [{ "url": "https://gnosis.blockscout.com/", "hostedBy": "blockscout" }]
        }
        ```
        
        `explorers[].hostedBy`:
        
        - `blockscout` - Blockscout-operated; candidate for the unified PRO host (`api.blockscout.com/{chain_id}/...`). Still
          confirm with a live call; not every hosted target chain is fronted by the PRO host.
        - anything else - community-operated. Per-instance only; use `explorers[].url` directly (no key).
        
        Use `scripts/resolve-chain.sh <chain_id>` to extract these fields as `key=value` lines. The helper refuses non-target
        chain IDs and rejects Chainscout responses whose name does not match the target chain. For **name -> `chain_id`**, use
        `references/generated/target-mainnets.json` and `references/generated/chain-aliases.json` first.
        
        ## Target Chains Observed in Chainscout
        
        Observed on 2026-07-08. Presence here does not override the canonical explorer/RPC metadata in
        `references/generated/target-mainnets.json`.
        
        | Chain           | `chain_id` | Native | Hosted by  | Instance URL                                     | Notes                                             |
        | --------------- | ---------- | ------ | ---------- | ------------------------------------------------ | ------------------------------------------------- |
        | Arbitrum        | `42161`    | ETH    | blockscout | https://arbitrum.blockscout.com/                 |                                                   |
        | Arbitrum Nova   | `42170`    | ETH    | blockscout | https://arbitrum-nova.blockscout.com/            | Canonical explorer (Arbiscan Nova decommissioned) |
        | Base            | `8453`     | ETH    | blockscout | https://base.blockscout.com/                     |                                                   |
        | Celo            | `42220`    | CELO   | blockscout | https://celo.blockscout.com/                     |                                                   |
        | Ethereum        | `1`        | ETH    | blockscout | https://eth.blockscout.com/                      |                                                   |
        | Filecoin        | `314`      | FIL    | blockscout | https://filecoin.blockscout.com/                 |                                                   |
        | Gnosis          | `100`      | xDAI   | blockscout | https://gnosis.blockscout.com/                   |                                                   |
        | HyperEVM        | `999`      | HYPE   | self       | https://www.hyperscan.com/                       | Chainscout marks `isTestnet=true`                 |
        | Lightlink       | `1890`     | ETH    | blockscout | https://phoenix.lightlink.io/                    |                                                   |
        | Linea           | `59144`    | ETH    | self       | https://explorer.linea.build/                    |                                                   |
        | Mode            | `34443`    | ETH    | blockscout | https://explorer.mode.network/                   |                                                   |
        | Morph           | `2818`     | ETH    | self       | https://explorer.morph.network/                  | Separate API host; see explorerApiUrl             |
        | Optimism        | `10`       | ETH    | blockscout | https://explorer.optimism.io/                    |                                                   |
        | Polygon         | `137`      | POL    | blockscout | https://polygon.blockscout.com/                  |                                                   |
        | Robinhood Chain | `4663`     | ETH    | blockscout | https://robinhoodchain.blockscout.com/           |                                                   |
        | Scroll          | `534352`   | ETH    | blockscout | https://scroll.blockscout.com                    |                                                   |
        | Taiko           | `167000`   | ETH    | self       | https://blockscout.mainnet.taiko.xyz/            | Chainscout name is Taiko Alethia                  |
        | Unichain        | `130`      | ETH    | blockscout | https://unichain.blockscout.com                  |                                                   |
        | World Chain     | `480`      | ETH    | alchemy    | https://worldchain-mainnet.explorer.alchemy.com/ | Alchemy-hosted instance; prefer Etherscan V2      |
        | ZKsync Era      | `324`      | ETH    | blockscout | https://zksync.blockscout.com/                   |                                                   |
        
        ## Target Chains Absent or Unsafe in Chainscout
        
        Use Etherscan, a documented exceptional-history route, or the public RPC/explorer in
        `references/generated/target-mainnets.json` for these.
        
        | Chain     | `chain_id` | Notes                                                                                                                                                                                                      |
        | --------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
        | Abstract  | `2741`     | Not returned by Chainscout                                                                                                                                                                                 |
        | Avalanche | `43114`    | Not returned by Chainscout                                                                                                                                                                                 |
        | Berachain | `80094`    | Not returned by Chainscout                                                                                                                                                                                 |
        | Blast     | `81457`    | Not returned by Chainscout                                                                                                                                                                                 |
        | BNB Chain | `56`       | Not returned by Chainscout                                                                                                                                                                                 |
        | Chiliz    | `88888`    | Not returned by Chainscout                                                                                                                                                                                 |
        | Core Dao  | `1116`     | Not returned by Chainscout                                                                                                                                                                                 |
        | Fantom    | `250`      | Chainscout still lists self-hosted FTMScout at https://ftmscout.com/, but as checked 2026-07-31 its frontend returns HTTP 200 while /api/v2/* data routes return HTTP 500; do not use it for evidence      |
        | Fraxtal   | `252`      | Not returned by Chainscout                                                                                                                                                                                 |
        | IoTeX     | `4689`     | Not returned by Chainscout                                                                                                                                                                                 |
        | Monad     | `143`      | Not returned by Chainscout                                                                                                                                                                                 |
        | Ronin     | `2020`     | Chainscout returns a different network for `2020`; app.roninchain.com blocks scripted access, so verify with `$chromium-browser` instead of curl or WebFetch                                               |
        | Sei       | `1329`     | Not returned by Chainscout                                                                                                                                                                                 |
        | Sonic     | `146`      | Not returned by Chainscout                                                                                                                                                                                 |
        | Sophon    | `50104`    | Not returned by Chainscout                                                                                                                                                                                 |
        | Superseed | `5330`     | Chromium verified 2026-09-15: explorer.superseed.xyz now serves Conduit Explorer, which does not support historical transactions, holdings, or transfers; do not use its stale Chainscout Blockscout route |
        | XDC       | `50`       | Not returned by Chainscout                                                                                                                                                                                 |
        | Zora      | `7777777`  | Not returned by Chainscout                                                                                                                                                                                 |
        
        ## Contributing
        
        Missing or wrong Blockscout registry data is fixed via PR to <https://github.com/blockscout/chainscout>. Requests to add
        non-target chains to this skill belong in <https://github.com/PaulRBerg/agent-skills>.
        
      • chain-aliases.json 3.6 KB
        {
          "aliases": [
            {
              "alias": "Arbitrum One",
              "chainName": "Arbitrum",
              "chainId": 42161
            },
            {
              "alias": "avalanche c-chain",
              "chainName": "Avalanche",
              "chainId": 43114
            },
            {
              "alias": "avax",
              "chainName": "Avalanche",
              "chainId": 43114
            },
            {
              "alias": "bsc",
              "chainName": "BNB Chain",
              "chainId": 56
            },
            {
              "alias": "binance smart chain",
              "chainName": "BNB Chain",
              "chainId": 56
            },
            {
              "alias": "bnb",
              "chainName": "BNB Chain",
              "chainId": 56
            },
            {
              "alias": "BNB Smart Chain",
              "chainName": "BNB Chain",
              "chainId": 56
            },
            {
              "alias": "Chiliz Chain",
              "chainName": "Chiliz",
              "chainId": 88888
            },
            {
              "alias": "coreDao",
              "chainName": "Core Dao",
              "chainId": 1116
            },
            {
              "alias": "mainnet",
              "chainName": "Ethereum",
              "chainId": 1
            },
            {
              "alias": "eth",
              "chainName": "Ethereum",
              "chainId": 1
            },
            {
              "alias": "ethereum",
              "chainName": "Ethereum",
              "chainId": 1
            },
            {
              "alias": "ethereum mainnet",
              "chainName": "Ethereum",
              "chainId": 1
            },
            {
              "alias": "fantom opera",
              "chainName": "Fantom",
              "chainId": 250
            },
            {
              "alias": "ftm",
              "chainName": "Fantom",
              "chainId": 250
            },
            {
              "alias": "fevm",
              "chainName": "Filecoin",
              "chainId": 314
            },
            {
              "alias": "filecoin evm",
              "chainName": "Filecoin",
              "chainId": 314
            },
            {
              "alias": "fvm",
              "chainName": "Filecoin",
              "chainId": 314
            },
            {
              "alias": "Filecoin Mainnet",
              "chainName": "Filecoin",
              "chainId": 314
            },
            {
              "alias": "frax",
              "chainName": "Fraxtal",
              "chainId": 252
            },
            {
              "alias": "gnosis chain",
              "chainName": "Gnosis",
              "chainId": 100
            },
            {
              "alias": "hyper evm",
              "chainName": "HyperEVM",
              "chainId": 999
            },
            {
              "alias": "hyperliquid",
              "chainName": "HyperEVM",
              "chainId": 999
            },
            {
              "alias": "lightlink phoenix",
              "chainName": "Lightlink",
              "chainId": 1890
            },
            {
              "alias": "LightLink Phoenix Mainnet",
              "chainName": "Lightlink",
              "chainId": 1890
            },
            {
              "alias": "Linea Mainnet",
              "chainName": "Linea",
              "chainId": 59144
            },
            {
              "alias": "Mode Mainnet",
              "chainName": "Mode",
              "chainId": 34443
            },
            {
              "alias": "op",
              "chainName": "Optimism",
              "chainId": 10
            },
            {
              "alias": "OP Mainnet",
              "chainName": "Optimism",
              "chainId": 10
            },
            {
              "alias": "matic",
              "chainName": "Polygon",
              "chainId": 137
            },
            {
              "alias": "pol",
              "chainName": "Polygon",
              "chainId": 137
            },
            {
              "alias": "robinhood",
              "chainName": "Robinhood Chain",
              "chainId": 4663
            },
            {
              "alias": "Sei Network",
              "chainName": "Sei",
              "chainId": 1329
            },
            {
              "alias": "Taiko Alethia",
              "chainName": "Taiko",
              "chainId": 167000
            },
            {
              "alias": "Taiko Mainnet",
              "chainName": "Taiko",
              "chainId": 167000
            },
            {
              "alias": "worldchain",
              "chainName": "World Chain",
              "chainId": 480
            },
            {
              "alias": "XDC Network",
              "chainName": "XDC",
              "chainId": 50
            },
            {
              "alias": "zksync",
              "chainName": "ZKsync Era",
              "chainId": 324
            },
            {
              "alias": "zora network",
              "chainName": "Zora",
              "chainId": 7777777
            }
          ]
        }
        
      • etherscan-chains.md 4.3 KB
        # Etherscan Supported Target Chains Reference
        
        <!-- Generated by scripts/generate-evm-atlas.ts. Registry metadata comes from crypto-registry data/chains.json; edit skills/evm-atlas/references/atlas-overlays.json for Atlas-only fields. -->
        
        Source: <https://docs.etherscan.io/supported-chains> Live list: <https://api.etherscan.io/v2/chainlist> (returns the
        provider's authoritative `chainid` set)
        
        Verified against the live `chainlist` endpoint on 2026-07-08 (64 provider chains). This file intentionally lists only
        chains from `references/generated/target-mainnets.json`; provider-supported chains outside that list are out of scope
        for this skill.
        
        ## Target Mainnets (Free Tier Available)
        
        | Chain       | Chain ID | Notes         |
        | ----------- | -------- | ------------- |
        | Abstract    | `2741`   |               |
        | Arbitrum    | `42161`  |               |
        | Berachain   | `80094`  |               |
        | Blast       | `81457`  |               |
        | Celo        | `42220`  |               |
        | Ethereum    | `1`      | Default chain |
        | Fraxtal     | `252`    |               |
        | Gnosis      | `100`    |               |
        | HyperEVM    | `999`    |               |
        | Linea       | `59144`  |               |
        | Monad       | `143`    |               |
        | Polygon     | `137`    |               |
        | Sei         | `1329`   |               |
        | Sonic       | `146`    |               |
        | Taiko       | `167000` |               |
        | Unichain    | `130`    |               |
        | World Chain | `480`    |               |
        | XDC         | `50`     |               |
        
        ## Target Mainnets (Paid Plan Required)
        
        The following target chains require a paid Etherscan plan for data endpoints (balances, transactions, logs, etc.).
        **Lite ($49/mo) is sufficient**; `module=contract` endpoints (`getsourcecode`, `getabi`, etc.) work on every plan.
        
        | Chain     | Chain ID |
        | --------- | -------- |
        | Avalanche | `43114`  |
        | Base      | `8453`   |
        | BNB Chain | `56`     |
        | Optimism  | `10`     |
        
        ## Target Mainnets Not on Etherscan V2
        
        Route these target chains to Blockscout when available, then to the `primaryPublicRpc` from
        `references/generated/target-mainnets.json` if needed. Do not query Etherscan V2 for them.
        
        | Chain           | Chain ID  | Notes                                                                          |
        | --------------- | --------- | ------------------------------------------------------------------------------ |
        | Arbitrum Nova   | `42170`   | Not returned by the live chainlist; `nova.arbiscan.io` redirects to Blockscout |
        | Chiliz          | `88888`   | Not returned by the live chainlist                                             |
        | Core Dao        | `1116`    | Not returned by the live chainlist                                             |
        | Fantom          | `250`     | Not returned by the live chainlist                                             |
        | Filecoin        | `314`     | Not returned by the live chainlist                                             |
        | IoTeX           | `4689`    | Not returned by the live chainlist                                             |
        | Lightlink       | `1890`    | Not returned by the live chainlist                                             |
        | Mode            | `34443`   | Not returned by the live chainlist                                             |
        | Morph           | `2818`    | Not returned by the live chainlist                                             |
        | Robinhood Chain | `4663`    | Not returned by the live chainlist                                             |
        | Ronin           | `2020`    | Not returned by the live chainlist                                             |
        | Scroll          | `534352`  | Removed from Etherscan V2 on 2026-04-16; api.scrollscan.com no longer resolves |
        | Sophon          | `50104`   | Not returned by the live chainlist                                             |
        | Superseed       | `5330`    | Not returned by the live chainlist                                             |
        | ZKsync Era      | `324`     | Not currently supported by Etherscan V2                                        |
        | Zora            | `7777777` | Not returned by the live chainlist                                             |
        
        Testnets are outside this skill's target list. If the user asks about any non-target chain, ask them to file a feature
        request in <https://github.com/PaulRBerg/agent-skills>.
        
      • target-fallback-rpcs.json 7 KB
        {
          "chains": [
            {
              "chainName": "Abstract",
              "chainId": 2741,
              "fallbackPublicRpcs": ["https://abstract.drpc.org", "https://2741.rpc.thirdweb.com"]
            },
            {
              "chainName": "Arbitrum",
              "chainId": 42161,
              "fallbackPublicRpcs": [
                "https://arbitrum-one-rpc.publicnode.com",
                "https://42161.rpc.thirdweb.com",
                "https://rpcfree.com/arbitrum-rpc"
              ]
            },
            {
              "chainName": "Arbitrum Nova",
              "chainId": 42170,
              "fallbackPublicRpcs": [
                "https://arbitrum-nova-rpc.publicnode.com",
                "https://42170.rpc.thirdweb.com",
                "https://arbitrum-nova.drpc.org"
              ]
            },
            {
              "chainName": "Avalanche",
              "chainId": 43114,
              "fallbackPublicRpcs": ["https://avalanche-c-chain-rpc.publicnode.com", "https://43114.rpc.thirdweb.com"]
            },
            {
              "chainName": "Base",
              "chainId": 8453,
              "fallbackPublicRpcs": [
                "https://base-rpc.publicnode.com",
                "https://base.gateway.tenderly.co",
                "https://developer-access-mainnet.base.org"
              ]
            },
            {
              "chainName": "Berachain",
              "chainId": 80094,
              "fallbackPublicRpcs": [
                "https://berachain-rpc.publicnode.com",
                "https://80094.rpc.thirdweb.com",
                "https://rpc.berachain-apis.com"
              ]
            },
            {
              "chainName": "Blast",
              "chainId": 81457,
              "fallbackPublicRpcs": [
                "https://blast-rpc.publicnode.com",
                "https://blast.drpc.org",
                "https://81457.rpc.thirdweb.com"
              ]
            },
            {
              "chainName": "BNB Chain",
              "chainId": 56,
              "fallbackPublicRpcs": ["https://bsc-rpc.publicnode.com", "https://bsc.drpc.org", "https://56.rpc.thirdweb.com"]
            },
            {
              "chainName": "Celo",
              "chainId": 42220,
              "fallbackPublicRpcs": [
                "https://celo.drpc.org",
                "https://celo-rpc.publicnode.com",
                "https://42220.rpc.thirdweb.com"
              ]
            },
            {
              "chainName": "Chiliz",
              "chainId": 88888,
              "fallbackPublicRpcs": [
                "https://chiliz.publicnode.com",
                "https://88888.rpc.thirdweb.com",
                "https://rpc.ankr.com/chiliz"
              ]
            },
            {
              "chainName": "Core Dao",
              "chainId": 1116,
              "fallbackPublicRpcs": [
                "https://core.drpc.org",
                "https://1116.rpc.thirdweb.com",
                "https://rpc-core.icecreamswap.com"
              ]
            },
            {
              "chainName": "Ethereum",
              "chainId": 1,
              "fallbackPublicRpcs": ["https://eth.drpc.org", "https://rpc.flashbots.net", "https://1.rpc.thirdweb.com"]
            },
            {
              "chainName": "Fantom",
              "chainId": 250,
              "fallbackPublicRpcs": ["https://rpc2.fantom.network"]
            },
            {
              "chainName": "Filecoin",
              "chainId": 314,
              "fallbackPublicRpcs": ["https://314.rpc.thirdweb.com"]
            },
            {
              "chainName": "Fraxtal",
              "chainId": 252,
              "fallbackPublicRpcs": [
                "https://fraxtal-rpc.publicnode.com",
                "https://fraxtal.drpc.org",
                "https://fraxtal.gateway.tenderly.co"
              ]
            },
            {
              "chainName": "Gnosis",
              "chainId": 100,
              "fallbackPublicRpcs": [
                "https://gnosis-rpc.publicnode.com",
                "https://gnosis.drpc.org",
                "https://100.rpc.thirdweb.com"
              ]
            },
            {
              "chainName": "HyperEVM",
              "chainId": 999,
              "fallbackPublicRpcs": [
                "https://hyperliquid.drpc.org",
                "https://999.rpc.thirdweb.com",
                "https://gwan-ssl.wandevs.org:46891"
              ]
            },
            {
              "chainName": "IoTeX",
              "chainId": 4689,
              "fallbackPublicRpcs": ["https://4689.rpc.thirdweb.com"]
            },
            {
              "chainName": "Lightlink",
              "chainId": 1890,
              "fallbackPublicRpcs": ["https://1890.rpc.thirdweb.com"]
            },
            {
              "chainName": "Linea",
              "chainId": 59144,
              "fallbackPublicRpcs": ["https://linea-rpc.publicnode.com", "https://59144.rpc.thirdweb.com"]
            },
            {
              "chainName": "Mode",
              "chainId": 34443,
              "fallbackPublicRpcs": ["https://mode.drpc.org", "https://34443.rpc.thirdweb.com"]
            },
            {
              "chainName": "Monad",
              "chainId": 143,
              "fallbackPublicRpcs": ["https://monad.drpc.org", "https://143.rpc.thirdweb.com"]
            },
            {
              "chainName": "Morph",
              "chainId": 2818,
              "fallbackPublicRpcs": [
                "https://morph.drpc.org",
                "https://2818.rpc.thirdweb.com",
                "https://rpc-quicknode.morphl2.io"
              ]
            },
            {
              "chainName": "Optimism",
              "chainId": 10,
              "fallbackPublicRpcs": [
                "https://optimism-rpc.publicnode.com",
                "https://optimism.drpc.org",
                "https://10.rpc.thirdweb.com"
              ]
            },
            {
              "chainName": "Polygon",
              "chainId": 137,
              "fallbackPublicRpcs": [
                "https://polygon.drpc.org",
                "https://137.rpc.thirdweb.com",
                "https://rpc-mainnet.matic.quiknode.pro"
              ]
            },
            {
              "chainName": "Robinhood Chain",
              "chainId": 4663,
              "fallbackPublicRpcs": ["https://robinhoodchain.blockscout.com/api/eth-rpc"]
            },
            {
              "chainName": "Ronin",
              "chainId": 2020,
              "fallbackPublicRpcs": ["https://ronin.drpc.org", "https://2020.rpc.thirdweb.com"]
            },
            {
              "chainName": "Scroll",
              "chainId": 534352,
              "fallbackPublicRpcs": ["https://scroll.drpc.org", "https://534352.rpc.thirdweb.com"]
            },
            {
              "chainName": "Sei",
              "chainId": 1329,
              "fallbackPublicRpcs": ["https://sei.drpc.org", "https://1329.rpc.thirdweb.com"]
            },
            {
              "chainName": "Sonic",
              "chainId": 146,
              "fallbackPublicRpcs": [
                "https://sonic-rpc.publicnode.com",
                "https://sonic.drpc.org",
                "https://146.rpc.thirdweb.com"
              ]
            },
            {
              "chainName": "Sophon",
              "chainId": 50104,
              "fallbackPublicRpcs": ["https://50104.rpc.thirdweb.com"]
            },
            {
              "chainName": "Superseed",
              "chainId": 5330,
              "fallbackPublicRpcs": ["https://superseed.drpc.org", "https://5330.rpc.thirdweb.com"]
            },
            {
              "chainName": "Taiko",
              "chainId": 167000,
              "fallbackPublicRpcs": [
                "https://taiko-rpc.publicnode.com",
                "https://taiko.drpc.org",
                "https://167000.rpc.thirdweb.com"
              ]
            },
            {
              "chainName": "Unichain",
              "chainId": 130,
              "fallbackPublicRpcs": ["https://unichain-rpc.publicnode.com", "https://130.rpc.thirdweb.com"]
            },
            {
              "chainName": "World Chain",
              "chainId": 480,
              "fallbackPublicRpcs": [
                "https://worldchain.drpc.org",
                "https://480.rpc.thirdweb.com",
                "https://worldchain-mainnet.gateway.tenderly.co"
              ]
            },
            {
              "chainName": "XDC",
              "chainId": 50,
              "fallbackPublicRpcs": ["https://50.rpc.thirdweb.com", "https://erpc.xdcrpc.com", "https://rpc.xdc.org"]
            },
            {
              "chainName": "ZKsync Era",
              "chainId": 324,
              "fallbackPublicRpcs": ["https://zksync.drpc.org", "https://324.rpc.thirdweb.com"]
            },
            {
              "chainName": "Zora",
              "chainId": 7777777,
              "fallbackPublicRpcs": ["https://7777777.rpc.thirdweb.com", "https://rpc.zora.energy"]
            }
          ]
        }
        
      • target-mainnets.json 17.5 KB
        {
          "chains": [
            {
              "chainName": "Abstract",
              "chainId": 2741,
              "slug": "abstract",
              "category": "zk",
              "accountActivityModel": "native-account-abstraction",
              "primaryPublicRpc": "https://api.mainnet.abs.xyz",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://abscan.org",
              "explorerAddressUrl": "https://abscan.org/address/{address}",
              "explorerTxUrl": "https://abscan.org/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Arbitrum",
              "chainId": 42161,
              "slug": "arbitrum",
              "category": "nitro",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://arb1.arbitrum.io/rpc",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://arbiscan.io",
              "explorerAddressUrl": "https://arbiscan.io/address/{address}",
              "explorerTxUrl": "https://arbiscan.io/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Arbitrum Nova",
              "chainId": 42170,
              "slug": "arbitrum-nova",
              "category": "nitro",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://nova.arbitrum.io/rpc",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://arbitrum-nova.blockscout.com",
              "explorerAddressUrl": "https://arbitrum-nova.blockscout.com/address/{address}",
              "explorerTxUrl": "https://arbitrum-nova.blockscout.com/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Avalanche",
              "chainId": 43114,
              "slug": "avalanche",
              "category": "alt-l1",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://api.avax.network/ext/bc/C/rpc",
              "nativeCurrencySymbol": "AVAX",
              "explorerUrl": "https://snowscan.xyz",
              "explorerAddressUrl": "https://snowscan.xyz/address/{address}",
              "explorerTxUrl": "https://snowscan.xyz/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Base",
              "chainId": 8453,
              "slug": "base",
              "category": "op-stack",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://mainnet.base.org",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://basescan.org",
              "explorerAddressUrl": "https://basescan.org/address/{address}",
              "explorerTxUrl": "https://basescan.org/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Berachain",
              "chainId": 80094,
              "slug": "berachain",
              "category": "alt-l1",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.berachain.com",
              "nativeCurrencySymbol": "BERA",
              "explorerUrl": "https://berascan.com",
              "explorerAddressUrl": "https://berascan.com/address/{address}",
              "explorerTxUrl": "https://berascan.com/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Blast",
              "chainId": 81457,
              "slug": "blast",
              "category": "op-stack",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.blast.io",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://blastscan.io",
              "explorerAddressUrl": "https://blastscan.io/address/{address}",
              "explorerTxUrl": "https://blastscan.io/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "BNB Chain",
              "chainId": 56,
              "slug": "bsc",
              "category": "alt-l1",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://bsc-dataseed1.bnbchain.org",
              "nativeCurrencySymbol": "BNB",
              "explorerUrl": "https://bscscan.com",
              "explorerAddressUrl": "https://bscscan.com/address/{address}",
              "explorerTxUrl": "https://bscscan.com/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Celo",
              "chainId": 42220,
              "slug": "celo",
              "category": "op-stack",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://forno.celo.org",
              "nativeCurrencySymbol": "CELO",
              "explorerUrl": "https://celoscan.io",
              "explorerAddressUrl": "https://celoscan.io/address/{address}",
              "explorerTxUrl": "https://celoscan.io/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Chiliz",
              "chainId": 88888,
              "slug": "chiliz",
              "category": "alt-l1",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.chiliz.com",
              "nativeCurrencySymbol": "CHZ",
              "explorerUrl": "https://chiliscan.com",
              "explorerAddressUrl": "https://chiliscan.com/address/{address}",
              "explorerTxUrl": "https://chiliscan.com/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Core Dao",
              "chainId": 1116,
              "slug": "core-dao",
              "category": "alt-l1",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.coredao.org",
              "nativeCurrencySymbol": "CORE",
              "explorerUrl": "https://scan.coredao.org",
              "explorerAddressUrl": "https://scan.coredao.org/address/{address}",
              "explorerTxUrl": "https://scan.coredao.org/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Ethereum",
              "chainId": 1,
              "slug": "mainnet",
              "category": "mainnet",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://ethereum-rpc.publicnode.com",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://etherscan.io",
              "explorerAddressUrl": "https://etherscan.io/address/{address}",
              "explorerTxUrl": "https://etherscan.io/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Fantom",
              "chainId": 250,
              "slug": "fantom",
              "category": "alt-l1",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.fantom.network",
              "nativeCurrencySymbol": "FTM",
              "explorerUrl": "https://explorer.fantom.network",
              "explorerAddressUrl": "https://explorer.fantom.network/address/{address}",
              "explorerTxUrl": "https://explorer.fantom.network/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Filecoin",
              "chainId": 314,
              "slug": "filecoin",
              "category": "alt-l1",
              "accountActivityModel": "cross-vm",
              "primaryPublicRpc": "https://api.node.glif.io/rpc/v1",
              "nativeCurrencySymbol": "FIL",
              "explorerUrl": "https://filecoin.blockscout.com",
              "explorerAddressUrl": "https://filecoin.blockscout.com/address/{address}",
              "explorerTxUrl": "https://filecoin.blockscout.com/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Fraxtal",
              "chainId": 252,
              "slug": "fraxtal",
              "category": "op-stack",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.frax.com",
              "nativeCurrencySymbol": "FRAX",
              "explorerUrl": "https://fraxscan.com",
              "explorerAddressUrl": "https://fraxscan.com/address/{address}",
              "explorerTxUrl": "https://fraxscan.com/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Gnosis",
              "chainId": 100,
              "slug": "gnosis",
              "category": "alt-l1",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.gnosischain.com",
              "nativeCurrencySymbol": "xDAI",
              "explorerUrl": "https://gnosisscan.io",
              "explorerAddressUrl": "https://gnosisscan.io/address/{address}",
              "explorerTxUrl": "https://gnosisscan.io/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "HyperEVM",
              "chainId": 999,
              "slug": "hyperevm",
              "category": "alt-l1",
              "accountActivityModel": "cross-vm",
              "primaryPublicRpc": "https://rpc.hyperliquid.xyz/evm",
              "nativeCurrencySymbol": "HYPE",
              "explorerUrl": "https://hyperevmscan.io",
              "explorerAddressUrl": "https://hyperevmscan.io/address/{address}",
              "explorerTxUrl": "https://hyperevmscan.io/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "IoTeX",
              "chainId": 4689,
              "slug": "iotex",
              "category": "alt-l1",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://babel-api.mainnet.iotex.io",
              "nativeCurrencySymbol": "IOTX",
              "explorerUrl": "https://iotexscan.io",
              "explorerAddressUrl": "https://iotexscan.io/address/{address}",
              "explorerTxUrl": "https://iotexscan.io/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Lightlink",
              "chainId": 1890,
              "slug": "lightlink",
              "category": "alt-l2",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://replicator.phoenix.lightlink.io/rpc/v1",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://phoenix.lightlink.io",
              "explorerAddressUrl": "https://phoenix.lightlink.io/address/{address}",
              "explorerTxUrl": "https://phoenix.lightlink.io/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Linea",
              "chainId": 59144,
              "slug": "linea",
              "category": "zk",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.linea.build",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://lineascan.build",
              "explorerAddressUrl": "https://lineascan.build/address/{address}",
              "explorerTxUrl": "https://lineascan.build/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Mode",
              "chainId": 34443,
              "slug": "mode",
              "category": "op-stack",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://mainnet.mode.network",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://explorer.mode.network",
              "explorerAddressUrl": "https://explorer.mode.network/address/{address}",
              "explorerTxUrl": "https://explorer.mode.network/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Monad",
              "chainId": 143,
              "slug": "monad",
              "category": "alt-l1",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.monad.xyz",
              "nativeCurrencySymbol": "MON",
              "explorerUrl": "https://monadscan.com",
              "explorerAddressUrl": "https://monadscan.com/address/{address}",
              "explorerTxUrl": "https://monadscan.com/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Morph",
              "chainId": 2818,
              "slug": "morph",
              "category": "zk",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.morphl2.io",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://explorer.morph.network",
              "explorerAddressUrl": "https://explorer.morph.network/address/{address}",
              "explorerApiUrl": "https://explorer-api.morph.network/api",
              "explorerTxUrl": "https://explorer.morph.network/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Optimism",
              "chainId": 10,
              "slug": "optimism",
              "category": "op-stack",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://mainnet.optimism.io",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://optimistic.etherscan.io",
              "explorerAddressUrl": "https://optimistic.etherscan.io/address/{address}",
              "explorerTxUrl": "https://optimistic.etherscan.io/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Polygon",
              "chainId": 137,
              "slug": "polygon",
              "category": "alt-l1",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://polygon-bor-rpc.publicnode.com",
              "nativeCurrencySymbol": "POL",
              "explorerUrl": "https://polygonscan.com",
              "explorerAddressUrl": "https://polygonscan.com/address/{address}",
              "explorerTxUrl": "https://polygonscan.com/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Robinhood Chain",
              "chainId": 4663,
              "slug": "robinhood",
              "category": "nitro",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.mainnet.chain.robinhood.com",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://robinhoodchain.blockscout.com",
              "explorerAddressUrl": "https://robinhoodchain.blockscout.com/address/{address}",
              "explorerTxUrl": "https://robinhoodchain.blockscout.com/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Ronin",
              "chainId": 2020,
              "slug": "ronin",
              "category": "op-stack",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://api.roninchain.com/rpc",
              "nativeCurrencySymbol": "RON",
              "explorerUrl": "https://app.roninchain.com",
              "explorerAddressUrl": "https://app.roninchain.com/address/{address}",
              "explorerTxUrl": "https://app.roninchain.com/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Scroll",
              "chainId": 534352,
              "slug": "scroll",
              "category": "zk",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.scroll.io",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://scrollscan.com",
              "explorerAddressUrl": "https://scrollscan.com/address/{address}",
              "explorerTxUrl": "https://scrollscan.com/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Sei",
              "chainId": 1329,
              "slug": "sei",
              "category": "alt-l1",
              "accountActivityModel": "cross-vm",
              "primaryPublicRpc": "https://evm-rpc.sei-apis.com",
              "nativeCurrencySymbol": "SEI",
              "explorerUrl": "https://seiscan.io",
              "explorerAddressUrl": "https://seiscan.io/address/{address}",
              "explorerTxUrl": "https://seiscan.io/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Sonic",
              "chainId": 146,
              "slug": "sonic",
              "category": "alt-l1",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.soniclabs.com",
              "nativeCurrencySymbol": "S",
              "explorerUrl": "https://sonicscan.org",
              "explorerAddressUrl": "https://sonicscan.org/address/{address}",
              "explorerTxUrl": "https://sonicscan.org/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Sophon",
              "chainId": 50104,
              "slug": "sophon",
              "category": "zk",
              "accountActivityModel": "native-account-abstraction",
              "primaryPublicRpc": "https://rpc.sophon.xyz",
              "nativeCurrencySymbol": "SOPH",
              "explorerUrl": "https://explorer.sophon.xyz",
              "explorerAddressUrl": "https://explorer.sophon.xyz/address/{address}",
              "explorerTxUrl": "https://explorer.sophon.xyz/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Superseed",
              "chainId": 5330,
              "slug": "superseed",
              "category": "op-stack",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://mainnet.superseed.xyz",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://explorer.superseed.xyz",
              "explorerAddressUrl": "https://explorer.superseed.xyz/address/{address}",
              "explorerTxUrl": "https://explorer.superseed.xyz/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Taiko",
              "chainId": 167000,
              "slug": "taiko",
              "category": "zk",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.mainnet.taiko.xyz",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://taikoscan.io",
              "explorerAddressUrl": "https://taikoscan.io/address/{address}",
              "explorerTxUrl": "https://taikoscan.io/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Unichain",
              "chainId": 130,
              "slug": "unichain",
              "category": "op-stack",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://mainnet.unichain.org",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://uniscan.xyz",
              "explorerAddressUrl": "https://uniscan.xyz/address/{address}",
              "explorerTxUrl": "https://uniscan.xyz/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "World Chain",
              "chainId": 480,
              "slug": "world-chain",
              "category": "op-stack",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://worldchain-mainnet.g.alchemy.com/public",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://worldscan.org",
              "explorerAddressUrl": "https://worldscan.org/address/{address}",
              "explorerTxUrl": "https://worldscan.org/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "XDC",
              "chainId": 50,
              "slug": "xdc",
              "category": "alt-l1",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://rpc.xdcrpc.com",
              "nativeCurrencySymbol": "XDC",
              "explorerUrl": "https://xdcscan.com",
              "explorerAddressUrl": "https://xdcscan.com/address/{address}",
              "explorerTxUrl": "https://xdcscan.com/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "ZKsync Era",
              "chainId": 324,
              "slug": "zksync",
              "category": "zk",
              "accountActivityModel": "native-account-abstraction",
              "primaryPublicRpc": "https://mainnet.era.zksync.io",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://explorer.zksync.io",
              "explorerAddressUrl": "https://explorer.zksync.io/address/{address}",
              "explorerTxUrl": "https://explorer.zksync.io/tx/{tx_hash}",
              "routeMesh": true
            },
            {
              "chainName": "Zora",
              "chainId": 7777777,
              "slug": "zora",
              "category": "op-stack",
              "accountActivityModel": "ethereum-eoa",
              "primaryPublicRpc": "https://zora.drpc.org",
              "nativeCurrencySymbol": "ETH",
              "explorerUrl": "https://explorer.zora.energy",
              "explorerAddressUrl": "https://app.zerion.io/{address}/history?chain=zora",
              "explorerTxUrl": "https://explorer.zora.energy/tx/{tx_hash}",
              "routeMesh": true
            }
          ]
        }
        
    • workflows
      • address-sweeps.md 6.7 KB
        # Address Sweeps
        
        Use for address-wide historical activity and bootstrap discovery on target mainnets. The deterministic request/evidence
        rules live in `scripts/sweep-core.py`; this reference defines safety, agent decisions, and coverage interpretation.
        
        ## Safety Model
        
        - Accept only a 20-byte public EVM address. Never read or expose keys, mnemonics, or credentials.
        - Query only rows in `references/generated/target-mainnets.json`. A `cross-vm` result covers that chain's EVM execution
          environment only.
        - Fix one ISO-8601 UTC cutoff and one finalized or independently verified block per target chain. Record requested
          cutoff, resolution kind, chain ID, block number/hash/timestamp, and observation time. The checkpoint block must be at
          or before the cutoff. Reuse its exact hash and height for state and indexed history.
        - Never turn `latest`, an explorer page count, a provider error, an unbound zero counter, or a lagging index into a
          negative result.
        - Keep credentials out of plan/response artifacts. Supply them only to the agent-selected read-only provider command.
        
        If a checkpoint cannot be established, or required indexed channels cannot cover it, the result is unknown/partial.
        
        ## Profiles
        
        Name the profile before planning:
        
        - `general`: normal transactions, internal transactions, ERC-20, ERC-721, and ERC-1155 transfers, plus checkpointed
          nonce/native balance facts. This is the default historical-activity profile.
        - `bootstrap-discovery`: a narrower activity-discovery profile. It includes checkpointed native state, qualifying
          normal/internal native activity, ERC-20, and ERC-721; ERC-1155 and nonqualifying native noise are outside it.
        
        The helper owns the exact success/noise predicates. Do not silently apply bootstrap exclusions to a general sweep. A
        `bootstrap-discovery` negative means only “no activity qualifying under the `bootstrap-discovery` profile was found.”
        
        For the `bootstrap-discovery` profile, the helper may omit normal/internal history only when checkpointed nonce and
        balance are both zero and `accountActivityModel` is exactly `ethereum-eoa`. It default-denies this shortcut for missing,
        unrecognized, native-account-abstraction, cross-VM, or unknown models. Token history remains required.
        
        ## Agent Decisions Before Planning
        
        The agent owns:
        
        - target-chain selection and whether to stop after the first positive;
        - cutoff acquisition and checkpoint proof;
        - provider selection and fallback activation using `provider-routing.md`;
        - declared provider capabilities and independence groups;
        - optional quorum requirement;
        - final observed-fact versus inference wording.
        
        Prefer Blockscout on declared overlaps; use Etherscan where it is primary or after a concrete fallback trigger. A valid
        empty authoritative response does not trigger fallback. Run the existing plan-detection helpers before using paid
        Etherscan or Blockscout PRO capabilities. Quorum counts independent indexers, not hosts or state RPCs; provider
        disagreement is unknown, never a majority decision.
        
        ## Plan Interface
        
        Create an agent-selected input JSON with:
        
        - `address`;
        - `chain`: at least `id`, display name, and `accountActivityModel`;
        - `goal`: `historical-activity` or `bootstrap-discovery`;
        - named `profile`;
        - `checkpoint`: `requestedAt`, `resolutionKind`, `blockNumber`, `blockHash`, `blockTimestamp`, `observedAt`;
        - `providers`: stable ID, provider kind, independent-index group, and supported history-channel names;
        - `quorum`: positive integer, default `1`.
        
        Then run:
        
        ```sh
        uv run scripts/sweep-core.py plan --input <spec.json> > <plan.json>
        ```
        
        The credential-free plan has `schemaVersion: 1`, normalized required channels, checkpoint-bound JSON-RPC state requests,
        bounded indexed-history request specifications, and quorum ordering requirements. It does not acquire a checkpoint,
        choose a provider, or construct credential-bearing URLs.
        
        Execute only the emitted requests through the selected provider's documented adapter. Save responses in this shape:
        
        ```json
        {
          "state": {
            "nonce": { "ok": true, "result": "0x0", "blockHash": "<checkpoint-hash>" },
            "native-balance": { "ok": true, "result": "0x0", "blockHash": "<checkpoint-hash>" }
          },
          "providers": {
            "<provider-id>": {
              "indexedThrough": 123,
              "channels": {
                "txlist": { "ok": true, "complete": true, "rows": [] }
              }
            }
          }
        }
        ```
        
        `complete: true` means bounded pagination for that channel is exhausted through the checkpoint. Preserve native provider
        row fields; do not preclassify them.
        
        ## Evaluate Interface
        
        ```sh
        uv run scripts/sweep-core.py evaluate \
          --plan <plan.json> --responses <responses.json> > <result.json>
        ```
        
        The evaluator validates checkpoint binding, quantities, index bounds, response shapes, required channels, profile
        predicates, EOA zero-state eligibility, earliest qualifying evidence, and quorum agreement. It returns
        `positive|negative|unknown`, `checked`, `omitted`, explicit `gaps`, coverage, earliest evidence, and provider/quorum
        facts. Malformed external responses become coverage gaps when they can be isolated; malformed plan input fails.
        
        For offline conformance checks, use:
        
        ```sh
        uv run scripts/sweep-core.py validate blockscout-address-counters < response.json
        uv run scripts/sweep-core.py validate etherscan-transfer-topics --address <address> < response.json
        ```
        
        The existing shell validator commands remain output-compatible adapters.
        
        ## Coverage and Reporting
        
        A positive needs qualifying checkpoint-bounded evidence; it may still have partial channel coverage. A negative is valid
        only when every profile channel is checkpoint-bound and complete or appears in `omitted` under the named safe invariant.
        Missing channels, lag, malformed responses, provider disagreement, and unsupported actions make the result
        unknown/partial.
        
        For yes/no requests, lead with the result and first qualifying chain/action. For reports, include chain, checkpoint,
        profile, activity result, checked/omitted channels, coverage, provider, fallback, and quorum. Express checkpoints as at
        least `chain_id:block_number:block_hash` plus cutoff time.
        
        Do not claim inactivity on all EVM chains unless the exact target scope and general profile are complete. For
        `cross-vm`, append “in the EVM execution environment.” Keep observed provider facts separate from inference.
        
        ## Current Holdings
        
        For current native/token holdings, use `debank-portfolio.md` first (`blockscan-balances.md` for a named chain or DeBank
        gaps) and `provider-routing.md` for NFTs and API gaps. Pin native state to a finalized/verified checkpoint. Treat
        holdings endpoints observed at a provider head as separately timed evidence, and never infer token/NFT emptiness from
        native RPC alone. Indexed token amounts can be stale: confirm any amount that decides an action with RPC `balanceOf`, as
        `address-usd-value.md` does.
        
      • address-usd-value.md 8.8 KB
        # Address USD Value
        
        Use this reference for the current USD value of one or more public EVM addresses per target chain and in total: native
        balances plus fungible ERC-20 tokens. NFTs and historical values are out of scope; route them to
        `references/workflows/provider-routing.md`. DeFi positions stay out of every total; list them separately from
        `references/workflows/debank-portfolio.md` when requested. All steps are read-only. Callers own any threshold (for
        example, a "drained" or dust cutoff) and any token allow/deny policy; this workflow only returns values and coverage.
        
        ## Scope
        
        - Validate every address (20-byte hex). Use `<addr>` placeholders in saved specs and examples.
        - Check native balances on every row of `references/generated/target-mainnets.json`; native reads are cheap. A caller
          may narrow token lookups to a chain subset; report unchecked chains as out of the requested token scope, not as zero.
        - For `cross-vm` rows, scope values to the chain's EVM execution environment.
        - Before keyed API calls, check presence value-free: `[ -n "$BLOCKSCOUT_API_KEY" ] && echo set || echo unset`. Without
          the key, skip the Blockscout discovery route and fall through to DeBank.
        
        ## Native Balances
        
        1. Per chain, pin one block: `routemesh rpc <chainId> eth_getBlockByNumber --params='["finalized",false]'` and record
           its tag, number, hash, and timestamp. Use `"latest"` when the chain rejects `finalized` or returns block `0x0` for
           it, and say so: Chiliz (`88888`) nodes return genesis for both `finalized` and `safe` on RouteMesh and every listed
           public RPC, including `rpc.chiliz.com` (verified 2026-10-01). For a post-transfer check, when the finalized head
           predates the caller's verified receipt block, use a canonical `"latest"` checkpoint at or after that receipt block;
           record the reason and label it unfinalized. Never use pre-transfer state as the post-transfer balance.
        2. Send one `routemesh rpc <chainId> --json -` batch with an `eth_getBalance` request per address, each using the
           EIP-1898 `{ "blockHash": "<hash>", "requireCanonical": true }` selector. Apply the numeric-block fallback and
           same-endpoint block-identity checks from `provider-routing.md` when a provider rejects that selector.
        3. For a row without RouteMesh HTTP coverage, or after a RouteMesh coverage failure, follow the `primaryPublicRpc` then
           `references/generated/target-fallback-rpcs.json` order in `provider-routing.md`. A failed native read is a gap, never
           zero.
        4. Convert wei to native units with fixed-precision decimal arithmetic (`bc` with `scale=18`, or Python `decimal`).
           Never use binary floats for amounts, prices, or products.
        
        ## Prices
        
        - Price natives with one `cg price --ids <id,...> -o json` call covering every distinct native asset (ETH-native chains
          share one ID). Resolve unknown CoinGecko IDs once with `cg search <symbol-or-name> -o json`; never treat a symbol as
          unique.
        - Price each confirmed token holding by contract from CoinGecko. Resolve each chain's platform once from
          `https://api.coingecko.com/api/v3/asset_platforms`, matching `chain_identifier` to the chain ID. Map contracts to coin
          IDs with one keyless `https://api.coingecko.com/api/v3/coins/list?include_platform=true` download (match the platform
          and lowercased contract), then price every mapped ID in one `cg price --ids <id,...> -o json` call.
        - For a contract the coin list does not map, request
          `https://api.coingecko.com/api/v3/simple/token_price/<platform>?contract_addresses=<contract>&vs_currencies=usd`. The
          keyless endpoint accepts one contract per request, so query only holdings with a nonzero confirmed balance and pace
          requests. It can also quote contracts CoinGecko does not list (`coins/<platform>/contract/<contract>` returns
          `coin not found`), including a dead token quoted at hundreds of dollars; treat such a quote as unlisted, count the
          token as unpriced, and report the quote separately.
        - Use an indexer price (Blockscout `exchange_rate` on `addresses/<addr>` for natives or on a token holding, a DeBank or
          Blockscan row price) only when CoinGecko omits the native asset, has no platform for the chain, or has no price for
          the contract. Label that price with its source and apply Pricing Hygiene; Blockscout has priced tokens it lists with a
          zero market cap.
        - Record each price's source and the UTC observation time.
        
        ## Fungible Tokens
        
        Indexed holdings can be stale (a Blockscout list has shown a USDT balance whose on-chain `balanceOf` was zero), so
        indexers only discover token contracts: amounts come from RPC and prices from CoinGecko. Per chain and address, take the
        union of these discovery sources:
        
        1. **Caller candidates.** Always include contracts the caller supplies, such as tokens from its own transfer history.
        2. **Blockscout.** For targets the keyed gateway serves (see `references/generated/blockscout-chains.md` and
           `references/explorers/blockscout-api.md` for the per-instance exception), page
           `https://api.blockscout.com/<chainId>/api/v2/addresses/<addr>/tokens?type=ERC-20` until `next_page_params` is `null`.
           Keep each holding's `token.address_hash` and `decimals`, plus `exchange_rate` as a fallback price. An HTTP `402`
           (plan-gated chain; see `references/explorers/blockscout-endpoints.md`) or other failure falls through to DeBank; do
           not retry a `402`.
        3. **DeBank.** For target chains Blockscout does not cover, gates, or fails on, run the collector per
           `references/workflows/debank-portfolio.md` (token discovery for one address or many; no `Show all` click). From each
           `ok` record take the token contracts per target chain ID (`chainId` is the `chain/list` `network_id`), and the price
           as a fallback, and record its `observedAt`. A `failed` record is a discovery gap for that address's DeBank chains.
        4. **Blockscan.** For remaining target chains DeBank lacks or fails on, use the Chromium flow in
           `references/workflows/blockscan-balances.md`: match chains by exact `data-chainid`, take each row's token contract
           (and price as a fallback) from `#js-chain-table`, and record `Last updated`.
        
        When no indexer lists a chain's tokens, report an ERC-20 discovery gap for that chain even if caller candidates were
        confirmed. Never assume zero tokens.
        
        Confirm every discovered holding on-chain: per chain, batch `eth_call` `balanceOf(<addr>)` (selector `0x70a08231`) for
        each token at the pinned block hash, through the same route as that chain's native reads. Add `decimals()`
        (`0x313ce567`) when discovery did not supply it. Use the RPC amount: USD value = `balanceOf / 10^decimals × price`. An
        RPC zero drops the holding; a failed or malformed confirmation is a coverage gap for that token, never the indexed
        amount.
        
        ## Bulk Mode
        
        For many addresses, run API passes first: native batches across all target chains, Blockscout token lists, one
        `balanceOf` confirmation batch per chain, then CoinGecko contract prices for confirmed holdings. Run the DeBank
        collector for the addresses that still have gap chains, in gated batches as `debank-portfolio.md` directs, then
        Blockscan one page at a time for what remains. Keep request concurrency at or below each provider's limit (Blockscout
        `x-ratelimit-limit`; CoinGecko plan quota); back off on `429` as the provider references direct. Never run unbounded
        parallel requests.
        
        ## Pricing Hygiene
        
        - A token without a provider price contributes `$0` and is listed as unpriced with its amount and contract.
        - Flag a priced token as suspicious when its price looks spoofed: an unverified or unknown contract using a major symbol
          or name, a value implausible for its market (for example, exceeding its market cap or liquidity), or a price that
          disagrees sharply with CoinGecko for the same asset. List suspicious tokens separately and exclude them from every
          total.
        
        ## Nil and Totals
        
        - A chain is `nil` when its native balance is exactly zero and it has no confirmed, priced, non-suspicious token
          holdings. A chain with a native or ERC-20 coverage gap is never `nil`; report its value as a lower bound or unknown.
        - The address total sums chain values excluding suspicious tokens. It is `nil` only when every checked chain is `nil`
          and no gaps remain; with gaps, label it a lower bound.
        
        ## Output
        
        Lead each address with `### ⛓️ <addr> — <status word>`. Use one table row per chain with value:
        
        | Chain (ID) | Native (amount / USD) | Priced tokens (amount / USD) | Chain USD | Source | Block / checkpoint |
        | ---------- | --------------------- | ---------------------------- | --------- | ------ | ------------------ |
        
        Collapse `nil` chains into a count with their chain IDs. Then list the address total, unpriced tokens, suspicious
        tokens, `⚠️ Coverage gaps` (chain, channel, cause), and price source with UTC timestamp. Show native amounts at full
        precision without exponent notation. When a caller requests machine-readable output, emit the same fields as JSON with
        decimal strings for every amount and USD value.
        
      • blockscan-balances.md 3.6 KB
        # Blockscan Balances
        
        Use this reference for the current native or fungible-token balance of a public EVM wallet address on a named target
        chain. For a wallet-wide or cross-chain check, use `references/workflows/debank-portfolio.md` first; use Blockscan for
        target chains DeBank lacks, when DeBank fails, or as an Etherscan-family cross-check. Historical balances and NFT
        inventories remain on the existing provider routes. For per-chain and total USD value of one or more addresses, use
        `references/workflows/address-usd-value.md`.
        
        ## Chromium Workflow
        
        1. Validate the address, then resolve any named chain against `references/generated/target-mainnets.json`. Do not send a
           non-target chain to Blockscan.
        2. Open `https://blockscan.com/address/<addr>` with Chrome DevTools `new_page`, using Chromium rather than a direct
           Blockscan API or a general web search.
        3. Wait for the address page and `Token Holdings` portfolio to replace any initial `Just a moment...` challenge, then
           take a current accessibility snapshot.
        4. Use the chain card for its current portfolio value and `#js-chain-table` for token amount, price, and value. Select
           the requested chain before reading the table and paginate when the user requests complete holdings.
        5. Capture the page's `Last updated` value when present. Treat displayed amounts and fiat values as Blockscan's current,
           formatted portfolio data rather than exact raw-unit balances.
        
        Use Chrome DevTools `evaluate_script` when the accessibility snapshot does not expose a stable chain identifier. Match
        support by exact chain ID with:
        
        ```text
        input.address-transaction-chain[data-chainid="<chain-id>"]
        ```
        
        Its `data-search` value includes the Blockscan chain symbol. Use that symbol to locate the corresponding
        `.js-chain[data-chain="<symbol>"]` card; do not infer support from a similar display name.
        
        ## Coverage and Scope
        
        - For a named chain, the exact `data-chainid` match proves Blockscan currently offers that chain. A matching card with a
          zero token count or `$0.00` is a successful zero result, not a fallback condition.
        - For a wallet-wide fallback, intersect Blockscan's `data-chainid` values with the target chains still uncovered.
          Blockscan covers only Etherscan-family chains; query the existing API routes for target chains outside that
          intersection.
        - Ignore Blockscan chains outside the target-mainnet list. Do not report the page-wide `NET WORTH` as a target-only
          total because it can include those chains.
        - Keep successful Blockscan results when only some target chains or requested details require fallback.
        
        ## Fallbacks
        
        Use `references/workflows/provider-routing.md` for a named chain and `references/workflows/address-sweeps.md` for a
        wallet-wide check when:
        
        - Chrome DevTools MCP or Chromium is unavailable;
        - navigation fails, a challenge or error persists, the page is rate limited, or the required portfolio DOM is absent;
        - the resolved target chain ID is absent from Blockscan's supported-chain inputs; or
        - Blockscan cannot provide the exact/raw precision or current fungible-asset detail the user requested.
        
        Route historical balances and NFT inventory requests directly to those existing references. Report which Blockscan
        condition caused each fallback, then retain the existing Etherscan, Blockscout, and RPC order within the selected
        fallback reference.
        
        ## Output
        
        Return the resolved target chain names and IDs, current native or fungible-token amounts requested, Blockscan's
        displayed fiat values when relevant, freshness text, and the address-page URL. Identify fallback-derived facts by
        provider and separate them from Blockscan results.
        
      • blockscan-tx-lookup.md 3.7 KB
        # Blockscan Transaction Lookup
        
        Use this reference primarily to resolve an exact transaction hash whose chain is unknown, and return Blockscan's
        formatted status, parties, value, fee, and timestamp when available. A single unknown-chain lookup avoids probing each
        candidate chain's provider in turn. Blockscan's formatted summary is not raw transaction evidence.
        
        ## Route by Chain Context
        
        1. Validate the hash format (`0x` plus 64 hex chars).
        2. If the user supplied a chain, resolve it against `references/generated/target-mainnets.json`. Unless the user
           explicitly requested Blockscan as the evidence source, do not navigate to Blockscan; immediately use
           `references/workflows/provider-routing.md` for the transaction facts.
        3. For a known or suspected OP Mainnet transaction from before the final regenesis, apply
           `references/explorers/optimism-pre-regenesis.md` before requiring evidence from a current provider.
        4. Continue to the Chromium workflow only when the chain remains unknown or the user explicitly requested Blockscan.
        
        ## Chromium Workflow
        
        1. Open `https://blockscan.com/tx/<hash>` with Chrome DevTools `new_page`, using Chromium rather than a direct API or a
           general web search.
        2. Wait for the transaction heading to replace any initial `Just a moment...` challenge, then take a current
           accessibility snapshot.
        3. Identify the chain from the `<h1>` heading (`<Chain> Transaction Details`) and the `View on <Explorer>` link's
           `href`. Cross-check that `href`'s host against `explorerUrl` in `references/generated/target-mainnets.json` for the
           authoritative chain ID and name — the heading text and chain-logo image alone are display hints, not proof.
        4. If the resolved chain has no matching target-mainnet row, stop: this is out of scope per `SKILL.md`. Do not serve
           data for it and do not fall back to another provider to work around scope.
        5. Read status (success/failed/pending), timestamp, `from`/`to`, value, and fee directly from the page for the
           human-readable answer.
        
        ## Coverage and Scope
        
        - A rendered `<Chain> Transaction Details` page proves Blockscan currently indexes that hash on that chain.
        - `Error 404` (`Seems we got lost!`) is not proof the hash doesn't exist. Blockscan aggregates the Etherscan-family
          (`*scan`) explorers only, so a real transaction on a chain Blockscan doesn't cover, or one not yet indexed, also 404s.
          Never report "transaction not found" from a 404 alone.
        - Blockscan's summary fields are formatted/display data, not a substitute for an exact raw receipt.
        
        ## Fallbacks
        
        After Blockscan resolves the chain, use `references/workflows/provider-routing.md` when:
        
        - the request needs the full raw receipt, logs, internal transactions, decoded input, or another detail beyond
          Blockscan's summary.
        
        If the chain is unknown and Blockscan 404s or is unavailable, report the chain as unresolved and stop. Do not infer that
        the transaction does not exist or sweep every target chain's provider. Ask for a candidate chain or approval for that
        broader provider sweep.
        
        If the user explicitly requested Blockscan for a known chain, attempt it and report its coverage result, including when
        Chromium is unavailable, navigation fails, a challenge persists, the page is rate limited, or it 404s. Do not silently
        substitute another evidence source; identify any separately requested fallback facts by provider.
        
        ## Output
        
        Always return the transaction hash, resolved target chain name and ID when known, selected provider route, requested
        facts, and evidence source. Include the Blockscan URL and native-explorer URL surfaced by `View on <Explorer>` only when
        Blockscan was queried. Otherwise, report the provider route and its evidence without implying Blockscan confirmation.
        
      • debank-portfolio.md 12.2 KB
        # DeBank Portfolio
        
        Use this reference for the wallet-wide current holdings of a public EVM address across target chains: native and
        fungible-token balances plus DeFi protocol positions (liquidity, lending, staking, vesting). DeBank covers more target
        chains than Blockscan, including non-Etherscan chains. For one named chain, use
        `references/workflows/blockscan-balances.md`. Historical balances, NFT inventories, and transaction history stay on
        `references/workflows/provider-routing.md`.
        
        ## Global Queue
        
        DeBank's WAF limits the whole browser, so every agent on this host shares one allowance and one agent's burst blocks the
        rest. Hold a lease from `scripts/debank-gate.py` for all debank.com work: navigating, pasting the collector, `start`,
        and DOM reads. Leases are granted one at a time in FIFO order; state lives under
        `${XDG_STATE_HOME:-~/.local/state}/evm-atlas/debank-gate`.
        
        <!-- prettier-ignore -->
        ```sh
        python3 scripts/debank-gate.py acquire --label '<skill>: <purpose>' --profiles <n>
        ```
        
        - Each command prints one JSON line. Exit 0 with `"status": "granted"` means proceed and keep the `ticket`. Exit 3 with
          `"status": "queued"` (after `--wait`, default 240 s) reports `position`, `holder`, and `cooldownUntil`: rerun
          `acquire` with `--ticket <ticket>` to keep the place. A ticket not polled for 120 s is dropped. In Claude Code, run
          `acquire --wait 3600` in the background and continue when it exits.
        - `--profiles` is the number of addresses the lease covers, at most 25. Bulk work takes one lease per batch, so other
          agents' single-address checks get a turn between batches.
        - A lease expires after `--ttl` (600 s). Run `renew --ticket <ticket>` before then for longer work. Exit 4 (`"lost"`)
          means it expired and another agent may hold the gate: stop touching DeBank and acquire again.
        - When done, or on any failure, close the owned DeBank page, then run `release --ticket <ticket>`.
        - On a WAF block, run `block --ticket <ticket>`: it releases the lease and pauses the queue for every agent for 15
          minutes.
        - `status` shows the holder, cooldown, and queue. Never touch debank.com without a lease, even when the queue is long. A
          caller that cannot wait uses Fallbacks and reports the queue wait as the cause.
        
        ## Chromium Workflow
        
        1. With a lease held, validate each address (20-byte hex), then open an owned page on
           `https://debank.com/profile/<addr>` with Chrome DevTools `new_page`. When the cookie dialog appears, choose `Reject`.
        2. Never call `api.debank.com` balance endpoints yourself (DeBank's Cloud OpenAPI is paid). The profile page's calls are
           `fetch` GETs signed by the app (`x-api-*` headers): unsigned calls, even from inside the page, return
           `429 Request too fast`, and `credentials: 'include'` fails CORS. The collector captures the app's own responses
           instead.
        3. **Token discovery**, for one address or many, uses the collector. Read `scripts/debank-collect.js` and pass its whole
           contents verbatim as the `evaluate_script` `function`; the file has no trailing `;` on purpose, so it stays a
           pasteable arrow function. It returns `{ installed: true, reused: false }` (`reused: true` when the page already has
           it), installs `window.__debankCollect`, and wraps `window.fetch` to record the app's `used_chains` and `balance_list`
           responses. Per address it routes the page to the profile and succeeds once `used_chains` and a `balance_list` for
           every listed chain return 200 with `error_code` 0. DeBank lists the chains an address used, not the chains where it
           holds tokens, so an `ok` record with chains can hold zero tokens. The API includes small balances the UI folds, so no
           `Show all` click is needed. Start the run with a second `evaluate_script` call:
        
        <!-- prettier-ignore -->
        ```js
        async () => window.__debankCollect.start(["<addr>"])
        ```
        
        `start` lowercases and de-duplicates the addresses, throws for an invalid address, an active run, or a failed
        `chain/list` call, and otherwise returns `{ queued }` without waiting for the run, so awaiting it surfaces those errors.
        An optional second argument sets `{ timeoutMs: 30000, maxAttempts: 3, cooldownMs: 20000, haltAfter: 3 }` (the defaults).
        
        4. Poll `window.__debankCollect.status()` with short `evaluate_script` calls until `running` is `false`. It returns
           `{ running, total, done, ok, failed, pending, rateLimited, blocked, startedAt, elapsedMs }`, with `startedAt` an ISO
           string. A profile takes about 2.5 s (1.2-4.6 s). A `429`, error, or timeout pauses the page for `cooldownMs` and
           requeues the address until `maxAttempts`, after which its record is `failed`. After `haltAfter` consecutive failed
           attempts that saw a `429`, the run stops with `blocked: true` and fails every queued address; see Rate Limits and WAF
           Blocks.
        5. Save `window.__debankCollect.results()` by calling `evaluate_script` with
           `function: () => window.__debankCollect.results()` and a `filePath`. The path must be inside the MCP workspace roots
           (a git-ignored project directory); other paths are refused. `results()` returns the latest run's completed records in
           input order, and a new `start` clears them, so save first. Each record has `address`, `status` (`ok` or `failed`),
           `attempts`, `error` (when `failed`), `chains` (the `used_chains` slugs), `observedAt` (ISO string), and `tokens`,
           each `{ chainId, chain, contract, symbol, decimals, rawAmount, amount, price }`. `contract` is the lowercased ERC-20
           address or `"native"`, `chainId` is the chain's numeric `network_id` (`null` when `chain/list` has none for the
           slug), and `rawAmount` is an exact decimal string in raw units. A `failed` record carries `chains: []` and
           `tokens: []`; those arrays mean nothing, and only `status` counts.
        6. Read the `All Chain` summary and the DeFi protocol sections from the DOM only, on that address's own profile (after a
           run, the page shows the last address collected). Wait for `Data updated`; click `Unfold <n> chains` when present,
           then read per-chain USD values from the summary. DeFi sections follow the wallet table, each with a protocol name,
           USD value, and position type; read them from a fresh snapshot when the user requests positions.
        
        - `Data updated <age>` is the DeFi project-snapshot time, not wallet-token freshness. `20727 days ago` (the Unix epoch)
          means `portfolio/project_list` failed to load and says nothing about tokens. Token observation time is each record's
          `observedAt`.
        - The DOM wallet table folds small balances behind a toggle reading "Tokens with small balances are not displayed. Show
          all". It appears only when the wallet has at least 15 tokens and at least 4 are under min(0.1% of wallet USD, $1000).
          Click `Show all` only when you must read the table itself; the clickable is a `<span>` with an `<svg>` child, so a
          leaf-element text matcher never finds it. Scraping the folded table misses those tokens.
        
        ## Many Addresses
        
        One `start` call takes any number of addresses and routes a single owned page through their profiles (DeBank refuses to
        render in an iframe), so a loop never needs one page per address.
        
        - Use one owned page, opened with `new_page` (it reports visible, so timers are not throttled). Each profile costs about
          `10 + <chains>` requests, and bulk runs were blocked after every 30-55 profiles.
        - Split the addresses into batches of at most 25. Per batch: `acquire --profiles <batch size>`, `start` the batch, poll,
          save `results()`, then `release` and acquire again for the next batch.
        - The loop runs without being awaited, so no DevTools protocol timeout applies; poll `status()` with short calls.
        - A new `start` throws while a run is active and clears the previous results once it begins. To abort an active run,
          reload the page and paste the script again.
        - A `failed` record is a coverage gap, never an empty wallet; handle it per Fallbacks. An `ok` record with no tokens is
          DeBank's indexed zero.
        - Never read the page UI to decide that a wallet is empty. During rate limiting, a `429` on `used_chains` makes the page
          show "No assets yet" with a "Request too fast" toast, `429`s on the balance endpoints remove the wallet table, and DOM
          rows can linger from the previous profile after a route change.
        
        ## Rate Limits and WAF Blocks
        
        `Request too fast` (HTTP `429`, body `error_code: 429`, or the page toast) is DeBank's WAF rejecting the request. It is
        a coverage gap, never evidence about the wallet, and never a reason to call `api.debank.com` another way. Classify it by
        how it arrived:
        
        - **Unsigned call.** A direct `fetch`, `curl`, or WebFetch of `api.debank.com` balance endpoints always gets `429`, so
          retrying it never works. Switch to the collector; never present the `429` as DeBank being down.
        - **Burst.** `rateLimited > 0` while records still finish `ok` is the collector absorbing short bursts; no action.
        - **Block.** `status().blocked` is `true`, `start` throws `chain/list failed: HTTP 429`, or the toast persists. DeBank
          is rejecting the whole browser; blocks lasted 5-13 minutes. Do not reload, re-paste, open more pages, or restart in a
          loop: each request prolongs it. Close the page and run `debank-gate.py block --ticket <ticket>` so every queued agent
          waits out the cooldown instead of extending it. Then acquire again for the `failed` addresses only; the queue grants
          the lease after the cooldown. If that retry is also blocked, take the remaining addresses through Fallbacks.
        
        Confirm a block in Chromium before reporting it: the `status()` output, the record `error` strings, or the `429`
        responses in `list_network_requests`. Report it as `DeBank WAF rate-limit block ("Request too fast")` with the
        verification method, the retry made, and the affected addresses.
        
        ## Chain Mapping
        
        - DeBank names chains by slug (`eth`, `scrl`, `xdai`, `era`). The collector maps slugs to chain IDs through the
          `network_id` field of the keyless `https://api.debank.com/chain/list`. Match target chains by exact `chainId`, never
          by slug or display name.
        - A token whose `chainId` is `null` (`chain/list` has no numeric `network_id` for its slug, as for non-EVM balances such
          as Hyperliquid spot and perps) is not a target chain: report it with DeFi positions, never as a target chain.
        
        ## Coverage and Scope
        
        - Derive coverage at runtime: target chains whose ID appears in `chain/list` are DeBank-supported. `used_chains` bounds
          the chains the collector queries, so a supported target chain absent from a record is DeBank's indexed zero, not an
          RPC-confirmed zero.
        - Ignore non-target chains. Sum target-chain tokens for any target-only total; never report the page-wide total as one.
        - DeBank drops spam server-side (every returned token had `is_scam` and `is_suspicious` false), so it never reports spam
          tokens, and absence from a record does not show that a token is not held.
        - `rawAmount` is exact in raw units (with `decimals`); `amount` and `price` are DeBank's. They are still indexer data,
          and `address-usd-value.md` confirms amounts by RPC before using them. Apply the Pricing Hygiene there before using
          DeBank prices in totals.
        - Keep DeFi positions separate from wallet balances.
        
        ## Fallbacks
        
        - Target chains missing from `chain/list`: use `blockscan-balances.md` when Blockscan lists the chain ID, otherwise
          `provider-routing.md`.
        - Navigation fails, an error or challenge persists, an address's record is `failed`, or a WAF block survives the one
          retry in Rate Limits and WAF Blocks: use `blockscan-balances.md`, then `address-sweeps.md` for remaining target
          chains.
        - Chrome DevTools MCP or Chromium is unavailable: use `address-sweeps.md` (or the API passes in `address-usd-value.md`).
        - On-chain precision required: confirm with RPC `balanceOf` and `eth_getBalance` as `address-usd-value.md` does.
        
        Report which condition caused each fallback.
        
        ## Output
        
        Return the profile URL and the collector `observedAt` per address; add the `Data updated` age only when DeFi positions
        were read, labeled as the DeFi snapshot time. Then one row per non-empty target chain (name, ID, native and token
        amounts, DeBank USD), the target-only sum, and DeFi positions as a separate labeled list (protocol, chain, position
        type, USD). For many addresses, also give the saved results path and the `ok` and `failed` counts. Add counts of
        excluded non-target chains and coverage gaps (including each `failed` address) with their cause. Separate
        fallback-derived facts by provider.
        
      • dex-transactions.md 8 KB
        # DEX Transaction Evidence
        
        Use this workflow for wallet-facing DEX history and for interpreting known transactions or orders. It covers swaps and
        orders, liquidity and position actions, approvals and permits, rewards, migrations, native wrapping, refunds, and
        cancellations. It is evidence-only: do not quote, construct, sign, submit, or administer anything.
        
        ## Resolve Before Interpretation
        
        1. For a transaction hash with a named chain, resolve it against `references/generated/target-mainnets.json`, then use
           `references/workflows/provider-routing.md` directly for the raw transaction, receipt, logs, and decoded input. Stop
           on a non-target chain.
        2. When the chain is unknown, use `references/workflows/blockscan-tx-lookup.md` once to resolve it, then continue
           through `references/workflows/provider-routing.md`. Apply the OP Mainnet pre-regenesis exception in `SKILL.md` before
           requiring a current-provider receipt.
        3. Require exact provider evidence before describing an on-chain result. Except for the documented OP Mainnet legacy
           route, this means the exact receipt. A failed or pending receipt cannot prove a completed trade, liquidity change,
           claim, migration, wrap, refund, or cancellation.
        4. For address history, use the resolved chain's provider route to identify candidate transactions, then inspect each
           candidate's receipt. A transaction-list label or method name is not enough.
        5. For an off-chain order identifier, load only the relevant protocol reference, resolve its candidate fill,
           cancellation, or refund transactions, then verify those transactions on-chain. If the identifier or API namespace is
           not globally chain-scoped, ask for a chain. Never default to Ethereum or fan out across chains without approval.
        
        An order may have no maker transaction. Distinguish order creation or signing, API acceptance, on-chain fill,
        cancellation, expiry, and refund as separate lifecycle facts.
        
        ## Evidence Stack
        
        Prefer evidence in this order and retain conflicts:
        
        1. Raw receipt status and emitted logs from the resolved chain.
        2. Transaction calldata decoded with the verified ABI for the exact target and implementation.
        3. Runtime bytecode, proxy implementation, factory provenance, and current official deployment feeds.
        4. Traces and internal calls for wrappers, routers, callbacks, native value, and underlying liquidity.
        5. Protocol APIs for known identifiers and protocol-native status.
        6. Explorer labels, decoded method names, token symbols, and UI descriptions as hints only.
        
        If traces are unavailable, report every route or internal-call claim they would have established as a coverage gap.
        Never promote an explorer label, a four-byte selector guess, or matching source text to deployment proof.
        
        ## Classify the Interaction
        
        Choose one primary interaction class and list secondary legs:
        
        | Class                 | Required evidence                                                                  |
        | --------------------- | ---------------------------------------------------------------------------------- |
        | Swap or order fill    | Successful receipt plus wallet asset deltas and protocol/router/pool evidence      |
        | Order lifecycle       | Signed order/API record plus fill, invalidation, expiry, or refund evidence        |
        | Liquidity or position | Pool/periphery events, position or LP identifiers, and wallet asset/receipt deltas |
        | Approval or permit    | Exact spender, asset, amount/expiry/nonce, and emitted or consumed permit evidence |
        | Reward or claim       | Verified distributor/staking contract plus claimed-asset transfer                  |
        | Migration             | Verified migrator/wrapper plus linked source withdrawal and destination deposit    |
        | Wrap or unwrap        | Native-value trace and wrapped-token deposit/withdrawal or transfer evidence       |
        | Refund or sweep       | Proven return transfer/value trace and the component that returned it              |
        | Cancellation          | Protocol-native invalidation plus any resulting on-chain return                    |
        
        An approval-only transaction remains an approval even if its spender is a DEX router. A successful router call without
        wallet sold/received deltas may be a cancellation, claim, refund, or no-op; do not force it into a swap class.
        
        ## Attribute Every Layer
        
        Report these roles independently:
        
        - **Execution protocol:** the user-facing settlement or aggregation protocol, such as 1inch Fusion or CoW Protocol.
        - **Integration wrapper:** the root target or smart account that composed the action.
        - **Router or periphery:** the contract that dispatched swaps or position commands.
        - **Pool or settlement:** the core contract whose state changed.
        - **Underlying liquidity:** each AMM, pool version, or direct counterflow proven by calls or events.
        
        Do not relabel a 1inch or CoW execution as Uniswap merely because a solver or router used Uniswap liquidity. For a
        wrapper-driven Uniswap zap, identify the wrapper as the entrypoint and Uniswap as underlying liquidity. If the root
        target is not verified, report an unknown wrapper and continue only with the proven downstream calls.
        
        ## Compute Wallet-Level Asset Changes
        
        1. Select the wallet and role being reported: trader/maker, receiver, LP or position owner, operator, approver, reward
           claimant, or refund recipient. Do not combine unrelated participants in a batched transaction.
        2. Sum inbound and outbound ERC-20 `Transfer` logs for that wallet by token. Include ERC-721/ERC-1155 position transfers
           separately; never treat token IDs as fungible amounts.
        3. Use traces and native value transfers for native-asset legs. Preserve internal wraps and unwraps instead of silently
           replacing WETH with ETH or vice versa.
        4. Report the final wallet delta as sold, received, returned, or position assets. Keep temporary router/pool transfers
           as route evidence, not wallet proceeds.
        5. Report transaction gas separately from sold assets. Use the receipt's effective gas price, gas used, and
           chain-specific L1/data fee fields when present; identify who paid it. A relayer or solver may pay gas for an order.
        6. Preserve raw integer amounts and decimals evidence. If token metadata conflicts or is unavailable, do not invent a
           display amount.
        
        Refunded unused input is not swap output. A router sweep or unwrap may explain the wallet's final receipt without being
        a separate trade. Fee-on-transfer, rebasing, internal-balance, and hook-modified assets require trace or state evidence;
        event arithmetic alone may be incomplete.
        
        ## Interpretation Limits
        
        - Use exact-input or exact-output only when verified calldata or command encoding proves it.
        - Use the successful receipt and asset deltas for execution. Calldata limits are intent, not actual output.
        - Do not reconstruct a historical quote, slippage, price impact, or expected route from current data.
        - Do not claim full route composition from endpoint transfers alone. List only pools and interactions proven by
          calldata, logs, or traces.
        - Separate LP principal, accrued fees, rewards, and refunds only when contract accounting and event sequencing support
          the split. Otherwise report the combined collected amount and the missing evidence.
        - Treat protocol APIs as optional enrichment. Missing credentials, rate limits, stale indexing, unsupported chains, or
          conflicting status must degrade to on-chain evidence with a visible coverage gap.
        - Treat a verified fork as that fork. Matching Uniswap interfaces, bytecode, event signatures, or pool math does not
          make an unverified or independently deployed fork canonical Uniswap.
        
        ## Completion
        
        Return the fields required by `SKILL.md`. Keep `Observed facts`, `Inference`, and non-empty `⚠️ Coverage gaps` as
        separate sections. Name the evidence source for every material classification and include exact transaction, order,
        pool, and position identifiers without truncation.
        
        For a failed transaction, report attempted calldata only as attempted behavior and state that no receipt logs or state
        changes survived. For an unsupported chain, unavailable trace, unknown deployment, or API-only status, stop the affected
        claim at the strongest supported evidence.
        
      • provider-routing.md 19.8 KB
        # Provider Routing
        
        Read this reference only after resolving a chain in `references/generated/target-mainnets.json`.
        
        ## Discrete Read Contract
        
        This workflow owns every bounded JSON-RPC read, including reads requested by `cli-cast` for transaction preparation,
        simulation evidence, fee or nonce resolution, and post-broadcast verification. Accept the resolved chain, method and
        exact parameters or call object, block selector or checkpoint requirement, and purpose. Return the resolved chain name
        and ID, exact provider route, result, observed block or checkpoint, and coverage gaps.
        
        Keep the read here until it completes. Never invoke `cli-cast` for JSON-RPC transport or return a signing, mutation, or
        broadcast command. For two or more compatible contract reads on one chain, use Multicall3 at
        `0xcA11bde05977b3631167028862bE2a173976CA11`; do not batch calls whose result depends on the original `msg.sender`.
        
        ## Account and Transaction Data
        
        Choose one authoritative history provider per chain and sweep. A provider is authoritative for that result, not a
        globally canonical source. Keep a second provider only as a fallback:
        
        1. Use Blockscout on covered overlaps, especially when Etherscan cannot serve the chain on the detected plan, its
           pagination/rate/PRO limits make the requested sweep less complete, or Blockscout's native holdings/counters avoid
           those limits.
        2. Otherwise use Etherscan V2 when the chain is in `references/generated/etherscan-chains.md`, the detected plan can
           query it, and the needed actions accept the fixed cutoff.
        3. Use the other indexed provider as fallback when the authoritative provider is unavailable, malformed, behind the
           cutoff, rate/plan limited, or missing a required action. A valid empty response is a completed negative, not a
           fallback trigger. Move the affected result to the fallback; do not silently splice two negative responses into one
           complete result.
        4. If neither indexed provider covers the target, use its listed public RPC only for facts that JSON-RPC can prove.
           Missing indexed history remains unknown, never empty.
        
        On overlaps, Blockscout is not automatically secondary. In particular, prefer it for Base (`8453`), Optimism (`10`),
        Avalanche (`43114`), and BNB Chain (`56`) when `scripts/etherscan-detect-plan.sh` reports `paid_chains=false`, and when
        its unmetered per-instance or full-holdings routes are materially more complete than the available Etherscan plan. Do
        not infer API support from an Etherscan-shaped explorer URL.
        
        For raw Etherscan V2 endpoint parameters, plan gating, and error handling, see `references/explorers/etherscan-api.md`.
        For raw Blockscout endpoint parameters, plan gating, and error handling, see `references/explorers/blockscout-api.md`.
        
        ## Checkpoints and State
        
        Fix one required ISO-8601 UTC cutoff for the whole sweep. Resolve it once per chain to an exact finalized or otherwise
        independently verified block at or before that time. Record the requested cutoff, resolution kind (`finalized` or
        `verified`), block number, hash, timestamp, and observation time. For a timestamp lookup, prove that the returned `B` is
        the greatest block at or before the cutoff by checking `B.timestamp <= requestedAt` and either
        `B+1.timestamp > requestedAt` or that `B` is independently the current finalized head. Reuse that exact checkpoint in
        every request. Do not mix `latest`, different provider heads, or a newly resolved block into the same result. If no
        route can establish the checkpoint, mark the result unknown rather than inventing one.
        
        Batch `eth_getTransactionCount` and `eth_getBalance` with the EIP-1898 `{ blockHash, requireCanonical: true }` selector
        before indexed history. If a provider rejects that selector, a numeric fallback requires matching block-number/hash
        headers from the same endpoint immediately before and after the batch; otherwise try the next RPC or report unknown. The
        target row's `accountActivityModel` controls whether zero nonce plus zero balance may satisfy a profile's native-history
        shortcut:
        
        - Allow the shortcut only for exact `ethereum-eoa`.
        - Default-deny it for `native-account-abstraction`, `cross-vm`, `unknown`, a missing field, or an unrecognized value.
        - Under the `bootstrap-discovery` profile, the exact `ethereum-eoa` zero-state invariant may omit both `txlist` and
          `txlistinternal` wholesale. That profile counts a successful outgoing normal row or a successful positive-value
          normal/internal row touching the address; zero-value inbound normal/internal noise is outside it. The invariant never
          covers token/NFT transfers. Apply the profile rules in `references/workflows/address-sweeps.md` before calling an
          address inactive; a general policy that counts zero-value calls must still query those channels.
        
        For `cross-vm`, scope all state, history, and negative claims to the chain's EVM execution environment. EVM evidence
        does not cover the native non-EVM account environment and cannot prove whole-chain inactivity.
        
        An indexer result is cutoff-complete only when the provider is synced through the checkpoint and the query is bounded to
        it. Filter or paginate past post-cutoff rows; an unbounded newest-first empty/non-empty page is not equivalent to a
        checkpointed result.
        
        Quorum is optional and must be explicit. When requested, enforce it strictly across independent indexed providers that
        cover the same checkpoint and channel set. PRO and per-instance Blockscout surfaces backed by the same index are one
        provider. Descending one-row probes establish existence only. For a positive quorum, every provider must query every
        required channel ascending from genesis or fully paginate its bounded result, apply the same profile predicates, and
        return the same earliest qualifying transaction hash, block, action/channel, and timestamp. A negative quorum requires
        valid empty coverage from every provider. Errors and unsupported channels are not votes; never weaken the requested
        quorum, and report disagreement as unknown.
        
        ## RouteMesh and Public RPC
        
        Use the `routemesh` CLI exclusively for RouteMesh. For HTTP RPC, require the target row's `routeMesh: true` and the
        exact chain ID in `routemesh chains --transport rpc`. The `routeMesh` flag describes HTTP coverage; check WebSocket
        coverage separately with `routemesh chains --transport ws`. Never construct a RouteMesh URL or inspect, request, or
        print an API key.
        
        Assume the user has already run `routemesh init`. On `insufficient_credits`, stop the route and report that the
        RouteMesh account needs credits. For other credential errors, stop that route and tell the user to obtain an API key
        from <https://routeme.sh/app/consumer/api-keys>, run `routemesh init`, then retry. Do not run initialization or fall
        back to a raw RouteMesh endpoint. If `routemesh` is unavailable, report that the CLI is required; do not work around it
        with direct HTTP.
        
        Use `routemesh ping CHAIN_ID` to verify a RouteMesh chain route. Use `routemesh rpc CHAIN_ID METHOD --params=JSON` for
        one read-only JSON-RPC request, or `--json=JSON` for a complete request or batch. Never pass `--allow-write`.
        
        For an activity watch or a bounded wait before rechecking a pending receipt, confirm `routemesh schema subscribe` is
        available and the exact target chain is in `chains --transport ws`, then use:
        
        ```sh
        routemesh --timeout 60s subscribe CHAIN_ID newHeads --count 1
        routemesh --timeout 60s subscribe CHAIN_ID logs --json=FILTER --count 1
        routemesh --timeout 30s subscribe CHAIN_ID newPendingTransactions --count 5
        ```
        
        `FILTER` accepts only `address` and `topics`; use `--json -` for stdin. Choose the subscription and filter that answer
        the request. Type availability is provider-dependent. If the local CLI lacks `schema subscribe`, report that it needs
        updating. Keep a finite count (1–1000) and timeout; both JSON and NDJSON buffer until the full count arrives. A timeout
        or disconnect fails with no partial stdout and no automatic reconnect. Never interpret it as evidence of no activity.
        
        Preserve reorg heads and logs with `removed: true`. Notifications do not prove finality, historical completeness, or a
        successful transaction. After a notification, verify the relevant receipt, state, or explicit log range through the
        existing evidence commands. A new subscription does not recover events missed while disconnected; subscriptions do not
        replace historical sweeps or bridge-protocol status checks.
        
        RouteMesh routing is method-specific, so a successful command proves only that exact method, parameters, and block. It
        does not establish archive coverage for the key, chain, or another method.
        
        | Class                 | Representative methods                                                                         | Archive-state requirement                                                                            |
        | --------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
        | Current or block data | `eth_chainId`, `eth_blockNumber`, `eth_getBlockByNumber`                                       | No historical state trie required                                                                    |
        | Historical chain data | `eth_getTransactionByHash`, `eth_getTransactionReceipt`, `eth_getBlockReceipts`, `eth_getLogs` | Not inherently an archive-state read, but an upstream can prune or incompletely serve old chain data |
        | Historical state      | Old-block `eth_call`, `eth_getBalance`, `eth_getCode`, `eth_getStorageAt`, `eth_getProof`      | Requires an archive-capable state path for that method and block                                     |
        
        For historical state requests:
        
        1. Resolve and verify one exact block number, hash, and timestamp before querying state. Reuse that checkpoint for every
           request.
        2. Send the EIP-1898 `{ blockHash, requireCanonical: true }` selector with `routemesh rpc` where the method and provider
           support it.
        3. When a numeric block fallback is required, use the verified block number and retain the same-endpoint
           block-number/hash consistency checks immediately before and after the request batch.
        4. Treat pruning, missing-trie/state-unavailable errors, malformed data, or a failed block-identity check as a coverage
           failure. Try the ordered independent RPC fallback; if that cannot serve the request, report the state as unknown.
           Never turn the failure into a zero balance or empty state.
        5. On an error or suspicious `null`, retain the CLI-provided batch ID for traceability, but never persist a credential.
        
        Do not rerun a failed RouteMesh command arbitrarily. The CLI already performs its bounded retry policy, and another
        attempt does not promise a different archive-capable pathway on repeat.
        
        For a specific transaction receipt:
        
        1. Use `routemesh receipt CHAIN_ID TX_HASH`. It verifies the transaction, receipt, and exact block header and recovers a
           proven receipt through `eth_getBlockReceipts` when the direct receipt is `null`.
        2. Accept its receipt only after the full transaction hash, block number, and block hash match the target checkpoint.
        3. On a CLI evidence or provider failure, use the ordered indexed-provider or public-RPC fallback. If none supplies the
           receipt, report receipt coverage as unknown.
        
        A method-specific `null` is inconclusive when the transaction is otherwise proven; it is not proof that the transaction
        does not exist. A successful receipt lookup repairs only that receipt path, never unrelated missing historical-state
        evidence.
        
        For `eth_getLogs`, pass an exact checkpoint-bounded filter to `routemesh logs --json=FILTER CHAIN_ID`. The CLI splits
        larger inclusive ranges into deterministic 10,000-block chunks and returns its checkpoint evidence. Do not treat a CLI
        error as an empty log result.
        
        Otherwise issue bounded direct HTTP JSON-RPC requests against the target's `primaryPublicRpc`, first verifying it with
        `eth_chainId`, then try `references/generated/target-fallback-rpcs.json` in order. Do not hand public-RPC reads to
        `cli-cast`. Public RPCs are best-effort and may be rate limited.
        
        ### Exact simulation failures
        
        Preserve the exact transaction object, block selector, CLI exit code, stdout JSON-RPC response, and redacted stderr
        diagnostics. A nonzero exit can accompany useful JSON-RPC error evidence; capture both streams before handling it.
        Retain every correlation ID: RouteMesh uses `X-Batch-Id` for individual requests and comma-separated `X-Batch-Ids` for
        batches, as documented in its [debugging guide](https://routeme.sh/docs/intro/debugging).
        
        When a simulation contradicts its supplied fields or checkpointed state:
        
        1. Verify the CLI payload with `--dry-run`, including sender, target, value, calldata, nonce, transaction type, gas, and
           fee fields. For an affordability error, compare the reported requirement with the exact transaction's upfront
           reserve. A reported gas allowance different from the supplied limit is a simulation-integrity warning.
        2. Replay the unchanged call and estimate through the ordered independent public-RPC fallback at the same verified
           checkpoint. Verify its chain ID first. A successful estimate does not validate a failed call: the two methods may use
           different upstreams. Do not repeatedly retry the same route or switch transaction type to explain a mismatch.
        3. If needed, use a bounded synthetic probe with different explicit gas limits to test whether the route honors gas. For
           a verified ordinary EOA with empty calldata, a below-intrinsic limit must fail. Where state overrides are supported,
           an isolated GAS-opcode probe can establish the executed allowance. These probes diagnose the provider; zeroing value
           or fees, changing gas, or overriding state never substitutes for the exact transaction simulation.
        4. When authenticated logs are available, use `$chromium-browser` to correlate request IDs with the logged parameters,
           result, and upstream. Preserve the distinction between the service's received payload and any unobserved forwarded
           payload. A provider label alone does not prove which layer changed execution semantics.
        
        Return the exact simulation outcome and any provider discrepancy separately. Keep the consuming workflow's simulation
        and approval requirements; diagnostic success does not authorize signing or broadcast. A failing RPC route does not
        establish chain-wide transaction-type incompatibility or justify changing static chain metadata.
        
        ## Explorer Links
        
        For address and transaction links, substitute `{address}` in the target row's `explorerAddressUrl` or `{tx_hash}` in
        `explorerTxUrl`. Preserve the full template, including query parameters: address history may use a different service
        from the transaction explorer. For block and token links, use `explorerUrl` plus
        `references/explorers/explorer-paths.json`. Verify nonstandard explorers in their UI; Ronin does not reliably follow
        Etherscan paths and its chain ID collides with a non-target Chainscout entry. Use `$chromium-browser` for OKLink and
        Ronin browser evidence. Browser availability does not establish a supported programmatic API route; use documented
        credentials for API access and never extract or reproduce the site's private request-signing headers.
        
        ### OKLink historical fallback
        
        For resolved Scroll (`534352`) and Ronin (`2020`) targets, [OKLink](https://www.oklink.com/) is an independent browser
        source for block details and indexed transaction history. Verified on 2026-09-08:
        
        | Target | Browser route and useful evidence                                                                                                                                                                                                                                      | Coverage boundary                                                                                                                                                                            |
        | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
        | Scroll | `https://www.oklink.com/scroll/address/<address>/internal` returned historical internal ETH transfers that matched Blockscout transaction hashes.                                                                                                                      | Set the requested date window explicitly and inspect the zero-value filter; a default recent window or value-only list cannot prove complete internal-call history.                          |
        | Ronin  | `https://www.oklink.com/ronin/block/<number>` serves pre-migration blocks, including the linked [2025 cutoff](https://www.oklink.com/ronin/block/51786916) and [successor](https://www.oklink.com/ronin/block/51786917). Address pages use `/ronin/address/<address>`. | Account-history pages disclose a lower bound of [block 25350000](https://www.oklink.com/ronin/block/25350000), 2023-06-27. They cannot prove earlier inactivity or genesis-complete history. |
        
        Record the chain, source URL, observation time, requested range, displayed filters, pagination/caps, and block
        identities. Convert displayed local times to UTC explicitly. Positive rows establish only the facts they show; a
        complete negative still requires every required channel and interval through the fixed cutoff. Do not splice partial
        provider negatives into a complete result.
        
        Scrollscan now serves Blockscout. Its native v2 internal-transaction list can return HTTP 200 with exhausted pagination
        while the compatibility API reports unprocessed internal transactions, even when global indexing indicators report
        completion. Preserve that semantic failure; the native list is useful positive evidence, not an independent fallback or
        proof that the missing traces are empty.
        
        Ronin's [2026 migration announcement](https://blog.roninchain.com/p/ronin-is-home) sunsets the legacy explorer. The new
        `explorer.roninchain.com` Blockscout deployment did not serve the pre-migration 2025 cutoff; the legacy
        `app.roninchain.com/explorer` UI reproduced Skynet 503 responses during the check above. Verify historical coverage
        separately from current-chain availability. OKLink can supply a legacy block boundary without supplying pre-2023 account
        history or exact historical account state.
        
        ## Exceptional History
        
        For HyperEVM (`999`) exact historical native-balance and nonce reads, do not use public JSON-RPC or RouteMesh: those
        routes can silently serve latest state for historical selectors. At the verified checkpoint, use Etherscan V2 `account`
        module's `balancehistory` action for the native balance and the `proxy` module's `eth_getTransactionCount` action with
        the checkpoint's hex block tag for the nonce. If an Etherscan route is unavailable or plan-limited, report that fact as
        unknown; do not fall back to RPC.
        
        For Fantom Opera (`250`) account history, do not use the unsafe FTMScout route returned by Chainscout. Read
        `references/explorers/fantom-opera.md` and preserve its partial-index boundary: GraphQL rows can provide positive
        evidence, but empty account lists cannot establish historical inactivity.
        
        For OP Mainnet data before `2021-11-11`, read `references/explorers/optimism-pre-regenesis.md` before interpreting
        provider or RPC results.
        
        For IoTeX (`4689`) nonce reads, `eth_getTransactionCount` with `latest` returns the actpool pending nonce, identical to
        `pending`, on both RouteMesh and the public RPC; only a numeric block selector returns the confirmed count. The nodes
        expose no `txpool_*` methods, and `eth_getBlockByNumber("pending")` omits actpool transactions. Report the confirmed
        nonce from the numeric checkpoint and treat `pending - confirmed` as the count of queued transactions. Verified
        2026-09-30: `latest` and `pending` both returned `11` while the numeric block returned `10`, and the sender's last mined
        transaction had nonce `9`.
        
    • atlas-overlays.json 16.3 KB
      {
        "metadata": {
          "blockscoutObservedAt": "2026-07-08",
          "etherscanProviderChainCount": 64,
          "etherscanVerifiedAt": "2026-07-08",
          "routeMeshVerifiedAt": "2026-07-15"
        },
        "chains": {
          "abstract": {
            "primaryPublicRpc": "https://api.mainnet.abs.xyz",
            "fallbackPublicRpcs": ["https://abstract.drpc.org", "https://2741.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          },
          "arbitrum": {
            "primaryPublicRpc": "https://arb1.arbitrum.io/rpc",
            "fallbackPublicRpcs": [
              "https://arbitrum-one-rpc.publicnode.com",
              "https://42161.rpc.thirdweb.com",
              "https://rpcfree.com/arbitrum-rpc"
            ],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://arbitrum.blockscout.com/"
            }
          },
          "arbitrum-nova": {
            "primaryPublicRpc": "https://nova.arbitrum.io/rpc",
            "fallbackPublicRpcs": [
              "https://arbitrum-nova-rpc.publicnode.com",
              "https://42170.rpc.thirdweb.com",
              "https://arbitrum-nova.drpc.org"
            ],
            "routeMesh": true,
            "etherscan": {
              "support": "unsupported",
              "notes": "Not returned by the live chainlist; `nova.arbiscan.io` redirects to Blockscout"
            },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://arbitrum-nova.blockscout.com/",
              "notes": "Canonical explorer (Arbiscan Nova decommissioned)"
            }
          },
          "avalanche": {
            "primaryPublicRpc": "https://api.avax.network/ext/bc/C/rpc",
            "fallbackPublicRpcs": ["https://avalanche-c-chain-rpc.publicnode.com", "https://43114.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "paid" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          },
          "base": {
            "primaryPublicRpc": "https://mainnet.base.org",
            "fallbackPublicRpcs": [
              "https://base-rpc.publicnode.com",
              "https://base.gateway.tenderly.co",
              "https://developer-access-mainnet.base.org"
            ],
            "routeMesh": true,
            "etherscan": { "support": "paid" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://base.blockscout.com/"
            }
          },
          "berachain": {
            "primaryPublicRpc": "https://rpc.berachain.com",
            "fallbackPublicRpcs": [
              "https://berachain-rpc.publicnode.com",
              "https://80094.rpc.thirdweb.com",
              "https://rpc.berachain-apis.com"
            ],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          },
          "blast": {
            "primaryPublicRpc": "https://rpc.blast.io",
            "fallbackPublicRpcs": [
              "https://blast-rpc.publicnode.com",
              "https://blast.drpc.org",
              "https://81457.rpc.thirdweb.com"
            ],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          },
          "bsc": {
            "primaryPublicRpc": "https://bsc-dataseed1.bnbchain.org",
            "fallbackPublicRpcs": ["https://bsc-rpc.publicnode.com", "https://bsc.drpc.org", "https://56.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "paid" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" },
            "chainscoutNamePattern": "BNB|BSC|Smart Chain"
          },
          "celo": {
            "primaryPublicRpc": "https://forno.celo.org",
            "fallbackPublicRpcs": [
              "https://celo.drpc.org",
              "https://celo-rpc.publicnode.com",
              "https://42220.rpc.thirdweb.com"
            ],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://celo.blockscout.com/"
            }
          },
          "chiliz": {
            "primaryPublicRpc": "https://rpc.chiliz.com",
            "fallbackPublicRpcs": [
              "https://chiliz.publicnode.com",
              "https://88888.rpc.thirdweb.com",
              "https://rpc.ankr.com/chiliz"
            ],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          },
          "core-dao": {
            "primaryPublicRpc": "https://rpc.coredao.org",
            "fallbackPublicRpcs": [
              "https://core.drpc.org",
              "https://1116.rpc.thirdweb.com",
              "https://rpc-core.icecreamswap.com"
            ],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" },
            "chainscoutNamePattern": "Core"
          },
          "mainnet": {
            "primaryPublicRpc": "https://ethereum-rpc.publicnode.com",
            "fallbackPublicRpcs": ["https://eth.drpc.org", "https://rpc.flashbots.net", "https://1.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "free", "notes": "Default chain" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://eth.blockscout.com/"
            }
          },
          "fantom": {
            "primaryPublicRpc": "https://rpc.fantom.network",
            "fallbackPublicRpcs": ["https://rpc2.fantom.network"],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": {
              "status": "unsafe",
              "notes": "Chainscout still lists self-hosted FTMScout at https://ftmscout.com/, but as checked 2026-07-31 its frontend returns HTTP 200 while /api/v2/* data routes return HTTP 500; do not use it for evidence"
            }
          },
          "filecoin": {
            "primaryPublicRpc": "https://api.node.glif.io/rpc/v1",
            "fallbackPublicRpcs": ["https://314.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://filecoin.blockscout.com/"
            },
            "chainscoutNamePattern": "Filecoin|Filecoin Virtual Machine|FVM"
          },
          "fraxtal": {
            "primaryPublicRpc": "https://rpc.frax.com",
            "fallbackPublicRpcs": [
              "https://fraxtal-rpc.publicnode.com",
              "https://fraxtal.drpc.org",
              "https://fraxtal.gateway.tenderly.co"
            ],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          },
          "gnosis": {
            "primaryPublicRpc": "https://rpc.gnosischain.com",
            "fallbackPublicRpcs": [
              "https://gnosis-rpc.publicnode.com",
              "https://gnosis.drpc.org",
              "https://100.rpc.thirdweb.com"
            ],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://gnosis.blockscout.com/"
            }
          },
          "hyperevm": {
            "primaryPublicRpc": "https://rpc.hyperliquid.xyz/evm",
            "fallbackPublicRpcs": [
              "https://hyperliquid.drpc.org",
              "https://999.rpc.thirdweb.com",
              "https://gwan-ssl.wandevs.org:46891"
            ],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "self",
              "instanceUrl": "https://www.hyperscan.com/",
              "notes": "Chainscout marks `isTestnet=true`"
            },
            "chainscoutNamePattern": "HyperEVM|Hyper"
          },
          "iotex": {
            "primaryPublicRpc": "https://babel-api.mainnet.iotex.io",
            "fallbackPublicRpcs": ["https://4689.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          },
          "lightlink": {
            "primaryPublicRpc": "https://replicator.phoenix.lightlink.io/rpc/v1",
            "fallbackPublicRpcs": ["https://1890.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://phoenix.lightlink.io/"
            },
            "chainscoutNamePattern": "Lightlink|LightLink"
          },
          "linea": {
            "primaryPublicRpc": "https://rpc.linea.build",
            "fallbackPublicRpcs": ["https://linea-rpc.publicnode.com", "https://59144.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "self",
              "instanceUrl": "https://explorer.linea.build/"
            }
          },
          "mode": {
            "primaryPublicRpc": "https://mainnet.mode.network",
            "fallbackPublicRpcs": ["https://mode.drpc.org", "https://34443.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://explorer.mode.network/"
            }
          },
          "monad": {
            "primaryPublicRpc": "https://rpc.monad.xyz",
            "fallbackPublicRpcs": ["https://monad.drpc.org", "https://143.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          },
          "morph": {
            "primaryPublicRpc": "https://rpc.morphl2.io",
            "fallbackPublicRpcs": [
              "https://morph.drpc.org",
              "https://2818.rpc.thirdweb.com",
              "https://rpc-quicknode.morphl2.io"
            ],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "self",
              "instanceUrl": "https://explorer.morph.network/",
              "notes": "Separate API host; see explorerApiUrl"
            }
          },
          "optimism": {
            "primaryPublicRpc": "https://mainnet.optimism.io",
            "fallbackPublicRpcs": [
              "https://optimism-rpc.publicnode.com",
              "https://optimism.drpc.org",
              "https://10.rpc.thirdweb.com"
            ],
            "routeMesh": true,
            "etherscan": { "support": "paid" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://explorer.optimism.io/"
            },
            "chainscoutNamePattern": "OP|Optimism"
          },
          "polygon": {
            "primaryPublicRpc": "https://polygon-bor-rpc.publicnode.com",
            "fallbackPublicRpcs": [
              "https://polygon.drpc.org",
              "https://137.rpc.thirdweb.com",
              "https://rpc-mainnet.matic.quiknode.pro"
            ],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://polygon.blockscout.com/"
            }
          },
          "robinhood": {
            "primaryPublicRpc": "https://rpc.mainnet.chain.robinhood.com",
            "fallbackPublicRpcs": ["https://robinhoodchain.blockscout.com/api/eth-rpc"],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://robinhoodchain.blockscout.com/"
            }
          },
          "ronin": {
            "primaryPublicRpc": "https://api.roninchain.com/rpc",
            "fallbackPublicRpcs": ["https://ronin.drpc.org", "https://2020.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": {
              "status": "unsafe",
              "notes": "Chainscout returns a different network for `2020`; app.roninchain.com blocks scripted access, so verify with `$chromium-browser` instead of curl or WebFetch"
            }
          },
          "scroll": {
            "primaryPublicRpc": "https://rpc.scroll.io",
            "fallbackPublicRpcs": ["https://scroll.drpc.org", "https://534352.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": {
              "support": "unsupported",
              "notes": "Removed from Etherscan V2 on 2026-04-16; api.scrollscan.com no longer resolves"
            },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://scroll.blockscout.com"
            }
          },
          "sei": {
            "primaryPublicRpc": "https://evm-rpc.sei-apis.com",
            "fallbackPublicRpcs": ["https://sei.drpc.org", "https://1329.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          },
          "sonic": {
            "primaryPublicRpc": "https://rpc.soniclabs.com",
            "fallbackPublicRpcs": [
              "https://sonic-rpc.publicnode.com",
              "https://sonic.drpc.org",
              "https://146.rpc.thirdweb.com"
            ],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          },
          "sophon": {
            "primaryPublicRpc": "https://rpc.sophon.xyz",
            "fallbackPublicRpcs": ["https://50104.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          },
          "superseed": {
            "primaryPublicRpc": "https://mainnet.superseed.xyz",
            "fallbackPublicRpcs": ["https://superseed.drpc.org", "https://5330.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": {
              "status": "unsafe",
              "notes": "Chromium verified 2026-09-15: explorer.superseed.xyz now serves Conduit Explorer, which does not support historical transactions, holdings, or transfers; do not use its stale Chainscout Blockscout route"
            }
          },
          "taiko": {
            "primaryPublicRpc": "https://rpc.mainnet.taiko.xyz",
            "fallbackPublicRpcs": [
              "https://taiko-rpc.publicnode.com",
              "https://taiko.drpc.org",
              "https://167000.rpc.thirdweb.com"
            ],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "self",
              "instanceUrl": "https://blockscout.mainnet.taiko.xyz/",
              "notes": "Chainscout name is Taiko Alethia"
            },
            "chainscoutNamePattern": "Taiko|Alethia"
          },
          "unichain": {
            "primaryPublicRpc": "https://mainnet.unichain.org",
            "fallbackPublicRpcs": ["https://unichain-rpc.publicnode.com", "https://130.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://unichain.blockscout.com"
            }
          },
          "world-chain": {
            "primaryPublicRpc": "https://worldchain-mainnet.g.alchemy.com/public",
            "fallbackPublicRpcs": [
              "https://worldchain.drpc.org",
              "https://480.rpc.thirdweb.com",
              "https://worldchain-mainnet.gateway.tenderly.co"
            ],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": {
              "status": "observed",
              "hostedBy": "alchemy",
              "instanceUrl": "https://worldchain-mainnet.explorer.alchemy.com/",
              "notes": "Alchemy-hosted instance; prefer Etherscan V2"
            },
            "chainscoutNamePattern": "World"
          },
          "xdc": {
            "primaryPublicRpc": "https://rpc.xdcrpc.com",
            "fallbackPublicRpcs": ["https://50.rpc.thirdweb.com", "https://erpc.xdcrpc.com", "https://rpc.xdc.org"],
            "routeMesh": true,
            "etherscan": { "support": "free" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          },
          "zksync": {
            "primaryPublicRpc": "https://mainnet.era.zksync.io",
            "fallbackPublicRpcs": ["https://zksync.drpc.org", "https://324.rpc.thirdweb.com"],
            "routeMesh": true,
            "etherscan": {
              "support": "unsupported",
              "notes": "Not currently supported by Etherscan V2"
            },
            "blockscout": {
              "status": "observed",
              "hostedBy": "blockscout",
              "instanceUrl": "https://zksync.blockscout.com/"
            },
            "chainscoutNamePattern": "ZKsync"
          },
          "zora": {
            "primaryPublicRpc": "https://zora.drpc.org",
            "fallbackPublicRpcs": ["https://7777777.rpc.thirdweb.com", "https://rpc.zora.energy"],
            "routeMesh": true,
            "etherscan": { "support": "unsupported" },
            "blockscout": { "status": "absent", "notes": "Not returned by Chainscout" }
          }
        }
      }
      
    • chain-categories.md 9.6 KB
      # Chain categories and exact-zero native sweeps
      
      `references/generated/target-mainnets.json` is the source of truth for every target's current `category`. Categories
      describe current architecture, not historical protocol behavior. Do not duplicate a target-chain roster here: resolve
      the row first, then report its category with the chain evidence.
      
      | Category | Key        | Scope and exact-zero implication                                                                                                                                                                                                                                                                                                                   |
      | -------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
      | Mainnet  | `mainnet`  | Ethereum only. A fixed-price plain native transfer is eligible when its current inclusion and standard-transfer proof holds.                                                                                                                                                                                                                       |
      | Alt L1   | `alt-l1`   | Ethereum-style L1s, deliberately including Polygon PoS. They can be eligible only with target-specific proof of fixed gas, a fixed full `gasPrice` debit, and no extra debits or credits; BNB Chain and Polygon PoS are examples.                                                                                                                  |
      | L2 OP    | `op-stack` | Ineligible for a guaranteed direct exact-zero transfer: automatic uncapped L1 data fees and operator fees can add to execution gas.                                                                                                                                                                                                                |
      | Nitro    | `nitro`    | Ineligible for a guaranteed direct exact-zero transfer: parent-chain posting economics and CollectTips-dependent charged pricing prevent the required fixed total debit proof.                                                                                                                                                                     |
      | ZK       | `zk`       | Includes ZK Stack, validium, and hybrid targets. Linea and Taiko can be eligible when a standard fixed legacy debit is proven at inclusion. Scroll and Morph are ineligible because of separate dynamic L1 fees. ZK Stack targets, including ZKsync Era, Abstract, and Sophon, are ineligible because pubdata, overhead, and refunds are variable. |
      | Alt L2   | `alt-l2`   | Fallback category. Treat as unknown: require bespoke target proof of the complete debit, or block. Lightlink can be eligible when a standard fixed legacy debit is proven at inclusion.                                                                                                                                                            |
      
      Category is only the first gate. An eligible category still requires current target-specific evidence that the
      transaction is an ordinary empty-calldata transfer, uses a fixed full charge at inclusion, has no additional debit or
      credit, and has a fixed gas use. Type-0 acceptance, a fixed bid shape, or successful simulation alone does not prove
      eligibility.
      
      For Ethereum-style accounting, an included legacy transaction pays its signed `gasPrice`: changes to base fees or
      admission quotes can prevent inclusion, but do not reprice the signed transaction. Linea's L1-cost-based profitability
      check is an admission rule, not an additional sender debit; Taiko also charges the signed legacy price without a
      separate L1 fee. With proven `21000` gas, ordinary EOAs, empty calldata, and unchanged balance/nonce/code, both permit
      `value = balance - 21000 * gasPrice`. This is conditional on inclusion, not a guarantee that a public transaction will
      be included.
      
      OP Stack means OP Stack-based execution, including forks: its L1 data fee and any enabled operator fee are added to
      execution cost. Nitro means the Arbitrum family, including AnyTrust chains: its parent-chain posting charge is converted
      into child-chain gas, not added again as a separate wei fee. Fixing the legacy bid does not fix either family's total
      charge. ZK covers distinct execution and proving systems, so use the named chain's fee branch rather than inferring a
      shared fee model from its proof technology.
      
      Scroll charges its L1 data fee on the full signed RLP bytes, and under Feynman that fee scales with byte length. Its
      `L1GasPriceOracle` predeploy (`0x5300000000000000000000000000000000000002`) prices exactly the bytes passed to
      `getL1Fee(bytes)` and, unlike OP Stack's `GasPriceOracle`, adds no signature overhead. A quote on an unsigned
      empty-calldata EIP-1559 transfer (about 46 bytes) is therefore less than half the charge on the signed transaction
      (about 114 bytes). Quote a maximum-size signed stand-in instead: the final fields serialized with nonzero 32-byte
      `r`/`s` placeholders. Verified 2026-09-29: the oracle quote on real signed bytes at the parent block equals the receipt
      `l1Fee`.
      
      Lightlink (`1890`) is an Alt L2 exception. It runs a Geth 1.10 fork without London: blocks carry no `baseFeePerGas`,
      `eth_maxPriorityFeePerGas` and `eth_feeHistory` are unsupported, and receipts omit `effectiveGasPrice`, so use legacy
      pricing only. No separate L1 data, operator, or rollup fee is debited from the sender. Enterprise Mode gasless
      transactions are signed with `gasPrice = 0`; the node does not reprice a signed nonzero price. Verified 2026-09-30:
      consecutive-block sender balance deltas equalled `gasUsed * gasPrice` exactly for ordinary legacy transactions, and an
      empty-calldata EOA transfer estimated `21000`. Because receipts lack `effectiveGasPrice`, verify the charged price as
      the signed `gasPrice` plus an exact receipt-block balance reconciliation.
      
      IoTeX (`4689`) is an Alt L1 exception. Legacy transactions are debited exactly `gasUsed * gasPrice` with no refund or
      extra debit. Receipts, including an empty-calldata transfer's on RouteMesh and the public RPC (verified 2026-09-30),
      report `effectiveGasPrice` equal to the signed `gasPrice`; if one omits it, fall back to exact receipt-block balance
      reconciliation. An empty-calldata EOA transfer charges `10000` gas, not `21000`, and `eth_estimateGas` still returns
      `21000`; a `21000` gas limit therefore leaves `11000 * gasPrice` behind. For an exact drain, use a `10000` gas limit,
      which `eth_call` and the node accept, instead of the estimate. Verified 2026-09-30: two empty-calldata transfers used
      exactly `10000` gas, and sender balance deltas reconciled to `value + gasUsed * gasPrice`; an exact-zero sweep at that
      limit left a `0` balance.
      
      Filecoin FEVM is an Alt L1 exception. FVM fee translation and overestimation require bespoke evidence; do not generalize
      Ethereum-style L1 fee behavior to it. A fresh EOA recipient does not alter the standard top-level transfer gas cost: the
      `25000` new-account `CALL` cost concerns the contract opcode, not a top-level transfer. Still exclude precompiles and
      protocol system destinations; `eth_getCode == 0x` alone does not establish that an address is an ordinary recipient.
      
      For a category-fee or exact-drain question, return the current category, target row, protocol/source evidence, and the
      decision before any signer setup. Atlas remains read-only; return evidence for `$cli-cast` and the calling workflow to
      construct, simulate, and account for a transaction.
      
      ## Sources
      
      - [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)
      - [OP Stack transaction fees](https://docs.optimism.io/op-stack/transactions/fees)
      - [Arbitrum gas and fees](https://docs.arbitrum.io/how-arbitrum-works/deep-dives/gas-and-fees) and
        [Nitro L1 pricing](https://github.com/OffchainLabs/nitro/blob/master/arbos/l1pricing/l1pricing.go)
      - [ZKsync Era fee structure](https://docs.zksync.io/zksync-protocol/era-vm/transactions/fee-model/fee-structure) and
        [bootloader](https://docs.zksync.io/zksync-protocol/era-vm/contracts/bootloader)
      - [Scroll transaction fees](https://docs.scroll.io/en/developers/transaction-fees-on-scroll/)
      - [Morph state transition](https://github.com/morph-l2/go-ethereum/blob/main/core/state_transition.go) and
        [rollup fee](https://github.com/morph-l2/go-ethereum/blob/main/rollup/fees/rollup_fee.go)
      - [Linea gas fees](https://github.com/Consensys/doc.linea/blob/main/docs/network/how-to/gas-fees.mdx) and
        [sequencer profitability validation](https://github.com/Consensys/linea-monorepo/blob/main/linea-besu/plugins/linea-sequencer/sequencer/src/main/java/lineth/sequencer/txpoolvalidation/validators/ProfitabilityValidator.java)
      - [Taiko legacy transaction](https://github.com/taikoxyz/taiko-geth/blob/taiko/core/types/tx_legacy.go) and
        [state transition](https://github.com/taikoxyz/taiko-geth/blob/taiko/core/state_transition.go)
      - [Filecoin FIP-0091](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0091.md),
        [FEVM gas differences](https://docs.filecoin.io/smart-contracts/filecoin-evm-runtime/difference-with-ethereum), and
        [Filecoin gas estimation](https://docs.filecoin.io/reference/exchanges/exchange-integration#automatic-gas-values)
      - [LightLink Enterprise Mode](https://docs.lightlink.io/lightlink-protocol/building-on-lightlink/enterprise-mode-overview)
      - [Geth top-level transaction gas](https://github.com/ethereum/go-ethereum/blob/master/core/state_transition.go)
      
  • scripts
    • blockscout-detect-plan.sh 1.7 KB
      #!/bin/bash
      # blockscout-detect-plan.sh — Detect Blockscout PRO plan + credits from $BLOCKSCOUT_API_KEY.
      #
      # Reads the rate-limit/credit response headers the PRO host returns on every
      # call. x-ratelimit-limit maps directly to the plan tier:
      #   5 -> free, 15 -> builder, 30 -> pro, 50 -> business
      #
      # Outputs key=value lines on stdout:
      #   plan=<free|builder|pro|business|unknown>
      #   rate_limit_rps=<int>
      #   rate_limit_remaining=<int>
      #   rate_limit_reset=<int seconds>
      #   credits_remaining=<int>
      #
      # Costs ~20 credits (one address call). Cache the result for the session.
      
      set -eu
      
      if [ -z "${BLOCKSCOUT_API_KEY:-}" ]; then
        echo "Error: BLOCKSCOUT_API_KEY is not set" >&2
        echo "Get a free key at: https://dev.blockscout.com/" >&2
        exit 1
      fi
      
      # Any valid call returns the headers we need; use a known mainnet address.
      addr="0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe"
      url="https://api.blockscout.com/1/api/v2/addresses/$addr"
      
      headers=$(curl -fsS -D - -o /dev/null \
        -H "authorization: Bearer $BLOCKSCOUT_API_KEY" "$url" 2>/dev/null) || {
        echo "Error: request failed — invalid key or network issue (PRO host returns 401 for a bad key)." >&2
        exit 1
      }
      
      # Case-insensitive header lookup; strip trailing CR.
      hval() {
        printf '%s' "$headers" | grep -i "^$1:" | head -1 \
          | sed 's/^[^:]*:[[:space:]]*//' | tr -d '\r'
      }
      
      rps=$(hval "x-ratelimit-limit")
      remaining=$(hval "x-ratelimit-remaining")
      reset=$(hval "x-ratelimit-reset")
      credits=$(hval "x-credits-remaining")
      
      case "$rps" in
        5)  plan="free" ;;
        15) plan="builder" ;;
        30) plan="pro" ;;
        50) plan="business" ;;
        *)  plan="unknown" ;;
      esac
      
      cat <<EOF
      plan=$plan
      rate_limit_rps=$rps
      rate_limit_remaining=$remaining
      rate_limit_reset=$reset
      credits_remaining=$credits
      EOF
      
    • check-conformance-fixtures.sh 940 B
      #!/bin/bash
      # Run the evm-atlas provider-response validators against synthetic fixtures.
      # This test is offline: it performs no network requests and reads no credentials.
      
      set -eu
      
      script_dir=$(CDPATH='' cd "$(dirname "$0")" && pwd)
      fixture_dir="$script_dir/../fixtures"
      example_address="0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe"
      
      bash "$script_dir/validate-blockscout-address-counters.sh" \
        < "$fixture_dir/blockscout-address-counters.json" >/dev/null
      bash "$script_dir/validate-etherscan-transfer-topics.sh" "$example_address" \
        < "$fixture_dir/etherscan-transfer-topic-response.json" >/dev/null
      if bash "$script_dir/validate-etherscan-transfer-topics.sh" "$example_address" \
        < "$fixture_dir/etherscan-transfer-topic-self-transfer.json" >/dev/null 2>&1; then
        echo "Error: a self-transfer incorrectly proved distinct inbound-only and outbound-only OR semantics." >&2
        exit 1
      fi
      
      echo "evm-atlas conformance fixtures are valid"
      
    • debank-collect.js 8.7 KB
      () => {
        // Paste verbatim as the Chrome DevTools MCP `evaluate_script` function on a debank.com page.
        // It records the app's own signed responses; it never calls balance endpoints itself.
        if (window.__debankCollect) return { installed: true, reused: true };
      
        const ADDRESS = /^0x[0-9a-f]{40}$/i;
        const USED_CHAINS = "/user/used_chains";
        const BALANCE_LIST = "/token/balance_list";
        const networkFetch = window.fetch;
        const waiters = new Set();
        let events = [];
        let chainIds = null;
        let run = null;
      
        const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
      
        function emit(event) {
          events.push(event);
          if (run && !run.finishedAt && (event.status === 429 || event.json?.error_code === 429)) run.rateLimited += 1;
          for (const waiter of [...waiters]) if (waiter.match(event)) waiter.done(event);
        }
      
        function trackedUrl(input) {
          try {
            const url = new URL(typeof input === "string" ? input : (input.url ?? String(input)), location.href);
            const tracked = url.hostname === "api.debank.com" && [USED_CHAINS, BALANCE_LIST].includes(url.pathname);
            return tracked ? url : null;
          } catch {
            return null;
          }
        }
      
        window.fetch = function (input, init) {
          const url = trackedUrl(input);
          if (!url) return networkFetch.call(window, input, init);
          const params = url.searchParams;
          const event = {
            path: url.pathname,
            address: (params.get("id") ?? params.get("user_addr") ?? "").toLowerCase(),
            chain: params.get("chain"),
            at: Date.now(),
          };
          return networkFetch.call(window, input, init).then(
            (res) => {
              res
                .clone()
                .json()
                .catch(() => null)
                .then((json) => emit({ ...event, status: res.status, json }));
              return res;
            },
            (error) => {
              emit({ ...event, status: 0, json: null, error: String(error) });
              throw error;
            },
          );
        };
      
        // Resolves with the first matching event (already recorded or future), or null at the deadline.
        function waitFor(match, deadline) {
          const seen = events.find(match);
          if (seen) return Promise.resolve(seen);
          return new Promise((resolve) => {
            const waiter = {
              match,
              done(event) {
                clearTimeout(timer);
                waiters.delete(waiter);
                resolve(event);
              },
            };
            const timer = setTimeout(() => waiter.done(null), Math.max(0, deadline - Date.now()));
            waiters.add(waiter);
          });
        }
      
        function problem(event, label) {
          if (event.status === 0) return `${label} network error: ${event.error}`;
          if (event.status !== 200) return `${label} HTTP ${event.status}`;
          if (!event.json) return `${label} returned invalid JSON`;
          if (event.json.error_code) return `${label} error_code ${event.json.error_code}`;
          return null;
        }
      
        function route(path) {
          history.pushState({}, "", path);
          dispatchEvent(new PopStateEvent("popstate", { state: {} }));
        }
      
        function toToken(item) {
          const hex = String(item.raw_amount_hex_str);
          return {
            chainId: chainIds.get(item.chain) ?? null,
            chain: item.chain,
            contract: ADDRESS.test(item.id) ? item.id.toLowerCase() : "native",
            symbol: item.symbol,
            decimals: item.decimals,
            rawAmount: BigInt(hex.startsWith("0x") ? hex : `0x${hex}`).toString(),
            amount: item.amount,
            price: item.price,
          };
        }
      
        async function collect(address, timeoutMs) {
          events = [];
          // The app ignores a route to the profile it already shows.
          if (location.pathname.toLowerCase() === `/profile/${address}`) route("/");
          const since = Date.now();
          const deadline = since + timeoutMs;
          const ours = (path) => (event) => event.path === path && event.address === address && event.at >= since;
          route(`/profile/${address}`);
      
          const used = await waitFor(ours(USED_CHAINS), deadline);
          if (!used) throw new Error("timeout waiting for used_chains");
          const usedProblem = problem(used, "used_chains");
          if (usedProblem) throw new Error(usedProblem);
          const chains = used.json.data?.chains;
          if (!Array.isArray(chains)) throw new Error("used_chains response has no data.chains array");
      
          const pending = new Set(chains);
          const tokens = [];
          while (pending.size > 0) {
            const event = await waitFor((e) => ours(BALANCE_LIST)(e) && pending.has(e.chain), deadline);
            if (!event) throw new Error(`timeout waiting for balance_list: ${[...pending].join(", ")}`);
            const listProblem = problem(event, `balance_list ${event.chain}`);
            if (listProblem) throw new Error(listProblem);
            if (!Array.isArray(event.json.data)) throw new Error(`balance_list ${event.chain} has no data array`);
            pending.delete(event.chain);
            tokens.push(...event.json.data.map(toToken));
          }
          return { chains, tokens };
        }
      
        function failedRecord(address, attempts, error) {
          return { address, status: "failed", attempts, error, chains: [], observedAt: new Date().toISOString(), tokens: [] };
        }
      
        async function drain(current, { timeoutMs, maxAttempts, cooldownMs, haltAfter }) {
          const queue = [...current.addresses];
          // Consecutive failed attempts that saw a 429; a sustained streak means DeBank's WAF is blocking this client.
          let limitedStreak = 0;
          while (queue.length > 0) {
            const address = queue.shift();
            const attempts = (current.attempts.get(address) ?? 0) + 1;
            current.attempts.set(address, attempts);
            const limitedBefore = current.rateLimited;
            try {
              const { chains, tokens } = await collect(address, timeoutMs);
              const observedAt = new Date().toISOString();
              current.records.set(address, { address, status: "ok", attempts, chains, observedAt, tokens });
              limitedStreak = 0;
            } catch (error) {
              limitedStreak = current.rateLimited > limitedBefore ? limitedStreak + 1 : 0;
              if (attempts < maxAttempts) queue.push(address);
              else current.records.set(address, failedRecord(address, attempts, error.message));
              if (limitedStreak >= haltAfter) {
                current.blocked = true;
                const reason = `rate-limit block: run halted after ${limitedStreak} consecutive rate-limited attempts`;
                for (const queued of queue)
                  current.records.set(queued, failedRecord(queued, current.attempts.get(queued) ?? 0, reason));
                break;
              }
              if (queue.length > 0) await sleep(cooldownMs);
            }
          }
          current.finishedAt = Date.now();
        }
      
        async function loadChainIds() {
          const res = await networkFetch.call(window, "https://api.debank.com/chain/list");
          const json = await res.json().catch(() => null);
          if (res.status !== 200 || !Array.isArray(json?.data?.chains))
            throw new Error(`chain/list failed: HTTP ${res.status}`);
          const ids = new Map();
          for (const chain of json.data.chains) {
            const id = Number(chain.network_id);
            if (Number.isSafeInteger(id)) ids.set(chain.id, id);
          }
          return ids;
        }
      
        async function start(addresses, { timeoutMs = 30000, maxAttempts = 3, cooldownMs = 20000, haltAfter = 3 } = {}) {
          if (run && !run.finishedAt) throw new Error("a run is already active; poll status() until running is false");
          if (!Array.isArray(addresses)) throw new Error("addresses must be an array");
          const invalid = addresses.filter((address) => typeof address !== "string" || !ADDRESS.test(address));
          if (invalid.length > 0) throw new Error(`invalid addresses: ${invalid.map(String).join(", ")}`);
          const unique = [...new Set(addresses.map((address) => address.toLowerCase()))];
          const current = {
            addresses: unique,
            attempts: new Map(),
            records: new Map(),
            rateLimited: 0,
            blocked: false,
            startedAt: Date.now(),
            finishedAt: null,
          };
          run = current;
          try {
            chainIds ??= await loadChainIds();
          } catch (error) {
            run = null;
            throw error;
          }
          drain(current, { timeoutMs, maxAttempts, cooldownMs, haltAfter });
          return { queued: unique.length };
        }
      
        function status() {
          const records = run ? [...run.records.values()] : [];
          const ok = records.filter((record) => record.status === "ok").length;
          const total = run ? run.addresses.length : 0;
          return {
            running: Boolean(run && !run.finishedAt),
            total,
            done: records.length,
            ok,
            failed: records.length - ok,
            pending: total - records.length,
            rateLimited: run ? run.rateLimited : 0,
            blocked: run ? run.blocked : false,
            startedAt: run ? new Date(run.startedAt).toISOString() : null,
            elapsedMs: run ? (run.finishedAt ?? Date.now()) - run.startedAt : 0,
          };
        }
      
        const results = () => (run ? run.addresses.map((address) => run.records.get(address)).filter(Boolean) : []);
      
        window.__debankCollect = { start, status, results };
        return { installed: true, reused: false };
      }
      
    • debank-gate.py 8.7 KB
      #!/usr/bin/env python3
      """Host-wide FIFO lease queue for DeBank access by concurrent agents.
      
      DeBank's WAF rate-limits the whole browser, so one agent's burst blocks every other agent. Each agent takes a lease
      here before touching debank.com and releases it afterwards; a reported block pauses the queue for everyone.
      """
      
      from __future__ import annotations
      
      import argparse
      import contextlib
      import fcntl
      import json
      import os
      import signal
      import sys
      import time
      import uuid
      from datetime import datetime, timezone
      from pathlib import Path
      
      DEFAULT_TTL = 600
      DEFAULT_WAIT = 240
      DEFAULT_BLOCK_MINUTES = 15
      MAX_PROFILES = 25
      # A queued agent must poll again within this window or lose its place, so an abandoned ticket cannot stall the queue.
      STALE_TICKET_SECONDS = 120
      POLL_SECONDS = 1.0
      
      EXIT_QUEUED = 3
      EXIT_LOST = 4
      
      
      def state_dir() -> Path:
          override = os.environ.get("DEBANK_GATE_DIR")
          if override:
              return Path(override)
          base = os.environ.get("XDG_STATE_HOME") or str(Path.home() / ".local" / "state")
          return Path(base) / "evm-atlas" / "debank-gate"
      
      
      def iso(ts: float) -> str | None:
          return datetime.fromtimestamp(ts, timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") if ts else None
      
      
      @contextlib.contextmanager
      def locked_state():
          directory = state_dir()
          directory.mkdir(parents=True, exist_ok=True)
          with open(directory / "gate.lock", "w") as lock:
              fcntl.flock(lock, fcntl.LOCK_EX)
              path = directory / "state.json"
              try:
                  state = json.loads(path.read_text())
              except (FileNotFoundError, json.JSONDecodeError):
                  state = {"queue": [], "lease": None, "cooldownUntil": 0}
              yield state
              tmp = path.with_suffix(".tmp")
              tmp.write_text(json.dumps(state, indent=2) + "\n")
              os.replace(tmp, path)
      
      
      def prune(state: dict, now: float) -> None:
          lease = state["lease"]
          if lease and lease["expiresAt"] <= now:
              state["lease"] = None
          state["queue"] = [t for t in state["queue"] if now - t["seenAt"] < STALE_TICKET_SECONDS]
      
      
      def enqueue(state: dict, label: str, profiles: int, now: float) -> str:
          ticket = {"id": uuid.uuid4().hex[:12], "label": label, "profiles": profiles, "enqueuedAt": now, "seenAt": now}
          state["queue"].append(ticket)
          return ticket["id"]
      
      
      def try_grant(state: dict, ticket_id: str, ttl: int, now: float) -> str:
          """Return "granted", "queued", or "unknown" (never issued, or dropped as stale)."""
          prune(state, now)
          lease = state["lease"]
          if lease and lease["ticket"] == ticket_id:
              return "granted"
          ticket = next((t for t in state["queue"] if t["id"] == ticket_id), None)
          if ticket is None:
              return "unknown"
          ticket["seenAt"] = now
          if lease or now < state["cooldownUntil"] or state["queue"][0]["id"] != ticket_id:
              return "queued"
          state["queue"].pop(0)
          state["lease"] = {
              "ticket": ticket_id,
              "label": ticket["label"],
              "profiles": ticket["profiles"],
              "grantedAt": now,
              "expiresAt": now + ttl,
          }
          return "granted"
      
      
      def renew(state: dict, ticket_id: str, ttl: int, now: float) -> bool:
          prune(state, now)
          lease = state["lease"]
          if not lease or lease["ticket"] != ticket_id:
              return False
          lease["expiresAt"] = now + ttl
          return True
      
      
      def release(state: dict, ticket_id: str, now: float) -> None:
          prune(state, now)
          if state["lease"] and state["lease"]["ticket"] == ticket_id:
              state["lease"] = None
          state["queue"] = [t for t in state["queue"] if t["id"] != ticket_id]
      
      
      def block(state: dict, ticket_id: str | None, minutes: float, now: float) -> None:
          state["cooldownUntil"] = max(state["cooldownUntil"], now + minutes * 60)
          if ticket_id:
              release(state, ticket_id, now)
      
      
      def summary(state: dict, now: float, ticket_id: str | None = None) -> dict:
          lease = state["lease"]
          ids = [t["id"] for t in state["queue"]]
          return {
              "now": iso(now),
              "holder": {"label": lease["label"], "profiles": lease["profiles"], "expiresAt": iso(lease["expiresAt"])}
              if lease
              else None,
              "cooldownUntil": iso(state["cooldownUntil"]) if state["cooldownUntil"] > now else None,
              "queued": len(ids),
              **({"position": ids.index(ticket_id) + 1} if ticket_id in ids else {}),
          }
      
      
      def emit(payload: dict) -> None:
          print(json.dumps(payload))
      
      
      def cmd_acquire(args: argparse.Namespace) -> int:
          if not 1 <= args.profiles <= MAX_PROFILES:
              sys.exit(f"--profiles must be between 1 and {MAX_PROFILES}; split larger runs into batches")
          deadline = time.time() + args.wait
          ticket_id = args.ticket
          pending = True
      
          def cancel(signum, frame):
              raise SystemExit(128 + signum)
      
          signal.signal(signal.SIGTERM, cancel)
          try:
              while True:
                  now = time.time()
                  with locked_state() as state:
                      result = try_grant(state, ticket_id, args.ttl, now) if ticket_id else "unknown"
                      if result == "unknown":
                          ticket_id = enqueue(state, args.label, args.profiles, now)
                          result = try_grant(state, ticket_id, args.ttl, now)
                      info = summary(state, now, ticket_id)
                  if result == "granted":
                      pending = False
                      emit({"status": "granted", "ticket": ticket_id, "expiresAt": info["holder"]["expiresAt"]})
                      return 0
                  if now >= deadline:
                      pending = False
                      emit({"status": "queued", "ticket": ticket_id, **info})
                      return EXIT_QUEUED
                  time.sleep(min(POLL_SECONDS, deadline - now))
          finally:
              # An interrupted wait gives up its place instead of stalling the head of the queue.
              if pending and ticket_id:
                  with locked_state() as state:
                      release(state, ticket_id, time.time())
      
      
      def cmd_renew(args: argparse.Namespace) -> int:
          now = time.time()
          with locked_state() as state:
              ok = renew(state, args.ticket, args.ttl, now)
              info = summary(state, now)
          if not ok:
              emit({"status": "lost", "ticket": args.ticket, **info})
              return EXIT_LOST
          emit({"status": "renewed", "ticket": args.ticket, "expiresAt": info["holder"]["expiresAt"]})
          return 0
      
      
      def cmd_release(args: argparse.Namespace) -> int:
          now = time.time()
          with locked_state() as state:
              release(state, args.ticket, now)
              info = summary(state, now)
          emit({"status": "released", "ticket": args.ticket, **info})
          return 0
      
      
      def cmd_block(args: argparse.Namespace) -> int:
          now = time.time()
          with locked_state() as state:
              block(state, args.ticket, args.minutes, now)
              info = summary(state, now)
          emit({"status": "cooldown", **info})
          return 0
      
      
      def cmd_status(args: argparse.Namespace) -> int:
          now = time.time()
          with locked_state() as state:
              prune(state, now)
              info = summary(state, now)
              info["queue"] = [{"label": t["label"], "profiles": t["profiles"]} for t in state["queue"]]
          emit(info)
          return 0
      
      
      def main(argv: list[str] | None = None) -> int:
          parser = argparse.ArgumentParser(description=__doc__)
          sub = parser.add_subparsers(dest="command", required=True)
      
          acquire = sub.add_parser("acquire", help="join the queue and wait for the lease")
          acquire.add_argument("--label", required=True, help="who is asking, e.g. 'sweep-eth holdings check'")
          acquire.add_argument("--profiles", type=int, default=1, help=f"profiles this lease covers (max {MAX_PROFILES})")
          acquire.add_argument("--ticket", help="resume a queued ticket and keep its place")
          acquire.add_argument("--wait", type=float, default=DEFAULT_WAIT, help="seconds to wait before returning queued")
          acquire.add_argument("--ttl", type=int, default=DEFAULT_TTL, help="lease lifetime in seconds")
          acquire.set_defaults(func=cmd_acquire)
      
          renew_parser = sub.add_parser("renew", help="extend a held lease")
          renew_parser.add_argument("--ticket", required=True)
          renew_parser.add_argument("--ttl", type=int, default=DEFAULT_TTL)
          renew_parser.set_defaults(func=cmd_renew)
      
          release_parser = sub.add_parser("release", help="free a lease or leave the queue")
          release_parser.add_argument("--ticket", required=True)
          release_parser.set_defaults(func=cmd_release)
      
          block_parser = sub.add_parser("block", help="report a WAF block: pause the queue and release the lease")
          block_parser.add_argument("--ticket")
          block_parser.add_argument("--minutes", type=float, default=DEFAULT_BLOCK_MINUTES)
          block_parser.set_defaults(func=cmd_block)
      
          sub.add_parser("status", help="show the holder, cooldown, and queue").set_defaults(func=cmd_status)
      
          args = parser.parse_args(argv)
          return args.func(args)
      
      
      if __name__ == "__main__":
          sys.exit(main())
      
    • etherscan-detect-plan.sh 3.1 KB
      #!/bin/bash
      # etherscan-detect-plan.sh — Detect Etherscan API plan tier from $ETHERSCAN_API_KEY.
      #
      # Outputs key=value lines on stdout:
      #   plan=<free|lite|standard|advanced|professional|pro_plus|enterprise|unknown>
      #   credit_limit=<int>
      #   credits_used=<int>
      #   credits_available=<int>
      #   limit_interval=<string>
      #   interval_expiry=<HH:MM:SS>
      #   pro_endpoints=<true|false>
      #   paid_chains=<true|false>
      #
      # Cache the result for the session — getapilimit itself consumes 1 credit, and
      # the paid-chain probe (when needed) consumes another.
      
      set -eu
      
      if [ -z "${ETHERSCAN_API_KEY:-}" ]; then
        echo "Error: ETHERSCAN_API_KEY is not set" >&2
        exit 1
      fi
      
      base="https://api.etherscan.io/v2/api"
      response=$(curl -fsS "$base?chainid=1&module=getapilimit&action=getapilimit&apikey=$ETHERSCAN_API_KEY")
      
      extract_num() {
        printf '%s' "$1" | grep -o "\"$2\":[0-9]*" | head -1 | grep -o '[0-9]*'
      }
      extract_str() {
        printf '%s' "$1" | grep -o "\"$2\":\"[^\"]*\"" | head -1 | sed 's/.*:"\([^"]*\)"/\1/'
      }
      
      status=$(extract_str "$response" "status")
      if [ "$status" != "1" ]; then
        msg=$(extract_str "$response" "message")
        res=$(extract_str "$response" "result")
        echo "Error: getapilimit failed — message=$msg result=$res" >&2
        exit 1
      fi
      
      credit_limit=$(extract_num "$response" "creditLimit")
      credits_used=$(extract_num "$response" "creditsUsed")
      credits_avail=$(extract_num "$response" "creditsAvailable")
      interval=$(extract_str "$response" "limitInterval")
      expiry=$(extract_str "$response" "intervalExpiryTimespan")
      
      # Map creditLimit → plan. Free and Lite share the 100k daily credit; the
      # difference is paid-only-chain access. Standard+ implies PRO endpoints and
      # paid-chain access; no probe needed.
      case "$credit_limit" in
        100000)  plan="free_or_lite";  pro_endpoints="false"; paid_chains="probe" ;;
        200000)  plan="standard";      pro_endpoints="true";  paid_chains="true"  ;;
        500000)  plan="advanced";      pro_endpoints="true";  paid_chains="true"  ;;
        1000000) plan="professional";  pro_endpoints="true";  paid_chains="true"  ;;
        1500000) plan="pro_plus";      pro_endpoints="true";  paid_chains="true"  ;;
        *)
          if [ "${credit_limit:-0}" -gt 1500000 ]; then
            plan="enterprise"; pro_endpoints="true"; paid_chains="true"
          else
            plan="unknown"; pro_endpoints="unknown"; paid_chains="unknown"
          fi
          ;;
      esac
      
      # Lite ($49/mo) unlocks the paid Etherscan target chains (Base, OP,
      # Avalanche, BNB) while Free does not. Probe a Base balance to disambiguate: status=1 → Lite,
      # status=0 → Free. PRO endpoints stay false on Lite (Standard plan and up).
      if [ "$plan" = "free_or_lite" ]; then
        probe=$(curl -fsS "$base?chainid=8453&module=account&action=balance&address=0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe&tag=latest&apikey=$ETHERSCAN_API_KEY" 2>/dev/null || printf '{"status":"0"}')
        probe_status=$(extract_str "$probe" "status")
        if [ "$probe_status" = "1" ]; then
          plan="lite"
          paid_chains="true"
        else
          plan="free"
          paid_chains="false"
        fi
      fi
      
      cat <<EOF
      plan=$plan
      credit_limit=$credit_limit
      credits_used=$credits_used
      credits_available=$credits_avail
      limit_interval=$interval
      interval_expiry=$expiry
      pro_endpoints=$pro_endpoints
      paid_chains=$paid_chains
      EOF
      
    • resolve-chain.sh 5 KB
      #!/bin/bash
      # resolve-chain.sh - Resolve a target chain_id to its Blockscout instance via Chainscout.
      #
      # Generated by scripts/generate-evm-atlas.ts. Registry metadata comes from crypto-registry data/chains.json; edit skills/evm-atlas/references/atlas-overlays.json for Atlas-only fields.
      #
      # Usage: resolve-chain.sh <target_chain_id>
      #
      # Outputs key=value lines on stdout:
      #   chain_id=<int>
      #   name=<string>
      #   native_currency=<symbol>
      #   instance_url=<url, ends in />
      #   api_url=<API base URL, without trailing slash>
      #   hosted_by=<blockscout|other>
      #   is_testnet=<true|false>
      #   layer=<int|>
      #   rollup_type=<string|>
      #
      # No API key required. Source: https://chains.blockscout.com/
      # Scope is intentionally limited to skills/evm-atlas/references/generated/target-mainnets.json.
      
      set -eu
      
      if [ $# -lt 1 ] || [ -z "${1:-}" ]; then
        echo "Usage: resolve-chain.sh <chain_id>" >&2
        exit 1
      fi
      
      chain_id="$1"
      
      target_name_pattern() {
        case "$1" in
          2741) printf '%s\n' 'Abstract' ;;
          42161) printf '%s\n' 'Arbitrum' ;;
          42170) printf '%s\n' 'Arbitrum Nova' ;;
          43114) printf '%s\n' 'Avalanche' ;;
          8453) printf '%s\n' 'Base' ;;
          80094) printf '%s\n' 'Berachain' ;;
          81457) printf '%s\n' 'Blast' ;;
          56) printf '%s\n' 'BNB|BSC|Smart Chain' ;;
          42220) printf '%s\n' 'Celo' ;;
          88888) printf '%s\n' 'Chiliz' ;;
          1116) printf '%s\n' 'Core' ;;
          1) printf '%s\n' 'Ethereum' ;;
          250) printf '%s\n' 'Fantom' ;;
          314) printf '%s\n' 'Filecoin|Filecoin Virtual Machine|FVM' ;;
          252) printf '%s\n' 'Fraxtal' ;;
          100) printf '%s\n' 'Gnosis' ;;
          999) printf '%s\n' 'HyperEVM|Hyper' ;;
          4689) printf '%s\n' 'IoTeX' ;;
          1890) printf '%s\n' 'Lightlink|LightLink' ;;
          59144) printf '%s\n' 'Linea' ;;
          34443) printf '%s\n' 'Mode' ;;
          143) printf '%s\n' 'Monad' ;;
          2818) printf '%s\n' 'Morph' ;;
          10) printf '%s\n' 'OP|Optimism' ;;
          137) printf '%s\n' 'Polygon' ;;
          4663) printf '%s\n' 'Robinhood Chain' ;;
          2020) printf '%s\n' 'Ronin' ;;
          534352) printf '%s\n' 'Scroll' ;;
          1329) printf '%s\n' 'Sei' ;;
          146) printf '%s\n' 'Sonic' ;;
          50104) printf '%s\n' 'Sophon' ;;
          5330) printf '%s\n' 'Superseed' ;;
          167000) printf '%s\n' 'Taiko|Alethia' ;;
          130) printf '%s\n' 'Unichain' ;;
          480) printf '%s\n' 'World' ;;
          50) printf '%s\n' 'XDC' ;;
          324) printf '%s\n' 'ZKsync' ;;
          7777777) printf '%s\n' 'Zora' ;;
          *)
            return 1
            ;;
        esac
      }
      
      expected_pattern=$(target_name_pattern "$chain_id") || {
        echo "Error: chain_id=$chain_id is outside the evm-atlas target list." >&2
        echo "Ask the user to file a feature request in https://github.com/PaulRBerg/agent-skills" >&2
        exit 2
      }
      
      unsafe_reason() {
        # shellcheck disable=SC2016
        case "$1" in
          250) printf '%s\n' 'Chainscout still lists self-hosted FTMScout at https://ftmscout.com/, but as checked 2026-07-31 its frontend returns HTTP 200 while /api/v2/* data routes return HTTP 500; do not use it for evidence' ;;
          2020) printf '%s\n' 'Chainscout returns a different network for `2020`; app.roninchain.com blocks scripted access, so verify with `$chromium-browser` instead of curl or WebFetch' ;;
          5330) printf '%s\n' 'Chromium verified 2026-09-15: explorer.superseed.xyz now serves Conduit Explorer, which does not support historical transactions, holdings, or transfers; do not use its stale Chainscout Blockscout route' ;;
          *)
            return 1
            ;;
        esac
      }
      
      if reason=$(unsafe_reason "$chain_id"); then
        echo "Error: chain_id=$chain_id is marked unsafe in the evm-atlas overlay." >&2
        echo "Reason: $reason" >&2
        exit 1
      fi
      
      resp=$(curl -fsS "https://chains.blockscout.com/api/chains/$chain_id" 2>/dev/null) || {
        echo "Error: Chainscout request failed for chain_id=$chain_id" >&2
        exit 1
      }
      
      sval() { printf '%s' "$resp" | grep -o "\"$1\":\"[^\"]*\"" | head -1 | sed 's/.*:"\(.*\)"/\1/'; }
      nval() { printf '%s' "$resp" | grep -o "\"$1\":[0-9]*" | head -1 | grep -o '[0-9]*'; }
      bval() { printf '%s' "$resp" | grep -oE "\"$1\":(true|false)" | head -1 | sed 's/.*://'; }
      
      name=$(sval "name")
      if [ -z "$name" ]; then
        echo "Error: chain_id=$chain_id not found in Chainscout" >&2
        exit 1
      fi
      if ! printf '%s' "$name" | grep -Eiq "$expected_pattern"; then
        echo "Error: Chainscout returned name=$name for target chain_id=$chain_id; expected match=$expected_pattern" >&2
        echo "Refusing to use a non-target Chainscout registry match." >&2
        exit 1
      fi
      
      native=$(sval "native_currency")
      # "url" only appears inside explorers[]; the first is the primary explorer.
      instance=$(printf '%s' "$resp" | grep -o '"url":"[^"]*"' | head -1 | sed 's/.*:"\(.*\)"/\1/')
      hosted=$(sval "hostedBy")
      testnet=$(bval "isTestnet")
      layer=$(nval "layer")
      rollup=$(sval "rollupType")
      
      api="${instance%/}/api"
      # Explicit registry API bases override stale Chainscout page-host routes.
      case "$chain_id" in
        2818) instance='https://explorer.morph.network/'; api='https://explorer-api.morph.network/api' ;;
      esac
      
      cat <<EOF
      chain_id=$chain_id
      name=$name
      native_currency=$native
      instance_url=$instance
      api_url=$api
      hosted_by=$hosted
      is_testnet=$testnet
      layer=$layer
      rollup_type=$rollup
      EOF
      
    • sweep-core.py 22.6 KB
      #!/usr/bin/env python3
      """Plan, evaluate, and validate deterministic EVM address-sweep evidence."""
      
      from __future__ import annotations
      
      import argparse
      import datetime as dt
      import json
      import re
      import sys
      from pathlib import Path
      from typing import Any
      
      
      ADDRESS_RE = re.compile(r"^0x[0-9a-fA-F]{40}$")
      HASH_RE = re.compile(r"^0x[0-9a-fA-F]{64}$")
      HEX_RE = re.compile(r"^0x(?:0|[1-9a-fA-F][0-9a-fA-F]*)$")
      TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
      HISTORY_CHANNELS = ("txlist", "txlistinternal", "tokentx", "tokennfttx", "token1155tx")
      PROFILE_CHANNELS = {
          "general": ("nonce", "native-balance", *HISTORY_CHANNELS),
          "bootstrap-discovery": ("nonce", "native-balance", "txlist", "txlistinternal", "tokentx", "tokennfttx"),
      }
      
      
      class SweepError(ValueError):
          pass
      
      
      def load_json(path: Path | None) -> Any:
          try:
              return json.loads(path.read_text(encoding="utf-8") if path else sys.stdin.read())
          except (OSError, json.JSONDecodeError) as exc:
              raise SweepError(f"cannot read JSON: {exc}") from exc
      
      
      def iso_time(value: Any, field: str) -> dt.datetime:
          if not isinstance(value, str):
              raise SweepError(f"{field} must be an ISO-8601 string")
          try:
              parsed = dt.datetime.fromisoformat(value.replace("Z", "+00:00"))
          except ValueError as exc:
              raise SweepError(f"{field} must be an ISO-8601 string") from exc
          if parsed.tzinfo is None:
              raise SweepError(f"{field} must include a timezone")
          return parsed
      
      
      def normalize_profile(value: Any) -> str:
          aliases = {"bootstrap": "bootstrap-discovery", "bootstrap discovery": "bootstrap-discovery"}
          normalized = aliases.get(value, value)
          if normalized not in PROFILE_CHANNELS:
              raise SweepError(f"unsupported profile: {value}")
          return normalized
      
      
      def validate_checkpoint(checkpoint: Any) -> dict[str, Any]:
          if not isinstance(checkpoint, dict):
              raise SweepError("checkpoint must be an object")
          required = ("requestedAt", "resolutionKind", "blockNumber", "blockHash", "blockTimestamp", "observedAt")
          missing = [field for field in required if field not in checkpoint]
          if missing:
              raise SweepError(f"checkpoint is missing: {', '.join(missing)}")
          if checkpoint["resolutionKind"] not in {"finalized", "verified"}:
              raise SweepError("checkpoint resolutionKind must be finalized or verified")
          if not isinstance(checkpoint["blockNumber"], int) or checkpoint["blockNumber"] < 0:
              raise SweepError("checkpoint blockNumber must be a non-negative integer")
          if not isinstance(checkpoint["blockHash"], str) or not HASH_RE.fullmatch(checkpoint["blockHash"]):
              raise SweepError("checkpoint blockHash must contain 32 hex bytes")
          requested = iso_time(checkpoint["requestedAt"], "checkpoint requestedAt")
          block_time = iso_time(checkpoint["blockTimestamp"], "checkpoint blockTimestamp")
          observed = iso_time(checkpoint["observedAt"], "checkpoint observedAt")
          if block_time > requested:
              raise SweepError("checkpoint blockTimestamp is after requestedAt")
          if observed < block_time:
              raise SweepError("checkpoint observedAt is before blockTimestamp")
          return {**checkpoint, "blockHash": checkpoint["blockHash"].lower()}
      
      
      def build_plan(spec: Any) -> dict[str, Any]:
          if not isinstance(spec, dict):
              raise SweepError("plan input must be an object")
          address = spec.get("address")
          if not isinstance(address, str) or not ADDRESS_RE.fullmatch(address):
              raise SweepError("address must contain exactly 20 hex bytes")
          chain = spec.get("chain")
          if not isinstance(chain, dict) or not isinstance(chain.get("id"), int) or chain["id"] <= 0:
              raise SweepError("chain.id must be a positive integer")
          model = chain.get("accountActivityModel")
          if not isinstance(model, str) or not model:
              raise SweepError("chain.accountActivityModel must be a non-empty string")
          goal = spec.get("goal", "historical-activity")
          if goal not in {"historical-activity", "bootstrap-discovery"}:
              raise SweepError(f"unsupported goal: {goal}")
          profile = normalize_profile(spec.get("profile", "general"))
          checkpoint = validate_checkpoint(spec.get("checkpoint"))
          providers = spec.get("providers")
          if not isinstance(providers, list) or not providers:
              raise SweepError("providers must be a non-empty array selected by the agent")
          normalized_providers: list[dict[str, Any]] = []
          provider_ids: set[str] = set()
          for provider in providers:
              if not isinstance(provider, dict) or not isinstance(provider.get("id"), str) or not provider["id"]:
                  raise SweepError("every provider must have a non-empty id")
              if provider["id"] in provider_ids:
                  raise SweepError(f"duplicate provider id: {provider['id']}")
              provider_ids.add(provider["id"])
              capabilities = provider.get("capabilities")
              if not isinstance(capabilities, list) or not all(channel in HISTORY_CHANNELS for channel in capabilities):
                  raise SweepError(f"provider {provider['id']} has invalid capabilities")
              normalized_providers.append(
                  {
                      "id": provider["id"],
                      "kind": provider.get("kind", "selected-indexer"),
                      "independenceGroup": provider.get("independenceGroup", provider["id"]),
                      "capabilities": list(dict.fromkeys(capabilities)),
                  }
              )
          quorum = spec.get("quorum", 1)
          if isinstance(quorum, dict):
              quorum = quorum.get("required", 1) if quorum.get("enabled", False) else 1
          if not isinstance(quorum, int) or quorum < 1:
              raise SweepError("quorum must be a positive integer")
          if quorum > len({provider["independenceGroup"] for provider in normalized_providers}):
              raise SweepError("quorum exceeds independent provider groups")
          required = list(PROFILE_CHANNELS[profile])
          state_requests = [
              {
                  "requestId": f"state:{channel}",
                  "channel": channel,
                  "transport": "json-rpc",
                  "method": method,
                  "params": [address.lower(), {"blockHash": checkpoint["blockHash"], "requireCanonical": True}],
                  "credentials": [],
              }
              for channel, method in (("nonce", "eth_getTransactionCount"), ("native-balance", "eth_getBalance"))
          ]
          history_requests = []
          for provider in normalized_providers:
              for channel in required:
                  if channel not in HISTORY_CHANNELS or channel not in provider["capabilities"]:
                      continue
                  history_requests.append(
                      {
                          "requestId": f"history:{provider['id']}:{channel}",
                          "providerId": provider["id"],
                          "channel": channel,
                          "transport": "indexed-history",
                          "operation": {"providerKind": provider["kind"], "action": channel},
                          "bounds": {"startBlock": 0, "endBlock": checkpoint["blockNumber"]},
                          "order": "asc" if quorum > 1 else "desc",
                          "credentials": [],
                      }
                  )
          return {
              "schemaVersion": 1,
              "address": address.lower(),
              "chain": chain,
              "goal": goal,
              "profile": profile,
              "checkpoint": checkpoint,
              "quorumRequirement": quorum,
              "requiredChannels": required,
              "providers": normalized_providers,
              "requests": {"state": state_requests, "history": history_requests},
          }
      
      
      def parse_quantity(value: Any, context: str) -> int:
          if isinstance(value, int) and not isinstance(value, bool) and value >= 0:
              return value
          if not isinstance(value, str):
              raise SweepError(f"{context} must be a non-negative integer or quantity string")
          if HEX_RE.fullmatch(value):
              return int(value, 16)
          if re.fullmatch(r"0|[1-9]\d*", value):
              return int(value)
          raise SweepError(f"{context} must be a non-negative integer or quantity string")
      
      
      def row_block(row: dict[str, Any]) -> int:
          value = row.get("blockNumber", row.get("block_number"))
          return parse_quantity(value, "row block number")
      
      
      def touches(row: dict[str, Any], address: str) -> bool:
          values = [row.get(key) for key in ("from", "to", "contractAddress", "created_contract")]
          return any(isinstance(value, str) and value.lower() == address for value in values)
      
      
      def failed_row(row: dict[str, Any]) -> bool:
          return str(row.get("isError", row.get("is_error", "0"))) == "1" or str(row.get("txreceipt_status", "1")) == "0" or bool(str(row.get("errCode", row.get("error", ""))).strip())
      
      
      def qualifies(profile: str, channel: str, row: dict[str, Any], address: str) -> bool:
          if failed_row(row) or not touches(row, address):
              return False
          if profile == "general" or channel in {"tokentx", "tokennfttx", "token1155tx"}:
              return True
          value = parse_quantity(row.get("value", 0), f"{channel} row value")
          if channel == "txlist":
              outgoing = isinstance(row.get("from"), str) and row["from"].lower() == address
              return outgoing or value > 0
          if channel == "txlistinternal":
              return value > 0
          return True
      
      
      def evidence(provider_id: str, channel: str, row: dict[str, Any]) -> dict[str, Any]:
          transaction_hash = row.get("hash", row.get("transactionHash", row.get("transaction_hash")))
          if not isinstance(transaction_hash, str) or not HASH_RE.fullmatch(transaction_hash):
              raise SweepError(f"qualifying {channel} row has no valid transaction hash")
          timestamp = row.get("timeStamp", row.get("timestamp"))
          return {
              "providerId": provider_id,
              "channel": channel,
              "transactionHash": transaction_hash.lower(),
              "blockNumber": row_block(row),
              "timestamp": timestamp,
          }
      
      
      def evidence_key(item: dict[str, Any]) -> tuple[Any, ...]:
          return (item["blockNumber"], str(item.get("timestamp") or ""), item["transactionHash"], item["channel"])
      
      
      def evaluate(plan: Any, responses: Any) -> dict[str, Any]:
          if not isinstance(plan, dict) or plan.get("schemaVersion") != 1:
              raise SweepError("plan schemaVersion must be 1")
          if not isinstance(responses, dict):
              raise SweepError("responses must be an object")
          checkpoint = validate_checkpoint(plan.get("checkpoint"))
          address = plan.get("address")
          if not isinstance(address, str) or not ADDRESS_RE.fullmatch(address):
              raise SweepError("plan address is invalid")
          profile = normalize_profile(plan.get("profile"))
          required = plan.get("requiredChannels")
          if required != list(PROFILE_CHANNELS[profile]):
              raise SweepError("plan requiredChannels do not match the profile")
          checked: list[dict[str, Any]] = []
          omitted: list[dict[str, str]] = []
          gaps: list[dict[str, str]] = []
          state = responses.get("state") or {}
          if not isinstance(state, dict):
              raise SweepError("responses.state must be an object")
          state_values: dict[str, int] = {}
          state_positive: dict[str, Any] | None = None
          for channel in ("nonce", "native-balance"):
              response = state.get(channel)
              if not isinstance(response, dict) or response.get("ok") is not True:
                  gaps.append({"channel": channel, "reason": "missing or failed checkpoint-bound state response"})
                  continue
              if response.get("blockHash", "").lower() != checkpoint["blockHash"] and response.get("checkpointBound") is not True:
                  gaps.append({"channel": channel, "reason": "state response is not bound to the checkpoint hash"})
                  continue
              try:
                  quantity = parse_quantity(response.get("result"), f"state {channel}")
              except SweepError as exc:
                  gaps.append({"channel": channel, "reason": str(exc)})
                  continue
              state_values[channel] = quantity
              checked.append({"channel": channel, "source": "state-rpc", "result": "positive" if quantity else "negative"})
              if quantity and state_positive is None:
                  state_positive = {"source": "state-rpc", "channel": channel, "value": quantity, "blockNumber": checkpoint["blockNumber"], "blockHash": checkpoint["blockHash"]}
      
          zero_shortcut = (
              profile == "bootstrap-discovery"
              and plan.get("chain", {}).get("accountActivityModel") == "ethereum-eoa"
              and state_values.get("nonce") == 0
              and state_values.get("native-balance") == 0
          )
          history_required = [channel for channel in required if channel in HISTORY_CHANNELS]
          if zero_shortcut:
              for channel in ("txlist", "txlistinternal"):
                  history_required.remove(channel)
                  omitted.append({"channel": channel, "reason": "ethereum-eoa zero-state invariant"})
      
          provider_responses = responses.get("providers") or {}
          if not isinstance(provider_responses, dict):
              raise SweepError("responses.providers must be an object")
          provider_results: list[dict[str, Any]] = []
          for provider in plan.get("providers", []):
              provider_id = provider["id"]
              response = provider_responses.get(provider_id)
              if not isinstance(response, dict):
                  provider_results.append({"providerId": provider_id, "complete": False, "earliest": None, "positive": False})
                  continue
              try:
                  indexed_through = parse_quantity(response.get("indexedThrough"), f"provider {provider_id} indexedThrough")
              except SweepError as exc:
                  gaps.append({"channel": "history", "reason": str(exc)})
                  provider_results.append({"providerId": provider_id, "complete": False, "earliest": None, "positive": False})
                  continue
              if indexed_through < checkpoint["blockNumber"]:
                  gaps.append({"channel": "history", "reason": f"provider {provider_id} is indexed only through {indexed_through}"})
                  provider_results.append({"providerId": provider_id, "complete": False, "earliest": None, "positive": False})
                  continue
              channels = response.get("channels")
              if not isinstance(channels, dict):
                  gaps.append({"channel": "history", "reason": f"provider {provider_id} channels are malformed"})
                  provider_results.append({"providerId": provider_id, "complete": False, "earliest": None, "positive": False})
                  continue
              provider_complete = True
              provider_evidence: list[dict[str, Any]] = []
              for channel in history_required:
                  channel_response = channels.get(channel)
                  if not isinstance(channel_response, dict) or channel_response.get("ok") is not True:
                      provider_complete = False
                      gaps.append({"channel": channel, "reason": f"provider {provider_id} response is missing or failed"})
                      continue
                  rows = channel_response.get("rows")
                  if not isinstance(rows, list) or not all(isinstance(row, dict) for row in rows):
                      provider_complete = False
                      gaps.append({"channel": channel, "reason": f"provider {provider_id} rows are malformed"})
                      continue
                  try:
                      bounded = all(row_block(row) <= checkpoint["blockNumber"] for row in rows)
                  except SweepError as exc:
                      provider_complete = False
                      gaps.append({"channel": channel, "reason": f"provider {provider_id}: {exc}"})
                      continue
                  if not bounded:
                      provider_complete = False
                      gaps.append({"channel": channel, "reason": f"provider {provider_id} returned a post-checkpoint row"})
                      continue
                  try:
                      qualifying = [row for row in rows if qualifies(profile, channel, row, address.lower())]
                      channel_evidence = [evidence(provider_id, channel, row) for row in qualifying]
                  except SweepError as exc:
                      provider_complete = False
                      gaps.append({"channel": channel, "reason": f"provider {provider_id}: {exc}"})
                      continue
                  provider_evidence.extend(channel_evidence)
                  complete = channel_response.get("complete") is True
                  if not channel_evidence and not complete:
                      provider_complete = False
                      gaps.append({"channel": channel, "reason": f"provider {provider_id} did not exhaust the bounded channel"})
                  elif channel_evidence and not complete:
                      provider_complete = False
                      gaps.append({"channel": channel, "reason": f"provider {provider_id} positive response is not complete enough for earliest evidence"})
                  checked.append(
                      {
                          "channel": channel,
                          "source": provider_id,
                          "result": "positive" if channel_evidence else "negative" if complete else "unknown",
                      }
                  )
              earliest = min(provider_evidence, key=evidence_key) if provider_evidence else None
              provider_results.append(
                  {
                      "providerId": provider_id,
                      "independenceGroup": provider["independenceGroup"],
                      "complete": provider_complete,
                      "positive": bool(provider_evidence),
                      "earliest": earliest,
                  }
              )
      
          quorum = plan.get("quorumRequirement", 1)
          agreement: bool | None = None
          earliest: dict[str, Any] | None = state_positive
          result = "unknown"
          if state_positive:
              result = "positive"
          elif quorum == 1:
              usable = next((provider for provider in provider_results if provider["positive"] or provider["complete"]), None)
              if usable and usable["positive"]:
                  result, earliest = "positive", usable["earliest"]
              elif usable and usable["complete"] and not gaps:
                  result = "negative"
              agreement = None
          else:
              votes = [provider for provider in provider_results if provider.get("complete")]
              distinct: list[dict[str, Any]] = []
              seen_groups: set[str] = set()
              for vote in votes:
                  if vote["independenceGroup"] not in seen_groups:
                      distinct.append(vote)
                      seen_groups.add(vote["independenceGroup"])
              if len(distinct) >= quorum:
                  selected = distinct[:quorum]
                  if all(vote["positive"] for vote in selected):
                      keys = {evidence_key(vote["earliest"]) for vote in selected}
                      agreement = len(keys) == 1
                      if agreement:
                          result, earliest = "positive", selected[0]["earliest"]
                  elif all(vote["complete"] and not vote["positive"] for vote in selected):
                      agreement, result = True, "negative"
                  else:
                      agreement = False
              if agreement is False:
                  gaps.append({"channel": "quorum", "reason": "independent history providers disagree"})
      
          coverage = "complete" if result == "negative" and not gaps else "partial" if checked or omitted else "unknown"
          if result == "positive" and earliest:
              coverage = "complete" if not gaps else "partial"
          return {
              "schemaVersion": 1,
              "result": result,
              "coverage": coverage,
              "profile": profile,
              "checkpoint": checkpoint,
              "checked": checked,
              "omitted": omitted,
              "gaps": gaps,
              "earliestQualifyingEvidence": earliest,
              "quorum": {"required": quorum, "agreement": agreement, "providers": provider_results},
          }
      
      
      def validate_blockscout(payload: Any) -> dict[str, Any]:
          fields = ("transactions_count", "token_transfers_count", "gas_usage_count", "validations_count")
          valid = isinstance(payload, dict) and all(isinstance(payload.get(field), str) and re.fullmatch(r"0|[1-9]\d*", payload[field]) for field in fields)
          return {"schemaVersion": 1, "validator": "blockscout-address-counters", "valid": valid, "counts": {field: payload.get(field) for field in fields} if isinstance(payload, dict) else {}}
      
      
      def validate_transfer_topics(payload: Any, address: str) -> dict[str, Any]:
          if not ADDRESS_RE.fullmatch(address):
              raise SweepError("address must contain exactly 20 hex bytes")
          address_topic = "0x" + "0" * 24 + address[2:].lower()
          rows = payload.get("result") if isinstance(payload, dict) and payload.get("status") == "1" else None
          conformant_rows = []
          if isinstance(rows, list):
              for row in rows:
                  topics = row.get("topics") if isinstance(row, dict) else None
                  if not isinstance(topics, list) or len(topics) < 3 or not all(isinstance(topic, str) for topic in topics[:3]):
                      conformant_rows = []
                      break
                  lowered = [topic.lower() for topic in topics]
                  if lowered[0] != TRANSFER_TOPIC or address_topic not in lowered[1:3]:
                      conformant_rows = []
                      break
                  conformant_rows.append(lowered)
          outbound = sum(row[1] == address_topic and row[2] != address_topic for row in conformant_rows)
          inbound = sum(row[2] == address_topic and row[1] != address_topic for row in conformant_rows)
          return {
              "schemaVersion": 1,
              "validator": "etherscan-transfer-topics",
              "valid": bool(conformant_rows and outbound and inbound),
              "outboundOnlyResults": outbound,
              "inboundOnlyResults": inbound,
          }
      
      
      def build_parser() -> argparse.ArgumentParser:
          parser = argparse.ArgumentParser(description=__doc__)
          subparsers = parser.add_subparsers(dest="command", required=True)
          plan = subparsers.add_parser("plan")
          plan.add_argument("--input", required=True, type=Path)
          evaluate_parser = subparsers.add_parser("evaluate")
          evaluate_parser.add_argument("--plan", required=True, type=Path)
          evaluate_parser.add_argument("--responses", required=True, type=Path)
          validate_parser = subparsers.add_parser("validate")
          validate_parser.add_argument("validator", choices=("blockscout-address-counters", "etherscan-transfer-topics"))
          validate_parser.add_argument("--input", type=Path)
          validate_parser.add_argument("--address")
          return parser
      
      
      def main() -> int:
          args = build_parser().parse_args()
          try:
              if args.command == "plan":
                  result = build_plan(load_json(args.input))
              elif args.command == "evaluate":
                  result = evaluate(load_json(args.plan), load_json(args.responses))
              else:
                  payload = load_json(args.input)
                  if args.validator == "blockscout-address-counters":
                      result = validate_blockscout(payload)
                  else:
                      if not args.address:
                          raise SweepError("etherscan-transfer-topics requires --address")
                      result = validate_transfer_topics(payload, args.address)
          except SweepError as exc:
              print(f"ERROR: {exc}", file=sys.stderr)
              return 64
          print(json.dumps(result, indent=2, ensure_ascii=False))
          return 0 if result.get("valid", True) else 1
      
      
      if __name__ == "__main__":
          raise SystemExit(main())
      
    • validate-blockscout-address-counters.sh 717 B
      #!/bin/bash
      # Compatibility adapter for sweep-core.py validate.
      
      set -eu
      
      if [ "$#" -ne 0 ]; then
        echo "Usage: validate-blockscout-address-counters.sh < response.json" >&2
        exit 2
      fi
      
      script_dir=$(CDPATH='' cd "$(dirname "$0")" && pwd)
      set +e
      result=$(python3 "$script_dir/sweep-core.py" validate blockscout-address-counters)
      rc=$?
      set -e
      if [ "$rc" -ne 0 ]; then
        echo "Error: response is not a conformant Blockscout address-counters object." >&2
        exit "$rc"
      fi
      printf '%s\n' "$result" | jq -r '
        "transactions_count=\(.counts.transactions_count)",
        "token_transfers_count=\(.counts.token_transfers_count)",
        "gas_usage_count=\(.counts.gas_usage_count)",
        "validations_count=\(.counts.validations_count)"
      '
      
    • validate-etherscan-transfer-topics.sh 689 B
      #!/bin/bash
      # Compatibility adapter for sweep-core.py validate.
      
      set -eu
      
      if [ "$#" -ne 1 ]; then
        echo "Usage: validate-etherscan-transfer-topics.sh <address> < response.json" >&2
        exit 2
      fi
      
      script_dir=$(CDPATH='' cd "$(dirname "$0")" && pwd)
      set +e
      result=$(python3 "$script_dir/sweep-core.py" validate etherscan-transfer-topics --address "$1")
      rc=$?
      set -e
      if [ "$rc" -ne 0 ]; then
        echo "Error: response does not prove distinct inbound-only and outbound-only Transfer topic OR semantics." >&2
        exit "$rc"
      fi
      printf '%s\n' "$result" | jq -r '
        "transfer_topic_query=conformant",
        "outbound_only_results=\(.outboundOnlyResults)",
        "inbound_only_results=\(.inboundOnlyResults)"
      '
      
  • SKILL.md 9.9 KB
    ---
    argument-hint: "<chain-name-or-id|address|transaction-hash|order-id>"
    compatibility: Requires the `routemesh` CLI initialized on macOS with `routemesh init` for RouteMesh requests.
    coordination: exempt
    name: evm-atlas
    skill-dependencies:
      - chromium-browser
      - cli-cast
    description:
      "Use for targeted EVM chain, account, transaction, RPC, explorer, bridge, and DEX evidence: chain name/ID, native
      symbol, RouteMesh, wallet balances and DeFi positions via DeBank/Blockscan in Chromium, cross-chain USD portfolio
      value or net worth, token/NFT holdings/transfers, tx history, funding origin via Etherscan/Blockscout/Chainscout;
      Across, Bungee, deBridge, Gas.zip, Hop, Layerswap, LayerZero, LI.FI, Relay, Socket, Symbiosis; Uniswap v1-v4,
      Universal Router, Permit2, 1inch Classic/Fusion/Fusion+, and CoW Swap, CoWSwap, CoW Protocol, or GPv2 swaps, orders,
      liquidity, approvals, permits, rewards, migrations, wrapping, cancellations, and refunds."
    ---
    
    # EVM Atlas
    
    This skill is coordination-exempt: skip the ai-coord gate for its declared work.
    
    Resolve and query only the target mainnets in `references/generated/target-mainnets.json`, under a strict read-only
    boundary.
    
    Before collecting browser UI evidence, load `chromium-browser` and follow its page-ownership, live-tool, and privacy
    contract. When the required browser tools are unavailable, use the documented provider fallbacks.
    
    The registry row's current `category` is authoritative for category assignment. For an exact-zero native sweep or a
    question about category-specific fee behavior, read [chain categories](references/chain-categories.md) after resolving
    the target. Do not infer a historical category or maintain a prose roster of target chains.
    
    ## Scope and Authority
    
    - Match displayed names, numeric chain IDs, and aliases from `references/generated/chain-aliases.json` to the
      authoritative target-mainnet rows.
    - If a chain is absent, do not route through another provider, web search, Chainlist, or an unlisted RPC to work around
      scope. Ask for a feature request at <https://github.com/PaulRBerg/agent-skills>.
    - Own every discrete read and bounded live subscription handed off by `cli-cast`, including chain, block, fee, nonce,
      `eth_call`, `eth_estimateGas`, transaction, receipt, log, balance, code, storage, proof, and ENS queries. Complete the
      read here even when its result will prepare, simulate, or verify later state-changing work.
    - Never sign messages, submit signatures, execute bridge steps, or broadcast transactions. Route state-changing Cast
      work to `cli-cast`.
    - DEX support is historical and evidence-only. Do not discover live quotes, construct or simulate new trades, prepare
      approvals or permits, submit orders, administer protocols, interpret CoW AMM positions, handle standalone 1inch limit
      orders, or assign semantics to arbitrary Uniswap v4 hooks.
    - Do not default to Ethereum. Infer from explicit chain context and unambiguous chain-specific tokens; ask when
      ambiguous.
    - Never echo, interpolate, or log API-key values (`ETHERSCAN_API_KEY`, `BLOCKSCOUT_API_KEY`, RPC keys). Check presence
      value-free with `[ -n "$ETHERSCAN_API_KEY" ] && echo set || echo unset`; never put `${VAR:-...}` or `${VAR:+...}`
      expansions in printed output.
    - Keyless Blockscout is sunset (July 2026) and hosted `*.blockscout.com` instance subdomains also rate-limit keyless
      traffic, so route every Blockscout-hosted chain through the keyed `https://api.blockscout.com/{chain_id}` gateway. See
      `references/explorers/blockscout-endpoints.md`.
    - Every agent on the host shares DeBank's rate limit. Hold a `scripts/debank-gate.py` lease for any debank.com access,
      including a quick profile look; see the Global Queue in `references/workflows/debank-portfolio.md`.
    - An unreachable or erroring indexer is a coverage gap, never evidence of zero activity. Confirm in Chromium before
      recording an endpoint as down or blocked, and state the verification method in results.
    
    ## Routing
    
    1. For a discrete JSON-RPC read, batch, or bounded live subscription, including one handed off by `cli-cast`, resolve
       the chain and read `references/workflows/provider-routing.md`. Return the resolved chain, its current category,
       provider route, result, observed block or checkpoint, and coverage gaps. Do not route the read back to `cli-cast`.
    2. For the current native or fungible-token balances or DeFi positions of a public wallet address across chains, read
       `references/workflows/debank-portfolio.md` first. For one named chain, read
       `references/workflows/blockscan-balances.md` first.
    3. For the current USD value of one or more addresses across target chains (portfolio value, net worth, drained or dust
       checks), read `references/workflows/address-usd-value.md`.
    4. For a specific transaction hash on a named chain, resolve the chain against
       `references/generated/target-mainnets.json`, then read `references/workflows/provider-routing.md` directly for the
       transaction facts. Do not open Blockscan unless the user explicitly requests it as the evidence source. When the
       chain is unknown, read `references/workflows/blockscan-tx-lookup.md` once to resolve it. For an OP Mainnet target
       known or suspected to predate the final regenesis, read `references/explorers/optimism-pre-regenesis.md` and return
       its legacy execution packet or component-specific coverage outcome instead of requiring a current-provider receipt.
       Otherwise, acquire the exact provider receipt and logs before DEX or bridge outcome interpretation.
    5. For an address-wide historical-activity or `bootstrap-discovery` sweep, read `references/workflows/address-sweeps.md`
       and use its deterministic plan/evaluate helper. For current holdings, use `references/workflows/debank-portfolio.md`
       first and provider routing for gaps.
    6. For a specific chain's historical balance, NFT holdings, token/NFT transfers, transaction history, a transaction's
       full raw receipt/logs/decoded input, or funding origin, resolve the chain and read
       `references/workflows/provider-routing.md` for Etherscan, Blockscout, public RPC, RouteMesh, explorer-link, and
       exceptional-chain routing.
    7. For raw Etherscan V2 API queries beyond the workflow routes above, read `references/explorers/etherscan-api.md`.
    8. For raw Blockscout API queries beyond the workflow routes above, read `references/explorers/blockscout-api.md`.
    9. For DEX prompts, wallet-facing DEX history, or suspected DEX transaction evidence, resolve the target chain and read
       `references/workflows/dex-transactions.md`. Load only the matching protocol-family reference:
       - Uniswap v1-v4, Universal Router, or Permit2: `references/dexes/uniswap.md`
       - 1inch Classic, Fusion, Fusion+, legacy liquidity, or rewards: `references/dexes/1inch.md`
       - CoW Swap, CoWSwap, CoW Protocol, or GPv2: `references/dexes/cow-protocol.md`
    10. Treat 1inch and CoW as execution protocols. Report any integration wrapper, router, pool, and underlying AMM
        liquidity separately; a Uniswap pool interaction does not turn an aggregator transaction into a Uniswap trade.
    11. For bridge-related prompts or transaction evidence, confirm known origin/destination chains are targets, then load
        only the matching reference:
    
        - Across: `references/bridges/across.md`
        - Bungee / Socket: `references/bridges/bungee.md`
        - Circle / CCTP / Gateway: `references/bridges/circle.md`
        - deBridge / DLN: `references/bridges/debridge.md`
        - Gas.zip: `references/bridges/gaszip.md`
        - Hop: `references/bridges/hop.md`
        - Layerswap: `references/bridges/layerswap.md`
        - LayerZero / Stargate / OFT / Aori: `references/bridges/layerzero.md`
        - LI.FI: `references/bridges/lifi.md`
        - Relay / Relay.link: `references/bridges/relay.md`
        - Symbiosis: `references/bridges/symbiosis.md`
        - 1inch Fusion+: `references/dexes/1inch.md`
    
    12. Treat bridge and DEX APIs as enrichment. Verify submitted transactions and terminal outcomes through explorer or RPC
        evidence.
    
    ## Completion
    
    Return the resolved target chain, current category, provider route, requested on-chain facts, and source
    URLs/transaction identifiers. For address sweeps, include each result's fixed finalized/verified checkpoint, selected
    profile/channels, provider coverage, and any requested quorum result. Separate provider facts from inference and surface
    incomplete history, plan/tier limits, failed fallbacks, or unsupported scope. Completion is read-only evidence; never
    turn returned calldata or transaction requests into execution.
    
    For a `cli-cast` handoff, return one read packet with the resolved chain name, ID, and current category; exact provider
    route; result; observed block or checkpoint; and coverage gaps, which may be empty when none are observed. Do not
    include a signing or broadcast command.
    
    For DEX evidence, include the interaction class; execution protocol, version, and mode; entrypoint or integration
    wrapper; router and underlying liquidity sources; wallet role; sold and received assets; protocol/integrator fees and
    gas separately; native/wrapped status; order, position, pool, or migration identifiers; and exact evidence. Do not call
    an approval-only or failed transaction a completed trade.
    
    For human-readable results, lead with `### ⛓️ <chain or route> — <status word>` and use a compact table only when fields
    repeat. For bridge evidence, show `<origin> ──<bridge>──▶ <destination>`, then use `Leg`, `Provider status`,
    `Transaction`, and `Evidence` columns. Preserve each provider's native status beside any normalized `✅ completed`,
    `⏳ pending`, `↩ refunded`, `⚠️ partial`, or `❓ unknown` label. Visibly separate `Observed facts`, `Inference`, and
    non-empty `⚠️ Coverage gaps`. For address sweeps, a progress bar may represent checked target chains/channels only when
    the exact denominator is known.
    
    Keep unsupported-scope and safety explanations direct. Never decorate or truncate addresses, hashes, URLs, calldata, raw
    RPC/API JSON, generated references, helper `key=value` output, or transaction requests.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related