圆形钱包适应器
EVM和索拉纳企业级混合适配器
以开发者控制的钱包对跨链操作进行无缝管理
只有服务 -主线, /utils,以及 /ucw/server
条目需要保密,决不能被捆绑到浏览器中.
使用 /ucw/client 用于浏览器安全UCW表面.
目录
概览
圆形钱包适应器是一个 混合适配器 * 提供对两者的统一访问 EVM和索拉纳 块链通过 Circle 开发者控制的钱包基础设施. 与特定生态系统的适配器(Viem, Ethers, Solana)不同,这个适配器使用单一实例在两个生态系统间无缝运行.
建造时间 企业和后端应用程序,此适配器集成 圆形钱包 财务报告和审定财务报表 圆形合同 提供安全、方案钱包管理.
为什么是圆形钱包适应器?
- 真正的跨生态系统支持 - 单一适配器实例跨越EVM链(Ethereum、Base、Polygon等)和索拉纳
- 企业级 - 以开发商控制的地址建在Circle的机构钱包基础设施上
- 设计安全 - API 密钥和实体秘密认证只保留证书服务器侧
- QQ 类型安全 - 采用类型脚本的严格模式建造,以保障完全类型安全
- 简化管理 - 所有链条的一个适配器消除了管理单独的EVM和索拉纳适配器的需要
- 完整的交易周期 - 充分准备/估计/执行两个生态系统的工作流程
- 生产准备 - 利用圆圈可靠的基础设施和内置钱包管理
何时以及如何使用圆形钱包适应器?
我是一个开发商 建立后端服务
如果你正在建造一个 服务器侧应用程序 这个适配器为两个生态系统提供了统一的接口.
完美为:
- 进行自动跨链转移的后端服务
- 企业应用程序在程序上管理钱包
- 需要多生态系统支持的服务器侧脚本
- 使用 Circle 保管基础设施的应用程序
示例
import { createCircleWalletsAdapter } from '@circle-fin/adapter-circle-wallets'
// Server-side only - credentials from secure environment variables
const adapter = createCircleWalletsAdapter({
apiKey: process.env.CIRCLE_API_KEY!,
entitySecret: process.env.CIRCLE_ENTITY_SECRET!,
})
// Same adapter works for both EVM and Solana operations!
我是一个开发商 制作一个 Kit 提供者
若建供养者(如道俗 BridgingProvider),需要同时跨越EVM和索拉纳,"圆形钱包"适应器提供了统一的抽象. 每个生态系统不需要单独的适配器.
福利:
- 用于跨生态系统操作的单一适配器
- 跨EVM和索拉纳链的一致API
- 通过 Circle 的基础设施内置钱包管理
- 明确钱包规格的开发者控制地址
仅使用服务器
主条目, /utils,以及 /ucw/server 只能用于服务器侧
(节点.js)环境. 浏览器将这些条目建为守护符
引导整齐器 /ucw/client。 。 。 .
安全原因:
- ❌ API 密钥 - 您的 Circle API 密钥决不能暴露在客户端代码中
- ❌ 实体机密 - 实体机密是敏感证书,必须只保留服务器侧面
- ❌ 钱包控制 - 开发者控制的钱包只能通过信任的后端服务访问
技术原因:
- ❌ 节点js 依赖性 - 需要节点Js内置模块(
crypto, (中文(简体) ),buffer等)在浏览器中不可用 - ❌ SDK要求 - Circle的开发者控制钱包 SDK 需要一个节点.js运行时环境
UCW 浏览器应用程序:
只导入运输不可知交易服务员和签名拒绝 客户端条目中的助手 :
import { createWaitForTransaction } from '@circle-fin/adapter-circle-wallets/ucw/client'
const waitForTransaction = createWaitForTransaction({
getStatus: ({ challengeId, signal }) =>
fetch(`/api/transactions/${challengeId}`, { signal }).then((response) =>
response.json(),
),
})
const result = await waitForTransaction({ challengeId })
浏览器帮助用户调用您的应用程序提供的端点 。 从来没有 直接调用圆圈,而不指定服务器如何认证 请求.
UCW 类型数据签名
服务器侧 UCW 适配器可以通过一个圆形在 EIP-712 键入数据上签名
挑战。 与交易不同——适配器读取的结果来自
圆形 - 发送签名 仅发给批准
挑战,在W3S浏览器SDK的挑战结果中. 拿回来
resolveTypedDataSignature数字 :
const adapter = await createCircleUserWalletAdapter({
apiKey,
userToken,
wallet,
// Your existing approval channel: forward the challenge to the browser.
onChallenge: ({ challengeId }) => notifyApprovalSurface(challengeId),
// The other half: return the signature the browser received for it.
resolveTypedDataSignature: ({ challengeId }) =>
awaitSignatureFromApprovalSurface(challengeId),
})
const signature = await adapter.signTypedData(typedData, { chain })
在浏览器中,签名出现在挑战结果上 W3S SDK 已经 给予你PIN批准:
sdk.execute(challengeId, (error, result) => {
if (error) return reportRejected(challengeId, error)
reportSignature(challengeId, result?.data?.signature)
})
无 resolveTypedDataSignature 适配器报告
supportsSignTypedData() === false 拒绝输入的数据请求,所以
调用者可以选择一个链上替代品,而不是在
老板已经批准.
留着 signTypedData 援引及其影响 resolveTypedDataSignature 承诺
活到批准客户返回签名为止。 圆形完成
挑战没有服务器可读签名, 因此如果进程在
批准,结果无法收回,呼叫者必须开始新的签名
操作。 在持久的服务器或工作流程中运行此流量; 无国籍者
在浏览器批准前结束的请求处理器不支持
部署形状.
认证客户端对服务器返回频道,并将每个挑战 ID 绑定到 用户和飞行中签字请求,然后才接受其签字。 这个 适配器验证返回的签名格式, 但是您的应用程序拥有 中继器的授权.
对于 SCA 钱包, Circle 返回钱包 车主 65字节的ECDSA签名,
,钱包通过 ERC-1271 验证 isValidSignature常规
ecrecover 回收所有者的密钥,而不是SCA地址,所以使用一个
ERC-1271-有意识验证器——USDC的签名-检查器路径,ERC-3009使用
授权,接受。 只对 ecrecover,包括标准
EIP-2612许可和Gateway 燃烧-intent流量,无法验证. 一个EOA钱包
生成标准签名 ecrecover 以地址验证.
对于其它浏览器钱包流,使用一个与浏览器相容的适配器:
@circle-fin/adapter-viem-v2- 对于带有浏览器钱包的EVM链@circle-fin/adapter-solana- 给索拉纳带浏览器钱包@circle-fin/adapter-ethers-v6- EVM链带以太.js
安装
npm install @circle-fin/adapter-circle-wallets
# or
yarn add @circle-fin/adapter-circle-wallets
先决条件:
- 节点.js 环境 (v18+建议)
- 圆形 API 密钥和实体密钥 圆形开发者控制台
初始设置 - 生成和注册实体秘密( 如果尚未完成)
如果你还没有和Circle建立实体秘密,你需要完成一个一次性的设置过程。 适配器提供方便设施(从Circle's SDK再出)来简化这个:
import {
generateEntitySecret,
registerEntitySecretCiphertext,
} from '@circle-fin/adapter-circle-wallets/utils'
// Step 1: Generate a new entity secret
const entitySecret = generateEntitySecret()
console.log('Generated entity secret:', entitySecret)
// Step 2: Register the entity secret with Circle
const response = await registerEntitySecretCiphertext({
apiKey: 'TEST_API_KEY:abc123:def456', // Your Circle API key
entitySecret: entitySecret,
})
console.log('Recovery file:', response.data?.recoveryFile)
// The recovery file is also automatically downloaded to your filesystem
重要安全说明:
- ⚠️ 实体机密 - 安全存放在环境变量中。 从不承诺给版本控制.
- ⚠️ 回收文件 - 备份生成的
recovery_file_<timestamp>.dat绝对安全 这是你唯一能恢复进入的方法 如果实体的秘密丢失. - ⚠️ 一次性设置 - 只需每个圆形账户/环境(测试/生产)进行一次。 如果你已经有一个实体 秘密注册,跳过这个步骤.
完成此设置( 或者如果您已经拥有证书)后, 在环境变量中隐藏您的实体 :
# .env file (add to .gitignore!)
CIRCLE_API_KEY=TEST_API_KEY:abc123:def456
CIRCLE_ENTITY_SECRET=your-generated-64-char-secret
说明: 这些公用事业从下列地点再出口:
@circle-fin/developer-controlled-wallets为方便. 详情见 圆形开发者控制钱包文档。 。 。 .
快速启动
基本设置
以您的 Circle 证书创建适配器实例 :
import { createCircleWalletsAdapter } from '@circle-fin/adapter-circle-wallets'
// Initialize adapter with Circle credentials (server-side only!)
const adapter = createCircleWalletsAdapter({
apiKey: process.env.CIRCLE_API_KEY!, // Format: TEST_API_KEY:abc:def or Base64
entitySecret: process.env.CIRCLE_ENTITY_SECRET!, // Format: 64 lowercase alphanumeric chars
})
// Works across both EVM and Solana!
// No need to create separate adapters for each ecosystem
API 密钥格式 :
- 环境前缀 :
TEST_API_KEY:abc123:def456或者说LIVE_API_KEY:xyz:uvw - Base64 编码: 标准 Base64 字符串
实体秘密格式 :
- 64个小写字母(例如,
abc123...× 64个字符)
用法实例
与桥基搭桥
使用带有 Bridge Kit 的 Circle Wallets 适配器进行跨 EVM 和 Solana 的无缝跨链 USDC 传输 :
import { createCircleWalletsAdapter } from '@circle-fin/adapter-circle-wallets'
import { BridgeKit } from '@circle-fin/bridge-kit'
// Single adapter instance for both ecosystems
const adapter = createCircleWalletsAdapter({
apiKey: process.env.CIRCLE_API_KEY!,
entitySecret: process.env.CIRCLE_ENTITY_SECRET!,
})
const kit = new BridgeKit()
// Bridge from Ethereum (EVM) to Solana - same adapter for both!
const result = await kit.bridge({
from: {
adapter,
chain: 'Ethereum',
address: '0x1234...', // EVM address (developer-controlled)
},
to: {
adapter, // Same adapter instance!
chain: 'Solana',
address: 'ABC123...', // Solana address (developer-controlled)
},
amount: '100.00',
token: 'USDC',
})
console.log('Bridge transaction:', result.transactionHash)
主要惠益:
- 一个跨生态系统接通的适配器(EVM QQ Solana)
- 明确钱包规格的开发者控制地址
- QQ 统一 API, 不论来源/ 目的地链类型
直接 SDK 访问
为高级操作访问基本的圆形钱包SDK:
import { createCircleWalletsAdapter } from '@circle-fin/adapter-circle-wallets'
const adapter = createCircleWalletsAdapter({
apiKey: process.env.CIRCLE_API_KEY!,
entitySecret: process.env.CIRCLE_ENTITY_SECRET!,
})
// Access Circle's Developer Controlled Wallets and Smart Contract Platform clients
const sdk = await adapter.getSdk()
// Use Developer Controlled Wallets client
const wallets = await sdk.devc.listWallets()
console.log('Available wallets:', wallets)
// Use Smart Contract Platform client
const contracts = await sdk.scp.listContracts()
console.log('Deployed contracts:', contracts)
API 参考
工厂函数
createCircleWalletsAdapter(options)
为服务器侧面使用创建圆形钱包适配器实例.
参数:
apiKey- Circle API 键(环境前缀或 Base64 格式)entitySecret- 圆形实体秘密(64个小写字母数字字符)baseUrl?- 可选自定义 Circle API 基础 URL(默认: Circle 生产终点)
回返情况: CircleWalletsAdapter 实例
掷出 : 如果 API 密钥或实体秘密格式无效, 验证错误
const adapter = createCircleWalletsAdapter({
apiKey: process.env.CIRCLE_API_KEY!,
entitySecret: process.env.CIRCLE_ENTITY_SECRET!,
baseUrl: 'https://api.circle.com/v1', // Optional
})
适应方法
getSdk()
返回深层钱包SDK客户端用于高级操作.
回返情况: Promise<CircleWalletsSDK>
const sdk = await adapter.getSdk()
// Access Developer Controlled Wallets client
sdk.devc.listWallets()
// Access Smart Contract Platform client
sdk.scp.listContracts()
继承方法
圆形钱包适应器扩展 HybridAdapter 并支持所有标准适配器方法:
prepare(params, ctx)- 准备合同交易signTypedData(typedData, ctx)- 签署EIP-712型数据或信件(索拉纳)getAddress(ctx)- 获取指定链条的钱包地址validateChainSupport(chain)- 验证链相容性
所有方法都支持 开发者控制的地址模式 - 您必须在操作上下文中指定地址.
用户控制钱包(UCW)
这个 /ucw/server 条目(亦从主条目再出) 构建适配器
他们的标志是: 用户自己的 圆形钱包。 每一个州都变成一个
Circle 在您的表面挑战钱包的主人, 所以适配器
没有签署授权。 工厂接受Circle智能合同账户
(SCAs),因此分批步骤(例如桥的批出+燃烧)在其中进行解剖执行
一个批准。 钱包助手仍然可以找到或提供 EOA 钱包
用于管理此适配器外的应用程序.
createCircleUserWalletAdapter(options)
创建用户控制的适配器。 默认情况下,它也会找到或创建 解析前用户钱包.
参数: 圆形 UCW 连接( C)apiKey + 键 userToken页:1 client, (中文(简体) ),
或代理服务器 baseUrl),可选 onChallenge 请求批准
通知,可选 resolveTypedDataSignature 启用的回调
键入数据签名,可选 resolveIdempotencyKey 回调
调用器控制的挑战分解,以及五个钱包表格中的一个:
| 表单 | 钱包解析 | 回返 |
|---|---|---|
chain | 在链上查找或创建钱包 | 单链 ViemAdapter |
chains | 每个链中查找或创建一个钱包, 提供缺失的钱包 一个 挑战 | MultiChainCircleUserWalletAdapter |
wallet | 使用给定的SCA CircleUserWallet 它的链条来自 wallet.blockchain | 单链 ViemAdapter |
walletId + 键 walletAddress + 键 chain + 键 accountType: 'SCA' | 使用明确链式SCA标识 | 单链 ViemAdapter |
wallets | 将每个链条的操作路线到该链条的钱包 | MultiChainCircleUserWalletAdapter |
可选择的各种形式: rpcUrls (由EVM链键入的每链覆盖)
页:1, runtime (伐木机和操作级的度量衡; 皮诺日志默认值)
将士们, onProgress, (中文(简体) ), resolveIdempotencyKey, (中文(简体) ), pollIntervalMs,以及 timeoutMs。 。 。 .
这个 chain 财务报告和审定财务报表 chains 还接受表格 signal 查找或创建时
钱包。 明确的钱包对象和钱包集导出其支持的链条
从每个钱包的 blockchain; 相互矛盾 chains 超接被否决 .
回返情况: Promise<ViemAdapter>,或
Promise<MultiChainCircleUserWalletAdapter> 联 合 国 chains 编号 : wallets
表单。 多链适配器为桥的两侧服务
举个例子.
掷出 : KitError 当选项无效时,链条不支持
Circle UCW,用户尚未完成钱包设置,或批准时间已出.
import { createCircleUserWalletAdapter } from '@circle-fin/adapter-circle-wallets/ucw/server'
import { BridgeKit } from '@circle-fin/bridge-kit'
const adapter = await createCircleUserWalletAdapter({
apiKey: process.env.CIRCLE_API_KEY!,
userToken, // the end user's Circle session token
chains: ['Arc_Testnet', 'Base_Sepolia'],
onChallenge: ({ challengeId }) => sendToBrowser(challengeId),
})
// One adapter, both sides of the bridge — no address in the context, because
// a user-controlled adapter resolves its own operating address.
const kit = new BridgeKit()
const result = await kit.bridge({
from: { adapter, chain: 'Arc_Testnet' },
to: { adapter, chain: 'Base_Sepolia' },
amount: '1.5',
})
onChallenge 将您申请的代码逐一提交至
用 Circle 的 W3S 浏览器 SDK 批准 。 浏览器安全
/ucw/client 条目 createWaitForTransaction 然后等待结果
通过你自己的地位终点——它从不直接叫"圆".
使用 onProgress 以发布服务器持有的生命周期状态。 该战略援引
按观察顺序连续处理器,每个更新都带有单调
sequence 作为防止被打烂的二级警卫 在运输中写作或
重试.
使用 resolveIdempotencyKey 为每个提供圆一等分度键
交易挑战。 解决者收到 walletId圆形
blockchain, (中文(简体) ), intent,交易 payload,以及 sequence,并且必须
返回非无 UUID 。 它包括合同的执行和本土转移;
类型数据签名不取密钥, 钱包设置挑战仍在继续
用于使用自己的随机键。 如果没有选择,交易也面临挑战
得到随机键.
重新运行安全是你的责任。 构造适配器 一个
操作 并从其持续的操作id、钱包、链条中获取密钥,
目标/呼吁数据/数值 payload.calls仅凭意图是不够的:
津贴的重新设置及其随后的核准均
{ action: 'token.approve' },而批量呼叫通常有
{ action: 'execute', step: 'batch' }。不使用 sequence 作为回放
身份:它计算解析器的引用,包括失败的引用,以及移位
重新运行。 应执行两次的意向相同的交易
需要明确的持续行动身份.
Circle 返回先前对重用密钥的挑战, 包括拒绝的, 过期或失败的挑战 。 在结果未知时重用密钥 。 之后 (单位:千美元) 经确认的终端不成功结果,坚持新的尝试id 重试前的那笔交易 仅连接或投票暂停 不足以推进尝试。 已改变的有效载荷(例如: 互换引号)在下面的食谱中也得到不同的密钥; 调和 提交新交易前的较早交易.
import { createHash } from 'node:crypto'
const toUuid = (seed: string): string => {
const hex = createHash('sha256').update(seed).digest('hex')
return [
hex.slice(0, 8),
hex.slice(8, 12),
`5${hex.slice(13, 16)}`,
`8${hex.slice(17, 20)}`,
hex.slice(20, 32),
].join('-')
}
// Construct one adapter per operation. Reuse this id only for its reruns.
const operationId = '2f1c6a7e-5b0d-4c3a-9e8f-1a2b3c4d5e6f'
// Restore persisted attempt entries on replay. Persist an increment only
// after a known terminal unsuccessful outcome, never an ambiguous timeout.
const attempts = new Map<string, number>()
const adapter = await createCircleUserWalletAdapter({
apiKey: process.env.CIRCLE_API_KEY!,
userToken,
chains: ['Arc_Testnet', 'Base_Sepolia'],
onChallenge: ({ challengeId }) => sendToBrowser(challengeId),
resolveIdempotencyKey: ({ walletId, blockchain, payload }) => {
const transactionId = JSON.stringify([
walletId,
blockchain,
payload.calls.map(({ to, data, value }) => [
to.toLowerCase(),
(data ?? '0x').toLowerCase(),
(value ?? 0n).toString(),
]),
])
const attempt = attempts.get(transactionId) ?? 0
return toUuid(JSON.stringify([operationId, transactionId, attempt]))
},
})
无效的结果和普通解析器例外在创建前失败
FATAL 验证 KitError;可在
cause.traceA级 KitError 由解决者所抛出,
因此,一个密钥存储器可以以RETryble出错表示已知的瞬态故障.
钱包助手
直接管理用户的钱包——所有三个都采用连接加一 选项对象,并按工具包的方式命名链:
findCircleUserWallet(connection, { chain, accountType? })- 用户的 链上钱包,或undefined从不创造一个.ensureCircleUserWallet(connection, { chain, accountType?, onChallenge?, pollIntervalMs?, timeoutMs?, signal? })- 说吧 找到钱包, 在丢失时通过挑战创建它 .ensureCircleUserWallets(connection, { chains, ... })- 几个人一样 链条,提供每一个缺失的 单项挑战并返回 请求顺序中的钱包.
掷出 : KitError 当链子不被识别或没有圆形时
等同,或(为保证的助手)当用户尚未建立
签名方法尚未——检查
CIRCLE_USER_WALLET_PIN_NOT_SET_ERROR_CODE 通过 circleErrorCode(error) 财务报告和审定财务报表
在重试前运行您的设置流程 .
import {
ensureCircleUserWallet,
findCircleUserWallet,
} from '@circle-fin/adapter-circle-wallets/ucw/server'
const existing = await findCircleUserWallet(auth, { chain: 'Arc_Testnet' })
const wallet = await ensureCircleUserWallet(auth, {
chain: 'Arc_Testnet',
onChallenge: ({ challengeId }) => sendToBrowser(challengeId),
})
// Pass the whole wallet to skip re-resolving it.
const adapter = await createCircleUserWalletAdapter({ ...auth, wallet })
支持的链条
Circle Wallets 适配器支持 CCTP v2 驱动链的被编程子集 eVM和索拉纳生态系统数字 :
主网(10个链条):
- 弧形
- 坐标
- 弧形
- 雪崩
- 基础
- 以利亚
- 修道院
- OP 主要网
- 多边形 PoS
- 索拉纳
- 单链
测试网( 10连锁):
- 弧度测试网
- 塞波利亚角
- 富士雪崩
- 塞波利亚基地
- 伊特鲁姆·塞波利亚
- 莫纳德测试网
- OP 塞波利亚
- 多边形 PoS 模拟
- 索拉纳·德夫内
- 单链塞波利亚
说明: 圆形钱包支持所有CCTP v2链的子集. 完整的 CCTP v2 链式支持, 带有外部钱包, 使用
@circle-fin/adapter-viem-v2(50个EVM链)或@circle-fin/adapter-solana-kit代表索拉纳.
许可证
这个项目根据Apache 2.0许可证获得许可. 联系人 支助 详细情况.