Multi-Party Agent Wallet
by @patrickcure
Operate and automate threshold multisignature workflows for MPC/MPA wallets on an isolated, dedicated host that contains no unrelated sensitive data or priva...
clawhub install mpa-wallet📖 About This Skill
name: mpa-wallet description: > Operate and automate threshold multisignature workflows for MPC/MPA wallets on an isolated, dedicated host that contains no unrelated sensitive data or private keys. version: 1.0.12 metadata: openclaw: requires: env: - KEYGEN_ID - AUTH_KEY_PATH - MPA_PATH - MPC_CONFIG_PATH - MPC_AUTH_URL - MANAGEMENT_PORT bins: - curl - jq - forge - cast - python3 - pip3 config: - "$AUTH_KEY_PATH/mpc_auth_ed25519" - "$MPC_CONFIG_PATH/configs.yaml" - "$MPA_PATH/.env" primaryEnv: MPC_AUTH_URL os: - linux homepage: https://clawhub.ai/patrickcure/mpa-wallet
Skill: MPA / MPC wallet agent (OpenClaw / Clawhub)
Use this skill when operating an AI agent (e.g. OpenClaw) that manages an mpc-auth node participating in a Multi-Party Agent (MPA) wallet: a single on-chain address (EVM today) whose MPC signature requires cooperation of **at least threshold+1 nodes in a Group**. No single node holds the full private key.
Examples of usage
Here are some examples of what a user can say to their AI agent
Prerequisites
This skill assumes the **operator has already provisioned an MPA wallet environment** in ContinuumDAO terms—not a single standalone node:
POST calls to the management API are authenticated without MetaMask (operator
provisions PublicMgtKey / allow-list). Operational signing details:
$MPA_PATH/references/ED25519_MANAGEMENT_KEY_SIGNING.md.Host security requirements (mandatory)
Scope and egress guardrails (mandatory)
$MPC_AUTH_URL:$MANAGEMENT_PORT, chain RPC calls configured by the node,
and local files under $MPA_PATH plus the dedicated management key path.
AUTH_KEY_PATH material as high-privilege signing input only: neverContinuumDAO documentation (end-user setup, before this skill applies):
cast for on-chain reads. multiSignRequest bodies for POST /multiSignRequest is only
via the repo helpers listed under When this skill applies—not arbitrary forge
projects or hand-written JSON.)Complete those guides first; then use this skill for **day-to-day agent
behavior** (messaging, multiSignRequest, agree/trigger/execute, and API
discipline).
New session and environment bootstrap
On session start, follow $MPA_PATH/references/AI_AGENT_NEW_SESSION.md:
load $MPA_PATH/.env, confirm MPA_PATH, MPC_AUTH_URL,
MANAGEMENT_PORT, AUTH_KEY_PATH, and MPC_CONFIG_PATH; ensure
$MPA_PATH/{recipes,references,scripts,tools} symlink to the clone under
MPC_CONFIG_PATH when the repo dirs exist; verify GET /health; then
set or obtain KEYGEN_ID (existing id or POST /keyGenRequest / agree
flow per $MPA_PATH/references/API_IMPLEMENTATION.md). That doc also restates
what the mpa-wallet skill is for.
Overview (read this first)
This section restates the ideas in $MPA_PATH/references/instructions.md
in a form suited for users and agents who do not yet see why Group,
KeyGen, threshold, and two signature types matter.
What you are operating
A Multi-Party Agent (MPA) wallet is one shared wallet address (EVM today: one Ethereum address) whose private key never exists whole on any server. It is created and used via Multi-Party Computation (MPC) across a Group of nodes (often VPSs). No single node can sign alone: producing a valid **MPC signature requires cooperation of at least threshold+1** nodes. The integer threshold is set when the KeyGen is created. That is how the address stays protected if one machine is compromised.
The MPC address works on any EVM network; it is not locked to one smart contract.
Humans and AI agents are symmetric. Some nodes are operated by people, others
by an agent (e.g. Open Claw). All use the same REST ideas: message the group
to propose and refine intent; build multiSignRequest bodies
only with $MPA_PATH/scripts/generateMultiSignRequestFromCompose.py,
$MPA_PATH/scripts/generateSignRequestWithFoundryScript.py, or
$MPA_PATH/recipes/ scripts (see $MPA_PATH/references/AI_AGENT_COMPOSE_MULTISIGNREQUEST.md
and $MPA_PATH/references/AI_AGENT_FORGE_SIGNREQUEST.md). Never invent or
hand-edit multiSignRequest JSON from scratch. Submit
POST /multiSignRequest with that helper output plus management clientSig
/ signedMessage (see $MPA_PATH/references/ED25519_MANAGEMENT_KEY_SIGNING.md).
Other nodes agree or reject with /signRequestAgree (the
originator does not need to agree), optionally add Thoughts, then **EVM
execution is only $MPA_PATH/scripts/executeSignResult.py**: that script
POSTs /triggerSignRequestById (management-signed, with txParams
/ messageHash when required), polls GET /getSignResultById until
MPC signatures exist, then eth_sendRawTransaction—do not call
POST /triggerSignRequestById directly or bypass the script. The agent’s
job is to take intent from the KeyGen messaging flow, run only those
helpers. Use multiSignJoin.py to merge two helper-sized JSON files into
one batch; save stdout and run multiSignJoin.py again (previous output
as --a or --b, plus another helper file) to extend ordered sequences on
the same chain—repeatedly for multi-step flows. Then POST /multiSignRequest
with management signing and later executeSignResult.py as required—never
fabricate multiSignRequest JSON except via the approved helpers (compose,
Forge, recipes, or multiSignJoin when merging two outputs).
https://www.getfoundry.sh/introduction/getting-started
The two signatures (critical distinction)
1. Management signature — Per-node API authentication. Each client has its
own key material; public keys are in config (e.g. mpc-config/configs.yaml).
Every POST to the management API must be signed by that client’s
management key (Ed25519 for agents, often MetaMask for interactive users). This
proves who is calling the API, not what the MPC wallet authorizes on-chain.
2. MPC signature — On-chain authorization by the shared wallet. There
is no single MPC private key file. Nodes run a protocol so that, only after
enough agreements, a signature valid for the MPC public address is
produced.
Groups, KeyGen, signing (short)
$MPA_PATH/references/instructions.md.
Thoughts guide whether to shelve and revise. With threshold+1
accepts, executeSignResult.py POSTs /triggerSignRequestById (MPC
signing), then broadcast txs and updateSignResultStatusById.Persistent context (why messages and Purpose matter)
Each node stores the same logical data over time: KeyGen messages
(listMessages, getMessageThread, …) and sign-request / sign-result metadata
(Purpose, Thoughts). That shared history is what future decisions
should use—regardless of which LLM or agent version is connected.
When this skill applies
POST /multiSignRequest:$MPA_PATH/scripts/generateMultiSignRequestFromCompose.py,
$MPA_PATH/scripts/generateSignRequestWithFoundryScript.py,
$MPA_PATH/scripts/multiSignJoin.py (including chained runs whose inputs are
prior helper or multiSignJoin output), or a script under $MPA_PATH/recipes/—see
$MPA_PATH/references/AI_AGENT_COMPOSE_MULTISIGNREQUEST.md. Never hand-write or
invent multiSignRequest JSON; add clientSig / signedMessage per
$MPA_PATH/references/ED25519_MANAGEMENT_KEY_SIGNING.md, then POST /multiSignRequest.
For the Foundry helper, feed forge script run-latest.json per
$MPA_PATH/references/AI_AGENT_FORGE_SIGNREQUEST.md. Common intents include **token
transfers, cross-chain calls, and other DeFi** protocol interactions.
Evaluating the multi-sign lifecycle (POST /signRequestAgree, etc.) when helping
operators interpret state. EVM trigger + broadcast: only
$MPA_PATH/scripts/executeSignResult.py (it POSTs /triggerSignRequestById—do
not call that endpoint directly).
POST /sendMessage, GET /getMessageThread) formultiSignJoin.py: merge two JSON outputs (recipes,multiSignJoin stdout saved to a file)
into one batch. Chain merges by running multiSignJoin.py again on
(merged file + next helper) to build longer same-chain sequences; then POST /multiSignRequest
with only that tool output plus management signing (see AI_AGENT_COMPOSE_MULTISIGNREQUEST.md).
POST calls toDo not confuse management signatures (per-node API auth) with **MPC signatures** (threshold signing over a message).
Core concepts
mpc-config/configs.yaml (e.g.
PublicMgtKey / NodeMgtKey).signRequestAgree)triggerSignRequestById; use POST /multiSignRequest, not
/signRequest (relayer/tx-check only).Environment (agent)
Default env file: $MPA_PATH/.env. Load this file first (if present) before
prompting for missing variables.
KEYGEN_ID — If set, prefer this KeyGen for signing when unambiguous. IfAUTH_KEY_PATH — Directory containing the Ed25519 management privateAUTH_KEY_FILENAME). If unset, scripts resolve
~/.ssh/mpc_auth_ed25519 (equivalent to ~/.ssh + default basename).
Default: Unset (→ ~/.ssh/mpc_auth_ed25519).AUTH_KEY_FILENAME — Basename of the key file inside AUTH_KEY_PATHmpc_auth_ed25519.MPC_MGT_ED25519_SEED_HEX — Optional override: exactly 64 hex characters$MPA_PATH/references/ED25519_MANAGEMENT_KEY_SIGNING.md
§ *Raw 32-byte seed vs key file*. If unset, scripts load AUTH_KEY_PATH
/ AUTH_KEY_FILENAME as a key file.MPA_PATH — Directory containing references, scripts, recipes, and tools.~/.mpa.MPC_CONFIG_PATH — Absolute path to the mpc-config repo root (cloneconfigs.yaml, scripts/, recipes/, references/,
tools/, docs/). Used for symlinks into $MPA_PATH and owner
docs. Default: /path/to/mpc-config.MPC_AUTH_URL — Base URL of the management API (host only).http://127.0.0.1.MANAGEMENT_PORT — Management API port (see ManagementAPIsPort inconfigs.yaml). Default: (often 8080).Base URL for a co-located node: $MPC_AUTH_URL:$MANAGEMENT_PORT (see
configs.yaml for ManagementAPIsPort).
MPA_PATH is a filesystem location, not a credential. primaryEnv is set to
MPC_AUTH_URL because the management API endpoint is the primary operational
target for this skill.
Schedule $MPA_PATH/scripts/mpc_event_listener.py (see KeyGen inbox poll
below)—not keygen_messaging_agent_poll.py alone. --keygen-messages
runs the same poll as keygen_messaging_agent_poll.py; KEYGEN_ID,
AUTH_KEY_PATH, and MPC_AUTH_URL apply. --sign-ready runs
executeSignResult.py per ready id (which POSTs /triggerSignRequestById).
KEYGEN_ID is not required for --sign-ready alone (that path still needs
AUTH_KEY_PATH / MPC_MGT_ED25519_SEED_HEX for management POSTs inside
executeSignResult.py). If KEYGEN_ID is not set for the keygen handler,
ask the user what to do, including stopping --keygen-messages polling with
cron.
Python dependencies
Use a dedicated virtual environment under $MPA_PATH/.venv (with the
default MPA_PATH, that is ~/.mpa/.venv). Install packages **into that
venv with pip from the venv (e.g. $MPA_PATH/.venv/bin/pip install …**)
or after source $MPA_PATH/.venv/bin/activate — not with bare pip3
on the system interpreter. PyPI names use hyphens where applicable (eth-account
installs the eth_account import).
Bootstrap once (if $MPA_PATH/.venv does not exist):
python3 -m venv "$MPA_PATH/.venv"
Then install into the venv:
1. Base (all signing scripts in $MPA_PATH/scripts):
$MPA_PATH/.venv/bin/pip install eth-account — pulls rlp, eth_utils,
hexbytes, etc., needed by generateSignRequestWithFoundryScript.py,
generateMultiSignRequestFromCompose.py, multiSignJoin.py, and
executeSignResult.py.
2. Compose + recipes: $MPA_PATH/.venv/bin/pip install PyNaCl — only
generateMultiSignRequestFromCompose.py and recipes/*.py need PyNaCl
(optional Ed25519 fields on payloads). Skip if you use only the Forge helper,
multiSignJoin, or executeSignResult.
3. KeyGen inbox poll + event listener: $MPA_PATH/.venv/bin/pip install cryptography
— satisfies keygen_messaging_agent_poll.py and mpc_event_listener.py
(same minimum as $MPA_PATH/scripts/requirements-keygen-agent.txt).
Alternatively:
$MPA_PATH/.venv/bin/pip install -r $MPA_PATH/scripts/requirements-keygen-agent.txt.
Verify packages before running helpers (agents should check the venv, not the
system python3):
"$MPA_PATH/.venv/bin/pip" show eth-account PyNaCl cryptography 2>/dev/null | grep -E '^Name:|^Version:'
"$MPA_PATH/.venv/bin/python" -c "import eth_account; import nacl; import cryptography"
(Adjust the import line if optional packages were skipped — e.g. omit nacl
/ cryptography when not installed.)
Run scripts with $MPA_PATH/.venv/bin/python — for example
$MPA_PATH/.venv/bin/python $MPA_PATH/scripts/generateSignRequestWithFoundryScript.py ....
Plain python3 ... only works if your shell’s PATH puts that venv first
(prefer the explicit venv python path in automation).
KeyGen inbox poll (@agent)
To notice unread channel messages directed at the agent without manual
GET /listMessages each time, schedule $MPA_PATH/scripts/mpc_event_listener.py
--keygen-messages (recommended: OpenClaw Gateway isolated cron; see
$MPC_CONFIG_PATH/docs/AGENT_ED25519_SETUP.md §8.5; OpenClaw cron:
https://docs.openclaw.ai/cron). That handler runs the same logic
as keygen_messaging_agent_poll.py; use the poll script only for one-off
debugging, not as the primary scheduled entry point.
If polling may already be configured, report whether it is active, the last run
time (if known), which handlers are enabled, and the period—then ask whether the
operator wants to change it. Ask the operator once whether they want scheduled
polling (and where: e.g. OpenClaw cron) before you add or change a timer. **If
they want a schedule, ask which period from the fixed set: every 1, 5, 10,
30, 60 minutes or every 2, 4, 6, 8, 10, 12, 24 hours**. Use
$MPA_PATH/scripts/mpc_cron_schedules.py (--interactive on a TTY, or
plain output for the table) to map the choice to OpenClaw --every and
crontab. Do not assume background polling: some setups only use ad-hoc
GET /listMessages / getMessageThread, and the handler **marks matched
messages read**. If they already declined, a cron entry already exists, or they
explicitly asked you to wire the poll, skip re-asking.
1. Once: install cryptography into $MPA_PATH/.venv (see **Python
dependencies** below).
2. Run: $MPA_PATH/.venv/bin/python
$MPA_PATH/scripts/mpc_event_listener.py --keygen-messages (and
KEYGEN_ID set; AUTH_KEY_PATH / MPC_AUTH_URL if not defaults).
--dry-run applies to the keygen handler only: list matches without
multiMarkMessagesRead.
3. Output: one JSON line: handlers.keygen_messages contains matches,
match_count, marked_ids (same shape as keygen_messaging_agent_poll.py
stdout). If KEYGEN_ID is missing, the keygen_messages handler fails
(set KEYGEN_ID). For a JSON error line on stdout instead, run
keygen_messaging_agent_poll.py directly (debug only).
4. After handlers.keygen_messages.match_count > 0: the handler only marks
messages read; you must read each match’s title and body (and pull
thread context with GET /getMessageThread / related APIs if needed),
infer intent, then act—invoke tools, management POST routes (e.g.
/sendMessage), or build multiSignRequest bodies with the approved
helpers and POST /multiSignRequest per
$MPA_PATH/references/AI_AGENT_COMPOSE_MULTISIGNREQUEST.md (never invent the
JSON), or both. Reply in-channel with POST /sendMessage when a text
response is appropriate (management-signed;
$MPA_PATH/references/API_KEYGEN_MESSAGING.md). Encode the same “parse → act”
expectation in any Open Claw cron --message that runs this script. Humans
can @agent in title or body to target the agent.
Event listener (mpc_event_listener.py) — optional extra handlers
Use the same mpc_event_listener.py when one cron should also run
sign_ready (GET /listSignRequestsReady → executeSignResult.py per
id, which POSTs /triggerSignRequestById, polls getSignResultById,
then broadcasts—optional --fast / --execute-fast). AI agents run
only executeSignResult.py for EVM execution. Ask the operator which
handlers to turn on: use --interactive (TTY) or explicit --keygen-messages
/ --sign-ready (or both). For sign_ready, --execute-fast selects
the executeSignResult --fast path (parallel receipt confirmation for batch
txs); omit it for sequential execution. --sign-ready-dry-run only lists ready
request ids (no executeSignResult). Before you add or change a timer for
this script, ask the operator once if they want scheduled polling (and where:
e.g. Open Claw cron); if yes, ask which period from the fixed set (**every 1, 5,
10, 30, 60 minutes or every 2, 4, 6, 8, 10, 12, 24 hours**) and map it with
mpc_cron_schedules.py to Open Claw --every and crontab. Apply the same
ask once / skip re-asking rules as the KeyGen inbox poll above. More
handlers can be added to the same script over time.
Default operational loop (high level)
1. Discuss in KeyGen messaging: human or other nodes POST /sendMessage;
everyone reads GET /getMessageThread (and related list/get APIs). Optionally
use mpc_event_listener.py --keygen-messages (KeyGen inbox poll above)
when the agent should wake on @agent mentions.
2. Plan: use KeyGen message context plus local/on-chain state to pick a concise
rationale based on the user's wishes and which path applies: generateMultiSignRequestFromCompose.py,
generateSignRequestWithFoundryScript.py, $MPA_PATH/recipes/, and—only when
merging two separate helper outputs—multiSignJoin.py (including chained joins
for longer same-chain sequences).
3. Build and propose: follow $MPA_PATH/references/AI_AGENT_COMPOSE_MULTISIGNREQUEST.md
— only those tools; Foundry broadcast JSON for generateSignRequestWithFoundryScript.py
per $MPA_PATH/references/AI_AGENT_FORGE_SIGNREQUEST.md. Most proposals: run one
compose/Forge/recipe helper and POST /multiSignRequest with that stdout (add clientSig /
signedMessage per ED25519_MANAGEMENT_KEY_SIGNING.md)—no multiSignJoin needed.
When you need two helper outputs in one batch: merge with multiSignJoin.py
(--first-nonce = MPC cast nonce for the first tx in that merge); repeat
with saved stdout plus another helper file to grow the batch. Include a concise Purpose
(≤256 chars). POST /multiSignRequest with only tool-produced JSON (never ad hoc).
4. Agree: other nodes (everyone except the originator of the sign
request) POST /signRequestAgree
(accept/reject) until threshold+1 agreements exist. The originator does
not need to agree—creating the request already records their intent. Optional
Thoughts per node to guide the agent (e.g. to POST /shelveSignRequest
and revise).
5. Approval gate (recommended default): require explicit human approval in
KeyGen messaging before executeSignResult.py (see step 6).
6. Trigger, MPC sign, and broadcast (AI agent): only
$MPA_PATH/scripts/executeSignResult.py. It POSTs /triggerSignRequestById
only when getSignResultById does not yet have MPC signatures. **EVM
(required): pass --sign-request-file PATH to the saved JSON from the same
compose/recipe run (stdout that includes bodyForSign with txNonce** /
txGasLimit / fee fields), plus --sign-request-id. The management API does
not return those fields on GET /getSignRequestById; the file is what lets the
script build txParams and messageHash for trigger (same as web Get Sig).
See $MPA_PATH/references/API_IMPLEMENTATION.md § POST /triggerSignRequestById.
After trigger, GET /getSignRequestById?tx_params=1 can return the stored snapshot.
The script polls GET /getSignResultById until signatures exist, then rebuilds and
eth_sendRawTransaction. Single-tx: same reconstruction as
continuumdao-node-app Execute — GET /getSignRequestById?tx_params=1 (or merged file),
GET /getChainDetails, RPC estimateGas / fee discovery, then r,s,v.
Batch: messageRawBatch[i] + batchsignatures[i]. By default
sequential receipt waits; --fast for concurrent confirms.
Readiness: GET /isSignRequestReadyById (see
$MPA_PATH/references/API_IMPLEMENTATION.md). Then
POST /updateSignResultStatusById with executed and transactionHash
(or batch hashes).
7. Report: POST /sendMessage summarizing what was done and what to expect.
8. Context: for future spends, use messages plus Purpose / Thoughts
on sign results (GET /listSignResults, GET /getSignRequestById /
getSignResultById).
Other API capabilities (agent)
Per $MPA_PATH/references/instructions.md, the agent may also: /keyGenRequest,
/keyGenRequestAgree, /addKnownAddress, /postChainDetails, /addToken,
health/version discovery, and fee/credit checks via GET /getGlobalNonceByKeyGenId
(and top-up gas as needed). For on-chain fee state on Linea mainnet, use the
subsection below.
Fee payment (Linea mainnet, chainId 59144)
ContinuumDAO’s fee / registration contract on Linea is deployed at a fixed
address. Agents should use Foundry cast against that contract with an RPC URL
taken from the node’s chain config (same pattern as other on-chain checks).
Fee contract (Linea mainnet): 0x55aD6Df6d8f8824486C3fd3373f1CF29eCecF0A3
On-chain register() from the MPC wallet (multiSignRequest): use the
linea_register.py recipe in
$MPA_PATH/references/AI_AGENT_COMPOSE_MULTISIGNREQUEST.md (recipes table)
(Linea 59144, register(), RPC from getChainDetails). **Fee-token
top-up (deposit) from the MPC wallet: linea_fee_deposit.py** in the same
table; the MPC must approve the fee contract for the ERC20 fee token before
deposit succeeds. For approve + deposit in a single batch
multiSignRequest, use forge/script/LineaFeeApproveDeposit.s.sol →
generateSignRequestWithFoundryScript.py (see
$MPA_PATH/references/AI_AGENT_FORGE_SIGNREQUEST.md § Linea fee). For read-only
cast checks below, any RPC URL from the node’s chain config is fine.
Variables: Set KEYGEN_ID to your KeyGen result id (see Environment).
Set WALLET_ADDRESS to the MPC wallet Ethereum address for that KeyGen
(from GET /getKeyGenResultById / ethereumaddress). Use your management
API base URL as $MPC_AUTH_URL:$MANAGEMENT_PORT.
RPC URL from the node (Linea chain_id 59144):
RPC=$(curl -s "$MPC_AUTH_URL:$MANAGEMENT_PORT/getChainDetails?chain_id=59144" | jq -r '.Data.rpcGateway')
Registration and fee state (cast reads only):
# Is the KeyGen registered?
cast call 0x55aD6Df6d8f8824486C3fd3373f1CF29eCecF0A3 \
"isRegistered(address)(bool)" $WALLET_ADDRESS --rpc-url $RPCFee config for this KeyGen
cast call 0x55aD6Df6d8f8824486C3fd3373f1CF29eCecF0A3 \
"keyGenFeeConfig(address)(address,uint256,uint256,uint256,bytes32)" \
$WALLET_ADDRESS --rpc-url $RPCGlobal nonce (for getRemainingNonces) — from the management API, not cast nonce
GNONCE=$(curl -s "$MPC_AUTH_URL:$MANAGEMENT_PORT/getGlobalNonceByKeyGenId?id=$KEYGEN_ID" | jq -r '.Data.globalnonce')Remaining signatures before top-up
cast call 0x55aD6Df6d8f8824486C3fd3373f1CF29eCecF0A3 \
"getRemainingNonces(address,uint256)(uint256)" $WALLET_ADDRESS $GNONCE --rpc-url $RPC
Note: globalnonce here is the KeyGen’s MPC signing counter from the API. It
is not the EVM account nonce from cast nonce. Fee payment / top-up can
be sent as ordinary EVM transactions from any funded wallet—they do not
require the multiSignRequest / threshold flow. Paying from a separate hot
wallet or custodian is often more convenient than routing top-ups through the MPC
wallet. If you do spend from the MPC address itself, those txs still go through
multiSignRequest as usual. For ABI-level details of the fee contract, see
$MPA_PATH/references/API_IMPLEMENTATION.md
and on-chain docs your deployment publishes.
Incoming MPC sign requests (policy)
When another member requests a signature, default decision inputs:
Purpose.POST /sendMessage awaiting guidance.Remember: threshold+1 accepts are required to generate the MPC signature.
Management API authentication (Ed25519)
POST to the management API requires a management signatureNodeMgtKey per node config).
clientSig on multiSignRequest signs messageToSign with theGET /getPublicMgtKeyNonce vs GET /getNodeMgtKeyNonce,GET /getAllowedEd25519MgtKeys, sign-clipboard (--inline /
--inline-file), and tools/ed25519_private_to_pubkey_hex.py:
$MPA_PATH/references/ED25519_MANAGEMENT_KEY_SIGNING.md (see §7 *Raw 32-byte
seed vs key file* for MPC_MGT_ED25519_SEED_HEX / recipe --ed25519-seed-hex
vs --ed25519-key-file).Creating multiSignRequest payloads
Skim: Foundry → $MPA_PATH/references/AI_AGENT_FORGE_SIGNREQUEST.md.
Compose JSON → $MPA_PATH/references/AI_AGENT_COMPOSE_MULTISIGNREQUEST.md.
Prefer Recipes below (linea_register, linea_fee_deposit, erc20_transfer,
…) before hand-writing compose JSON.
Do not invent or hand-edit multiSignRequest JSON from scratch. Only use
$MPA_PATH/scripts/generateMultiSignRequestFromCompose.py,
$MPA_PATH/scripts/generateSignRequestWithFoundryScript.py, or a script under
$MPA_PATH/recipes/ (see $MPA_PATH/references/AI_AGENT_COMPOSE_MULTISIGNREQUEST.md
and $MPA_PATH/references/AI_AGENT_FORGE_SIGNREQUEST.md for forge script
inputs to the Foundry helper). Typical flow: one helper run → add clientSig
/ signedMessage → POST /multiSignRequest (no multiSignJoin). When
you need two helper outputs in one proposal, merge with multiSignJoin.py;
repeat multiSignJoin.py using prior stdout as an input file to build longer
sequences (same chain, --first-nonce = first tx nonce in the final
batch—typically cast nonce on the MPC address). After any helper or
multiSignJoin run prints bodyForSign / messageToSign, add
clientSig (and signedMessage if required) per
ED25519_MANAGEMENT_KEY_SIGNING.md, then POST /multiSignRequest with that
JSON—the agent may call this endpoint; the restriction is never submitting a
payload the tools did not produce.
That reference doc has messageToSign / signedMessage / postBody
rules, multiSignJoin usage, and the recipes table.
Scripts
Location: Python scripts for API automation under $MPA_PATH/scripts/.
Dependencies: (Python dependencies above —
$MPA_PATH/.venv/bin/pip install eth-account, then PyNaCl for
compose/recipes). eth_account is required for all of the following.
generateSignRequestWithFoundryScript.py — Forge broadcast JSON →multiSignRequest JSON. Use when the user needs Foundry script logic for
complex flows.generateMultiSignRequestFromCompose.py — Compose JSON (one or batch calls)multiSignRequest body / messageToSign. Requires PyNaCl.multiSignJoin.py — Join two helper JSON files → one batchmultiSignRequest output. Chaining: reuse stdout as --a or --b
with another helper. --first-nonce = nonce of the first tx in the merge.
Valid after clientSig. See $MPA_PATH/references/AI_AGENT_COMPOSE_MULTISIGNREQUEST.md.executeSignResult.py — Only EVM path for AI agents: POSTs/triggerSignRequestById when needed, polls getSignResultById, then
broadcasts like web Execute. Single-tx: getSignRequestById?tx_params=1
+ chain/RPC; batch: messageRawBatch + batchsignatures. Sequential
by default; --fast for parallel confirm.keygen_messaging_agent_poll.py — Underlying KeyGen @agent poll (debugmpc_event_listener.py --keygen-messages for
scheduled runs.mpc_event_listener.py — Preferred cron entry: --keygen-messages--sign-ready → executeSignResult.py per ready id.mpc_cron_schedules.py — Maps poll periods (1–60 min, 2–24 h) to Open Claw--every and crontab.Recipes
Location: $MPA_PATH/recipes/ — thin CLIs around
generateMultiSignRequestFromCompose.py (same venv deps as compose). Check
here before hand-writing compose JSON; common operations are covered.
Per-script behavior, the recipes table, and examples:
$MPA_PATH/references/AI_AGENT_COMPOSE_MULTISIGNREQUEST.md.
References (bundled snapshots)
Location: API specs and agent notes under $MPA_PATH/references.
$MPC_CONFIG_PATH/docs/CONFIGURING_ED25519_KEYS.md (or $MPA_PATH/docs/...docs/ is symlinked) — Node owner: PublicMgtKey, add keys, store
agent private keys.$MPA_PATH/references/instructions.md — Human-oriented full workflow.$MPA_PATH/references/ED25519_MANAGEMENT_KEY_SIGNING.md — Ed25519 managementClientKeys.$MPA_PATH/references/AI_AGENT_FORGE_SIGNREQUEST.md — Foundry → helper →multiSignRequest; clientSig rules.$MPA_PATH/references/AI_AGENT_COMPOSE_MULTISIGNREQUEST.md — Compose JSON →multiSignRequest; generateMultiSignRequestFromCompose.py.$MPA_PATH/references/AI_AGENT_NEW_SESSION.md — .env, symlinks,GET /health, KEYGEN_ID / KeyGen creation.$MPA_PATH/references/API_IMPLEMENTATION.md — Canonical REST API (endpoints,$MPA_PATH/references/swagger.yaml — OpenAPI/Swagger for tooling.$MPA_PATH/references/TOKEN_STORAGE_SCHEMA.md — Local token config (ERC20,c3transfer, CTMRWA1, etc.).$MPA_PATH/references/API_KEYGEN_MESSAGING.md — Inter-node messaging API.$MPA_PATH/references/KNOWN_ADDRESSES_SCHEMA.md — Local address book (EOA and$MPA_PATH/references/README.md — Index of references.Tools
Location: $MPA_PATH/tools — includes sign-clipboard (management
POST signing), ed25519_private_to_pubkey_hex.py, and
check_ed25519_mgt_keygen.py (debug allow-list + ClientKeys vs a seed or key file).
Usage and --inline / --inline-file:
$MPA_PATH/references/ED25519_MANAGEMENT_KEY_SIGNING.md
§ Tools; sign-clipboard/README.md for flags.
Style notes for agents
POST /multiSignRequest: use only JSON fromgenerateMultiSignRequestFromCompose.py, generateSignRequestWithFoundryScript.py,
recipes/, or multiSignJoin.py (including chained multiSignJoin
runs whose inputs are prior helper or join output files)—never improvise the payload.
swagger.yaml first; use API_IMPLEMENTATIONThoughts and Purpose as durable audit and coordination context⚙️ Configuration
This skill assumes the **operator has already provisioned an MPA wallet environment** in ContinuumDAO terms—not a single standalone node:
POST calls to the management API are authenticated without MetaMask (operator
provisions PublicMgtKey / allow-list). Operational signing details:
$MPA_PATH/references/ED25519_MANAGEMENT_KEY_SIGNING.md.