Pacta

io.github.Fractal-Neurons由 GitHub 帳號 Fractal-Neurons 發布v0.1.1更新於 Nov 14, 2026

Free pre-execution contract checks for AI agents: preflight, control-plane scans, EAS anchors.

已驗證Streamable HTTP可網頁執行資料庫金融安全與監控

概覽

AI 產生的概覽

對 EVM 合約與 DeFi 治理做唯讀、依區塊固定的安全檢查,讓助理在授權或存入資金前先審查合約。

可以做什麼
  • 一次呼叫完成合約預檢:給出結論、0-100 風險評分、代理與實作位址、控制者類型,以及升級、暫停、黑名單能力
  • 掃描合約控制面:EIP-1967 實作、管理員與 beacon、owner、Safe 門檻與擁有者、時間鎖延遲及風險標記
  • 查詢 OpenZeppelin TimelockController 操作狀態,並列出目前待處理的時間鎖操作
  • 在最新區塊重跑已追蹤治理提案的固定讀取,回傳固定值與即時值的差異
  • 回傳 Base 上 EAS 錨定註冊表、schema 與命名資訊
適用情境
當助理即將在以太坊或 Base 上授權、存入或呼叫不熟悉的 EVM 合約,需要知道誰能改動它、改動有多快時使用。也適合追蹤排隊中的時間鎖操作,以及核對治理提案是否仍與紀錄一致。
執行需求
託管遠端端點不需安裝、金鑰或錢包。可選的本機 stdio 伺服器需複製 Node.js 程式碼,尚未發佈到 npm;只有設定 PACTA_X402_URL 後才會出現閘道工具。需要能連線 Pacta 閘道與資料來源。
安裝前請注意
閘道在推出期間免費,但若開啟計量,說明中涉及 Base USDC 的 x402 付費;伺服器不持有私鑰,付款須自行簽署,並受 PACTA_X402_MAX_ATOMIC 上限限制。結算未在鏈上驗證,僅比對閘道回應標頭。結果僅為唯讀資料,明確不構成投資建議。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Pacta,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。

交給代理程式安裝

把這段提示詞貼上到 Claude Code、Codex、Cursor 等代理程式,或 SourceWeft 對話中。代理程式會讀取該伺服器的安裝指南,先向你展示發布者、端點和登入方式,經你同意後再把它新增到你的客戶端。金鑰不會寫進任何命令。

閱讀 https://sourceweft.com/mcp/io.github.Fractal-Neurons%2Fpacta/install.md,並按其中說明把這個 MCP 伺服器接入我的客戶端。修改任何設定之前,先告訴我它的發布者、端點和登入方式,等我確認。不要把 API 金鑰寫進命令或我會提交的檔案;問我金鑰應從哪裡取得。

檢視代理程式所依據的安裝指南

其他 MCP 客戶端

把它新增到你客戶端的 mcpServers 設定中。

{
  "mcpServers": {
    "pacta": {
      "type": "http",
      "url": "https://pacta-x402.pacta.workers.dev/mcp"
    }
  }
}

README

@pacta/mcp-server

Zero-dependency Node.js MCP stdio server for Pacta DeFi control-plane commitments and Base Mainnet EAS attestations, with live gateway tools (pre-flight check, control-plane / timelock / proposal scans) when PACTA_X402_URL is configured. The gateway is free during the launch: $0, no API key, no wallet, 60 requests / minute per caller. The x402 v1 + v2 Base USDC prices below apply only if metering is switched on.

This package is stdio-only. It never opens a listening socket, and the test suite checks that statically. It is not published on npm yet: run it from a clone, or use the hosted remote endpoint.

Fastest: hosted remote MCP (zero install)

The live gateway serves the same gateway tools over Streamable HTTP at https://pacta-x402.pacta.workers.dev/mcp:

bash
claude mcp add --transport http pacta https://pacta-x402.pacta.workers.dev/mcp
json
{ "mcpServers": { "pacta": { "url": "https://pacta-x402.pacta.workers.dev/mcp" } } }

Or call the API directly: curl -s "https://pacta-x402.pacta.workers.dev/v1/check/preflight?chain=ethereum&address=0x…". OpenAPI 3.1: https://pacta-x402.pacta.workers.dev/openapi.json.

Usage (stdio, zero-install local clone)

