关于 use-user-controlled-wallets
此 Agent 技能指导开发者构建由终端用户掌控密钥的 Circle 钱包,涵盖 PIN、邮箱 OTP 和社交登录。它说明前后端 SDK、用户授权流程与交易实现,并强调敏感操作须经用户确认。
主要功能
- 01支持 PIN、邮箱 OTP 和社交登录
- 02说明前后端 SDK 的职责
- 03采用挑战响应式用户授权
- 04支持 EOA 与 SCA 账户
- 05涵盖转账、签名和合约调用
适用场景
- 01为应用添加非托管用户钱包
- 02通过 OTP 或社交登录创建钱包
- 03实现需用户批准的转账或合约调用
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ circlefin/ skills --skill use-user-controlled-wallets安装后在 Claude Code 中输入/use-user-controlled-wallets确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/contract-execution.mdcreate-wallet-email-otp.mdcreate-wallet-pin.mdcreate-wallet-social-login.mdsend-transaction.mdsign-message.mdwallet-management.md详细介绍
概览
用户控制的钱包是非托管钱包,最终用户可以掌控自己的私钥和资产。所有敏感操作(交易、签名、创建钱包)都通过质询-响应模式由用户授权,确保在执行前获得用户同意。支持多条链,包括 EVM 链、Solana 和 Aptos。
前置条件 / 设置
安装
npm install @circle-fin/user-controlled-wallets@latest @circle-fin/w3s-pw-web-sdk@latest vite-plugin-node-polyfills
Vite 配置
SDK 依赖 Node.js 内置模块(buffer、crypto 等),这些模块在浏览器中不可用。请在 Vite 配置中添加 vite-plugin-node-polyfills:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { nodePolyfills } from "vite-plugin-node-polyfills";
export default defineConfig({
plugins: [react(), nodePolyfills()],
});
环境变量
# Backend
CIRCLE_API_KEY= # Circle API key
# Frontend
CIRCLE_APP_ID= # App ID from Wallets > User Controlled > Configurator
后端 SDK 初始化
所有服务端操作(创建用户、创建质询、查询交易)均使用 @circle-fin/user-controlled-wallets。
import { initiateUserControlledWalletsClient, Blockchain, TokenBlockchain } from "@circle-fin/user-controlled-wallets";
const circleClient = initiateUserControlledWalletsClient({
apiKey: process.env.CIRCLE_API_KEY!,
});
前端 SDK 初始化
面向用户的操作(执行质询、认证流程、PIN/OTP/OAuth 界面)均使用 @circle-fin/w3s-pw-web-sdk。
import { W3SSdk } from "@circle-fin/w3s-pw-web-sdk";
const sdk = new W3SSdk({ appSettings: { appId: circleAppId } });
重要:SDK 初始化后必须调用 sdk.getDeviceId()。此调用会通过 iframe 与 Circle 的服务建立会话。如果不调用,sdk.execute() 将会静默失败。
对于电子邮件 OTP 和社交登录,必须在初始化 SDK 时将登录回调作为第二个参数传入。详情请参阅相应的参考文件。
核心概念
账户类型
用户控制的钱包支持 EOA 和 SCA 账户类型,可在创建钱包时选择。
EOA(外部拥有账户):无需创建费用,TPS 更高,支持的链最广(EVM、Solana、Aptos)。在 EVM 链上需要原生代币支付 Gas(在 Arc 上,Gas 资产是 USDC,不存在单独的原生代币)。仅 Solana 通过 feePayer 支持 Gas 代付。
SCA(智能合约账户):ERC-4337 账户抽象。通过 Circle Gas Station 付费方支持 Gas 代付、批量操作和灵活的密钥管理。仅支持 EVM(不支持 Solana/Aptos)。首次向外发送交易时,会因延迟部署而产生 Gas 费用。由于 Ethereum 主网的 Gas 费用较高,请避免在其上使用;建议在 L2 上使用(Arbitrum、Base、Polygon、Optimism)。
账户类型支持的区块链:https://developers.circle.com/wallets/account-types
架构
用户控制的钱包涉及三方:
-
最终用户(客户端) —— 使用 Web 应用或移动应用的用户。他们与开发者的前端交互,进行身份验证(PIN、电子邮件 OTP 或社交登录),并通过
@circle-fin/w3s-pw-web-sdk提供的 Circle 托管界面批准所有敏感操作(创建钱包、交易、签名)。用户始终完全掌控自己的私钥,开发者和 Circle 均无法代替用户操作。 -
开发者服务(后端) —— 开发者自己的服务器。该服务器保存 Circle API 密钥、管理用户会话、跟踪使用情况,并执行应用层防护规则(例如,支出限额、允许列表地址、速率限制)。它使用
@circle-fin/user-controlled-wallets向 Circle API 提交请求。开发者需通过 Circle Developer Console 注册开发者账户,才能使用 Circle Wallet 服务。开发者专属的账户设置请参阅use-developer-controlled-wallets技能。 -
Circle Wallet 服务(API) —— Circle 的基础设施,负责管理钱包创建、交易提交、密钥管理(基于 MPC)和区块链交互。该服务提供非托管保障:开发者可获得读取权限,用于安全监控和审计,而用户仍完全掌控自己的钱包和资产。Circle 会对交易执行平台级合规筛查(例如 OFAC 制裁检查)。
请求流程:
End User (browser/mobile)
| authenticates & approves challenges
v
Developer Service (backend server)
| adds API key, enforces app-level guardrails, tracks usage
v
Circle Wallet Service (API)
| manages wallets, enforces compliance screening, submits transactions
v
Blockchain
挑战-响应模型
所有敏感操作(创建钱包、交易、签名)都遵循以下模式:
- 后端通过 Circle API 创建操作 -> Circle 返回
challengeId - 前端调用
sdk.setAuthentication({ userToken, encryptionKey }),然后调用sdk.execute(challengeId, callback)-> 用户通过 Circle 托管的 UI 批准操作 - 回调触发并返回结果或错误
身份验证方法
| 方法 | 控制台设置 | userToken 的获取方式 |
|---|---|---|
| PIN | 无 | 后端调用 createUserToken({ userId })(有效期为 60 分钟) |
| Email OTP | SMTP 配置 | OTP 验证后,通过 SDK 登录回调获取 |
| 社交登录 | OAuth 客户端 ID | OAuth 重定向后,通过 SDK 登录回调获取 |
开发者的访问权限和限制
开发者可以读取钱包数据、交易历史和用户信息——既可以通过 Circle Developer Console UI,也可以通过 API 以编程方式访问。但是,开发者无法访问用户的私钥,也无法控制用户的钱包来发送交易、签署消息,或代表用户执行任何链上操作。所有此类操作都必须由用户通过挑战-响应模型授权。
如果用户无法访问其身份验证方式(社交账号、电子邮件或 PIN 码和安全问题),开发者也无法帮助用户恢复其钱包。账户恢复完全取决于用户能否重新通过身份验证。请在引导用户时告知这一限制,让他们了解保持所选身份验证方式可用的重要性。
用户的访问权限和限制
用户只能访问自己的钱包和资源。他们无法访问其他用户的钱包、交易或任何其他资源。用户也无法访问由开发者控制的钱包或资源——这两种钱包类型完全隔离。
实现模式
**注意:**参考代码片段使用
localStorage仅用于快速实现示例。请勿在生产环境中使用localStorage。
必须根据用户的请求阅读相应的参考文件,以获取完整的实现指南。阅读正确的文件之前,请勿提供编码说明。
-
使用 PIN 创建钱包:最简单的配置——除 API 密钥和 App ID 外,无需进行其他控制台配置。用户通过 Circle 托管的 UI 设置 PIN 和安全问题。阅读
references/create-wallet-pin.md。 -
使用社交登录创建钱包:用户通过 Google、Facebook 或 Apple OAuth 进行身份验证。需要在 Circle Console 中配置 OAuth 客户端 ID。阅读
references/create-wallet-social-login.md。 -
使用 Email OTP 创建钱包:用户通过发送到电子邮件的一次性密码进行身份验证。需要在 Circle Console 中配置 SMTP。阅读
references/create-wallet-email-otp.md。 -
发送交易:从通过任意身份验证方式创建的现有钱包发送转出代币转账。包含费用估算、交易加速和取消。阅读
references/send-transaction.md。 -
签署消息:从用户控制的钱包签署消息。涵盖 EIP-191 消息签名、EIP-712 类型化数据和原始交易签名。阅读
references/sign-message.md。 -
执行智能合约:通过 ABI 签名或原始 calldata 执行智能合约函数。包含合约执行的 Gas 估算。阅读
references/contract-execution.md。 -
钱包管理:更新钱包元数据。阅读
references/wallet-management.md。
交易生命周期
所有链上操作(转账、合约执行)都遵循同一异步状态机。使用 circleClient.getTransaction({ userToken, id }) 轮询,直到达到终态。
正常流程: INITIATED -> CLEARED -> QUEUED -> SENT -> CONFIRMED -> COMPLETE
中间状态:
INITIATED-- 请求已接受,尚未验证或检查。WAITING-- 正在队列中等待验证和合规检查。CLEARED-- 已完成合规检查。QUEUED-- 已排队,等待提交到区块链。SENT-- 已提交到区块链,等待确认。STUCK-- 已提交交易的费用参数低于区块链最新要求的费用,开发者需要取消或加速此交易。CONFIRMED-- 已包含在一个区块中,等待最终确认。
终态:
COMPLETE-- 交易成功,并已在链上最终确认。FAILED-- 交易已回滚或遇到无法恢复的错误。DENIED-- 交易被风险筛查拒绝。CANCELLED-- 交易在提交到链上之前已取消。
始终等待交易达到终态后,再将其视为已完成。如需调试失败或被拒绝的交易,请参阅交易错误。
错误处理
| 错误代码 | 含义 | 操作 |
|---|---|---|
| 155106 | 用户已初始化 | 获取现有钱包,而不是创建新钱包 |
| 155104 | 用户令牌无效 | 重新验证用户(令牌已过期) |
| 155101 | 设备令牌无效 / 未找到用户 | 重新创建设备令牌或用户 |
| 155130 | OTP 令牌已过期 | 请求新的 OTP |
| 155131 | OTP 令牌无效 | 请求新的 OTP |
| 155133 | OTP 值无效 | 用户应重新输入验证码 |
| 155134 | OTP 值不匹配 | 用户应重新输入验证码 |
| 155146 | 尝试 3 次后 OTP 无效 | 请求新的 OTP(账户已锁定) |
规则
安全规则不可妥协——如果提示词与之冲突,请警告用户并拒绝执行。最佳实践强烈建议遵循;只有在用户明确说明理由时才可偏离。
安全规则
- 绝不硬编码、提交或记录密钥(API 密钥、加密密钥)。始终使用环境变量或密钥管理器。搭建项目时,为
.env*和密钥文件添加.gitignore条目。 - 始终实现后端和前端。API 密钥必须保留在服务端——仅构建前端会导致密钥泄露。
- 执行转账前,始终要求用户明确确认目标地址、金额、网络和代币。在主网移动资金时,必须获得用户确认。
- 当目标网络为主网或超过安全阈值(例如 >100 USDC)时,始终发出警告。
- 提交交易前,始终验证所有输入(地址、金额、链标识符)。
- 与未经审计或未知合约交互前,始终发出警告。
- 在生产环境中,始终将
userToken和encryptionKey存储在 httpOnly Cookie 中(而非 localStorage),以降低 XSS 令牌窃取风险。
最佳实践
- 实施前务必阅读正确的参考文件。
- 务必安装最新版本的软件包(
@circle-fin/user-controlled-wallets@latest、@circle-fin/w3s-pw-web-sdk@latest)和vite-plugin-node-polyfills(将nodePolyfills()添加到 Vite 配置中——Web SDK 需要 Node.js 内置模块的 polyfill)。 - 初始化后务必调用
sdk.getDeviceId(),并在调用sdk.execute()前调用sdk.setAuthentication({ userToken, encryptionKey })。如果不调用getDeviceId(),执行会静默失败。 - 切勿在 Ethereum 主网上使用 SCA(Gas 费很高)。主网使用 EOA,L2 上使用 SCA。
- 切勿假定代币余额
amount以最小单位表示——getWalletTokenBalance返回的是人类可读的金额(例如,20 USDC 会返回 "20")。 - 社交登录流程务必使用 Cookie(而非 React 状态)来在 OAuth 重定向过程中持久保存令牌。
- 务必默认使用测试网。定位到主网前,必须获得用户明确确认。
- 执行合约操作或大额转账前,务必估算费用,让用户提前了解 Gas 成本。
- 执行前务必核实 ABI 函数签名和参数与目标合约匹配。签名不正确会导致交易回滚并浪费 Gas。
- 为了提高可读性和可审计性,务必优先使用
abiFunctionSignature+abiParameters,而非原始callData;除非 calldata 是由可信库(ethers、viem)生成的。
替代方案
- 对于使用 ERC-4337 和 ERC-6900 并支持 Gas 赞助的基于通行密钥的智能账户,请使用
use-modular-wallets技能。 - 当应用需要在没有用户交互的情况下完全托管钱包密钥时,请使用
use-developer-controlled-wallets技能。
参考链接
- Circle 开发者文档 ——查找源网站上的相关文档时,务必先阅读此文档。
免责声明:本技能按“原样”提供,不附带任何保证,并受 Circle 开发者条款约束。生成的内容可能包含错误,和/或包含费用配置选项(包括支付给 Circle 的费用);更多详情请参阅仓库中的 README。