00 / READ THIS FIRST
A governed wallet is a boundary, not a bank account.
The agent proposes a transaction. ARCANUM evaluates the proposal against a signed policy doctrine. Only the governed wallet can settle it, and every verdict becomes a ledger record. The model never receives the operator key.
Agent proposes
A typed spend intent enters the policy surface.
Policy decides
Caps, destinations, and velocity are checked.
Human intervenes
Exceptions pause until quorum is satisfied.
01 / DEPLOY A GOVERNED WALLET
Start with a wallet that cannot improvise.
Create the wallet from the operator console, then bind the first doctrine before funding it. Use Arc testnet for the complete rehearsal: the same signing ceremony, a safe balance.
- 1
Connect the operator
Open the console with your EOA and complete Sign-In with Ethereum (SIWE). The message includes your workspace domain, Arc testnet, a nonce, and an expiry. Never paste a private key into an agent runtime.
- 2
Name the boundary
Register the wallet as procurement-bot and record 0x3f…9a2c in the inventory. The wallet address is the stable identity used in every ledger decision.
- 3
Fund the rehearsal
Send a small USDC balance on Arc testnet. Confirm the chain ID and token contract in the wallet drawer before your first proposal.
arcana wallet inspect \ --wallet 0x3f...9a2c \ --network arc-testnet
02 / AUTHOR A POLICY DOCTRINE
Write the exception before the request.
A doctrine is executable policy with an accountable author. Keep it narrow: name approved vendors, set a transaction cap, and describe what happens when the facts fall outside the line.
vendor in [ AWS, OpenAI ]
and amount ≤ $500
then
ALLOW · record to ledger
otherwise
ESCALATE · require 2 operators
Publish only after a second operator reviews the rendered rule. A doctrine change is a new signed instrument; it does not rewrite previous ledger decisions.
03 / HANDLE YOUR FIRST RESTRAINT
A pause is a successful control.
When growth-bot asks Anthropic for $2,100.00, the request should stop. Review the reason, compare it to the doctrine, then make a recorded decision. Speed is not the objective; legibility is.
Open restraint queue ↗04 / OPERATOR RUNBOOK
Keep the record useful.
05 / SDK QUICKSTART
Deploy your first GuardedWallet.
Stand up a governed agent wallet on Arc Testnet in five steps. Every transaction it attempts will be evaluated against a Doctrine before it can settle on-chain.
- 1
Install the SDK
Add the Arcanum SDK to your project. It ships with the ArcanumClient, typed contract ABIs, and every verdict type.
- 2
Configure the signer
Point the client at Arc Testnet and supply an admin signer that will own the Doctrine.
- 3
Deploy the wallet with a Doctrine
Create a GuardedWallet through WalletFactory and attach spend limits, category caps, and an escalation quorum.
- 4
Fund the wallet
Transfer test USDC to the deployed address; it appears in the AGENT REGISTER immediately.
- 5
Watch the Event Stream
Every attempted transaction now flows through the governed event stream with a verdict.
npm install arcanum-sdk viem
Snippets are files, not terminal commands. Save the block below as test.mjs, then run node test.mjs. It reads a live GuardedWallet on Arc Testnet: real policy, real verdicts, no keys required.
import { ArcanumClient } from "arcanum-sdk";
import { defineChain, formatUnits, parseUnits } from "viem";
const arcTestnet = defineChain({
id: 5042002,
name: "Arc Testnet",
nativeCurrency: { name: "USDC", symbol: "USDC", decimals: 6 },
rpcUrls: { default: { http: ["https://arc-testnet.drpc.org"] } },
});
// A live GuardedWallet on Arc Testnet. Swap in your own after you deploy it.
const client = new ArcanumClient({
walletAddress: "0x34Ce73E82d48bF377597D8A3c80f9a6DF2085EFa",
chain: arcTestnet,
rpcUrl: "https://arc-testnet.drpc.org",
});
const policy = await client.getPolicy();
console.log("Per-tx cap:", formatUnits(policy.perTxCap, 6), "USDC");
const allowed = await client.simulate({
to: "0xF45C70f2b08397419b11751041c0D9547CcEDEaD", // allowlisted vendor
amount: parseUnits("1", 6),
});
console.log("Allowlisted vendor:", allowed.verdict);
const denied = await client.simulate({
to: "0xB1a111A87A454977F5fB2c02F547D0f346e23Ca8", // unknown vendor
amount: parseUnits("1", 6),
});
console.log("Unknown vendor:", denied.verdict, denied.reason ?? "");Ready to deploy your own? Save this as deploy.mjs, set OPERATOR_KEY to a funded Arc Testnet key, and run it.
import { WalletFactoryAbi } from "arcanum-sdk";
import { createWalletClient, defineChain, http, parseUnits } from "viem";
import { privateKeyToAccount } from "viem/accounts";
const arcTestnet = defineChain({
id: 5042002,
name: "Arc Testnet",
nativeCurrency: { name: "USDC", symbol: "USDC", decimals: 6 },
rpcUrls: { default: { http: ["https://arc-testnet.drpc.org"] } },
});
// The operator account that owns the Doctrine. Gas on Arc is paid in USDC.
const account = privateKeyToAccount(process.env.OPERATOR_KEY);
const walletClient = createWalletClient({ account, chain: arcTestnet, transport: http() });
// WalletFactory on Arc Testnet
const WALLET_FACTORY = "0x51A560589e23AcD2e57173641267f4583e0e65E7";
const policy = {
perTxCap: parseUnits("50", 6),
daily24hCap: parseUnits("500", 6),
monthlyRollingCap: parseUnits("5000", 6),
allowedCategories: 0b11111n,
escalationThreshold: parseUnits("100", 6),
requireAllowlist: true,
};
const agentSigner = "0xYourAgentSignerAddress"; // your AI agent's signing address
const council = [account.address]; // escalation approvers
const txHash = await walletClient.writeContract({
address: WALLET_FACTORY,
abi: WalletFactoryAbi,
functionName: "createWallet",
args: [account.address, "ResearchAgent", policy, [agentSigner], council, 1],
});
console.log("Deployed:", txHash);On-chain policy changes affect real testnet state. Test with small limits first.
A quorum of 1 disables multi-party approval. Use at least 2 for treasury wallets.
USDC token amounts are expressed in 6-decimal base units. One dollar is parseUnits("1", 6).