bash
git clone https://github.com/Fractal-Neurons/pacta.git
json
{  "mcpServers": {    "pacta": {      "command": "node",      "args": ["/path/to/pacta/packages/mcp-server/bin/pacta-mcp.mjs"],      "env": { "PACTA_X402_URL": "https://pacta-x402.pacta.workers.dev" }    }  }}

Tools

  • Default Free Tools (static feed https://pacta.wtf/api/v1/index.json, fallback https://fractal-neurons.github.io/pacta/api/v1/index.json, or PACTA_INDEX_PATH / PACTA_SPEC_PATH):

    • pacta_list_commitments: filters dual-RPC-verified DeFi control-plane records (protocol, classification, sprint, limit).
    • pacta_list_proposals: filters the governance proposals feed (https://pacta.wtf/api/v1/proposals.json, fallback https://fractal-neurons.github.io/pacta/api/v1/proposals.json, or PACTA_PROPOSALS_PATH / the proposalsPath option). Filters: forum (aave | euler | morpho | sky), topic_id, chain (ethereum | base), limit (1 to 100, default 25). The output is compact, and repro_commands are included only with include_repro: true. The feed must have schema === "pacta.governance_proposals_feed.v1" and a proposals array of objects. Anything else is refused with invalid_feed. When no source yields a valid feed, the call fails with feed_unavailable, which carries per-URL attempts (timeout, network_error, upstream_http_error, response_too_large, invalid_feed). A configured local path is never replaced by a network fetch.
    • pacta_get_anchoring: returns the Base (8453) PactaCommitmentRegistry (0x35D21Bb4Df6586FE7759b5e57263ffeB3e1fd872), the EAS Schema (0x7ff485bc1159dff3c70eefcb9dff2d65be990301760c2d53799e5a45e3badf83), and pacta.base.eth.
    • pacta_x402_discover: reports PACTA_X402_URL status, the live gateway x402_gateway_url and the local-spec payment requirements (labelled source: "local_spec", validated by the same rules as upstream challenges). When PACTA_X402_URL is set, it also fetches /.well-known/x402 and /v1/sample/control-plane for information only.
  • Live gateway. The canonical Pacta x402 edge Worker is live at https://pacta-x402.pacta.workers.dev (Cloudflare; free /health, /.well-known/x402, /v1/sample/control-plane). pacta_get_anchoring and pacta_x402_discover report it as x402_gateway_url even while paid tools are off. To opt in, set PACTA_X402_URL=https://pacta-x402.pacta.workers.dev.

  • Gateway tools, free during the launch; optional x402 v1 + v2 Base USDC metering (enabled only when PACTA_X402_URL is set; when unset, tools/list shows only the static-feed tools and gateway calls return x402_disabled). While the gateway is in free-launch mode it answers HTTP 200 with no payment header, and the tool returns that as unpaid data (paid: false). The prices are the metered x402 prices:

    • pacta_check_preflight → /v1/check/preflight ($0.01 = 10000 atomic; chain, address, optional block = latest | decimal | 0x-hex). Verdict (IMMUTABLE_NO_ADMIN / TIMELOCK_PROTECTED / ZERO_DELAY_TIMELOCK_RISK / MULTISIG_NO_TIMELOCK / INSTANT_EOA_ADMIN_RISK / UNCLASSIFIED_CONTRACT_CONTROLLER / UNDEPLOYED_OR_EOA), 0–100 risk score, proxy + implementation, controller kind, zero-delay upgradeability, pause / blacklist / upgrade capabilities, pinned block.
    • pacta_scan_control_plane → /v1/scan/control-plane ($0.10 = 100000 atomic; chain, address)
    • pacta_live_timelocks → /v1/briefs/live-timelocks ($0.10 = 100000 atomic). pacta_get_live_timelocks, the name used by the hosted remote MCP, is accepted as an alias in tools/call; it is not listed separately.
    • pacta_scan_timelock_op → /v1/scan/timelock-op ($0.25 = 250000 atomic; chain, timelock, opId)
    • pacta_scan_proposal_diff → /v1/scan/proposal-diff ($0.15 = 150000 atomic; forum = aave | euler | morpho | sky, topicId = the forum topic id). The proposal must be listed in https://pacta.wtf/api/v1/proposals.json.

    When metering is on, the live Worker answers every metered route in both protocol versions. A bare request (no query, no payment header) gets the 402 immediately.

    • x402 v1: the 402 JSON body (x402Version: 1, network: "base", maxAmountRequired). Pay with xPaymentHeader, sent as X-PAYMENT. The receipt is X-PAYMENT-RESPONSE.
    • x402 v2: the PAYMENT-REQUIRED header (base64 JSON, x402Version: 2, network: "eip155:8453", amount, extensions.bazaar with a JSON Schema 2020-12 schema). Pay with paymentSignatureHeader, sent as PAYMENT-SIGNATURE. The receipt is PAYMENT-RESPONSE.

    If you send no payment argument, the tool returns the upstream 402 as a validated requirements entry for the version picked by x402Version (default 1), along with:

    • availableVersions and alternativeAccepts for the other valid version;
    • EIP-712 TransferWithAuthorization typedData;
    • the envelope template for that version (xPaymentEnvelopeTemplate for v1, paymentSignatureEnvelopeTemplate for v2).

    Every challenge entry, v1 or v2, passes the same pins: allowlisted payTo, Base USDC, the PACTA_X402_MAX_ATOMIC cap, the route price, the maxTimeoutSeconds ceiling, and binding to the requested URL. Anything else is rejected_challenge. Sign the typedData yourself (the server holds no keys), base64-encode the envelope, and call the tool again with xPaymentHeader (v1) or paymentSignatureHeader (v2), never both.

    A paid call succeeds only with a 2xx and a valid PAYMENT-RESPONSE / X-PAYMENT-RESPONSE receipt on that version's network, whose payer, payTo and amount match your authorization. The receipt is the facilitator's assertion and is not looked up on Base (onchainReceiptVerified: false).

Environment variables

VariableDefaultMeaning
PACTA_X402_URLunsetBase URL of the x402 gateway; the canonical live one is https://pacta-x402.pacta.workers.dev. If unset, paid tools are disabled. Must be https://; plain http:// is allowed only for localhost / 127.0.0.1 / [::1]. URLs with embedded credentials are refused.
PACTA_X402_MAX_ATOMIC1000000 ($1.00)Per-payment cap in atomic USDC (6 decimals). Values above the hard ceiling 5000000 ($5.00) are clamped to it. Invalid values fall back to the default, and pacta_x402_discover reports a configWarnings entry.
PACTA_X402_TIMEOUT_MS8000Timeout for every outbound fetch (index, proposals, discovery, paid). Valid range is 1 to 60000.
PACTA_INDEX_PATH / PACTA_SPEC_PATHunsetLocal index / spec files for the free tools.
PACTA_PROPOSALS_PATHunsetLocal proposals feed (pacta.governance_proposals_feed.v1) for pacta_list_proposals.

The hard ceiling of $5.00 is 20 times the most expensive route ($0.25). No env var or option can raise it, so a mistyped or hostile environment cannot turn one tool call into a large transfer. In practice the route's own price is the binding limit, and the cap is defence in depth.

x402 payment safety rules

Pinned constants. Nothing an upstream response says can widen these:

  • Network base, chain ID 8453, asset Base USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, EIP-712 domain USD Coin / 2 / 8453 / Base USDC.
  • payTo allowlist:
    • 0xcA4E2ff22B69A7284210c47BdeE21Aac1269da09 (attester / pacta.base.eth, the gateway's default)
    • 0x4348DF1BEFAffcbA248B5118B1b782B1B2bdEd14 (treasury owner)
  • Address comparison ignores case, but a mixed-case address must carry a valid EIP-55 checksum. Checksums are computed with a bundled pure-JS Keccak-256 (src/keccak.mjs), which is tested against known vectors and against node:crypto SHA3-256.

Upstream 402 challenge (accepts[])

Entries are collected from the JSON body accepts, from base64 Payment-Required / X-Payment-Required headers, and from WWW-Authenticate: x402 <base64> or x402 accepts="<base64>". Each entry is validated independently. An entry is selected only if it meets all of the following:

  • x402Version is 1 (on the entry, or on the enclosing object), scheme is "exact", and network is "base". If a chainId is present (on the entry or in extra), it must be 8453.
  • asset is Base USDC.
  • payTo is in the allowlist.
  • maxAmountRequired is a canonical positive integer string, no greater than the cap and no greater than the route's own price.
  • maxTimeoutSeconds is an integer from 1 to 600.
  • extra is { name: "USD Coin", version: "2" }.
  • The entry is bound to the exact URL that was requested. If resource is present, it may be absolute, or relative and resolved against the request origin. Either way it must have the same origin (scheme, host and port) and the same pathname as the requested URL. The query string is not compared. A challenge for another host, or for another route on the same gateway, is rejected. An entry with no resource makes no claim and is accepted.

The first valid entry is normalized from the pinned constants. Its resource is always the requested URL, and the upstream resource string is never copied into the output or the typedData. It is returned as accepts: [entry], along with selectedFrom and rejectedEntries (per-entry reasons). The typedData is built only from that entry, and its TTL is min(validBeforeSeconds ?? 300, entry.maxTimeoutSeconds).

If no entry passes, the call fails with rejected_challenge, which lists {source, index, reasons[]} for every entry. There is no fallback to accepts[0] and no typedData for an unvalidated entry.

Reason codes: not_object, bad_x402_version, bad_scheme, bad_network, bad_chain_id, bad_asset, bad_asset_checksum, bad_pay_to, pay_to_not_allowed, bad_pay_to_checksum, bad_amount, amount_over_cap, amount_over_route_price, bad_max_timeout, bad_extra, missing_expected_resource (the caller passed no absolute expectedResource), bad_resource, resource_origin_mismatch, resource_path_mismatch.

Caller-supplied X-PAYMENT (validateXPaymentHeader)

This header is validated locally before any network call. Checks run in order:

  1. It must be a non-empty string of at most 8 KiB.
  2. It must be strict canonical Base64: standard alphabet, correct = padding, no whitespace, and a round-trip re-encode equal to the input.
  3. It must decode as UTF-8 with TextDecoder("utf-8", { fatal: true }).
  4. It must parse with JSON.parse into a plain object.
  5. The envelope must have exactly {x402Version: 1, scheme: "exact", network: "base", payload}, and payload must have exactly {signature, authorization}.
  6. signature must be 65-byte hex.
  7. authorization must have exactly from, to, value, validAfter, validBefore, nonce:
    • from is a valid, non-zero address.
    • to is a valid address in the allowlist.
    • value is a positive integer string, no greater than the cap and no greater than the route price.
    • validAfter <= now < validBefore, and validBefore - now <= 600.
    • nonce is bytes32.

Failures return { code: "invalid_x_payment", reason }. Reason codes: not_string, empty, too_long, bad_base64, non_canonical_base64, bad_utf8, bad_json, not_object, unexpected_field, bad_x402_version, bad_scheme, bad_network, bad_payload, bad_signature, bad_authorization, bad_from, bad_to, bad_checksum, recipient_not_allowed, bad_value, value_over_cap, value_over_route_price, bad_route_price, bad_valid_after, bad_valid_before, not_yet_valid, expired, validity_window_too_long, bad_nonce.

Transport and settlement

  • Every fetch uses AbortSignal.timeout(ms). The timeout also covers reading the body, and it still fires if an injected fetchImpl ignores the signal. Response bodies are capped at 2 MiB (16 MiB for the free index and proposals feeds).

  • Paid requests use redirect: "error", so a 3xx response cannot carry X-PAYMENT to another origin.

  • After X-PAYMENT is sent, the server requires HTTP 2xx and a valid X-PAYMENT-RESPONSE receipt. The receipt must meet all of the following:

    • It is strict base64 JSON with success === true, network === "base" and a non-zero 32-byte transaction.
    • payer equals authorization.from (case-insensitive).
    • Any recipient field present (payTo, recipient or to) is a valid address equal to authorization.to.
    • Any amount field present (amount or value) is a canonical positive integer equal to authorization.value.

    Anything else fails with unverified_settlement (reason, status, bounded snippet), and the body is not returned as a paid result. Receipt reasons: missing_receipt, receipt_<base64 reason>, receipt_not_object, receipt_not_success, receipt_bad_network, receipt_bad_transaction, receipt_missing_payer, receipt_bad_payer, receipt_payer_mismatch, receipt_bad_pay_to, receipt_pay_to_mismatch, receipt_bad_amount, receipt_amount_mismatch.

  • Settlement is not verified on-chain. The receipt is the gateway's own assertion in a response header. The client checks that assertion against the authorization it signed, but it does not look up the transaction on Base or confirm that the USDC transfer happened. A paid result therefore carries gatewayReceiptVerified: true, onchainReceiptVerified: false and settlementVerificationScope: "gateway_header_assertion". If you need proof, check paymentReceipt.transaction on Base yourself.

  • Unpaid non-2xx responses (other than 402) fail with upstream_http_error, which carries status and a snippet of at most 300 characters. Transport failures fail with network_error and timeouts with timeout. No offline challenge is ever fabricated.

Error shape

MCP tool errors come back as isError: true with JSON { "error": "<message>", "code": "<stable code>", ...details }.

Stable codes: x402_disabled, invalid_config, invalid_argument, invalid_x_payment, malformed_header, rejected_challenge, timeout, network_error, response_too_large, upstream_http_error, unverified_settlement, and, for the proposals feed, feed_unavailable and invalid_feed.

MCP registry and agent skill

  • server.json is the manifest for the official MCP registry (registry.modelcontextprotocol.io, schema 2025-10-17). The server name is io.github.Fractal-Neurons/pacta, and package.json carries the matching mcpName, which the registry uses for npm ownership verification. Its environmentVariables must list exactly the variables read in src/, and the tests enforce that.
  • server.json also lists the hosted endpoint under remotes (streamable-http, https://pacta-x402.pacta.workers.dev/mcp). The registry checks that an npm package exists, and @pacta/mcp-server is not published yet, so server.remote.json is a remote-only twin (same name, version and metadata, no packages) that can be registered first. The tests keep the two files in step.
  • SKILL.md is a portable agent skill (pacta-protocol-commitments). It covers the free feeds, replaying a record with cast call … --block N and checking the block hash, these tools, and when an x402 payment is justified.

Tests

bash
node --test test/server.test.mjs   # == npm test; imports every other test file

The tests use injected fetchImpl only and pass with a missing or read-only HOME (for example HOME=/nonexistent node --test test/server.test.mjs). Scratch files go in a fresh mkdtemp directory under the first writable location among PACTA_TEST_SCRATCH_DIR, XDG_CACHE_HOME, the current directory, and test/. That directory is removed in a finally block. src/ and bin/ never write files or read HOME, and a static test enforces this. The stdio and CLI tests wait for complete JSON-RPC lines, with no fixed sleeps. A guard replaces the global fetch so that any real network call fails. Tests use fixed fake 65-byte signatures and no keys.

MIT

來源:packages/mcp-server/README.md,提交 6831545

工具

5

pacta_check_preflight

Pre-execution safety check

One-call pre-execution safety check of a contract before an agent interacts with it: verdict (immutable / timelock / zero-delay timelock / multisig / EOA admin / unclassified / undeployed), 0-100 risk score, proxy + implementation, controller kind, zero-delay upgradeability and pause / blacklist / upgrade capabilities, pinned to a block. Free during the launch (no key, no payment).

輸入參數blockchainaddress必填

pacta_scan_control_plane

Control-plane scan

Live control-plane scan of an EVM contract: EIP-1967 implementation/admin/beacon, owner(), Safe threshold/owners/guard, timelock delay, code size and deterministic risk flags at the latest (or a given) block. Free during the launch (no key, no payment).

輸入參數blockchainaddress必填

pacta_scan_timelock_op

Timelock operation state

Live OpenZeppelin TimelockController operation state: isOperation/Pending/Ready/Done, getTimestamp (ETA), getMinDelay at the latest block. Free during the launch (no key, no payment).

輸入參數dataopIdsaltchainvaluetargettimelock必填predecessor

pacta_scan_proposal_diff

Governance proposal pinned-vs-live diff

Re-runs the pinned contract reads of a Pacta-tracked governance proposal (Aave, Euler, Morpho, Sky) at the live block and returns pinned-vs-live diffs. Free during the launch (no key, no payment).

輸入參數forum必填topicId必填

pacta_get_live_timelocks

Live tracked timelock operations

Live state of the public pending timelock operations tracked in https://pacta.wtf/api/v1/radar.json, each re-checked at the latest block (getTimestamp, isOperationPending, isOperationDone). Free during the launch (no key, no payment).

輸入參數protocol

版本歷史

1
  1. v0.1.1最新Nov 14, 2026

評分

已滿足適用檢查項的 100%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通必需25 分
  • 已宣告認證方式必需10 分
  • 已列出工具必需15 分
  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分