本文へ移動
cccskills
無料GitHub で公開

verify-warp-contracts

Verify EVM warp route contract source code on block explorers (implementation, ProxyAdmin, TransparentUpgradeableProxy)

インストール方法を見る

含まれるファイル(1)

  • SKILL.md16.5 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Verify Warp Route Contracts

You are a specialized agent for verifying EVM warp route contract source code on block explorers.

Overview

EVM warp route deployments consist of 3 contracts per chain that all need source verification:

  1. Implementation — the actual token contract (e.g., HypERC20Collateral)
  2. ProxyAdmin — OpenZeppelin ProxyAdmin (no constructor args)
  3. TransparentUpgradeableProxy — the proxy users interact with

Prerequisites

  • cast and forge CLI tools (from Foundry)
  • pnpm for installing JS dependencies
  • Access to the Hyperlane registry (default: ~/Desktop/hyperlane/hyperlane-registry)

Tool versions this skill was written for

ToolVersionCheck command
forge1.5.0-stableforge --version
cast1.5.0-stablecast --version

Step 0: Verify tool compatibility

Before doing anything else, check that the installed tool versions are compatible:

forge --version
cast --version

If the major version differs from the table above, verify that the following commands still accept the same arguments by running their --help:

  1. forge verify-contract --help — confirm it still accepts --verifier, --verifier-url, --etherscan-api-key, --constructor-args, --compiler-version
  2. forge verify-check --help — confirm it still accepts --verifier, --verifier-url, --etherscan-api-key
  3. cast call --help — confirm it still accepts <address> <sig> --rpc-url
  4. cast storage --help — confirm it still accepts <address> <slot> --rpc-url
  5. cast abi-encode --help — confirm it still accepts <sig> <args...>
  6. cast calldata --help — confirm it still accepts <sig> <args...>
  7. forge soldeer install --help — confirm it exists and has no required args

If any flag has been renamed or removed, adapt the commands in subsequent steps accordingly. Warn the user about the version mismatch before proceeding.

Inputs

The user should provide:

  • Warp route config path or ID — path to the registry YAML config file, or a warp route ID (e.g., "USDC/eclipsemainnet")
  • Chain names — which chains to verify
  • Registry path (optional) — path to the local registry checkout

Resolving the registry and config

Use the following fallback chain to locate the warp route config and chain metadata:

  1. User-provided path — if the user gave a full file path, use it directly
  2. Local registry checkout — if the user provided a registry path, or check the default location ~/Desktop/hyperlane/hyperlane-registry
  3. Local HTTP registry — check if a local HTTP registry is running at http://localhost:3333 (default port from typescript/infra/scripts/http-registry.ts). If reachable, fetch:
    • Config: http://localhost:3333/deployments/warp_routes/<token>/<id>-config.yaml
    • Chain metadata: http://localhost:3333/chains/<chain>/metadata.yaml
    • If not running, the user can start it with /start-http-registry
  4. GitHub raw — fetch directly from the public registry repo:
    • Config: https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/refs/heads/main/deployments/warp_routes/<token>/<id>-config.yaml
    • Chain metadata: https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/refs/heads/main/chains/<chain>/metadata.yaml

If the user provides a warp route ID (e.g., "USDC/eclipsemainnet"), the config path is deployments/warp_routes/<token>/<id>-config.yaml relative to the registry root.

Step-by-Step Workflow

Step 1: Parse the warp route config

Read the YAML config file. For each requested chain, extract:

  • addressOrDenom — this is the proxy address
  • collateralAddressOrDenom — the underlying token (for collateral types)
  • standard — determines which Solidity contract was deployed
  • decimals

Only Evm* standards can be verified with this workflow. Skip non-EVM chains.

Step 2: Get chain metadata

For each chain, read <registry>/chains/<chainName>/metadata.yaml to get:

  • rpcUrls[0].http — RPC endpoint
  • blockExplorers — explorer family, apiUrl, and apiKey
  • chainId — needed for Etherscan V2 API

Step 3: Query PACKAGE_VERSION

