一键导入
verify-warp-contracts
Verify EVM warp route contract source code on block explorers (implementation, ProxyAdmin, TransparentUpgradeableProxy)
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Verify EVM warp route contract source code on block explorers (implementation, ProxyAdmin, TransparentUpgradeableProxy)
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Update standing fee quotes for the CROSS/moonpay warp route. Use when asked to set, update, or change fees/bps for specific chains, tokens, or directions in the MoonPay route. Also use when asked to show or read current standing quotes.
Encode and decode raw Hyperlane messages to/from packed hex bytes using the CLI. Use when you need to construct a message for testing, inspect a raw message from logs or a transaction, or decode a warp transfer body.
Helper skill for using Hyperlane CLI address conversion utilities (addressToBytes32 and bytes32ToAddress) for cross-chain message formatting
A skill that allows a user to validate that a warp route deployment will be in the expected state after applying some configuration
Simulate pending Safe (multisig) governance txs for a warp route by replaying the literal calldata onto anvil forks, self-relaying any ICA messages, then running warp check against the desired registry config. Use to verify that a not-yet-signed Safe batch produces the intended warp route config before signing.
Add a new chain to an existing warp route owned by a customer. Reads a Linear ticket, adds the new chain to the deploy.yaml, builds a customer-specific strategy file for existing chains, runs warp apply, and outputs transaction files for the customer to sign via their multisig.
| name | verify-warp-contracts |
| description | Verify EVM warp route contract source code on block explorers (implementation, ProxyAdmin, TransparentUpgradeableProxy) |
You are a specialized agent for verifying EVM warp route contract source code on block explorers.
EVM warp route deployments consist of 3 contracts per chain that all need source verification:
cast and forge CLI tools (from Foundry)pnpm for installing JS dependencies~/Desktop/hyperlane/hyperlane-registry)| Tool | Version | Check command |
|---|---|---|
| forge | 1.5.0-stable | forge --version |
| cast | 1.5.0-stable | cast --version |
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:
forge verify-contract --help — confirm it still accepts --verifier, --verifier-url, --etherscan-api-key, --constructor-args, --compiler-versionforge verify-check --help — confirm it still accepts --verifier, --verifier-url, --etherscan-api-keycast call --help — confirm it still accepts <address> <sig> --rpc-urlcast storage --help — confirm it still accepts <address> <slot> --rpc-urlcast abi-encode --help — confirm it still accepts <sig> <args...>cast calldata --help — confirm it still accepts <sig> <args...>forge soldeer install --help — confirm it exists and has no required argsIf any flag has been renamed or removed, adapt the commands in subsequent steps accordingly. Warn the user about the version mismatch before proceeding.
The user should provide:
Use the following fallback chain to locate the warp route config and chain metadata:
~/Desktop/hyperlane/hyperlane-registryhttp://localhost:3333 (default port from typescript/infra/scripts/http-registry.ts). If reachable, fetch:
http://localhost:3333/deployments/warp_routes/<token>/<id>-config.yamlhttp://localhost:3333/chains/<chain>/metadata.yaml/start-http-registryhttps://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/refs/heads/main/deployments/warp_routes/<token>/<id>-config.yamlhttps://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/refs/heads/main/chains/<chain>/metadata.yamlIf 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.
Read the YAML config file. For each requested chain, extract:
addressOrDenom — this is the proxy addresscollateralAddressOrDenom — the underlying token (for collateral types)standard — determines which Solidity contract was deployeddecimalsOnly Evm* standards can be verified with this workflow. Skip non-EVM chains.
For each chain, read <registry>/chains/<chainName>/metadata.yaml to get:
rpcUrls[0].http — RPC endpointblockExplorers — explorer family, apiUrl, and apiKeychainId — needed for Etherscan V2 APIOn 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").
# 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.
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>
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:
| Standard | Contract | Constructor Signature |
|---|---|---|
| EvmHypCollateral | HypERC20Collateral | (address erc20, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox) |
| EvmHypSynthetic | HypERC20 | (uint8 decimals, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox) |
| EvmHypNative | HypNative | (uint256 scaleNumerator, uint256 scaleDenominator, address mailbox) |
| EvmHypCollateralFiat | HypFiatToken | (address fiatToken, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox) |
| EvmHypXERC20 | HypXERC20 | (address xerc20, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox) |
| EvmHypXERC20Lockbox | HypXERC20Lockbox | (address lockbox, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox) |
| EvmHypRebaseCollateral | HypERC4626Collateral | (address vault, uint256 scaleNumerator, uint256 scaleDenominator, address mailbox) |
| EvmHypOwnerCollateral | HypERC4626OwnerCollateral | (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> ...
None. ProxyAdmin inherits from Ownable with no explicit 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 deploymentThe 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:
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.
Fall back to creation tx sender — if creation bytecode is unavailable, get the creation transaction:
<apiUrl>?module=contract&action=getcontractcreation&contractaddresses=<address> → returns contractCreator fieldhttps://api.etherscan.io/v2/api?chainid=<chainId>&module=contract&action=getcontractcreation&contractaddresses=<address>&apikey=<key>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:
<apiUrl>?module=contract&action=getcontractcreation&contractaddresses=<address> → creationBytecode fieldhttps://api.etherscan.io/v2/api?chainid=<chainId>&module=contract&action=getcontractcreation&contractaddresses=<address>&apikey=<key>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).For each contract, use forge verify-contract from the worktree's solidity directory.
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
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 | Path |
|---|---|
| HypERC20Collateral | contracts/token/HypERC20Collateral.sol:HypERC20Collateral |
| HypERC20 | contracts/token/HypERC20.sol:HypERC20 |
| HypNative | contracts/token/HypNative.sol:HypNative |
| HypFiatToken | contracts/token/extensions/HypFiatToken.sol:HypFiatToken |
| HypXERC20 | contracts/token/extensions/HypXERC20.sol:HypXERC20 |
| HypXERC20Lockbox | contracts/token/extensions/HypXERC20Lockbox.sol:HypXERC20Lockbox |
| HypERC4626Collateral | contracts/token/extensions/HypERC4626Collateral.sol:HypERC4626Collateral |
| HypERC4626OwnerCollateral | contracts/token/extensions/HypERC4626OwnerCollateral.sol:HypERC4626OwnerCollateral |
| ProxyAdmin | dependencies/@openzeppelin-contracts-4.9.3/contracts/proxy/transparent/ProxyAdmin.sol:ProxyAdmin |
| TransparentUpgradeableProxy | dependencies/@openzeppelin-contracts-4.9.3/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy |
For each chain, verify in this order:
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>]
After verification, provide the user with explorer links for visual confirmation:
<explorer_url>/address/<address>Ask the user before removing the worktree:
git worktree remove /tmp/hyp-verify-<version>
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.
Etherscan V2 free tier doesn't support all chains for querying (e.g., getcontractcreation), but verification submission still works. For data queries:
cast storage, cast call) instead of explorer APIs for on-chain datacast tx <txHash> input --rpc-url <rpc> to fetch creation bytecode via RPC if you have the tx hashAlways verify encoded constructor args against the creation bytecode tail before submitting. If they don't match, re-check:
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.scaleDenominator may be 1e12.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).
Check foundry.toml in the worktree's solidity directory for solc_version. It's typically 0.8.22 but may differ across versions.
When the user invokes this skill: