use-user-controlled-wallets について
エンドユーザーが鍵を管理する Circle ウォレットの構築を支援するエージェントスキルです。PIN、メール OTP、ソーシャルログインに対応し、フロントエンドとバックエンドの SDK、ユーザー承認フロー、取引実装を説明します。
主な機能
- 01PIN、メール OTP、ソーシャルログインに対応
- 02フロントエンドとバックエンドの SDK の役割を説明
- 03チャレンジレスポンス方式でユーザーが承認
- 04EOA と SCA アカウントに対応
- 05送金、署名、コントラクト呼び出しを解説
活用シーン
- 01アプリにノンカストディアルなユーザーウォレットを追加
- 02OTP またはソーシャルログインでウォレットを作成
- 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 の UI)には、@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 とソーシャルログインでは、2 番目の引数としてログインコールバックを指定して SDK を初期化する必要があります。詳細については、該当するリファレンスファイルを参照してください。
主な概念
アカウントの種類
ユーザー管理型ウォレットでは、ウォレットの作成時に EOA と SCA のアカウントタイプを選択できます。
EOA(Externally Owned Account):作成手数料が不要で、TPS が高く、幅広いチェーン(EVM、Solana、Aptos)に対応しています。EVM チェーンではガス代としてネイティブトークンが必要です(Arc ではガス資産が USDC のため、別個のネイティブトークンはありません)。ガスのスポンサー機能は、Solana で feePayer を使用する場合にのみ利用できます。
SCA(Smart Contract Account):ERC-4337 のアカウント抽象化に対応しています。Circle Gas Station のペイマスターによるガスのスポンサー、バッチ操作、柔軟なキー管理を利用できます。対応しているのは EVM のみです(Solana/Aptos には対応していません)。最初の送金トランザクションでは、遅延デプロイのためのガス代が発生します。ガス代が高いため、Ethereum メインネットでの使用は避け、L2(Arbitrum、Base、Polygon、Optimism)で使用してください。
アカウントタイプごとの対応ブロックチェーンについては、次を参照してください:https://developers.circle.com/wallets/account-types
アーキテクチャ
ユーザー管理型ウォレットには、次の 3 者が関与します。
-
エンドユーザー(クライアント) -- Web アプリまたはモバイルアプリを使用する人です。開発者のフロントエンドを操作して認証(PIN、メール OTP、ソーシャルログイン)を行い、
@circle-fin/w3s-pw-web-sdkを介して Circle のホスト型 UI で、機密性の高い操作(ウォレットの作成、トランザクション、署名)をすべて承認します。ユーザーは秘密鍵を完全に管理します。開発者も Circle も、ユーザーに代わって操作することはできません。 -
開発者サービス(バックエンド) -- 開発者独自のサーバーです。Circle API キーを保持し、ユーザーセッションを管理して、使用状況を追跡し、アプリケーションレベルのガードレールルール(支出上限、許可リストに登録されたアドレス、レート制限など)を適用します。
@circle-fin/user-controlled-walletsを使用して Circle の API にリクエストを送信します。開発者は Circle Developer Console で開発者アカウントを登録し、Circle Wallet サービスを利用できます。開発者アカウント固有のセットアップについては、use-developer-controlled-walletsスキルを参照してください。 -
Circle Wallet サービス(API) -- ウォレットの作成、トランザクションの送信、キー管理(MPC ベース)、ブロックチェーンとの連携を管理する Circle のインフラストラクチャです。非カストディアルであることを保証するため、開発者にはセキュリティ監視と監査用の読み取りアクセス権が付与され、ユーザーはウォレットと資産を完全に管理します。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 分) |
| メール OTP | SMTP 設定 | OTP の検証後、SDK のログインコールバックから取得します |
| ソーシャルログイン | OAuth クライアント ID | OAuth リダイレクト後、SDK のログインコールバックから取得します |
開発者のアクセス権と制限
開発者は、Circle Developer Console の UI または API を介したプログラムによる方法で、ウォレットデータ、トランザクション履歴、ユーザー情報を読み取ることができます。ただし、開発者はユーザーの秘密鍵にアクセスできず、ユーザーのウォレットを操作して、トランザクションの送信、メッセージへの署名、ユーザーに代わってオンチェーン操作を実行することはできません。これらの操作にはすべて、チャレンジ・レスポンスモデルを通じたユーザーの承認が必要です。
また、ユーザーが認証方法(ソーシャルアカウント、メール、PIN コードとセキュリティ質問)にアクセスできなくなった場合、開発者はユーザーのウォレットの復旧を支援することはできません。アカウントの復旧は、ユーザーが再認証できるかどうかに完全に依存します。選択した認証方法へのアクセスを維持することの重要性を理解してもらうため、この制限についてオンボーディング時にユーザーへ伝えてください。
ユーザーのアクセス権と制限
ユーザーがアクセスできるのは、自分自身のウォレットとリソースのみです。他のユーザーのウォレット、トランザクション、その他のリソースには一切アクセスできません。また、開発者が管理するウォレットやリソースにもアクセスできません。2 種類のウォレットは完全に分離されています。
実装パターン
注: 参照コードのスニペットでは、すぐに動作する例を作成する目的でのみ
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を読んでください。 -
メール OTP でウォレットを作成:ユーザーはメールで送信されたワンタイムパスコードを使って認証します。Circle Console で SMTP を設定する必要があります。
references/create-wallet-email-otp.mdを読んでください。 -
トランザクションを送信:いずれかの認証方法で作成された既存のウォレットから、トークンの送金を行います。手数料の見積もり、トランザクションの高速化、キャンセルについて説明します。
references/send-transaction.mdを読んでください。 -
メッセージに署名:ユーザーが管理するウォレットからメッセージに署名します。EIP-191 メッセージ署名、EIP-712 型付きデータ、未加工トランザクションへの署名について説明します。
references/sign-message.mdを読んでください。 -
スマートコントラクトを実行:ABI シグネチャまたは未加工 calldata を使って、スマートコントラクト関数を実行します。コントラクト実行時のガス見積もりも含まれます。
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 キーは必ずサーバー側に置いてください。フロントエンドのみのビルドでは API キーが漏えいします。
- 送金を実行する前に、宛先、金額、ネットワーク、トークンについて、必ずユーザーの明示的な確認を求めてください。メインネットで資金を移動する場合は、必ず確認を受けてください。
- メインネットを対象とする場合や、安全基準値(例:100 USDC 超)を超える場合は、必ず警告してください。
- トランザクションを送信する前に、すべての入力(アドレス、金額、チェーン識別子)を必ず検証してください。
- 監査されていないコントラクトや未知のコントラクトとやり取りする前に、必ず警告してください。
- 本番環境では、XSS によるトークン窃取を軽減するため、
userTokenとencryptionKeyを localStorage ではなく httpOnly Cookie に必ず保存してください。
ベストプラクティス
- 実装前に、必ず正しいリファレンスファイルを読みます。
- 必ず最新のパッケージ(
@circle-fin/user-controlled-wallets@latest、@circle-fin/w3s-pw-web-sdk@latest)とvite-plugin-node-polyfillsをインストールします(Web SDKにはNode.jsの組み込みポリフィルが必要なため、Viteの設定にnodePolyfills()を追加します)。 - 初期化後は必ず
sdk.getDeviceId()を呼び出し、sdk.execute()の前に必ずsdk.setAuthentication({ userToken, encryptionKey })を呼び出します。getDeviceId()を呼び出さないと、executeはエラーを表示せずに失敗します。 - EthereumメインネットではSCAを使用しません(ガス代が高いため)。メインネットではEOAを、L2ではSCAを使用します。
- トークン残高の
amountが最小単位であると決めつけてはいけません。getWalletTokenBalanceは人が読みやすい形式の金額を返します(例:20 USDCの場合は「20」)。 - ソーシャルログインのフローでは、トークンをOAuthリダイレクト後も保持するため、必ずReactの状態ではなくCookieを使用します。
- 必ずテストネットをデフォルトにします。メインネットを指定する前に、ユーザーに明示的な確認を求めます。
- コントラクトの実行や大規模な送金の前には、必ず手数料を見積もり、ユーザーが事前にガス代を把握できるようにします。
- 実行前に、ABIの関数シグネチャとパラメーターが対象のコントラクトに合致していることを必ず確認します。シグネチャが正しくないとリバートが発生し、ガス代が無駄になります。
- 可読性と監査可能性を高めるため、信頼できるライブラリ(ethers、viem)でcalldataを生成する場合を除き、生の
callDataよりもabiFunctionSignatureとabiParametersを必ず優先します。
代替手段
- ERC-4337とERC-6900を使用し、ガス代のスポンサーを受けられるパスキー方式のスマートアカウントには、
use-modular-walletsスキルを使用します。 - アプリケーションでユーザーの操作なしにウォレットキーを完全にカストディする必要がある場合は、
use-developer-controlled-walletsスキルを使用します。
リファレンスリンク
- Circle Developer Docs -- ソースウェブサイトから関連ドキュメントを探す際は、必ず最初にお読みください。
免責事項:このスキルは「現状有姿」で提供され、いかなる保証もありません。Circle Developer Termsが適用されます。また、生成される出力には誤りが含まれる場合や、手数料設定オプション(Circleに支払われる手数料を含む)が含まれる場合があります。詳しくは、リポジトリのREADMEをご覧ください。