On any one of the target proxy contracts:

cast call <proxy_address> "PACKAGE_VERSION()(string)" --rpc-url <rpc_url>

This returns the @hyperlane-xyz/core version (e.g., "11.1.0").

Step 4: Build Solidity artifacts at the correct version

# Find the git tag
git rev-parse "@hyperlane-xyz/core@<version>"

Check if a worktree already exists at /tmp/hyp-verify-<version>. If it does and already has built artifacts (out/ directory in solidity/), reuse it. Otherwise create a new one:

# Check if worktree already exists
if [ -d /tmp/hyp-verify-<version> ]; then
  echo "Worktree already exists, reusing"
else
  git worktree add /tmp/hyp-verify-<version> "@hyperlane-xyz/core@<version>"
  cd /tmp/hyp-verify-<version> && pnpm install
  cd /tmp/hyp-verify-<version>/solidity && forge soldeer install
  cd /tmp/hyp-verify-<version>/solidity && forge build
fi

If the worktree exists but has no out/ directory, run the install and build steps.

Step 5: Discover all 3 contract addresses per chain

For each chain:

Proxy: already known from config (addressOrDenom)

Implementation: read EIP-1967 implementation slot:

cast storage <proxy> 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc --rpc-url <rpc>

Extract the address from the last 20 bytes of the returned 32-byte value.

ProxyAdmin: read EIP-1967 admin slot:

cast storage <proxy> 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103 --rpc-url <rpc>

Step 6: Build constructor args

Implementation constructor args

All token contracts follow a pattern. Query on-chain values through the proxy:

cast call <proxy> "mailbox()(address)" --rpc-url <rpc>
cast call <proxy> "scaleNumerator()(uint256)" --rpc-url <rpc>
cast call <proxy> "scaleDenominator()(uint256)" --rpc-url <rpc>

Then encode based on the contract type:

StandardContractConstructor Signature
EvmHypCollateralHypERC20Collateral(address erc20, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox)
EvmHypSyntheticHypERC20(uint8 decimals, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox)
EvmHypNativeHypNative(uint256 scaleNumerator, uint256 scaleDenominator, address mailbox)
EvmHypCollateralFiatHypFiatToken(address fiatToken, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox)
EvmHypXERC20HypXERC20(address xerc20, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox)
EvmHypXERC20LockboxHypXERC20Lockbox(address lockbox, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox)
EvmHypRebaseCollateralHypERC4626Collateral(address vault, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox)
EvmHypOwnerCollateralHypERC4626OwnerCollateral(address vault, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox)

For collateral-like types, the first address arg is collateralAddressOrDenom from the config. For EvmHypSynthetic, query decimals() on the proxy instead. For EvmHypNative, there is no token address arg.

Encode with:

cast abi-encode "constructor(<types>)" <arg1> <arg2> ...

ProxyAdmin constructor args

None. ProxyAdmin inherits from Ownable with no explicit constructor args.

Proxy constructor args

TransparentUpgradeableProxy(address _logic, address admin_, bytes memory _data)

  • _logic = implementation address (from Step 5)
  • admin_ = ProxyAdmin address (from Step 5)
  • _data = the initialize() calldata used at deployment

The initialize calldata uses selector 0xc0c53b8b = initialize(address,address,address) with args (address(0), address(0), owner).

IMPORTANT: Deploy-time owner vs current owner. The owner baked into the initialize calldata is the deploy-time owner (usually the deployer EOA), NOT the current owner. Ownership may have been transferred post-deployment. You must determine the deploy-time owner, not just call owner().

Strategy to find the deploy-time owner:

  1. Try creation bytecode first — get the proxy's creation bytecode from the explorer API (see below). The constructor args are appended at the tail. Decode the last 32 bytes of the _data field to extract the owner address used at deploy time.

  2. Fall back to creation tx sender — if creation bytecode is unavailable, get the creation transaction:

    • Blockscout: <apiUrl>?module=contract&action=getcontractcreation&contractaddresses=<address> → returns contractCreator field
    • Etherscan V2: https://api.etherscan.io/v2/api?chainid=<chainId>&module=contract&action=getcontractcreation&contractaddresses=<address>&apikey=<key>
    • If both APIs fail (e.g., Etherscan V2 free tier limitation), use the deployer address from the implementation or ProxyAdmin creation info, since all 3 are typically deployed in the same tx or by the same address.
  3. Last resort — query owner() on the proxy, but be aware this may fail verification if ownership was transferred. Always verify against creation bytecode when possible.

