HomeBrowseUpload
← Back to registry
// Skill profile

SIWA SDK

version: 0.2.0

by buildersgarden · published 2026-03-22

开发工具数据处理加密货币
Total installs
0
Stars
★ 0
Last updated
2026-03
// Install command
$ claw add gh:buildersgarden/buildersgarden-siwa
View on GitHub
// Full documentation

---

name: siwa

version: 0.2.0

description: >

SIWA (Sign-In With Agent) authentication for ERC-8004 registered agents.

---

# SIWA SDK

Sign-In With Agent (SIWA) lets AI agents authenticate with services using their ERC-8004 onchain identity.

Install

npm install @buildersgarden/siwa

Skills

Agent-Side (Signing)

Choose based on your wallet provider:

  • [Bankr](https://siwa.id/skills/bankr/skill.md) — Bankr Agent API wallets
  • [Circle](https://siwa.id/skills/circle/skill.md) — Circle developer-controlled wallets
  • [Privy](https://siwa.id/skills/privy/skill.md) — Privy server wallets
  • [Private Key](https://siwa.id/skills/private-key/skill.md) — Raw private key (viem LocalAccount)
  • [Keyring Proxy](https://siwa.id/skills/keyring-proxy/skill.md) — Self-hosted proxy with optional 2FA
  • Server-Side (Verification)

  • [Server-Side Verification](https://siwa.id/skills/server-side/skill.md) — Next.js, Express, Hono, Fastify
  • SDK Modules

    | Import | Description |

    |--------|-------------|

    | `@buildersgarden/siwa` | Core: signSIWAMessage, verifySIWA, createSIWANonce, parseSIWAMessage, buildSIWAMessage, createClientResolver, parseChainId |

    | `@buildersgarden/siwa/signer` | Signer factories (see wallet-specific skills above) |

    | `@buildersgarden/siwa/erc8128` | ERC-8128 HTTP signing/verification |

    | `@buildersgarden/siwa/receipt` | HMAC receipt helpers |

    | `@buildersgarden/siwa/nonce-store` | Nonce stores (Memory, Redis, KV) |

    | `@buildersgarden/siwa/identity` | SIWA_IDENTITY.md helpers |

    | `@buildersgarden/siwa/registry` | Onchain agent registration |

    | `@buildersgarden/siwa/client-resolver` | Dynamic PublicClient resolution for multi-chain servers |

    | `@buildersgarden/siwa/next` | Next.js middleware (withSiwa, siwaOptions) |

    | `@buildersgarden/siwa/express` | Express middleware (siwaMiddleware, siwaJsonParser, siwaCors) |

    | `@buildersgarden/siwa/hono` | Hono middleware (siwaMiddleware, siwaCors) |

    | `@buildersgarden/siwa/fastify` | Fastify middleware (siwaPlugin, siwaAuth) |

    | `@buildersgarden/siwa/x402` | x402 payment helpers |

    | `@buildersgarden/siwa/captcha` | Reverse CAPTCHA (prove you're an AI) |

    ---

    x402 Payments (Agent-Side)

    When an API requires payment, it returns HTTP **402** with a `Payment-Required` header. The agent decodes the payment options, constructs a signed payment, and retries with a `Payment-Signature` header — all while maintaining SIWA authentication.

    Handling a 402 Response

    import {
      encodeX402Header,
      decodeX402Header,
      type PaymentRequired,
      type PaymentPayload,
    } from "@buildersgarden/siwa/x402";
    import { signAuthenticatedRequest } from "@buildersgarden/siwa/erc8128";
    
    // 1. Make initial authenticated request (may get 402)
    const signedRequest = await signAuthenticatedRequest(
      new Request("https://api.example.com/premium", { method: "POST" }),
      receipt,
      signer,
      84532,
    );
    
    const res = await fetch(signedRequest);
    
    if (res.status === 402) {
      // 2. Decode payment requirements from header
      const header = res.headers.get("Payment-Required");
      const { accepts, resource } = decodeX402Header<PaymentRequired>(header!);
    
      // 3. Pick a payment option and construct payload
      const option = accepts[0];
      const payload: PaymentPayload = {
        signature: "0x...",  // sign the payment with your wallet
        payment: {
          scheme: option.scheme,
          network: option.network,
          amount: option.amount,
          asset: option.asset,
          payTo: option.payTo,
        },
        resource,
      };
    
      // 4. Retry with both SIWA auth + payment header
      const retryRequest = await signAuthenticatedRequest(
        new Request("https://api.example.com/premium", {
          method: "POST",
          headers: {
            "Payment-Signature": encodeX402Header(payload),
          },
        }),
        receipt,
        signer,
        84532,
      );
    
      const paidRes = await fetch(retryRequest);
      // paidRes.headers.get("Payment-Response") contains { txHash, ... }
    }

    x402 Headers

    | Header | Direction | Description |

    |--------|-----------|-------------|

    | `Payment-Required` | Server → Agent | Base64-encoded JSON with accepted payment options. Sent with 402. |

    | `Payment-Signature` | Agent → Server | Base64-encoded signed payment payload. |

    | `Payment-Response` | Server → Agent | Base64-encoded settlement result with transaction hash. |

    Pay-Once Sessions

    Some endpoints use **pay-once mode**: the first request requires payment, subsequent requests from the same agent to the same resource pass through without payment until the session expires. If you receive a 200 on a previously-paid endpoint, the session is still active — no need to pay again.

    ---

    Captcha (Reverse CAPTCHA)

    SIWA includes a "reverse CAPTCHA" mechanism — inspired by [MoltCaptcha](https://github.com/MoltCaptcha/MoltCaptcha) — that proves an entity **is** an AI agent, not a human. Challenges exploit how LLMs generate text in a single autoregressive pass (satisfying multiple constraints simultaneously), while humans must iterate.

    Two integration points:

    1. **Sign-in flow** — server requires captcha before issuing a nonce

    2. **Per-request** — middleware randomly challenges agents during authenticated API calls

    Agent-Side: Handling a Captcha Challenge

    The SDK provides two convenience wrappers for the captcha retry pattern:

    #### Sign-In Captcha: `solveCaptchaChallenge()`

    import { solveCaptchaChallenge } from "@buildersgarden/siwa/captcha";
    
    // 1. Request nonce
    const nonceRes = await fetch("/api/siwa/nonce", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ address, agentId, agentRegistry }),
    });
    const data = await nonceRes.json();
    
    // 2. Detect + solve captcha if required
    const captcha = await solveCaptchaChallenge(data, async (challenge) => {
      // LLM generates text satisfying all constraints in a single pass
      // challenge: { topic, format, lineCount, asciiTarget, wordCount?, timeLimitSeconds, ... }
      // Your LLM generates text satisfying all constraints in one pass.
      // Use any provider (Anthropic, OpenAI, etc.) — the solver just returns a string.
      return await generateText(challenge);
    });
    
    if (captcha.solved) {
      // 3. Retry with challenge response
      const retryRes = await fetch("/api/siwa/nonce", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ address, agentId, agentRegistry, challengeResponse: captcha.challengeResponse }),
      });
    }

    #### Per-Request Captcha: `retryWithCaptcha()`

    import { signAuthenticatedRequest, retryWithCaptcha } from "@buildersgarden/siwa/erc8128";
    
    const url = "https://api.example.com/action";
    const body = JSON.stringify({ key: "value" });
    
    // 1. Sign and send
    const signed = await signAuthenticatedRequest(
      new Request(url, { method: "POST", body }),
      receipt, signer, chainId,
    );
    const response = await fetch(signed);
    
    // 2. Detect + solve captcha, re-sign, and get retry request
    const result = await retryWithCaptcha(
      response,
      new Request(url, { method: "POST", body }), // fresh request (original body consumed)
      receipt, signer, chainId,
      async (challenge) => generateText(challenge), // your LLM solver
    );
    
    if (result.retry) {
      const retryResponse = await fetch(result.request);
    }

    > **Note:** Pass a **fresh, unconsumed** Request to `retryWithCaptcha` — the original is consumed after signing/sending.

    Difficulty Levels

    | Level | Time Limit | Constraints |

    |-------|-----------|-------------|

    | `easy` | 30s | Line count + ASCII sum of first chars |

    | `medium` | 20s | + word count |

    | `hard` | 15s | + character at specific position |

    | `extreme` | 10s | + total character count |

    Links

  • [Documentation](https://siwa.id/docs)
  • [ERC-8004](https://eips.ethereum.org/EIPS/eip-8004)
  • [ERC-8128](https://eips.ethereum.org/EIPS/eip-8128)
  • // Comments
    Sign in with GitHub to leave a comment.
    // Related skills

    More tools from the same signal band