跳到正文
cccskills
免费在 GitHub 公开

fetch-safe-tx-batch

Fetch and decode a pending Safe (multisig) governance batch from the Safe Transaction Service into a chain-tagged transactions JSON file that `/warp-route-check` (or any downstream consumer with the same input shape) ingests. Safe-API integration only — no forks, no replay, no simulation.

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(1)

  • SKILL.md10.5 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Fetch Safe Tx Batch — fetch + decode front end

Fetch a pending Safe / Heimdall multisig batch from the Safe Transaction Service, decode the MultiSend wrapper into inner transactions, tag each with the source chainId, and emit a single JSON file that /warp-route-check consumes. /warp-route-check then forks the touched chains, replays the batch under impersonated owners, self-relays any cross-chain ICA messages, and runs hyperlane warp check against the target registry config to verify the batch produces the desired state before anyone signs it.

This skill is intentionally narrow. The Safe Transaction Service integration (v2 endpoint URL, 308 redirect, MultiSend decode) is the only piece that's specific to Safe-style batches. Keeping it in its own skill leaves /warp-route-check generic — any chain-tagged transactions JSON works as input, regardless of whether it came from a Safe batch, a Heimdall queue, or a hand-rolled tx list.

When to use

  • An engineer shares Heimdall / Safe links for a warp route update and asks "do these txs lead to the desired config?"
  • Before signing any warp route governance batch — the simulation downstream verifies the result against the target registry config.

Input Parameters

ParameterRequiredDescription
safe_txsYesOne or more (chainId, safeAddress, safeTxHash) triples, or Safe / Heimdall URLs to derive them from. Multiple batches are concatenated into one chain-tagged transactions file.
output_dirNoWhere to write the decoded files. Defaults to ~/.hyperlane/safe-batches/<timestamp>/.

Solana / Squads and other non-EVM legs cannot be EVM-forked — verify those separately. This skill handles EVM Safe batches only.

Prerequisites

  • curl, python3, jq on PATH. No anvil / cast / forge needed in this skill — those run in /warp-route-check.

Instructions

Step 1 — Fetch and decode each Safe batch

The Safe Transaction Service returns the batch as a single MultiSend (multiSend(bytes), selector 0x8d80ff0a). Decode it into the inner txs the Safe executes (to, value, data). Note the 308 redirect (-L) and the v2 endpoint.

First set up one clean working directory for this invocation. Never reuse a directory that already holds decoded files from a previous run — Step 2 merges by an explicit manifest, but a stale *.decoded.json left in the directory is still a corruption hazard, so fail closed if the target isn't clean:

WORK=${output_dir:-~/.hyperlane/safe-batches/$(date +%s)}
mkdir -p "$WORK/decoded"
if compgen -G "$WORK/decoded/*.decoded.json" > /dev/null; then
  echo "ERROR: $WORK/decoded already contains decoded files from a previous run;" >&2
  echo "use a fresh output_dir (or clear it) so stale batches are not merged." >&2
  exit 1
fi
: > "$WORK/manifest"   # exact list of files decoded in THIS invocation

Then, for each (chainId, safeAddress, safeTxHash) triple in safe_txs:

SAFE_TX_HASH=0x....              # from the Safe / Heimdall link
CHAIN_ID=1                        # the executing chain

# Resolve the Safe Transaction Service base URL for THIS chain from the local
# Hyperlane registry — the same source the infra Safe scripts use
# (chains/<chain>/metadata.yaml -> gnosisSafeTransactionServiceUrl). Never
# hardcode a host or assume mainnet.
META=$(grep -rlE "^chainId:[[:space:]]*${CHAIN_ID}[[:space:]]*$" "$REGISTRY_PATH/chains/"*/metadata.yaml | head -1)
[ -n "$META" ] || { echo "ERROR: no registry chain with chainId ${CHAIN_ID}." >&2; exit 1; }
BASE=$(grep -m1 "^gnosisSafeTransactionServiceUrl:" "$META" | sed 's/^[^:]*:[[:space:]]*//; s/[[:space:]]*$//; s#/*$##')
[ -n "$BASE" ] || { echo "ERROR: chainId ${CHAIN_ID} has no gnosisSafeTransactionServiceUrl in the registry." >&2; exit 1; }
# Match the SDK's normalizeSafeTxServiceUrl: append /api unless already present.
case "$BASE" in */api) TX_SERVICE="$BASE";; *) TX_SERVICE="$BASE/api";; esac

curl -sL "${TX_SERVICE}/v2/multisig-transactions/${SAFE_TX_HASH}/" \
  -o "$WORK/decoded/${CHAIN_ID}-${SAFE_TX_HASH}.json"

