Skills siwa-privy
install
source · Clone the upstream repo
git clone https://github.com/openclaw/skills
manifest:
skills/buildersgarden/siwa/privy/skill.mdsource content
SIWA Privy Signer
Sign SIWA messages using Privy's server wallets.
Install
npm install @buildersgarden/siwa @privy-io/node
Create Signer
import { PrivyClient } from "@privy-io/node"; import { createPrivySiwaSigner } from "@buildersgarden/siwa/signer"; const privy = new PrivyClient({ appId: process.env.PRIVY_APP_ID!, appSecret: process.env.PRIVY_APP_SECRET!, }); const signer = createPrivySiwaSigner({ client: privy, walletId: process.env.PRIVY_WALLET_ID!, walletAddress: process.env.PRIVY_WALLET_ADDRESS! as `0x${string}`, });
Register as ERC-8004 Agent
If your agent doesn't have an ERC-8004 identity yet, register onchain using Privy's transaction API:
import { encodeFunctionData } from "viem"; const IDENTITY_REGISTRY_ADDRESS = "0x8004A818BFB912233c491871b3d84c89A494BD9e"; const BASE_SEPOLIA_CAIP2 = "eip155:84532"; const IDENTITY_REGISTRY_ABI = [ { name: "register", type: "function", inputs: [{ name: "agentURI", type: "string" }], outputs: [{ name: "agentId", type: "uint256" }], }, ] as const; // Prepare agent metadata const metadata = { name: "My Agent", description: "A helpful AI assistant", capabilities: ["chat", "analysis"], }; const agentURI = `data:application/json;base64,${Buffer.from(JSON.stringify(metadata)).toString("base64")}`; // Encode the register function call const data = encodeFunctionData({ abi: IDENTITY_REGISTRY_ABI, functionName: "register", args: [agentURI], }); // Send the transaction const result = await privy.wallets().ethereum().sendTransaction( process.env.PRIVY_WALLET_ID!, { caip2: BASE_SEPOLIA_CAIP2, params: { transaction: { to: IDENTITY_REGISTRY_ADDRESS, data: data, }, }, } ); console.log("Transaction hash:", result.hash);
SIWA Authentication Flow
The authentication flow consists of two steps:
Note: The URLs below (
) are placeholders. Replace them with your own server that implements the SIWA verification endpoints. See the Server-Side Verification skill for implementation details.api.example.com
- Get a nonce from the server's
endpoint/siwa/nonce - Sign and verify by sending the signature to
/siwa/verify
Step 1: Request Nonce
const nonceRes = await fetch("https://api.example.com/siwa/nonce", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ address: await signer.getAddress(), agentId: 42, agentRegistry: "eip155:84532:0x8004A818BFB912233c491871b3d84c89A494BD9e", }), }); const { nonce, nonceToken, issuedAt, expirationTime } = await nonceRes.json();
Step 2: Sign and Verify
import { signSIWAMessage } from "@buildersgarden/siwa"; const { message, signature, address } = await signSIWAMessage({ domain: "api.example.com", uri: "https://api.example.com/siwa", agentId: 42, agentRegistry: "eip155:84532:0x8004A818BFB912233c491871b3d84c89A494BD9e", //According to the chain chainId: 84532, nonce, issuedAt, expirationTime, }, signer); // Send to server for verification const verifyRes = await fetch("https://api.example.com/siwa/verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message, signature, nonceToken }), }); const { receipt, agentId } = await verifyRes.json(); // Store the receipt for authenticated API calls
Sign Authenticated Request (ERC-8128)
import { signAuthenticatedRequest } from "@buildersgarden/siwa/erc8128"; const request = new Request("https://api.example.com/action", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "execute" }), }); const signedRequest = await signAuthenticatedRequest( request, receipt, // from SIWA sign-in signer, 84532, ); const response = await fetch(signedRequest);
Environment Variables
PRIVY_APP_ID=your-privy-app-id PRIVY_APP_SECRET=your-privy-app-secret PRIVY_WALLET_ID=your-wallet-id PRIVY_WALLET_ADDRESS=0x...