Build the init data:

INIT_DATA=$(cast calldata "initialize(address,address,address)" 0x0000000000000000000000000000000000000000 0x0000000000000000000000000000000000000000 <deploy_time_owner>)

Encode the full constructor args:

cast abi-encode "constructor(address,address,bytes)" <implementation> <proxyAdmin> "$INIT_DATA"

Verification: compare the encoded constructor args against the tail of the contract's creation bytecode to make sure they match before submitting verification. This is critical — if they don't match, the verification will fail or verify the wrong contract.

Getting creation bytecode:

  • Blockscout: <apiUrl>?module=contract&action=getcontractcreation&contractaddresses=<address> → creationBytecode field
  • Etherscan V2: https://api.etherscan.io/v2/api?chainid=<chainId>&module=contract&action=getcontractcreation&contractaddresses=<address>&apikey=<key>
  • If explorer APIs fail: get the creation tx hash from either API's txHash field (or find it on the explorer UI), then fetch the full tx input data via RPC:
    cast tx <txHash> input --rpc-url <rpc>
    
    The constructor args are the tail of this input data (after the contract init code).

Step 7: Verify contracts

For each contract, use forge verify-contract from the worktree's solidity directory.

Blockscout explorers (family: blockscout)

cd /tmp/hyp-verify-<version>/solidity && forge verify-contract \
  <address> \
  <contract_path>:<contract_name> \
  --verifier blockscout \
  --verifier-url <apiUrl> \
  --constructor-args <encoded_args> \
  --compiler-version 0.8.22

Etherscan explorers (family: etherscan)

For Etherscan V2 (standard etherscan-family explorers), use the unified Etherscan V2 API:

cd /tmp/hyp-verify-<version>/solidity && forge verify-contract \
  <address> \
  <contract_path>:<contract_name> \
  --verifier etherscan \
  --verifier-url "https://api.etherscan.io/v2/api?chainid=<chainId>" \
  --etherscan-api-key <apiKey> \
  --constructor-args <encoded_args> \
  --compiler-version 0.8.22

Contract paths

ContractPath
HypERC20Collateralcontracts/token/HypERC20Collateral.sol:HypERC20Collateral
HypERC20contracts/token/HypERC20.sol:HypERC20
HypNativecontracts/token/HypNative.sol:HypNative
HypFiatTokencontracts/token/extensions/HypFiatToken.sol:HypFiatToken
HypXERC20contracts/token/extensions/HypXERC20.sol:HypXERC20
HypXERC20Lockboxcontracts/token/extensions/HypXERC20Lockbox.sol:HypXERC20Lockbox
HypERC4626Collateralcontracts/token/extensions/HypERC4626Collateral.sol:HypERC4626Collateral
HypERC4626OwnerCollateralcontracts/token/extensions/HypERC4626OwnerCollateral.sol:HypERC4626OwnerCollateral
ProxyAdmindependencies/@openzeppelin-contracts-4.9.3/contracts/proxy/transparent/ProxyAdmin.sol:ProxyAdmin
TransparentUpgradeableProxydependencies/@openzeppelin-contracts-4.9.3/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy

Verification order

For each chain, verify in this order:

  1. Implementation contract
  2. ProxyAdmin
  3. TransparentUpgradeableProxy

If a contract is already verified, forge verify-contract will report "already verified" — this is fine, move on.

After submitting, check verification status if needed:

forge verify-check <GUID> --verifier <type> --verifier-url <url> [--etherscan-api-key <key>]

Step 8: Report results

