Claude Skill
Functor ships a Claude Code skill so any Claude-powered agent can build with @functornetwork/agentic-wallet correctly out of the box. The skill teaches Claude when to reach for the SDK, the six core functions, the common workflows, and the gotchas that bite at integration time.
Install
Save the skill into your project (or your user-level skills directory) at .claude/skills/functor-agentic-wallet/SKILL.md:
mkdir -p .claude/skills/functor-agentic-wallet
curl -fsSL https://docs.functor.sh/skill.md \
-o .claude/skills/functor-agentic-wallet/SKILL.mdRestart Claude Code (or open a new conversation) and Claude will load the skill automatically when a prompt matches its triggers — agent wallets, scoped permissions, session keys, passkey wallets, cross-agent authorization checks, or "an AI that can act on my wallet."
The full skill content is reproduced below for reference. The canonical source is packages/wallet/SKILL.md in the SDK repo.
When to reach for this skill
Recognize these patterns:
- "I want an AI agent that can trade / pay / mint on my behalf"
- "Grant this bot a $50/day spending limit on USDC"
- "How do two agents verify each other on-chain"
- "Build a wallet that recovers from a passkey"
- "Non-custodial wallet for my app, but I don't want users to handle seed phrases"
- "Revoke this key — make sure it can't sign anymore"
- "Check whether
<address>is allowed to act on<wallet>right now"
If the user is operating an existing wallet from Claude itself (one-off transactions, granting sessions to other agents), prefer the MCP server — it exposes the SDK as tools/slash commands. If the user is writing code that uses Functor, use this SDK directly.
The SDK in 6 functions
import {
createWallet, // smart account from a local private key (CLI, script, agent)
createPasskeyWallet, // smart account from a passkey (Face ID / Touch ID), browser
execute, // run calls as wallet admin OR as a session
grantSession, // admin authorizes a scoped session key on-chain
revokeSession, // admin pulls authority; effect is immediate
recoverFromPasskey, // browser: rebuild wallet handle from any saved passkey
} from "@functornetwork/agentic-wallet";The private key lives wherever your code runs — your laptop, your agent's process, an OS keychain. Functor never sees it. Custody is local to the integrator.
Every wallet operation goes through one of these. execute is overloaded — admin path takes (wallet, signer, calls); session path takes (session, calls).
Workflows
Local wallet from a private key
import { createWallet, signerFromPrivateKey, execute } from "@functornetwork/agentic-wallet";
// Key is read from wherever your code keeps it — env var, OS keychain,
// encrypted file. It never leaves the process.
const signer = signerFromPrivateKey(process.env.AGENT_PRIVATE_KEY as `0x${string}`);
const wallet = await createWallet({ signer });
// Send 0.001 ETH. First execute also registers the admin key in Keystore —
// happens transparently inside the same userOp.
const result = await execute(wallet, signer, {
to: "0xRecipient...",
value: 1_000_000_000_000_000n, // 0.001 ETH in wei
});
console.log(result.status, result.transactionHash);Browser wallet with passkey
import { createPasskeyWallet, execute } from "@functornetwork/agentic-wallet";
const wallet = await createPasskeyWallet({
rpId: "myapp.example",
user: { name: "alice@example.com", displayName: "Alice" },
});
// `wallet.signer` is the PasskeySigner — used the same way as any other signer.Grant a session to an agent
import { grantSession } from "@functornetwork/agentic-wallet";
const session = await grantSession(wallet, adminSigner, {
permissions: {
calls: [{ to: "0xUniswapRouter..." }], // only this contract
spend: [{
limit: 100_000_000n, // 100 USDC (6 decimals)
period: "day",
token: "0xUSDC...",
}],
},
expiry: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60, // 7 days
});
// Hand `session` to whichever process runs the agent. Persist these fields:
// walletAddress, publicKey, permissions, expiry, and the signer's private key
// (signer.export() if your signer is a private-key signer). The agent needs
// the exact permissions+expiry at execute time — the on-chain validator
// matches them byte-for-byte against the authorization committed at grant.Agent acts using a session
import { execute } from "@functornetwork/agentic-wallet";
const result = await execute(session, [
{ to: "0xUniswapRouter...", data: "0x...", value: 0n },
]);Verify any key on-chain (from any tool)
import { createPublicClient, http, keccak256 } from "viem";
import { sepolia } from "viem/chains";
import { SEPOLIA } from "@functornetwork/agentic-wallet";
const client = createPublicClient({ chain: sepolia, transport: http() });
const KEYSTORE_ABI = [{
name: "getActiveKeys", type: "function", stateMutability: "view",
inputs: [{ name: "user", type: "address" }],
outputs: [{ type: "bytes32[]" }],
}] as const;
const active = await client.readContract({
address: SEPOLIA.keyStore,
abi: KEYSTORE_ABI,
functionName: "getActiveKeys",
args: [walletAddress],
});
const authorized = active.includes(keccak256(sessionPublicKey));This is the killer feature: a wallet that has never heard of your app can still verify whether a given key is authorized. No vendor lock-in.
Revoke a session
await revokeSession(wallet, adminSigner, session);
// or, if you only kept the public key:
await revokeSession(wallet, adminSigner, sessionPublicKey);Revocation revokes the key in Keystore and pulls the session's on-chain authority in the same userOp. The session's next signed call reverts at validation. Revocation is monotonic in Keystore v1.0.0 — to restore access, grant a fresh session.
Recover a passkey wallet
import { recoverFromPasskey } from "@functornetwork/agentic-wallet";
const wallet = await recoverFromPasskey({ rpId: "myapp.example" });
// Browser shows the passkey picker; user picks one, biometric prompt, done.
// Two on-chain reads, no server, no localStorage required.When to use the MCP server vs the SDK directly
| You are… | Use |
|---|---|
| Writing TypeScript/JS code that needs wallet ops | @functornetwork/agentic-wallet (this SDK) |
| Operating wallets interactively from Claude Code | @functornetwork/mcp server, tools like create_wallet, grant_session |
| Building a UI that signs from the browser | @functornetwork/agentic-wallet with createPasskeyWallet |
| Running a local agent that holds its own session | @functornetwork/agentic-wallet with execute(session, calls) |
The MCP server is a thin wrapper around this SDK — anything the MCP does, you can do directly with the SDK.
Notes
- Funding.
createWalletfaucets enough testnet ETH on Sepolia to activate the wallet. On mainnet, fund the address yourself before the firstexecute. PassskipFaucet: trueto skip the faucet entirely. - First execute registers the admin. The Keystore
initialRegisterKeyis auto-prepended on the wallet's first admin-signed action. Don't pre-call it. The wallet is "live" but not on-chain until that first tx. - Sessions must be byte-exact on execute. The on-chain validator matches
permissions + expiry + role + publicKeyexactly to the hash committed at grant time. Re-serializing through a sloppy JSON path (bigints → number, period reordering) breaks the match. Persist theSessionobject verbatim or reconstruct it identically. - Empty calls means no calls.
execute(wallet, signer, [])is rejected. Pass at least one call. permissions.callsomitted = unrestricted. If you don't passcalls, the session can call any contract within its spend cap. Set both unless that's truly what you want.- Sepolia only in v0.
SEPOLIAis the only network export. The architecture is chain-agnostic; mainnet addresses aren't deployed yet.
Network
import { SEPOLIA } from "@functornetwork/agentic-wallet";
// SEPOLIA.keyStore = 0xfBDe00E03Bf281bAc666043B14dBb8FAbcf22b14 (v1.0.0)
// SEPOLIA.keyStoreController = 0x8bBabE825EcFCBB32f7B60D973FbA0B923b8e782 (v1.0.0)
// SEPOLIA.portoRelayUrl = https://rpc.porto.shWhat never changes
- Functor never sees the private key. Custody follows the signer the integrator brings.
- Every authorization is on-chain in Keystore — readable by any tool, on any chain that bridges to it.
- Revoke is one tx, immediate.