python3 - "$WORK" "$CHAIN_ID" "$SAFE_TX_HASH" << 'EOF'
import json, sys
W, chain_id, safe_tx = sys.argv[1], int(sys.argv[2]), sys.argv[3]
src = f"{W}/decoded/{chain_id}-{safe_tx}.json"
d = json.load(open(src))
raw = d['data'][2:]
assert raw[:8] == '8d80ff0a', "expected MultiSend"
rest = raw[8:]; length = int(rest[64:128], 16); packed = rest[128:128 + length * 2]
b = bytes.fromhex(packed); i = 0; out = []
while i < len(b):
    op = b[i]; i += 1; to = '0x' + b[i:i+20].hex(); i += 20
    val = int.from_bytes(b[i:i+32], 'big'); i += 32
    dl = int.from_bytes(b[i:i+32], 'big'); i += 32
    # Preserve the MultiSend operation (0 = CALL, 1 = DELEGATECALL). The fork
    # replay in /warp-route-check executes inner txs as plain CALLs, so a
    # delegatecall would be silently misrepresented — fail closed instead of
    # downgrading it. Warp-management batches are all CALLs; a delegatecall
    # here needs manual handling.
    if op != 0:
        sys.exit(f"inner tx {len(out)} of {chain_id}-{safe_tx} is a delegatecall "
                 f"(operation={op}); the fork replay only supports CALL — handle "
                 "this batch manually")
    out.append({
        'chainId': chain_id,
        'safeAddress': d['safe'],
        'safeTxHash': safe_tx,
        'to': to,
        'value': str(val),
        'operation': op,
        'data': '0x' + b[i:i+dl].hex(),
    })
    i += dl
json.dump(out, open(f"{W}/decoded/{chain_id}-{safe_tx}.decoded.json", 'w'), indent=2)
print(f"chainId={chain_id} safe={d['safe']} innerTxs={len(out)}")
EOF

# Record this file so Step 2 merges only what THIS invocation produced.
echo "$WORK/decoded/${CHAIN_ID}-${SAFE_TX_HASH}.decoded.json" >> "$WORK/manifest"

The executing safe address is each batch's defaultSender — /warp-route-check impersonates it on the fork. The inner txs typically split into:

  • Direct calls on the local (executing-chain) routers: enrollRemoteRouters (0xe9198bf9), enrollCrossCollateralRouters (0x081954bc), setDestinationGas (0xb1bd6436), setFeeRecipient (0xe74b981b).
  • ICA fan-out to remote chains: callRemoteWithOverrides (0xeab4eaa4) on the origin InterchainAccountRouter. Each carries inner enroll calls destined for a remote router — these only take effect after the dispatched message is relayed (handled in /warp-route-check).

Step 2 — Combine decoded inner txs into one chain-tagged transactions file

Concatenate only the files decoded in this invocation (from the manifest — not a wildcard over the directory, which could pull in stale batches) into a single array and write to <output_dir>/transactions.json:

jq -s 'add' $(cat "$WORK/manifest") > "$WORK/transactions.json"
echo "total inner txs: $(jq 'length' $WORK/transactions.json)"
echo "unique chainIds: $(jq -r '[.[].chainId] | unique | join(",")' $WORK/transactions.json)"
echo "unique safe senders: $(jq -r '[.[].safeAddress] | unique | join(",")' $WORK/transactions.json)"

The resulting transactions.json is the format /warp-route-check expects in Step 5 (it jq's .[].chainId and .[].to).

Step 3 — Hand off to /warp-route-check

Tell the user:

Decoded <N> inner txs across <M> chains. Output:

  • <output_dir>/transactions.json — combined chain-tagged batch (the file to pass to /warp-route-check)
  • <output_dir>/decoded/<chainId>-<safeTxHash>.decoded.json — per-source-batch decoded copy (debugging / per-batch inspection)

Run /warp-route-check next, passing <output_dir>/transactions.json as the transactions-file input. It will fork every touched chain, impersonate each target's current on-chain owner(), replay the inner txs from that owner, self-relay any cross-chain ICA messages, and verify the resulting state against the target registry config.

Caveat — sender vs. on-chain owner. /warp-route-check replays from each target's queried owner(), not from the safeAddress recorded here. That is correct only when the recorded Safe IS the current on-chain owner of every target. When they differ — a direct Safe call whose target owner is a different address, or an ICA callRemote whose origin owner differs — the replay runs under the wrong sender and the result is not a faithful verification. Grouping/replaying by the recorded sender is a /warp-route-check change tracked as part-2; until then, do NOT treat this handoff as a verification gate for batches where the recorded Safe is not the target's current owner (fall back to the post-execution registry-PR CI).

End the message there. This skill does NOT run forks, replay txs, or call warp check — those are /warp-route-check's responsibilities.

Gotchas

  • Safe Tx Service URL: use -L (308 redirect) and the /api/v2/multisig-transactions/<safeTxHash>/ path.
  • MultiSend layout: selector 0x8d80ff0a; packed per inner tx is op(1) to(20) value(32) len(32) data(len).
  • Inner-call selectors (sanity-check the decode):
    • enrollRemoteRouters — 0xe9198bf9
    • enrollCrossCollateralRouters — 0x081954bc
    • setDestinationGas — 0xb1bd6436
    • setFeeRecipient — 0xe74b981b
    • callRemoteWithOverrides (ICA fan-out) — 0xeab4eaa4
  • Non-EVM batches (Solana / Squads) — out of scope for this skill. They reach signers via a different propose path (file submitter, manual hand-off) and don't pass through Safe Transaction Service.

Related skills

  • /warp-route-check — the simulation engine this skill feeds into.
  • /warp-fork — lower-level fork primitive used by /warp-route-check.
  • /start-http-registry — serve the target / PR registry to fork+check.
  • /self-relay-hyperlane-message — deliver a dispatched ICA message manually (used inside /warp-route-check).

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

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-monorepo772026年10月22日 更新

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-monorepo772026年10月22日 更新

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-monorepo772026年10月22日 更新

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-monorepo772026年10月22日 更新

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-monorepo772026年10月22日 更新

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-monorepo772026年10月22日 更新

查看 hyperlane-xyz 的全部技能

报告此技能的问题