はじめに
初めまして。 『DApps開発入門』という本や色々記事を書いているかるでねです。
以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!
今回は「x402」についてまとめていきます。
x402とは
「x402」は、HTTPの 402 Payment Required ステータスコードを活用したオープンな支払いプロトコルです。
このプロトコルは、APIやコンテンツへのアクセスを直接HTTP上で課金できる仕組みを提供します。 アカウント管理やセッション管理が不要で、プログラマティックに支払いを実現できる点が特徴です。
公式サイトは以下になります。
公式ドキュメントは以下になります。
主な特徴
「x402」には以下の特徴があります。
- HTTP 402ステータスコードをベースにした標準的なプロトコル 既存のHTTPワークフローに透過的に統合できます。
- アカウント不要のプログラマティックな支払い ウォレットを使用した直接的な支払いが可能です。
- マルチネットワーク・マルチトークンサポート 様々なブロックチェーンネットワークとトークンに対応できます。
- AIエージェントとの高い親和性 システム間決済に最適化された設計です。
主要コンポーネント
「x402」プロトコルを構成する主要な登場人物とその役割について説明します。
Client
Clientは、有料リソースへのアクセスをリクエストするエンティティです。 買い手(Buyer)とも呼ばれます。
Clientの主な責任は以下の通りです。
- リクエスト送信 HTTPリクエストをリソースサーバーに送信します。
- 支払い要件の処理
402 Payment Requiredレスポンスから支払い詳細を抽出します。 - ペイロード作成 提供された要件に基づいて有効な支払いペイロードを構築します。
- リトライ
署名されたペイロードを
PAYMENT-SIGNATUREヘッダーに含めて再送信します。
Clientは、CSV保管や認証トークン管理が不要です。 すべてのやり取りがステートレスなHTTPリクエストで行われます。 これにより、AIエージェントを含む買い手がアカウント作成や認証情報管理なしでトランザクションを実行できます。
Server
Serverは、サービスへのアクセスに支払いを強制するリソースプロバイダーです。 売り手(Seller)とも呼ばれます。
Serverの主な責任は以下の通りです。
- 支払い要件の定義
402 Payment Requiredレスポンスで必要な支払い詳細を提供します。 - ペイロード検証 支払いペイロードをローカルまたはFacilitatorサービスを通じて検証します。
- 取引決済 支払いが確認された場合、決済を実行します。
- リソース提供
成功した支払い後、リクエストされたリソースと
PAYMENT-RESPONSEヘッダーを返却します。
Serverは、クライアント情報やセッション状態を保持する必要がありません。 検証と決済はリクエストごとに処理されます。
Facilitator
Facilitatorは、クライアント(買い手)とサーバー(売り手)間の支払い検証と決済プロセスを簡潔にする、オプションだが推奨されるサービスです。
Facilitatorの主な責任は以下の通りです。
- 支払い検証 クライアントから提出された支払いペイロードがサーバーの要件を満たしているか確認します。
- 決済処理 検証済みの支払いをブロックチェーンに送信し、確認を監視します。
- レスポンス提供 検証と決済の結果をサーバーに返し、クライアント要求への対応判断を支援します。
Facilitatorを使用する利点は以下の通りです。
- 運用負荷軽減 サーバーはブロックチェーンノードと直接やり取りする必要がありません。
- プロトコル一貫性 サービス全体で標準化された検証・決済フローを実現します。
- 迅速な統合 ブロックチェーン開発の専門知識がなくても支払い受け入れが可能になります。
Facilitatorは資金保有者(カストディアン)ではなく、クライアントが提供する署名済みペイロードに基づいてオンチェーン取引の検証と実行のみを行います。
Wallet
Walletは、買い手と売り手の両者にとって重要な機能を果たします。
買い手側の機能は以下の通りです。
- 暗号資産を保管する
- 支払いペイロードに署名する
- オンチェーン支払いをプログラム方式で承認する
売り手側の機能は以下の通りです。
- 暗号資産による支払いを受け取る
- サーバー設定内で支払い受取先を定義する
ウォレットアドレスは、支払い送受信と検証の際の識別子として機能します。 同時に、プロトコル内のユニークな識別子としても機能するため、プロトコル全体における個人・団体の認識に不可欠です。
プログラムによる支払いと安全な鍵管理には、Coinbase Wallet APIが推奨されています。
Bazaar
Bazaarは、APIで検索できるサービス一覧として機能するサービス検索機能です。 Discovery Layerとも呼ばれます。
開発者とAIエージェントがx402対応APIエンドポイントを見つけ、統合するのを支援します。
Bazaarが提供する機能は以下の通りです。
- 購入者向け 利用可能なx402対応サービスをプログラムで発見し、機能、価格、スキーマを理解できます。
- 販売者向け x402対応サービスがグローバルな開発者とAIエージェントのオーディエンスに自動的に認識されます。
- AIエージェント向け 事前に統合されていない動的なサービス発見が可能となり、クエリ、検索、支払い、利用ができます。
Bazaarの動作方式は以下の通りです。
システムは単純な /list エンドポイントを提供し、CDP Facilitatorに登録されたすべてのx402互換サービスを返します。
サービスは、CDP Facilitatorを使用してBazaar拡張機能を有効にすると自動的にオプトインされます。
Bazaarは現在、初期開発段階にあり、機能とAPIは、フィードバック収集と機能拡張に応じて変更される可能性があります。
解決する課題
「x402」が解決する課題について説明します。
従来の決済システムの高額な手数料
従来の決済システムでは、クレジットカードや決済代行サービスを使用すると、数%〜10%程度の手数料が発生します。 APIのリクエストごとに少額の課金を行う場合、手数料が取引額を上回ってしまうことがあります。
「x402」では、暗号通貨を使用することで手数料を大幅に削減できます。 ブロックチェーンのネットワーク手数料のみで決済が完了します。
アカウント管理とセッション管理の複雑性
従来の決済システムでは、ユーザーアカウントの作成、認証情報の管理、セッションの維持が必要です。 これらの仕組みは、システム間の決済には適していません。
「x402」では、ウォレットを使用した直接的な支払いが可能です。 アカウント作成やログインのプロセスが不要になります。
マイクロペイメントへの対応不足
従来の決済システムでは、少額決済(マイクロペイメント)に対応していないことが多いです。 手数料の最低金額が設定されているため、数円〜数十円の取引が経済的に成立しません。
「x402」では、暗号通貨の特性を活かして、少額決済を実現できます。 リクエストごとに数円程度の課金も可能になります。
AIエージェントとの非互換性
従来の決済システムは、人間が操作することを前提に設計されています。 AIエージェントが自律的に決済を行う仕組みには適していません。
「x402」は、プログラマティックな支払いを前提に設計されています。 AIエージェントが自動的にAPIにアクセスし、支払いを完了できます。
アーキテクチャと仕組み
「x402」のアーキテクチャと動作フローについて説明します。
主要コンポーネント
「x402」は以下の3つのコンポーネントで構成されています。
Client(買い手)
リソースにアクセスしたいクライアントです。 ウォレット機能を持ち、支払いを実行します。
Server(売り手)
APIやコンテンツを提供するサーバーです。 402レスポンスを返し、支払いを要求します。
Facilitator(仲介者)
支払いの検証と決済を担当するサービスです。
/verify と /settle の2つのエンドポイントを提供します。
支払いフロー
「x402」の支払いフローは以下の手順で進みます。
sequenceDiagram
participant Client as Client (買い手)
participant Server as Server (売り手)
participant Facilitator as Facilitator (仲介者)
Client->>Server: リソースをリクエスト
Server->>Client: 402 Payment Required<br/>(支払い指示を含む)
Client->>Client: 支払いペイロードを準備
Client->>Server: 支払い情報と共にリクエスト
Server->>Facilitator: /verify (支払い検証)
Facilitator->>Server: 検証結果
Server->>Facilitator: /settle (決済確定)
Facilitator->>Server: 決済結果
Server->>Client: リソースを提供具体的なフローは以下の通りです。
Step 1: リソースのリクエスト
Clientが保護されたリソースにアクセスします。
GET /api/protected-resource HTTP/1.1
Host: example.comStep 2: 402レスポンスの返却
Serverが 402 Payment Required ステータスコードと支払い指示を返します。
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"payment_required": true,
"amount": "0.001",
"currency": "ETH",
"recipient": "0x1234...",
"facilitator_url": "https://facilitator.example.com"
}Step 3: 支払いの実行
Clientがウォレットを使用して支払いを実行します。 支払い情報を含めて再度リクエストを送信します。
GET /api/protected-resource HTTP/1.1
Host: example.com
X-Payment-Proof: {"tx_hash": "0xabcd...", "signature": "..."}Step 4: 支払いの検証
Serverが Facilitator の /verify エンドポイントを呼び出して支払いを検証します。
POST /verify HTTP/1.1
Host: facilitator.example.com
Content-Type: application/json
{
"payment_proof": {"tx_hash": "0xabcd...", "signature": "..."},
"expected_amount": "0.001",
"expected_recipient": "0x1234..."
}Step 5: 決済の確定
検証が成功した後、Serverが /settle エンドポイントを呼び出して決済を確定します。
POST /settle HTTP/1.1
Host: facilitator.example.com
Content-Type: application/json
{
"payment_id": "payment_123",
"confirmation": true
}Step 6: リソースの提供
決済が完了した後、Serverがリソースを提供します。
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": "Protected resource content"
}Facilitatorの役割
「Facilitator」は、支払いプロトコルの中核を担う重要なコンポーネントです。
主な役割は以下の通りです。
- 支払いの検証 トランザクションの有効性、金額の正確性、受取人の一致を確認します。
- 決済の確定 支払いが正当であることを保証し、二重支払いを防ぎます。
- ネットワーク間の差異の吸収 異なるブロックチェーンネットワークに対応します。
ブロックチェーン決済の実装フロー
「x402」における、ブロックチェーンを使用した実際の決済フローについて詳しく説明します。
全体フロー(ブロックチェーン含む)
ブロックチェーンレイヤーを含めた完全な決済フローは以下の通りです。
sequenceDiagram
participant Client as Client (Buyer)
participant Wallet as Wallet
participant Blockchain as Blockchain Network
participant Server as Server (Seller)
participant Facilitator as Facilitator
Client->>Server: リソースをリクエスト
Server->>Client: 402 Payment Required<br/>(amount, recipient, network)
Note over Client,Wallet: 支払い準備
Client->>Wallet: トランザクション作成依頼
Wallet->>Wallet: トランザクション署名
Wallet->>Blockchain: トランザクション送信
Note over Blockchain: ブロックチェーン処理
Blockchain->>Blockchain: トランザクション検証
Blockchain->>Blockchain: ブロックに含める
Blockchain-->>Wallet: トランザクションハッシュ
Note over Client,Server: 支払い証明の提出
Wallet->>Client: tx_hash, signature
Client->>Server: リクエスト + Payment Proof
Note over Server,Facilitator: 検証プロセス
Server->>Facilitator: /verify (tx_hash, expected amount/recipient)
Facilitator->>Blockchain: トランザクション情報取得
Blockchain-->>Facilitator: トランザクション詳細
Facilitator->>Facilitator: 金額・受取人・ステータス検証
Facilitator-->>Server: 検証結果 (valid: true)
Note over Server,Facilitator: 決済確定
Server->>Facilitator: /settle (payment_id)
Facilitator->>Facilitator: 決済記録を保存
Facilitator-->>Server: 決済完了
Server->>Client: リソース提供トランザクション作成と送信
Clientは、402レスポンスを受け取った後、ウォレットを使用してブロックチェーントランザクションを作成します。
Step 1: トランザクションパラメータの準備
402レスポンスから取得した情報を基に、トランザクションパラメータを準備します。
const paymentInfo = {
amount: '0.001', // ETH
recipient: '0x1234567890abcdef...',
network: 'base', // または 'ethereum', 'polygon' など
currency: 'ETH'
};Step 2: ウォレットでトランザクションを作成
ウォレットSDKを使用して、トランザクションを作成し署名します。
import { createWalletClient, http } from 'viem';
import { base } from 'viem/chains';
const walletClient = createWalletClient({
chain: base,
transport: http()
});
// トランザクション作成
const hash = await walletClient.sendTransaction({
to: paymentInfo.recipient,
value: parseEther(paymentInfo.amount)
});
console.log('Transaction Hash:', hash);
// 例: 0xabcdef1234567890...Step 3: ブロックチェーンネットワークでの処理
トランザクションは以下の流れでブロックチェーンに記録されます。
-
メンプールへの追加 送信されたトランザクションは、まずメンプール(未確認トランザクションのプール)に追加されます。
-
バリデーターによる検証 ネットワークのバリデーターがトランザクションの有効性を検証します。 署名の正当性、送信者の残高、ガス代の支払い能力などを確認します。
-
ブロックへの組み込み 検証済みのトランザクションは、新しいブロックに含まれます。 Base(Layer 2)では、約2秒ごとに新しいブロックが生成されます。
-
確定 ブロックがチェーンに追加されると、トランザクションは確定します。 Layer 2ネットワークでは、ほぼ即座に確定しますが、Ethereumメインネットでは数分かかる場合があります。
Facilitatorによる検証プロセス
Facilitatorは、ブロックチェーンから直接トランザクション情報を取得して検証します。
Step 1: トランザクション情報の取得
Facilitatorは、RPCノードを経由してブロックチェーンからトランザクション情報を取得します。
import { createPublicClient, http } from 'viem';
import { base } from 'viem/chains';
const publicClient = createPublicClient({
chain: base,
transport: http()
});
// トランザクション情報を取得
const transaction = await publicClient.getTransaction({
hash: paymentProof.tx_hash
});
console.log('Transaction Details:', {
from: transaction.from,
to: transaction.to,
value: transaction.value,
blockNumber: transaction.blockNumber
});Step 2: 検証項目のチェック
Facilitatorは以下の項目を検証します。
function verifyPayment(transaction, expectedParams) {
// 1. 受取人アドレスの確認
if (transaction.to !== expectedParams.recipient) {
return { valid: false, reason: 'Recipient mismatch' };
}
// 2. 支払い金額の確認
const actualAmount = formatEther(transaction.value);
if (actualAmount < expectedParams.amount) {
return { valid: false, reason: 'Insufficient amount' };
}
// 3. トランザクションのステータス確認
const receipt = await publicClient.getTransactionReceipt({
hash: transaction.hash
});
if (receipt.status !== 'success') {
return { valid: false, reason: 'Transaction failed' };
}
// 4. 十分な確認数があるか
const currentBlock = await publicClient.getBlockNumber();
const confirmations = currentBlock - receipt.blockNumber;
if (confirmations < REQUIRED_CONFIRMATIONS) {
return { valid: false, reason: 'Insufficient confirmations' };
}
// すべてのチェックが通過
return { valid: true };
}Step 3: 二重支払いの防止
Facilitatorは、同じトランザクションハッシュが複数回使用されないように記録します。
// データベースに決済記録を保存
async function settlePay(txHash: string, serverId: string) {
// トランザクションハッシュが既に使用されていないか確認
const existing = await db.settlements.findOne({ txHash });
if (existing) {
throw new Error('Transaction already settled');
}
// 新しい決済記録を作成
await db.settlements.create({
txHash,
serverId,
settledAt: new Date(),
status: 'completed'
});
}対応ネットワークと確認時間
異なるブロックチェーンネットワークでは、確認時間とコストが異なります。
| ネットワーク | ブロック生成時間 | 推奨確認数 | 確認完了時間 | ガス代目安 |
|---|---|---|---|---|
| Ethereum | 約12秒 | 6〜12ブロック | 1〜2分 | 高い($1〜10) |
| Base | 約2秒 | 1〜3ブロック | 数秒〜10秒 | 非常に低い($0.01未満) |
| Polygon | 約2秒 | 128ブロック | 約4分 | 低い($0.01〜0.1) |
| Arbitrum | 約0.25秒 | 1〜5ブロック | 数秒 | 低い($0.01〜0.1) |
推奨ネットワーク
マイクロペイメントを実現する場合、Layer 2ネットワーク(Base、Arbitrum、Optimism)の使用を推奨します。 ガス代が非常に低く、確認時間も短いため、リクエストごとの少額課金に適しています。
トランザクション失敗時の処理
トランザクションが失敗した場合の処理フローです。
ケース1: ガス代不足
try {
const hash = await walletClient.sendTransaction({
to: paymentInfo.recipient,
value: parseEther(paymentInfo.amount)
});
} catch (error) {
if (error.message.includes('insufficient funds')) {
// ユーザーに残高不足を通知
console.error('Insufficient balance for gas fees');
}
}ケース2: トランザクションリバート
スマートコントラクトを経由する場合、条件が満たされずにリバートする可能性があります。
const receipt = await publicClient.getTransactionReceipt({
hash: txHash
});
if (receipt.status === 'reverted') {
// リバート理由を取得
const reason = await publicClient.getTransactionRevertReason({
hash: txHash
});
console.error('Transaction reverted:', reason);
}ケース3: ネットワーク遅延
ネットワークが混雑している場合、トランザクションの確認に時間がかかります。 この場合、Clientは定期的にトランザクションステータスをポーリングします。
async function waitForConfirmation(txHash: string, maxWaitTime: number = 60000) {
const startTime = Date.now();
while (Date.now() - startTime < maxWaitTime) {
const receipt = await publicClient.getTransactionReceipt({
hash: txHash
});
if (receipt && receipt.status === 'success') {
return receipt;
}
// 2秒待機してから再試行
await new Promise(resolve => setTimeout(resolve, 2000));
}
throw new Error('Transaction confirmation timeout');
}ERC規格との連携
「x402」は、既存のERC規格と組み合わせることで、より高度な決済体験を実現できます。
ERC-3009: Transfer with Authorization
「ERC-3009」は、EIP-712署名を使用したメタトランザクションを実現する規格です。
主な特徴
- アトミックな送金 ERC-2612(Permit)が承認のみを行うのに対し、ERC-3009は送金自体を実行します。 単一の呼び出しで完結します。
- ランダムnonce 連続したnonceではなく、32バイトのランダムnonceを使用します。 これにより、数千の並列承認を競合なく生成できます。
- ガスレス取引 ユーザーはETHを保有せずにトークン送金を実行できます。 ガス代は別のアドレス(Relayerなど)が負担します。
x402での活用方法
ERC-3009を使用すると、x402の決済フローを大幅に簡素化できます。
sequenceDiagram
participant Client as Client (Buyer)
participant Server as Server (Seller)
participant Relayer as Gas Relayer
participant USDC as USDC Contract (ERC-3009)
participant Blockchain as Blockchain
Client->>Server: リソースをリクエスト
Server->>Client: 402 Payment Required<br/>(amount, recipient)
Note over Client: オフチェーン署名
Client->>Client: EIP-712署名を作成<br/>(transferWithAuthorization)
Client->>Server: リクエスト + Signature
Note over Server,Relayer: サーバーがガス代を負担
Server->>Relayer: 署名を転送
Relayer->>USDC: transferWithAuthorization(signature)
USDC->>Blockchain: トランザクション実行
Blockchain-->>USDC: 確定
USDC-->>Relayer: 成功
Relayer-->>Server: 成功
Server->>Client: リソース提供実装例
import { encodeFunctionData, keccak256, toHex } from 'viem';
// EIP-712ドメイン定義
const domain = {
name: 'USD Coin',
version: '2',
chainId: 8453, // Base
verifyingContract: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' // USDC on Base
};
// TransferWithAuthorizationの型定義
const types = {
TransferWithAuthorization: [
{ name: 'from', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'validAfter', type: 'uint256' },
{ name: 'validBefore', type: 'uint256' },
{ name: 'nonce', type: 'bytes32' }
]
};
// クライアント側:署名を作成
async function createTransferAuthorization(
from: string,
to: string,
value: bigint,
validAfter: number,
validBefore: number
) {
// ランダムnonceを生成
const nonce = keccak256(toHex(Date.now()));
const message = {
from,
to,
value,
validAfter,
validBefore,
nonce
};
// EIP-712署名を作成
const signature = await walletClient.signTypedData({
domain,
types,
primaryType: 'TransferWithAuthorization',
message
});
return { message, signature, nonce };
}
// サーバー側:署名を検証して実行
async function executeTransferWithAuthorization(
message: any,
signature: string
) {
const usdcContract = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
// r, s, vに分解
const r = signature.slice(0, 66);
const s = '0x' + signature.slice(66, 130);
const v = parseInt(signature.slice(130, 132), 16);
// transferWithAuthorizationを呼び出し
const data = encodeFunctionData({
abi: usdcABI,
functionName: 'transferWithAuthorization',
args: [
message.from,
message.to,
message.value,
message.validAfter,
message.validBefore,
message.nonce,
v,
r,
s
]
});
// Relayerがガス代を支払ってトランザクションを送信
const txHash = await relayerClient.sendTransaction({
to: usdcContract,
data
});
return txHash;
}メリット
- ユーザーはETHを保有する必要がない USDCのみを保有していれば、x402での支払いが可能になります。
- サーバーがガス代を管理 ユーザー体験を向上させるため、サーバー側でガス代を負担できます。
- 並列処理が可能 ランダムnonceにより、複数のAIエージェントが同時に署名を生成しても競合しません。
ERC-2612: Permit(ERC20のガスレス承認)
「ERC-2612」は、ERC20トークンのガスレス承認を実現する規格です。
主な特徴
- 署名による承認
approve()トランザクションの代わりに、EIP-712署名で承認を行います。 - ガス代不要 承認のためのガス代が不要になります。
- 広範な採用 DAI、UNI、COMPなど、多くのERC20トークンで採用されています。
x402での活用方法
ERC-2612を使用すると、スマートコントラクトベースの決済フローを簡素化できます。
// ユーザー側:Permitを作成
async function createPermit(
owner: string,
spender: string,
value: bigint,
deadline: number
) {
const nonce = await tokenContract.read.nonces([owner]);
const domain = {
name: 'Dai Stablecoin',
version: '1',
chainId: 8453,
verifyingContract: daiAddress
};
const types = {
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' }
]
};
const message = { owner, spender, value, nonce, deadline };
const signature = await walletClient.signTypedData({
domain,
types,
primaryType: 'Permit',
message
});
return { message, signature };
}
// サーバー側:Permitを実行して送金
async function executePermitAndTransfer(
permit: any,
signature: string
) {
const { v, r, s } = splitSignature(signature);
// 1. Permitで承認
await tokenContract.write.permit([
permit.owner,
permit.spender,
permit.value,
permit.deadline,
v,
r,
s
]);
// 2. transferFromで送金
await tokenContract.write.transferFrom([
permit.owner,
permit.spender,
permit.value
]);
}ERC-8004: Trustless Agents(AIエージェント向け標準)
「ERC-8004」は、信頼不要なAIエージェントのための標準規格です。
主な特徴
ERC-8004は、3つのオンチェーンレジストリを定義します。
- Identity Registry ERC-721(NFT)を使用して、各エージェントに普遍的で移植可能な、検閲耐性のあるオンチェーンIDを提供します。
- Reputation Registry エージェントのパフォーマンスの恒久的な「オンチェーン監査証跡」を作成します。 オフチェーンの評価スコアリングシステムの生データソースとして機能します。
- Validation Registry 暗号経済的なステーキングまたはTEE(Trusted Execution Environment)やゼロ知識証明を使用した暗号的証明を通じて、タスク検証をサポートします。
x402での活用方法
ERC-8004とx402を組み合わせることで、信頼性の高いAIエージェント決済エコシステムを構築できます。
sequenceDiagram
participant AIAgent as AI Agent
participant IdentityNFT as Identity Registry (ERC-721)
participant ReputationReg as Reputation Registry
participant x402Server as x402 API Server
participant ValidationReg as Validation Registry
Note over AIAgent,IdentityNFT: エージェント登録
AIAgent->>IdentityNFT: registerAgent()
IdentityNFT-->>AIAgent: Agent NFT #123
Note over AIAgent,x402Server: x402決済フロー
AIAgent->>x402Server: リクエスト + Agent ID #123
x402Server->>IdentityNFT: verifyAgent(#123)
IdentityNFT-->>x402Server: 有効なエージェント
x402Server->>ReputationReg: getReputation(#123)
ReputationReg-->>x402Server: Score: 95/100
x402Server->>AIAgent: 402 Payment Required
AIAgent->>AIAgent: ERC-3009署名作成
AIAgent->>x402Server: リクエスト + Payment Signature
x402Server->>x402Server: 支払い検証・決済
x402Server->>AIAgent: API Response
Note over AIAgent,ValidationReg: タスク完了の記録
x402Server->>ValidationReg: recordCompletion(#123, task_id)
ValidationReg->>ReputationReg: updateReputation(#123, +1)実装例
// AIエージェントの登録
async function registerAIAgent(
agentMetadata: {
name: string;
description: string;
capabilities: string[];
}
) {
const identityRegistry = '0x...'; // ERC-8004 Identity Registry
// エージェントNFTをミント
const txHash = await identityContract.write.registerAgent([
agentMetadata.name,
agentMetadata.description,
JSON.stringify(agentMetadata.capabilities)
]);
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
const agentId = receipt.logs[0].topics[1]; // NFT Token ID
return agentId;
}
// x402サーバー側:エージェントの検証
async function verifyAgent(agentId: bigint) {
// 1. Identity Registryでエージェントの存在を確認
const owner = await identityContract.read.ownerOf([agentId]);
if (!owner) {
throw new Error('Invalid agent ID');
}
// 2. Reputation Registryで評価を取得
const reputation = await reputationContract.read.getReputation([agentId]);
if (reputation.score < MINIMUM_REPUTATION_SCORE) {
throw new Error('Agent reputation too low');
}
// 3. 過去の取引履歴を確認
const history = await reputationContract.read.getHistory([agentId]);
return {
agentId,
owner,
reputation: reputation.score,
totalTransactions: history.length,
verified: true
};
}
// タスク完了後の評価記録
async function recordTaskCompletion(
agentId: bigint,
taskId: string,
success: boolean
) {
// Validation Registryにタスク完了を記録
await validationContract.write.recordCompletion([
agentId,
taskId,
success,
Date.now()
]);
// Reputation Registryを更新
const scoreChange = success ? 1 : -2;
await reputationContract.write.updateReputation([agentId, scoreChange]);
}メリット
- 信頼性の可視化 エージェントの過去の実績をオンチェーンで確認できます。
- 不正防止 評価が低いエージェントとの取引を拒否できます。
- 長期的なインセンティブ 良い実績を積むことで、より多くの取引機会を得られます。
ERC-1363: Payable Tokens(支払いとコールバックの統合)
「ERC-1363」は、トークン送金と同時にコントラクト関数を呼び出せる規格です。
主な特徴
- transferAndCall / transferFromAndCall トークン送金と同時に受取側のコントラクト関数を呼び出します。 1トランザクションで支払いとAPI利用が完結します。
- approveAndCall 承認と同時にコントラクト関数を実行します。 2段階の処理を1トランザクションに統合できます。
仕組みとフロー
ERC-1363は、ERC-20を拡張してコールバック機能を追加した規格です。
インターフェース定義
interface IERC1363 {
function transferAndCall(address to, uint256 value) external returns (bool);
function transferAndCall(address to, uint256 value, bytes memory data) external returns (bool);
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
function transferFromAndCall(address from, address to, uint256 value, bytes memory data) external returns (bool);
function approveAndCall(address spender, uint256 value) external returns (bool);
function approveAndCall(address spender, uint256 value, bytes memory data) external returns (bool);
}
interface IERC1363Receiver {
function onTransferReceived(
address operator,
address from,
uint256 value,
bytes memory data
) external returns (bytes4);
}
interface IERC1363Spender {
function onApprovalReceived(
address owner,
uint256 value,
bytes memory data
) external returns (bytes4);
}動作フロー
-
transferAndCallの処理
- クライアントが
transferAndCall(to, value, data)を呼び出します。 - トークンコントラクトが
transfer()を実行して送金します。 - 送金成功後、受取側の
onTransferReceived()コールバックを呼び出します。 - コールバックが正しいセレクター(
0x88a7ca5c)を返せば成功です。
- クライアントが
-
approveAndCallの処理
- クライアントが
approveAndCall(spender, value, data)を呼び出します。 - トークンコントラクトが
approve()を実行して承認します。 - 承認成功後、承認先の
onApprovalReceived()コールバックを呼び出します。 - コールバックが正しいセレクター(
0x7b04a2d0)を返せば成功です。
- クライアントが
コールバック検証
受取側コントラクトは、必ず IERC1363Receiver または IERC1363Spender インターフェースを実装する必要があります。
実装していない場合、トランザクションはリバートします。
// 受取側の実装例
contract PayableReceiver is IERC1363Receiver {
function onTransferReceived(
address operator,
address from,
uint256 value,
bytes memory data
) external override returns (bytes4) {
// 処理を実行
// ...
// 成功を示すセレクターを返す
return IERC1363Receiver.onTransferReceived.selector;
}
}ERC-20との違い
| 項目 | ERC-20 | ERC-1363 |
|---|---|---|
| トランザクション数 | 2回(approve + transferFrom) | 1回(transferAndCall) |
| コールバック | なし | あり(onTransferReceived) |
| ガス代 | 高い | 低い(1トランザクション) |
| アトミック性 | 保証されない | 保証される |
x402での活用方法
ERC-1363を使用すると、支払いとAPI呼び出しを1トランザクションで実行できます。
sequenceDiagram
participant Client as Client
participant Token as ERC-1363 Token (USDC)
participant x402Server as x402 API Server
Client->>Token: transferAndCall(x402Server, amount, apiParams)
Token->>Token: トークン送金処理
Token->>x402Server: onTransferReceived(from, amount, apiParams)
x402Server->>x402Server: API処理実行
x402Server-->>Token: 成功
Token-->>Client: API結果を返却実装例
// x402サーバー側:ERC-1363レシーバーを実装
contract X402APIServer is IERC1363Receiver {
// transferAndCallを受け取った時の処理
function onTransferReceived(
address operator,
address from,
uint256 value,
bytes calldata data
) external override returns (bytes4) {
// dataにAPIパラメータがエンコードされている
(string memory apiEndpoint, bytes memory params) = abi.decode(
data,
(string, bytes)
);
// 支払い金額を検証
require(value >= getAPIPrice(apiEndpoint), 'Insufficient payment');
// API処理を実行
bytes memory result = executeAPI(apiEndpoint, params);
// 成功を返す
return IERC1363Receiver.onTransferReceived.selector;
}
function executeAPI(
string memory endpoint,
bytes memory params
) internal returns (bytes memory) {
// API処理の実装
if (keccak256(bytes(endpoint)) == keccak256('generate-image')) {
return generateImage(params);
}
// ...
}
}
// クライアント側:transferAndCallで支払いとAPI呼び出しを同時実行
const erc1363Token = '0x...'; // ERC-1363対応トークン
const x402Server = '0x...';
// APIパラメータをエンコード
const apiParams = encodeFunctionData({
abi: apiABI,
functionName: 'encodeParams',
args: ['generate-image', { prompt: '猫の画像', style: 'realistic' }]
});
// 1トランザクションで支払い+API呼び出し
const txHash = await walletClient.writeContract({
address: erc1363Token,
abi: erc1363ABI,
functionName: 'transferAndCall',
args: [
x402Server,
parseUnits('0.1', 6), // 0.1 USDC
apiParams
]
});メリット
- トランザクション数の削減 2段階の処理を1トランザクションに統合し、ガス代を節約できます。
- アトミック性の保証 支払いとAPI実行が分離せず、どちらかが失敗すれば全体がリバートします。
- ユーザー体験の向上 ユーザーは1回の署名でAPI利用と支払いを完了できます。
ERC-2771: Meta Transactions(ガスレス取引の標準)
「ERC-2771」は、Trusted Forwarder経由でガスレス取引を実現する標準規格です。
主な特徴
- Trusted Forwarder ユーザーの署名付きメッセージをリレイヤーが代理送信します。 リレイヤーがガス代を負担します。
- メタトランザクション ユーザーはETHを保有せずにトランザクションを実行できます。
- 互換性 既存のスマートコントラクトを最小限の変更で対応できます。
仕組みとフロー
ERC-2771は、Trusted Forwarderパターンを使用してメタトランザクションを実現します。
アーキテクチャ
User (署名のみ) → Trusted Forwarder → Relayer (ガス代支払い) → Target Contract主要コンポーネント
-
Trusted Forwarder
- ユーザーの署名を検証します。
- Relayerにトランザクションを転送します。
- nonce管理を行い、リプレイ攻撃を防ぎます。
-
Relayer
- Forwarderからトランザクションを受け取ります。
- 自身のETHを使用してガス代を支払います。
- ブロックチェーンにトランザクションを送信します。
-
ERC2771Context(ターゲットコントラクト)
_msgSender()関数をオーバーライドします。msg.senderではなく、実際の署名者を返します。
_msgSender()の動作
abstract contract ERC2771Context {
address private immutable _trustedForwarder;
constructor(address trustedForwarder) {
_trustedForwarder = trustedForwarder;
}
function isTrustedForwarder(address forwarder) public view returns (bool) {
return forwarder == _trustedForwarder;
}
function _msgSender() internal view virtual override returns (address sender) {
if (isTrustedForwarder(msg.sender) && msg.data.length >= 20) {
// msg.dataの末尾20バイトが実際の送信者アドレス
assembly {
sender := shr(96, calldataload(sub(calldatasize(), 20)))
}
} else {
sender = msg.sender;
}
}
}メッセージフォーマット
Forwarderは、オリジナルの msg.data の末尾に実際の送信者アドレス(20バイト)を追加します。
[オリジナルのcalldata] + [実際の送信者アドレス(20バイト)]nonce管理
contract MinimalForwarder {
mapping(address => uint256) private _nonces;
function getNonce(address from) public view returns (uint256) {
return _nonces[from];
}
function verify(
ForwardRequest calldata req,
bytes calldata signature
) public view returns (bool) {
address signer = _recoverSigner(req, signature);
return _nonces[req.from] == req.nonce && signer == req.from;
}
function execute(
ForwardRequest calldata req,
bytes calldata signature
) public payable returns (bool, bytes memory) {
require(verify(req, signature), 'Invalid signature');
_nonces[req.from]++;
// ターゲットコントラクトを呼び出し
(bool success, bytes memory returndata) = req.to.call{
gas: req.gas,
value: req.value
}(abi.encodePacked(req.data, req.from));
return (success, returndata);
}
}セキュリティ考慮事項
- Trusted Forwarderの信頼性: Forwarderコントラクトは信頼できる実装を使用する必要があります。
- Replay攻撃防止: nonce管理により、同じ署名が複数回使用されることを防ぎます。
- 署名検証: EIP-712形式の署名を使用し、改ざんを防ぎます。
x402での活用方法
ERC-2771を使用すると、ユーザーはETHを保有せずにx402で支払いができます。
sequenceDiagram
participant Client as Client (ETHなし)
participant Forwarder as Trusted Forwarder
participant Relayer as Gas Relayer
participant x402Server as x402 API Server
participant Blockchain as Blockchain
Client->>Client: メタトランザクションを作成・署名
Client->>Forwarder: 署名付きメッセージ送信
Note over Forwarder,Relayer: リレイヤーがガス代負担
Forwarder->>Relayer: トランザクション実行依頼
Relayer->>Blockchain: トランザクション送信(ガス代支払い)
Blockchain->>x402Server: execute(from=Client, data=...)
x402Server->>x402Server: API処理実行
x402Server-->>Blockchain: 成功
Blockchain-->>Client: API結果実装例
// クライアント側:メタトランザクションを作成
async function createMetaTransaction(
from: string,
to: string,
data: string
) {
const forwarder = '0x...'; // Trusted Forwarder
const nonce = await forwarderContract.read.getNonce([from]);
const message = {
from,
to,
value: 0n,
gas: 1000000n,
nonce,
data
};
// EIP-712署名を作成
const signature = await walletClient.signTypedData({
domain: {
name: 'MinimalForwarder',
version: '0.0.1',
chainId: 8453,
verifyingContract: forwarder
},
types: {
ForwardRequest: [
{ name: 'from', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'gas', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'data', type: 'bytes' }
]
},
primaryType: 'ForwardRequest',
message
});
return { message, signature };
}
// リレイヤー側:メタトランザクションを実行
async function executeMetaTransaction(
request: any,
signature: string
) {
const forwarder = '0x...';
// リレイヤーがガス代を支払ってトランザクションを送信
const txHash = await relayerClient.writeContract({
address: forwarder,
abi: forwarderABI,
functionName: 'execute',
args: [request, signature]
});
return txHash;
}
// x402サーバー側:ERC-2771に対応
contract X402Server is ERC2771Context {
constructor(address trustedForwarder)
ERC2771Context(trustedForwarder)
{}
function processAPI(bytes calldata params) external {
// _msgSender()は実際の送信者(クライアント)を返す
address actualSender = _msgSender();
// API処理を実行
// ...
}
}メリット
- ユーザーはETH不要 USDCのみを保有していればx402で支払い可能です。
- オンボーディング改善 新規ユーザーがETHを購入する必要がなくなります。
- サーバーがUXを制御 ガス代をサーバー側で負担し、ユーザー体験を向上できます。
ERC-7677: Paymaster Web Service(ガス代完全隠蔽)
「ERC-7677」は、Paymasterサービスでガス代を完全に隠蔽する最新規格です。
主な特徴
- Paymaster統合 ERC-4337アカウント抽象化と連携し、ガス代をスポンサーできます。
- HTTP APIベース Paymasterサービスに HTTP API でアクセスできます。
- 柔軟なガス代支払い ERC20トークンでガス代を支払う、サーバーが全額負担するなど、多様な方式に対応します。
仕組みとフロー
ERC-7677は、ERC-4337アカウント抽象化の上に構築されたPaymasterサービスのHTTP API規格です。
ERC-4337 UserOperationの構造
struct UserOperation {
address sender; // スマートアカウントのアドレス
uint256 nonce; // リプレイ攻撃防止用
bytes initCode; // アカウント作成コード(初回のみ)
bytes callData; // 実行する関数呼び出し
uint256 callGasLimit; // 実行用のガス上限
uint256 verificationGasLimit; // 署名検証用のガス上限
uint256 preVerificationGas; // Bundler補償用のガス
uint256 maxFeePerGas; // ガス単価の上限
uint256 maxPriorityFeePerGas; // 優先手数料の上限
bytes paymasterAndData; // Paymaster情報
bytes signature; // ユーザー署名
}Paymasterのライフサイクル
-
準備フェーズ(pm_getPaymasterStubData)
- クライアントが未署名のUserOperationを作成します。
- PaymasterサービスにRPCリクエストを送信します。
- Paymasterが
paymasterAndDataのスタブ(仮データ)を返します。
-
署名フェーズ
- クライアントがUserOperationに署名します。
- 署名にはPaymasterのスタブデータが含まれます。
-
最終化フェーズ(pm_getPaymasterData)
- Bundlerが署名済みUserOperationをPaymasterに送信します。
- Paymasterが最終的な
paymasterAndDataを返します。 - この中にPaymasterの署名が含まれます。
-
実行フェーズ(validatePaymasterUserOp)
- EntryPointコントラクトがPaymasterの
validatePaymasterUserOpを呼び出します。 - Paymasterがガス代負担を承認します。
- EntryPointコントラクトがPaymasterの
-
精算フェーズ(postOp)
- トランザクション実行後、
postOpが呼び出されます。 - 実際のガス消費量を記録し、必要に応じて精算します。
- トランザクション実行後、
RPC APIメソッド
// pm_getPaymasterStubData: スタブデータ取得
{
"jsonrpc": "2.0",
"id": 1,
"method": "pm_getPaymasterStubData",
"params": [{
"sender": "0x...",
"nonce": "0x1",
"callData": "0x...",
"callGasLimit": "0x100000",
"verificationGasLimit": "0x100000",
"preVerificationGas": "0x5000",
"maxFeePerGas": "0x3b9aca00",
"maxPriorityFeePerGas": "0x3b9aca00"
}, "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"] // EntryPoint address
}
// レスポンス
{
"result": {
"paymasterAndData": "0x...",
"preVerificationGas": "0x6000", // 調整された値
"verificationGasLimit": "0x120000", // 調整された値
"callGasLimit": "0x110000" // 調整された値
}
}
// pm_getPaymasterData: 最終データ取得
{
"jsonrpc": "2.0",
"id": 2,
"method": "pm_getPaymasterData",
"params": [{
// 署名済みUserOperation全体
"sender": "0x...",
...
"signature": "0x..."
}, "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"]
}Paymasterコントラクトの実装
interface IPaymaster {
function validatePaymasterUserOp(
UserOperation calldata userOp,
bytes32 userOpHash,
uint256 maxCost
) external returns (bytes memory context, uint256 validationData);
function postOp(
PostOpMode mode,
bytes calldata context,
uint256 actualGasCost
) external;
}
enum PostOpMode {
opSucceeded, // 成功
opReverted, // リバート
postOpReverted // postOp自体がリバート
}スポンサーシップの種類
-
完全スポンサー
- Paymasterがガス代を全額負担します。
- ユーザーは何も支払いません。
-
ERC20トークン支払い
- ユーザーがERC20トークン(USDC等)でガス代を支払います。
- PaymasterがETHに変換して支払います。
-
クレジット制
- ユーザーが事前にクレジットを購入します。
- Paymasterがクレジットから差し引きます。
この仕組みのメリット
- 完全なガスレス体験: ユーザーはガス代を全く意識せずに利用できます。ETHの保有が不要です。
- 柔軟なビジネスモデル: サーバー側でガス代を負担、クレジット購入、ERC20で支払いなど、多様な方式を選択できます。
- 標準化されたAPI: HTTP APIで統一されているため、複数のPaymasterサービスを簡単に切り替えられます。
- ERC-4337との統合: アカウント抽象化の標準と連携し、将来性が高く、エコシステムが成長しています。
x402での活用方法
ERC-7677を使用すると、ユーザーはガス代を意識せずにx402を利用できます。
sequenceDiagram
participant Client as Client
participant Bundler as Bundler (ERC-4337)
participant Paymaster as Paymaster Service
participant EntryPoint as EntryPoint Contract
participant x402Server as x402 API Server
Client->>Client: UserOperationを作成
Client->>Paymaster: pm_getPaymasterStubData(userOp)
Paymaster-->>Client: paymasterData
Client->>Client: UserOpに署名
Client->>Bundler: eth_sendUserOperation(userOp)
Bundler->>Paymaster: pm_getPaymasterData(userOp)
Paymaster->>Paymaster: ガス代負担を承認
Paymaster-->>Bundler: sponsorshipData
Bundler->>EntryPoint: handleOps([userOp])
EntryPoint->>Paymaster: validatePaymasterUserOp(userOp)
Paymaster-->>EntryPoint: 承認
EntryPoint->>x402Server: API実行
x402Server-->>EntryPoint: 成功
Note over Paymaster: Paymasterがガス代を支払う
EntryPoint->>Paymaster: postOp(actualGasCost)
EntryPoint-->>Client: API結果実装例
// クライアント側:PaymasterサービスでUserOperationを作成
async function createSponsoredUserOp(
sender: string,
callData: string
) {
const paymasterUrl = 'https://paymaster.example.com/rpc';
// Step 1: Paymaster stub dataを取得
const stubData = await fetch(paymasterUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'pm_getPaymasterStubData',
params: [{
sender,
nonce: '0x1',
callData,
callGasLimit: '0x100000',
verificationGasLimit: '0x100000',
preVerificationGas: '0x5000',
maxFeePerGas: '0x3b9aca00',
maxPriorityFeePerGas: '0x3b9aca00'
}]
})
});
const { result: paymasterData } = await stubData.json();
// Step 2: UserOperationを作成して署名
const userOp = {
sender,
nonce: '0x1',
callData,
callGasLimit: '0x100000',
verificationGasLimit: '0x100000',
preVerificationGas: '0x5000',
maxFeePerGas: '0x3b9aca00',
maxPriorityFeePerGas: '0x3b9aca00',
...paymasterData,
signature: '0x' // 後で署名
};
// 署名を追加
userOp.signature = await signUserOp(userOp);
// Step 3: Bundlerに送信
const txHash = await fetch(bundlerUrl, {
method: 'POST',
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'eth_sendUserOperation',
params: [userOp, entryPointAddress]
})
});
return txHash;
}
// Paymaster側:ガス代スポンサーシップを実装
contract X402Paymaster is IPaymaster {
mapping(address => uint256) public credits;
// クレジット購入
function purchaseCredits() external payable {
credits[msg.sender] += msg.value;
}
// ガス代を負担するか判定
function validatePaymasterUserOp(
UserOperation calldata userOp,
bytes32 userOpHash,
uint256 maxCost
) external override returns (bytes memory context, uint256 validationData) {
address sender = userOp.sender;
// クレジット残高を確認
require(credits[sender] >= maxCost, 'Insufficient credits');
// クレジットを消費
credits[sender] -= maxCost;
return ('', 0);
}
// ガス代精算
function postOp(
PostOpMode mode,
bytes calldata context,
uint256 actualGasCost
) external override {
// 実際のガス代を記録
// 必要に応じて払い戻し処理
}
}メリット
- 完全なガスレス体験 ユーザーはガス代を全く意識せずに利用できます。
- 柔軟なビジネスモデル サーバー側でガス代を負担、クレジット購入、ERC20で支払いなど、多様な方式を選択できます。
- ERC-4337との統合 アカウント抽象化の標準と連携し、将来性が高いです。
ERC-1337: Recurring Payments(サブスクリプション決済)
「ERC-1337」は、定期支払いをブロックチェーン上で実現する規格です。
主な特徴
- 自動定期支払い ユーザーが一度承認すれば、定期的に自動で支払いが実行されます。
- キャンセル可能 ユーザーはいつでもサブスクリプションをキャンセルできます。
仕組みとフロー
ERC-1337は、定期的な支払いをオンチェーンで管理する仕組みを提供します。
Subscription構造
struct Subscription {
address subscriber; // 支払う人
address provider; // 受け取る人
address token; // 支払うトークン(ERC20)
uint256 amount; // 1回あたりの支払い額
uint256 interval; // 支払い間隔(秒単位)
uint256 lastPayment; // 最後の支払い時刻
uint256 startTime; // サブスク開始時刻
bool active; // アクティブかどうか
}動作フロー
-
サブスクリプション作成
- ユーザーが
subscribe(provider, token, amount, interval)を呼び出します。 - トークンの
approve()で定期支払いを承認します。 - Subscription構造体が作成されます。
- ユーザーが
-
定期支払い実行
- 誰でも
executePayment(subscriptionId)を呼び出せます。 - コントラクトが以下をチェックします:
- サブスクリプションがアクティブか
block.timestamp >= lastPayment + intervalか(支払い時期が来ているか)- ユーザーの残高が十分か
- 条件を満たせば、
transferFrom()で支払いを実行します。 lastPaymentを更新します。
- 誰でも
-
キャンセル
- サブスクライバーが
cancel(subscriptionId)を呼び出します。 activeフラグをfalseに設定します。- 以降、支払いは実行されません。
- サブスクライバーが
実装の詳細
contract SubscriptionManager {
mapping(bytes32 => Subscription) public subscriptions;
event SubscriptionCreated(bytes32 indexed subId, address indexed subscriber, address indexed provider);
event PaymentExecuted(bytes32 indexed subId, uint256 amount, uint256 timestamp);
event SubscriptionCancelled(bytes32 indexed subId);
function subscribe(
address provider,
address token,
uint256 amount,
uint256 interval
) external returns (bytes32) {
bytes32 subId = keccak256(
abi.encodePacked(msg.sender, provider, token, block.timestamp)
);
subscriptions[subId] = Subscription({
subscriber: msg.sender,
provider: provider,
token: token,
amount: amount,
interval: interval,
lastPayment: block.timestamp,
startTime: block.timestamp,
active: true
});
emit SubscriptionCreated(subId, msg.sender, provider);
return subId;
}
function executePayment(bytes32 subId) external {
Subscription storage sub = subscriptions[subId];
require(sub.active, 'Subscription not active');
require(
block.timestamp >= sub.lastPayment + sub.interval,
'Too early'
);
// トークン送金
IERC20(sub.token).transferFrom(
sub.subscriber,
sub.provider,
sub.amount
);
sub.lastPayment = block.timestamp;
emit PaymentExecuted(subId, sub.amount, block.timestamp);
}
function cancel(bytes32 subId) external {
Subscription storage sub = subscriptions[subId];
require(sub.subscriber == msg.sender, 'Not subscriber');
sub.active = false;
emit SubscriptionCancelled(subId);
}
// サブスク情報を取得
function getSubscription(bytes32 subId) external view returns (Subscription memory) {
return subscriptions[subId];
}
// 次の支払い期日を取得
function getNextPaymentDue(bytes32 subId) external view returns (uint256) {
Subscription memory sub = subscriptions[subId];
return sub.lastPayment + sub.interval;
}
}自動実行の仕組み
ブロックチェーンには自動実行の仕組みがないため、以下の方法で定期支払いを実現します:
-
Keeper Network(Chainlink Automation等)
- オフチェーンのKeeperが定期的にチェックします。
- 支払い時期が来ていれば
executePayment()を実行します。
-
インセンティブ付き実行
- 誰でも
executePayment()を呼び出せるようにします。 - 実行者に手数料を支払う仕組みを追加します。
- 誰でも
function executePaymentWithReward(bytes32 subId) external {
Subscription storage sub = subscriptions[subId];
// 通常の支払い処理
IERC20(sub.token).transferFrom(sub.subscriber, sub.provider, sub.amount);
// 実行者に報酬を支払う
uint256 reward = sub.amount / 100; // 1%の報酬
IERC20(sub.token).transferFrom(sub.subscriber, msg.sender, reward);
sub.lastPayment = block.timestamp;
}この仕組みのメリット
- SaaSモデルの実現: APIの月額課金やサブスクリプションモデルを実装できます。従来のWeb2サービスと同様の収益モデルをWeb3で実現できます。
- 自動化: ユーザーは毎月手動で支払う必要がなくなります。一度承認すれば、自動的に支払いが続きます。
- 透明性: すべての支払い履歴がオンチェーンに記録されます。いつでも確認できます。
- ユーザーの管理権: ユーザーはいつでもサブスクリプションをキャンセルできます。プロバイダーの同意は不要です。
x402での活用方法
APIの月額利用料や定期課金モデルを実装できます。
// サブスクリプション契約
contract X402Subscription {
struct Subscription {
address subscriber;
address provider;
uint256 amount;
uint256 interval; // 支払い間隔(秒)
uint256 lastPayment;
bool active;
}
mapping(bytes32 => Subscription) public subscriptions;
// サブスクリプション作成
function subscribe(
address provider,
uint256 amount,
uint256 interval
) external {
bytes32 subId = keccak256(
abi.encodePacked(msg.sender, provider, block.timestamp)
);
subscriptions[subId] = Subscription({
subscriber: msg.sender,
provider: provider,
amount: amount,
interval: interval,
lastPayment: block.timestamp,
active: true
});
}
// 定期支払い実行
function executePayment(bytes32 subId) external {
Subscription storage sub = subscriptions[subId];
require(sub.active, 'Subscription not active');
require(
block.timestamp >= sub.lastPayment + sub.interval,
'Too early'
);
// トークン送金
IERC20(usdcToken).transferFrom(sub.subscriber, sub.provider, sub.amount);
sub.lastPayment = block.timestamp;
}
// キャンセル
function cancel(bytes32 subId) external {
require(subscriptions[subId].subscriber == msg.sender, 'Not subscriber');
subscriptions[subId].active = false;
}
}メリット
- SaaSモデルの実現 APIの月額課金やサブスクリプションモデルを実装できます。
- 自動化 ユーザーは毎月手動で支払う必要がなくなります。
ERC-7857: Intelligent NFTs for AI Agents
「ERC-7857」は、AIエージェント専用のNFT規格です。
主な特徴
- AIモデルの所有権 AIエージェントの知能、パーソナリティ、学習データをNFTとして表現します。
- 暗号化されたデータ保存 プライバシーを保護しながら、AIの状態を保存できます。
- 譲渡可能 AIエージェントをNFTとして売買・譲渡できます。
仕組みとフロー
ERC-7857は、AIエージェントの「知能」をNFTとして所有・取引可能にする規格です。
iNFT(Intelligent NFT)のメタデータ構造
{
"name": "AI Agent #123",
"description": "GPT-4ベースの画像生成エージェント",
"image": "ipfs://QmXxx.../avatar.png",
"intelligence": {
"modelType": "gpt-4",
"modelUri": "ipfs://QmYyyy.../model.enc",
"encryption": "AES-256-GCM",
"decryptionKey": "encrypted_with_owner_pubkey",
"capabilities": [
"image-generation",
"text-to-image",
"style-transfer"
],
"personality": {
"tone": "professional",
"language": "en",
"customization": "ipfs://QmZzzz.../personality.json"
},
"trainingData": {
"dataset": "private-dataset-v1",
"fineTuneUri": "ipfs://QmWwww.../finetune.enc",
"version": "1.2.3"
}
},
"performance": {
"accuracy": 0.95,
"speed": "fast",
"costPerRequest": "0.01 USDC"
}
}暗号化とアクセス制御
-
オーナーのみがアクセス可能
- AIモデルの重みは暗号化されてIPFSに保存されます。
- 復号鍵はNFTオーナーの公開鍵で暗号化されています。
- オーナーだけが秘密鍵で復号鍵を取得し、モデルを使用できます。
-
譲渡時の処理
- NFTが譲渡されると、復号鍵を新しいオーナーの公開鍵で再暗号化します。
- 元のオーナーはモデルにアクセスできなくなります。
インターフェース定義
interface IERC7857 {
// iNFTをミント
function mintAgent(
address to,
string memory modelUri,
bytes memory encryptedKey,
string memory metadata
) external returns (uint256 tokenId);
// モデルURIを取得
function getModelUri(uint256 tokenId) external view returns (string memory);
// 暗号化された復号鍵を取得
function getEncryptedKey(uint256 tokenId) external view returns (bytes memory);
// オーナーのみ:復号鍵を更新(譲渡時)
function updateEncryptedKey(uint256 tokenId, bytes memory newEncryptedKey) external;
}ライフサイクル
-
作成(Mint)
- AIモデルを訓練します。
- モデルを暗号化してIPFSにアップロードします。
- 復号鍵をオーナーの公開鍵で暗号化します。
- iNFTをミントします。
-
使用(Usage)
- NFTオーナーが暗号化鍵を復号します。
- モデルをIPFSから取得して復号します。
- AIエージェントとして実行します。
-
譲渡(Transfer)
- NFTを新しいオーナーに転送します。
_afterTokenTransferフックで復号鍵を新オーナーの公開鍵で再暗号化します。
実装例
contract IntelligentNFT is ERC721, IERC7857 {
struct AgentData {
string modelUri; // 暗号化モデルのURI
bytes encryptedKey; // 復号鍵(オーナーの公開鍵で暗号化)
string metadata; // メタデータURI
uint256 createdAt; // 作成日時
}
mapping(uint256 => AgentData) private _agents;
uint256 private _tokenIdCounter;
function mintAgent(
address to,
string memory modelUri,
bytes memory encryptedKey,
string memory metadata
) external override returns (uint256) {
uint256 tokenId = _tokenIdCounter++;
_agents[tokenId] = AgentData({
modelUri: modelUri,
encryptedKey: encryptedKey,
metadata: metadata,
createdAt: block.timestamp
});
_safeMint(to, tokenId);
return tokenId;
}
function getModelUri(uint256 tokenId) external view override returns (string memory) {
require(_exists(tokenId), 'Token does not exist');
return _agents[tokenId].modelUri;
}
function getEncryptedKey(uint256 tokenId) external view override returns (bytes memory) {
require(ownerOf(tokenId) == msg.sender, 'Not owner');
return _agents[tokenId].encryptedKey;
}
function updateEncryptedKey(uint256 tokenId, bytes memory newEncryptedKey) external override {
require(ownerOf(tokenId) == msg.sender, 'Not owner');
_agents[tokenId].encryptedKey = newEncryptedKey;
}
}この仕組みのメリット
- AIの所有権を明確化: AIエージェントの知能を所有・譲渡可能な資産として扱えます。独自に訓練したモデルを保護できます。
- プライバシー保護: 暗号化により、モデルの重みやトレーニングデータが漏洩しません。オーナーのみがアクセスできます。
- マーケットプレイス: 高性能なAIエージェントをNFTマーケットプレイスで売買できます。AI開発者の収益化を支援します。
- x402との統合: AIエージェントのIDとしてNFTを使用し、支払い時に提示することで信頼性を証明できます。
x402での活用方法
AIエージェントのアイデンティティとして使用し、x402決済時に提示します。
// AIエージェントNFTをミント
async function mintIntelligentNFT(
agentMetadata: {
name: string;
modelUri: string; // 暗号化されたAIモデルのURI
capabilities: string[];
}
) {
const nftContract = '0x...'; // ERC-7857コントラクト
const txHash = await walletClient.writeContract({
address: nftContract,
abi: erc7857ABI,
functionName: 'mintAgent',
args: [
agentMetadata.name,
agentMetadata.modelUri,
JSON.stringify(agentMetadata.capabilities)
]
});
return txHash;
}ERC-6551: Token Bound Accounts(NFTウォレット)
「ERC-6551」は、NFTが独自のウォレットを持つことを可能にする規格です。
主な特徴
- NFTがウォレットを持つ 各NFTに固有のEthereumアドレスが割り当てられます。
- 資産の所有 NFT自体がETH、ERC20、他のNFTなどを保有できます。
- 取引履歴 NFTのオンチェーン履歴が記録されます。
仕組みとフロー
ERC-6551は、各NFTに専用のスマートコントラクトウォレット(Token Bound Account)を紐付ける仕組みです。
アーキテクチャ
ERC-721 NFT → Token Bound Account (TBA) → 資産(ETH, ERC20, NFT等)主要コンポーネント
-
Registry(レジストリ)
- すべてのToken Bound Accountを管理します。
createAccount()関数でTBAを作成します。account()関数で既存TBAのアドレスを計算します。
-
Implementation(実装コントラクト)
- TBAの実際のロジックを実装します。
execute()関数でトランザクションを実行します。- NFTオーナーのみが操作できます。
-
Token Bound Account(TBA)
- NFTごとに一意のアドレスを持つプロキシコントラクトです。
- 資産を保有できます。
- NFTオーナーが制御します。
アドレス計算方法
TBAのアドレスは、以下のパラメータから決定論的に計算されます:
function account(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
) public view returns (address) {
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(
abi.encodePacked(
implementation,
chainId,
tokenContract,
tokenId
)
)
)
);
return address(uint160(uint256(hash)));
}この計算により、同じNFTに対して常に同じTBAアドレスが生成されます。
ライフサイクル
-
TBA作成
Registry.createAccount()を呼び出します。- ERC-1167(Minimal Proxy)を使用してImplementationのプロキシを作成します。
- プロキシコントラクトがデプロイされます。
-
資産の入金
- 誰でもTBAアドレスにETHやトークンを送信できます。
- TBAが資産を保有します。
-
トランザクション実行
- NFTオーナーが
TBA.execute(to, value, data)を呼び出します。 - TBAが指定されたコントラクトを呼び出します。
- ガス代はNFTオーナーが支払います。
- NFTオーナーが
-
NFT譲渡
- NFTが譲渡されると、TBAの制御権も新オーナーに移ります。
- 資産もすべて新オーナーのものになります。
Registry実装例
contract ERC6551Registry {
event AccountCreated(
address account,
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
);
function createAccount(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt,
bytes calldata initData
) external returns (address) {
bytes memory code = _creationCode(implementation, chainId, tokenContract, tokenId, salt);
address _account = Create2.computeAddress(
bytes32(salt),
keccak256(code)
);
if (_account.code.length != 0) {
return _account;
}
_account = Create2.deploy(0, bytes32(salt), code);
if (initData.length != 0) {
(bool success, ) = _account.call(initData);
require(success, 'Initialization failed');
}
emit AccountCreated(_account, implementation, chainId, tokenContract, tokenId, salt);
return _account;
}
function account(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
) external view returns (address) {
bytes32 bytecodeHash = keccak256(
_creationCode(implementation, chainId, tokenContract, tokenId, salt)
);
return Create2.computeAddress(bytes32(salt), bytecodeHash);
}
}Implementation実装例
contract TokenBoundAccount {
function execute(
address to,
uint256 value,
bytes calldata data,
uint8 operation
) external payable returns (bytes memory) {
require(_isValidSigner(msg.sender), 'Invalid signer');
if (operation == 0) {
// Call
(bool success, bytes memory result) = to.call{value: value}(data);
require(success, 'Call failed');
return result;
} else if (operation == 1) {
// DelegateCall
(bool success, bytes memory result) = to.delegatecall(data);
require(success, 'Delegatecall failed');
return result;
}
revert('Invalid operation');
}
function _isValidSigner(address signer) internal view returns (bool) {
// NFTのオーナーを取得
(uint256 chainId, address tokenContract, uint256 tokenId) = _token();
if (chainId != block.chainid) return false;
return IERC721(tokenContract).ownerOf(tokenId) == signer;
}
function _token() internal view returns (uint256, address, uint256) {
bytes memory footer = new bytes(0x60);
assembly {
extcodecopy(address(), add(footer, 0x20), 0x4d, 0x60)
}
return abi.decode(footer, (uint256, address, uint256));
}
}この仕組みのメリット
- NFTが資産を保有: NFT自体がウォレットとして機能し、ETHやERC20トークン、他のNFTを保有できます。ゲーム内アイテムや収益を直接NFTに紐付けられます。
- 統一された所有権: NFTとその資産が一体化します。NFTを譲渡すれば、すべての資産も一緒に譲渡されます。
- 取引履歴の記録: TBAのトランザクション履歴がオンチェーンに記録されます。NFTの「歴史」を追跡できます。
- 後方互換性: 既存のERC-721 NFTにも適用できます。新しいNFTコントラクトを作成する必要はありません。
ERC-7857 + ERC-6551: AI Agent NFTとウォレットの統合
ERC-7857(AI Agent NFT)とERC-6551(Token Bound Account)を組み合わせることで、AIエージェントが独自のウォレットを持ち、自律的に決済できるようになります。
sequenceDiagram
participant Owner as オーナー
participant AgentNFT as AI Agent NFT (ERC-7857)
participant TBA as Token Bound Account (ERC-6551)
participant x402API as x402 API
participant USDC as USDC Contract
Note over Owner,AgentNFT: AI Agent NFTをミント
Owner->>AgentNFT: mintAgent(metadata)
AgentNFT-->>Owner: Agent NFT #123
Note over Owner,TBA: Token Bound Accountを作成
Owner->>TBA: createAccount(AgentNFT, #123)
TBA-->>Owner: TBA Address: 0xABC...
Note over Owner,TBA: TBAにUSDCを送金
Owner->>USDC: transfer(TBA, 10 USDC)
Note over AgentNFT,x402API: AIエージェントが自律的に支払い
AgentNFT->>x402API: GET /api/generate-image
x402API-->>AgentNFT: 402 (amount: 0.1 USDC)
AgentNFT->>TBA: executePayment(x402API, 0.1 USDC)
TBA->>USDC: transfer(x402API, 0.1 USDC)
USDC-->>TBA: 成功
TBA->>x402API: POST /api/generate-image
x402API-->>TBA: 画像データ
TBA-->>AgentNFT: 画像データ実装例
// Token Bound Accountを作成
async function createTokenBoundAccount(
nftContract: string,
tokenId: bigint
) {
const registryAddress = '0x...'; // ERC-6551 Registry
const txHash = await walletClient.writeContract({
address: registryAddress,
abi: erc6551RegistryABI,
functionName: 'createAccount',
args: [
implementationAddress, // TBA実装コントラクト
8453n, // chainId (Base)
nftContract,
tokenId,
0n, // salt
'0x' // initData
]
});
// TBAアドレスを計算
const tbaAddress = await registryContract.read.account([
implementationAddress,
8453n,
nftContract,
tokenId,
0n
]);
return tbaAddress;
}
// AIエージェントNFT + TBAでx402決済
class AIAgentWithWallet {
private nftId: bigint;
private tbaAddress: string;
async payAndExecuteAPI(apiUrl: string, params: any) {
// Step 1: APIに価格を問い合わせ
const response = await fetch(apiUrl, {
method: 'POST',
body: JSON.stringify(params)
});
if (response.status === 402) {
const paymentInfo = await response.json();
// Step 2: Token Bound Accountから支払い
const txHash = await publicClient.writeContract({
address: this.tbaAddress,
abi: tbaABI,
functionName: 'execute',
args: [
usdcAddress,
0n,
encodeFunctionData({
abi: erc20ABI,
functionName: 'transfer',
args: [paymentInfo.recipient, parseUnits(paymentInfo.amount, 6)]
}),
0 // call operation
],
account: this.getNFTOwner() // NFTオーナーが署名
});
// Step 3: 支払い証明を提出してAPI実行
const apiResponse = await fetch(apiUrl, {
method: 'POST',
headers: {
'X-Payment-Hash': txHash,
'X-Agent-NFT': this.nftId.toString()
},
body: JSON.stringify(params)
});
return await apiResponse.json();
}
}
}メリット
- AIエージェントのアイデンティティと資産を統合 NFTがアイデンティティ、TBAがウォレットとして機能します。
- オーナーによる管理 NFTオーナーがエージェントの資産と行動を制御できます。
- 取引履歴の記録 エージェントの全ての取引がオンチェーンで記録されます。
ERC-7579: Modular Smart Accounts(モジュラー拡張)
「ERC-7579」は、モジュラーなスマートアカウントの標準規格です。
主な特徴
- プラグイン可能なモジュール Validator、Executor、Fallback Handlerなどのモジュールを追加できます。
- 相互運用性 異なるプロバイダー間でモジュールを共有できます。
- ERC-4337互換 アカウント抽象化と完全に統合されています。
仕組みとフロー
ERC-7579は、スマートアカウントにプラグイン形式でモジュールを追加・削除できる標準規格です。
モジュールタイプ
ERC-7579では3種類のモジュールタイプが定義されています:
-
Validator(検証モジュール)
- UserOperationの署名を検証します。
- 複数の検証方法をサポートできます(ECDSA、Passkey、マルチシグ等)。
validateUserOp()関数を実装します。
-
Executor(実行モジュール)
- スマートアカウントに代わってトランザクションを実行します。
- 自動化されたアクションやバッチ処理に使用します。
execute()関数を実装します。
-
Fallback Handler(フォールバックハンドラー)
- スマートアカウントが対応していない関数呼び出しを処理します。
- 新しい機能を追加できます。
fallback()/receive()関数を実装します。
インターフェース定義
interface IERC7579Account {
function installModule(
uint256 moduleTypeId,
address module,
bytes calldata initData
) external;
function uninstallModule(
uint256 moduleTypeId,
address module,
bytes calldata deInitData
) external;
function isModuleInstalled(
uint256 moduleTypeId,
address module,
bytes calldata additionalContext
) external view returns (bool);
function execute(
address target,
uint256 value,
bytes calldata data
) external;
function executeBatch(
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata datas
) external;
}
interface IModule {
function onInstall(bytes calldata data) external;
function onUninstall(bytes calldata data) external;
function isModuleType(uint256 typeId) external view returns (bool);
function isInitialized(address smartAccount) external view returns (bool);
}モジュールのライフサイクル
-
インストール
installModule(moduleTypeId, moduleAddress, initData)を呼び出します。- モジュールの
onInstall(initData)が実行されます。 - アカウントの状態にモジュールが登録されます。
-
使用
- Validatorモジュール: UserOperation検証時に呼び出されます。
- Executorモジュール: アカウントが
execute()を呼び出します。 - Fallbackモジュール: 未定義の関数呼び出し時に実行されます。
-
アンインストール
uninstallModule(moduleTypeId, moduleAddress, deInitData)を呼び出します。- モジュールの
onUninstall(deInitData)が実行されます。 - クリーンアップ処理が行われます。
Validatorモジュールの実装例
contract ECDSAValidator is IValidator {
mapping(address => address) public owners;
function onInstall(bytes calldata data) external override {
address owner = abi.decode(data, (address));
owners[msg.sender] = owner;
}
function onUninstall(bytes calldata data) external override {
delete owners[msg.sender];
}
function validateUserOp(
UserOperation calldata userOp,
bytes32 userOpHash
) external view override returns (uint256 validationData) {
address owner = owners[msg.sender];
bytes32 hash = userOpHash.toEthSignedMessageHash();
address recovered = hash.recover(userOp.signature);
if (recovered == owner) {
return 0; // 成功
}
return 1; // 失敗
}
function isModuleType(uint256 typeId) external pure override returns (bool) {
return typeId == 1; // Validator type
}
}Executorモジュールの実装例
contract ScheduledExecutor is IExecutor {
struct ScheduledTask {
address target;
uint256 value;
bytes data;
uint256 executeAfter;
bool executed;
}
mapping(address => ScheduledTask[]) public tasks;
function scheduleTask(
address target,
uint256 value,
bytes calldata data,
uint256 delay
) external {
tasks[msg.sender].push(ScheduledTask({
target: target,
value: value,
data: data,
executeAfter: block.timestamp + delay,
executed: false
}));
}
function executeScheduledTask(uint256 taskId) external {
ScheduledTask storage task = tasks[msg.sender][taskId];
require(!task.executed, 'Already executed');
require(block.timestamp >= task.executeAfter, 'Too early');
task.executed = true;
IERC7579Account(msg.sender).execute(task.target, task.value, task.data);
}
function isModuleType(uint256 typeId) external pure override returns (bool) {
return typeId == 2; // Executor type
}
}モジュールの組み合わせ例
// スマートアカウントに複数のモジュールをインストール
smartAccount.installModule(1, ecdsaValidator, abi.encode(ownerAddress));
smartAccount.installModule(1, passkeyValidator, abi.encode(passkeyData));
smartAccount.installModule(2, scheduledExecutor, '');
smartAccount.installModule(2, x402PaymentExecutor, '');
smartAccount.installModule(3, erc1271Fallback, '');この仕組みのメリット
- 機能の柔軟な拡張: x402決済機能、定期支払い、マルチシグなど、必要な機能をモジュールとして追加できます。既存のスマートアカウントを変更せずに拡張できます。
- 相互運用性: 異なるプロバイダー(Safe、Biconomy、ZeroDev等)のモジュールを共有できます。一度開発したモジュールを複数のスマートアカウントで使用できます。
- アップグレード可能: モジュールの追加・削除が容易です。セキュリティ脆弱性が見つかったモジュールを即座に削除し、新しいバージョンをインストールできます。
- 責任分離: 各モジュールが特定の機能のみを担当します。コードの保守性と監査性が向上します。
x402での活用方法
AIエージェントのスマートアカウントに、x402決済モジュールを追加できます。
// x402決済モジュール
contract X402PaymentModule is IExecutor {
function executeX402Payment(
address apiServer,
uint256 amount,
bytes calldata apiParams
) external returns (bytes memory) {
// USDCを送金
IERC20(usdcToken).transfer(apiServer, amount);
// APIを呼び出し
(bool success, bytes memory result) = apiServer.call(apiParams);
require(success, 'API call failed');
return result;
}
// ERC-7579のExecutorインターフェースを実装
function onInstall(bytes calldata data) external override {}
function onUninstall(bytes calldata data) external override {}
function isModuleType(uint256 typeId) external pure override returns (bool) {
return typeId == 2; // Executor module
}
}
// モジュールをインストール
async function installX402Module(
smartAccountAddress: string,
moduleAddress: string
) {
const txHash = await walletClient.writeContract({
address: smartAccountAddress,
abi: erc7579ABI,
functionName: 'installModule',
args: [
2n, // Executor module type
moduleAddress,
'0x' // init data
]
});
return txHash;
}メリット
- 機能の柔軟な拡張 x402決済機能をモジュールとして追加できます。
- アップグレード可能 モジュールの追加・削除が容易です。
ERC-5169: Client Script URI(miniDApp統合)
「ERC-5169」は、トークンにクライアントスクリプトを紐付ける規格です。
主な特徴
- scriptURI関数 トークンコントラクトからクライアントスクリプトのURIを取得できます。
- miniDApp トークン専用の小規模DAppをホストできます。
- TokenScript ウォレットでトークン固有の操作UIを提供できます。
仕組みとフロー
ERC-5169は、トークンコントラクトにクライアント側のスクリプト(JavaScript、HTML等)を紐付け、ウォレットやDAppで実行できるようにする規格です。
インターフェース定義
interface IERC5169 {
event ScriptUpdate(string[] newScriptURI);
function scriptURI() external view returns (string[] memory);
function setScriptURI(string[] memory newScriptURI) external;
}scriptURIの形式
// 複数のスクリプトURIを返せる
[
"ipfs://QmXxxx.../script.js", // IPFS
"https://example.com/token-script.js", // HTTP/HTTPS
"ar://abcd1234.../app.html" // Arweave
]動作フロー
-
スクリプトの登録
- トークンコントラクトの所有者が
setScriptURI()を呼び出します。 - スクリプトのURIリストを設定します。
ScriptUpdateイベントが発火します。
- トークンコントラクトの所有者が
-
ウォレット側での取得
- ウォレットがトークンを検出します。
scriptURI()を呼び出してスクリプトURIを取得します。- URIからスクリプトをダウンロードします。
-
実行とレンダリング
- スクリプトを検証します(ハッシュ検証、署名検証等)。
- サンドボックス環境で実行します。
- トークン固有のUIを表示します。
セキュリティ検証
スクリプトの安全性を確保するため、以下の方法が推奨されます:
-
不変URIの使用
- IPFS等のコンテンツアドレス可能なストレージを使用します。
- URIにコンテンツハッシュが含まれるため、改ざんを検出できます。
-
署名検証(EIP-3668準拠)
- スクリプト提供者がスクリプトに署名します。
- ウォレットが署名を検証してから実行します。
-
サンドボックス実行
- iframe内で実行し、親ページへのアクセスを制限します。
- Content Security Policy(CSP)を適用します。
miniDAppの実装例
contract X402APIToken is ERC20, IERC5169 {
string[] private _scriptURIs;
event ScriptUpdate(string[] newScriptURI);
constructor() ERC20('X402 API Credit', 'X402') {
_scriptURIs = new string[](2);
_scriptURIs[0] = 'ipfs://QmXxxx.../x402-ui.html';
_scriptURIs[1] = 'ipfs://QmYyyy.../x402-script.js';
}
function scriptURI() external view override returns (string[] memory) {
return _scriptURIs;
}
function setScriptURI(string[] memory newScriptURI) external override onlyOwner {
_scriptURIs = newScriptURI;
emit ScriptUpdate(newScriptURI);
}
}クライアント側の実装
// ウォレット側:scriptURIを取得してスクリプトを読み込む
async function loadTokenScript(tokenAddress: string) {
// Step 1: scriptURIを取得
const scriptURIs = await publicClient.readContract({
address: tokenAddress,
abi: erc5169ABI,
functionName: 'scriptURI'
});
if (!scriptURIs || scriptURIs.length === 0) {
console.log('No scripts available for this token');
return;
}
// Step 2: IPFSからスクリプトを取得
const response = await fetch(scriptURIs[0]);
const scriptContent = await response.text();
// Step 3: セキュリティ検証
const isSecure = await verifyScriptSecurity(scriptContent, scriptURIs[0]);
if (!isSecure) {
console.error('Script security verification failed');
return;
}
// Step 4: サンドボックス内で実行
const iframe = document.createElement('iframe');
iframe.sandbox = 'allow-scripts allow-same-origin';
iframe.srcdoc = scriptContent;
document.body.appendChild(iframe);
}
// セキュリティ検証
async function verifyScriptSecurity(
content: string,
uri: string
): Promise<boolean> {
// IPFS URIの場合、ハッシュを検証
if (uri.startsWith('ipfs://')) {
const expectedHash = uri.replace('ipfs://', '').split('/')[0];
const actualHash = await calculateHash(content);
return expectedHash === actualHash;
}
// HTTPS URIの場合、署名を検証(EIP-3668準拠)
// ...
return true;
}TokenScriptの例
<!-- x402-ui.html -->
<!DOCTYPE html>
<html>
<head>
<title>X402 API Credit</title>
<script src="https://cdn.jsdelivr.net/npm/viem"></script>
</head>
<body>
<h1>X402 API Credit Manager</h1>
<div>
<h2>Balance</h2>
<p id="balance">Loading...</p>
</div>
<div>
<h2>Purchase Credits</h2>
<input type="number" id="amount" placeholder="Amount (USDC)">
<button onclick="purchaseCredits()">Buy</button>
</div>
<script>
// トークンコントラクトのアドレスを取得
const tokenAddress = window.parent.tokenAddress;
// 残高を取得
async function loadBalance() {
const balance = await readContract({
address: tokenAddress,
abi: erc20ABI,
functionName: 'balanceOf',
args: [window.ethereum.selectedAddress]
});
document.getElementById('balance').textContent = balance.toString();
}
// クレジットを購入
async function purchaseCredits() {
const amount = document.getElementById('amount').value;
await writeContract({
address: tokenAddress,
abi: x402ABI,
functionName: 'purchaseCredits',
args: [parseUnits(amount, 6)]
});
await loadBalance();
}
loadBalance();
</script>
</body>
</html>この仕組みのメリット
- トークン固有のUI: x402 API利用のための専用UIを提供できます。ウォレット内で直接API購入や管理が可能になります。
- ウォレット統合: メジャーなウォレット(MetaMask、Trust Wallet等)がERC-5169に対応すれば、トークンを表示するだけで自動的にUIが表示されます。
- 動的な機能追加:
setScriptURI()でスクリプトを更新すれば、トークンの機能を動的に拡張できます。コントラクトのアップグレードは不要です。 - ユーザー体験の向上: 技術的な知識がなくても、視覚的なUIでトークンを操作できます。Web3の利用障壁を下げます。
x402での活用方法
x402 APIトークンにminiDAppを紐付けて、ユーザーフレンドリーなUIを提供できます。
// x402 APIトークンにscriptURIを実装
contract X402APIToken is ERC20, IERC5169 {
string private _scriptURI;
constructor() ERC20('X402 API Credit', 'X402') {
_scriptURI = 'ipfs://QmXxxxxxx'; // miniDAppのIPFS URI
}
function scriptURI() external view override returns (string[] memory) {
string[] memory uris = new string[](1);
uris[0] = _scriptURI;
return uris;
}
function setScriptURI(string[] memory newScriptURI) external onlyOwner {
_scriptURI = newScriptURI[0];
}
}
// クライアント側:scriptURIからminiDAppを読み込み
async function loadMiniDApp(tokenAddress: string) {
const scriptURIs = await publicClient.readContract({
address: tokenAddress,
abi: erc5169ABI,
functionName: 'scriptURI'
});
// IPFSからminiDAppを読み込み
const response = await fetch(scriptURIs[0]);
const miniDAppCode = await response.text();
// miniDAppを実行(セキュリティ検証後)
// ...
}メリット
- トークン固有のUI x402 API利用のための専用UIを提供できます。
- ウォレット統合 ウォレット内でAPI利用インターフェースを表示できます。
その他の関連ERC規格
ERC-4337: Account Abstraction
スマートコントラクトウォレットを使用して、より柔軟な決済フローを実現できます。 バッチ処理やガスレス取引など、高度な機能を提供します。
ERC-1271: Standard Signature Validation
スマートコントラクトウォレットが署名を検証できるようにします。 マルチシグウォレットやDAOによる支払い承認に活用できます。
ユースケース
「x402」の具体的なユースケースについて説明します。
リクエストごとのAPI課金
従来のAPIサービスでは、月額固定料金や従量課金プランが一般的です。 「x402」を使用すると、リクエストごとに課金できます。
例えば、画像生成APIで1リクエストあたり10円を課金できます。 ユーザーは必要な分だけ支払うことができ、サービス提供者は柔軟な価格設定が可能になります。
AIエージェントの自律的決済
AIエージェントが複数のAPIを呼び出して作業を行う場合、従来は人間が事前にAPI keyを取得して設定する必要がありました。
「x402」を使用すると、AIエージェントが自律的に必要なAPIを見つけ、支払いを行い、サービスを利用できます。 アカウント作成やAPI key管理が不要になります。
コンテンツペイウォール
記事や動画などのコンテンツに対して、読者が閲覧するたびに課金できます。
従来のサブスクリプションモデルでは、ユーザーが読みたい記事が1つだけでも月額料金を支払う必要がありました。 「x402」では、記事ごとに少額の支払いを実現できます。
マイクロサービスの収益化
マイクロサービスアーキテクチャにおいて、サービス間の呼び出しに対して課金できます。
社内の複数チームがAPIを提供し合う場合、リソースの利用状況を可視化し、コストを適切に配分できます。
API機能の再販・プロキシサービス
既存のAPIをラップして、付加価値を提供するプロキシサービスを構築できます。
例えば、複数のAI APIを統合したサービスを提供し、リクエストごとに課金できます。 プロキシ側が「x402」で支払いを受け取り、バックエンドのAPIに支払いを転送する仕組みを実現できます。
AI Agentを活用した実装アイデア
「x402」とERC-8004を組み合わせることで、AIエージェントが自律的に決済を行うエコシステムを構築できます。
ここでは、実際の実装アイデアとコード例を紹介します。
自律的なAPI利用エージェント
AIエージェントが自律的にAPIを発見し、支払いを行い、タスクを完了する仕組みです。
ユースケース
- 複数のAI APIを呼び出して、データ分析レポートを作成
- 画像生成APIと翻訳APIを組み合わせて、多言語対応のビジュアルコンテンツを作成
- 天気予報APIと交通情報APIを使用して、最適な移動ルートを提案
実装の流れ
sequenceDiagram
participant User as ユーザー
participant AIAgent as AI Agent
participant IdentityReg as Identity Registry
participant x402API as x402 API (画像生成)
participant USDC as USDC Contract
participant ReputationReg as Reputation Registry
User->>AIAgent: タスク依頼「猫の画像を生成して」
Note over AIAgent,IdentityReg: 初回のみ:エージェント登録
AIAgent->>IdentityReg: registerAgent()
IdentityReg-->>AIAgent: Agent NFT #456
Note over AIAgent,x402API: API発見と価格確認
AIAgent->>x402API: GET /api/generate-image
x402API-->>AIAgent: 402 Payment Required (amount: 0.1 USDC)
Note over AIAgent: 予算チェック
AIAgent->>AIAgent: タスク価値評価:0.1 USDC
AIAgent->>AIAgent: 予算確認:OK
Note over AIAgent,USDC: ガスレス決済(ERC-3009)
AIAgent->>AIAgent: EIP-712署名作成
AIAgent->>x402API: POST /api/generate-image<br/>+ Payment Signature + Agent ID #456
Note over x402API: エージェント検証
x402API->>IdentityReg: verifyAgent(#456)
IdentityReg-->>x402API: 有効
x402API->>ReputationReg: getReputation(#456)
ReputationReg-->>x402API: Score: 85/100
Note over x402API,USDC: 決済実行
x402API->>USDC: transferWithAuthorization(signature)
USDC-->>x402API: 成功
Note over x402API: 画像生成
x402API->>x402API: 画像生成処理
x402API-->>AIAgent: 画像データ
Note over AIAgent,ReputationReg: レピュテーション更新
x402API->>ReputationReg: recordCompletion(#456, task_id, true)
ReputationReg->>ReputationReg: updateScore(#456, +1)
AIAgent-->>User: 生成した猫の画像シーケンス図の詳細解説
このシーケンス図は、AIエージェントがx402プロトコルを使用して自律的にAPIを利用する全体の流れを示しています。
1. エージェント登録フェーズ(初回のみ)
AIエージェントは、最初にIdentity Registryにエージェントとして登録します。
registerAgent() を呼び出すと、エージェントを識別するためのNFT(この例では #456)が発行されます。
このNFTは、エージェントのアイデンティティを証明するために使用されます。
2. API発見と価格確認フェーズ
AIエージェントは、タスクを実行するためにAPIエンドポイント(GET /api/generate-image)にリクエストします。
APIは 402 Payment Required ステータスコードを返し、必要な支払い金額(0.1 USDC)を通知します。
この仕組みにより、エージェントは事前にコストを把握できます。
3. 予算チェックフェーズ
エージェントは、タスクの価値を評価し、事前に設定された予算内でタスクを実行できるかチェックします。 予算が不足している場合、タスクは中止されます。 この自律的な判断により、エージェントは予算オーバーを防ぎます。
4. ガスレス決済フェーズ
エージェントは、ERC-3009(transferWithAuthorization)を使用してガスレス決済を実行します。
EIP-712形式の署名を作成し、APIリクエストと共に送信します。
この署名には、支払い金額、受取人アドレス、エージェントID、有効期限などが含まれます。
5. エージェント検証フェーズ
API側は、受け取ったエージェントID(#456)の有効性をIdentity Registryで確認します。 同時に、Reputation Registryからエージェントの評価スコア(この例では85/100)を取得します。 スコアが低いエージェントは、APIの利用を拒否される場合があります。
6. 決済実行フェーズ
APIは、エージェントから受け取った署名を使用して、USDC Contractの transferWithAuthorization() を呼び出します。
署名が有効であれば、エージェントのウォレットからAPIの受取アドレスへUSDCが転送されます。
この処理は、エージェント自身がトランザクションを送信する必要がないため、ガスレスで実行されます。
7. タスク実行フェーズ
決済が完了すると、APIは画像生成処理を実行し、結果をエージェントに返します。
8. レピュテーション更新フェーズ
タスクが正常に完了すると、APIはReputation Registryに完了を記録します。 エージェント #456 のスコアが +1 されます。 このスコアは、将来のAPI利用時の信頼性指標として使用されます。
マルチエージェント協調システム
複数のAIエージェントが役割分担して、大規模なタスクを遂行する仕組みです。
ユースケース
- 翻訳エージェント、校正エージェント、レイアウトエージェントが協力して多言語ドキュメントを作成
- データ収集エージェント、分析エージェント、レポート生成エージェントが連携してビジネスレポートを作成
- 音声認識エージェント、要約エージェント、質問応答エージェントが協調して会議の議事録を作成
実装の流れ
sequenceDiagram
participant User as ユーザー
participant CoordinatorAgent as コーディネーター Agent #100
participant TranslationAgent as 翻訳 Agent #101
participant ProofreadAgent as 校正 Agent #102
participant x402Translation as x402 翻訳API
participant x402Proofread as x402 校正API
participant USDC as USDC Contract
participant ReputationReg as Reputation Registry
User->>CoordinatorAgent: タスク依頼「英語論文を日本語に翻訳して校正」
Note over CoordinatorAgent: タスク分解
CoordinatorAgent->>CoordinatorAgent: サブタスク1: 翻訳<br/>サブタスク2: 校正
Note over CoordinatorAgent,TranslationAgent: サブタスク1の委託
CoordinatorAgent->>TranslationAgent: 翻訳依頼
TranslationAgent->>x402Translation: GET /api/translate
x402Translation-->>TranslationAgent: 402 (amount: 0.5 USDC)
TranslationAgent->>TranslationAgent: EIP-712署名作成
TranslationAgent->>x402Translation: POST + Signature + Agent ID #101
x402Translation->>USDC: transferWithAuthorization()
USDC-->>x402Translation: 成功
x402Translation-->>TranslationAgent: 翻訳結果
x402Translation->>ReputationReg: recordCompletion(#101, +1)
TranslationAgent-->>CoordinatorAgent: 翻訳完了
Note over CoordinatorAgent,ProofreadAgent: サブタスク2の委託
CoordinatorAgent->>ProofreadAgent: 校正依頼
ProofreadAgent->>x402Proofread: GET /api/proofread
x402Proofread-->>ProofreadAgent: 402 (amount: 0.3 USDC)
ProofreadAgent->>ProofreadAgent: EIP-712署名作成
ProofreadAgent->>x402Proofread: POST + Signature + Agent ID #102
x402Proofread->>USDC: transferWithAuthorization()
USDC-->>x402Proofread: 成功
x402Proofread-->>ProofreadAgent: 校正結果
x402Proofread->>ReputationReg: recordCompletion(#102, +1)
ProofreadAgent-->>CoordinatorAgent: 校正完了
Note over CoordinatorAgent: タスク統合
CoordinatorAgent->>ReputationReg: recordCompletion(#100, +1)
CoordinatorAgent-->>User: 最終成果物(翻訳+校正済み論文)シーケンス図の詳細解説
このシーケンス図は、複数のAIエージェントが協調して大規模なタスクを遂行する仕組みを示しています。
1. タスク受理と分解フェーズ
ユーザーから「英語論文を日本語に翻訳して校正」というタスクをコーディネーターエージェント(#100)が受け取ります。 コーディネーターは、タスクを「翻訳」と「校正」の2つのサブタスクに分解します。 各サブタスクには、専門のエージェントを割り当てます。
2. サブタスク1の実行(翻訳エージェント)
コーディネーターは、翻訳エージェント(#101)にサブタスクを委託します。
翻訳エージェントは、x402翻訳APIにリクエストし、402 Payment Required(0.5 USDC)を受け取ります。
EIP-712署名を作成し、支払い情報と共にAPIにリクエストを送信します。
翻訳APIは、USDC Contractで決済を実行し、翻訳結果を返します。
タスク完了後、Reputation Registryに記録され、翻訳エージェント(#101)のスコアが +1 されます。
3. サブタスク2の実行(校正エージェント)
翻訳が完了すると、コーディネーターは校正エージェント(#102)に次のサブタスクを委託します。
校正エージェントは、x402校正APIにリクエストし、402 Payment Required(0.3 USDC)を受け取ります。
翻訳エージェントと同様のプロセスで決済を実行し、校正結果を取得します。
タスク完了後、校正エージェント(#102)のスコアが +1 されます。
4. タスク統合と完了
コーディネーターエージェントは、翻訳と校正の結果を統合し、最終成果物を作成します。 コーディネーター自身もReputation Registryにタスク完了を記録し、スコアが +1 されます。 最終成果物(翻訳+校正済み論文)をユーザーに返します。
マルチエージェント協調の利点
- 役割分担: 各エージェントが専門タスクに特化し、効率的に処理
- 並列実行: 独立したサブタスクを並列実行し、全体の処理時間を短縮(この例では順次実行ですが、並列化も可能)
- スケーラビリティ: エージェントを追加することで、より複雑なタスクに対応可能
- レピュテーション管理: 各エージェントが独立したスコアを持ち、信頼性を可視化
従来の決済システムとの比較
「x402」と従来の決済システムの違いを表形式で整理します。
| 項目 | 従来の決済システム | x402 |
|---|---|---|
| 手数料 | 3〜10%程度 | ネットワーク手数料のみ(1%未満) |
| 統合の複雑度 | 高い(アカウント管理、認証、セッション管理が必要) | 低い(HTTPリクエストに統合) |
| マイクロペイメント対応 | 困難(手数料が取引額を上回る可能性) | 可能(少額決済に最適化) |
| システム間決済 | 非対応(人間の操作が前提) | 対応(プログラマティックな支払い) |
| アカウント管理 | 必須 | 不要 |
| 決済速度 | 即時〜数日 | ブロックチェーン依存(数秒〜数分) |
| プライバシー | 個人情報が必要 | ウォレットアドレスのみ |
| 国際送金 | 困難(為替、手数料が高額) | 容易(ボーダーレス) |
x402の制約
「x402」には以下の制約があります。
- 暗号通貨インフラへの依存 ウォレットとブロックチェーンネットワークが必要です。
- 法定通貨決済への非対応 クレジットカードや銀行振込では利用できません。
- ネットワークごとの実装差異 異なるブロックチェーンネットワークでは、実装の詳細が異なります。
- 開発段階のエコシステム プロトコルが進化中であり、仕様が変更される可能性があります。
はじめ方
「x402」を始めるためのリソースを紹介します。
公式ドキュメント
公式ドキュメントには、詳細な仕様と実装ガイドが含まれています。
Quickstartガイド
Seller(売り手)向けとBuyer(買い手)向けのQuickstartガイドが用意されています。
公式ドキュメントの「Quickstart」セクションを参照してください。
ツールとテンプレート
以下のツールやテンプレートが提供されています。
- MCP Server統合 Model Context Protocolとの統合が可能です。
- Vercel スターターテンプレート Next.jsアプリケーションで簡単に始められます。
- CLI ツール コマンドラインから「x402」を操作できます。
コミュニティとサポート
「x402」はApache-2.0ライセンスのオープンソースプロジェクトです。 GitHubでコントリビューションを受け付けています。
現在、Coinbase Developer Platformがドキュメント作成をスポンサーしています。
最後に
今回は「x402」についてまとめてきました。