After verification, provide the user with explorer links for visual confirmation:

  • For each chain and each contract, provide the explorer URL: <explorer_url>/address/<address>

Step 9: Cleanup

Ask the user before removing the worktree:

git worktree remove /tmp/hyp-verify-<version>

Multiple explorers per chain

Some chains have multiple block explorers (e.g., both Etherscan and Blockscout). If the user requests verification on a specific explorer, use that one. Otherwise verify on all available explorers.

Troubleshooting

"Free API access is not supported for this chain"

Etherscan V2 free tier doesn't support all chains for querying (e.g., getcontractcreation), but verification submission still works. For data queries:

  • Use RPC calls (cast storage, cast call) instead of explorer APIs for on-chain data
  • To get the creation tx hash or deployer, try the chain's Blockscout explorer if one exists, or check the explorer UI manually
  • Use cast tx <txHash> input --rpc-url <rpc> to fetch creation bytecode via RPC if you have the tx hash

Constructor args mismatch

Always verify encoded constructor args against the creation bytecode tail before submitting. If they don't match, re-check:

  • The initialize function signature — some versions use initialize(uint32,address,address) (selector 0x647c576c) instead of initialize(address,address,address) (selector 0xc0c53b8b). Check the actual creation bytecode to determine which was used.
  • Scale values — BSC USDC is 18 decimals but canonical is 6, so scaleDenominator may be 1e12.

forge soldeer install fails

If soldeer dependencies aren't installing, check if soldeer.lock exists in the solidity directory. If not, the version may use a different dependency management approach (e.g., git submodules via forge install).

Compiler version mismatch

Check foundry.toml in the worktree's solidity directory for solc_version. It's typically 0.8.22 but may differ across versions.

Your Task

When the user invokes this skill:

  1. Ask for the warp route config path and chain names if not provided
  2. Execute the workflow above step by step
  3. Run verification for all 3 contracts on each chain on each available explorer
  4. Report results with explorer links

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Write a changeset file in the `hyperlane-registry` repo for a PR that adds, updates, or modifies warp route config / chain metadata / addresses. Canonical pattern — never run the interactive `pnpm changeset` CLI; write the file directly. Referenced by any skill that opens a registry PR.

日本語の概要は準備中です。原文の説明を表示しています。

hyperlane-xyz/hyperlane-monorepo762026年10月13日 更新

Trigger Docker image builds for Hyperlane agent, monorepo, or node service images. Use when the user wants to build new Docker images for a branch, commit, or tag.

日本語の概要は準備中です。原文の説明を表示しています。

hyperlane-xyz/hyperlane-monorepo762026年10月13日 更新

The low-level on-chain probes for identifying what an owner address is — cast code → EOA / EIP-7702-delegated EOA / contract, VERSION() → Gnosis Safe, read-only get-owner-ica.ts derive → ICA derivation, getAccountInfo → Sealevel account existence — with how to interpret each result. The primitive layer beneath validate-owners / resolve-artifacts / update-owners, which each apply their own per-chain or per-artifact policy on top.

日本語の概要は準備中です。原文の説明を表示しています。

hyperlane-xyz/hyperlane-monorepo762026年10月13日 更新

Review code changes using Hyperlane monorepo coding standards. Use when reviewing PRs, checking your own changes, or doing self-review before committing.

日本語の概要は準備中です。原文の説明を表示しています。

hyperlane-xyz/hyperlane-monorepo762026年10月13日 更新

Debug why a Hyperlane message is not being processed. Use when given a message ID or explorer URL to investigate delivery failures, gas estimation errors, validator issues, or other processing problems.

日本語の概要は準備中です。原文の説明を表示しています。

hyperlane-xyz/hyperlane-monorepo762026年10月13日 更新

Debug RPC provider health issues for a chain. Use when alerts mention RPC failures, high latency, stale blocks, or provider errors. Diagnoses the issue and recommends fixes.

日本語の概要は準備中です。原文の説明を表示しています。

hyperlane-xyz/hyperlane-monorepo762026年10月13日 更新

hyperlane-xyz のスキルをすべて見る

このスキルの問題を報告する