跳至主要内容

合约事件与 Webhook

ONE SDK 允许你使用 webhook 订阅链上合约事件。当匹配的事件被触发时,Engine 会向你配置的端点发送包含解码事件数据的 HTTP POST 请求。

工作原理

合约触发 Transfer 事件
|
v
ONE Engine 索引器检测到事件
|
v
Engine 将事件与活跃的 webhook 进行匹配
|
v
向你的 webhook URL 发送 HTTP POST,携带事件载荷

createWebhook

注册新的 webhook 事件订阅。

方法签名

client.createWebhook(params: CreateWebhookParams): Promise<ApiResponse<Webhook>>

CreateWebhookParams

import type { CreateWebhookParams, WebhookEventType } from '@one_deploy/sdk';

interface CreateWebhookParams {
url: string; // 你的 HTTPS 端点
eventType: WebhookEventType; // 要订阅的事件类别
contractAddress?: string; // 筛选特定合约
chainId?: number; // 筛选特定链
eventName?: string; // 筛选特定事件名(如 'Transfer')
secret?: string; // 用于 HMAC 签名验证的共享密钥
metadata?: Record<string, unknown>; // 附加到 webhook 的自定义元数据
active?: boolean; // webhook 是否激活(默认 true)
}

WebhookEventType

type WebhookEventType =
| 'contract.event' // 合约发出的任意事件
| 'contract.transfer' // ERC-20 / ERC-721 / ERC-1155 Transfer 事件
| 'contract.approval' // ERC-20 Approval 事件
| 'contract.deploy' // 合约部署确认
| 'transaction.confirmed' // 任何交易确认
| 'transaction.failed'; // 任何交易失败

Webhook 响应

interface Webhook {
id: string; // 唯一 webhook ID
url: string;
eventType: WebhookEventType;
contractAddress?: string;
chainId?: number;
eventName?: string;
active: boolean;
createdAt: string; // ISO-8601
}

示例

监听 ERC-20 Transfer 事件

import { useOneEngine } from '@one_deploy/sdk';

function SetupTransferWebhook() {
const { client } = useOneEngine();

async function subscribe() {
const res = await client.createWebhook({
url: 'https://api.myapp.com/webhooks/transfers',
eventType: 'contract.transfer',
contractAddress: '0xA0b8...3E7a', // USDC 合约
chainId: 1, // Ethereum 主网
eventName: 'Transfer',
secret: 'whsec_my_shared_secret_123',
});

if (res.success) {
console.log('Webhook created:', res.data.id);
} else {
console.error('Failed:', res.error?.message);
}
}

return <button onClick={subscribe}>Subscribe to USDC Transfers</button>;
}

监听合约上的所有事件

省略 eventName 以接收合约发出的每个事件:

const res = await client.createWebhook({
url: 'https://api.myapp.com/webhooks/all-events',
eventType: 'contract.event',
contractAddress: '0x5566...7788',
chainId: 137,
secret: 'whsec_polygon_events',
});

监听部署确认

当你部署的任何合约被确认时收到通知:

const res = await client.createWebhook({
url: 'https://api.myapp.com/webhooks/deploys',
eventType: 'contract.deploy',
secret: 'whsec_deploy_notifications',
});

监听交易失败

监控所有合约的失败交易:

const res = await client.createWebhook({
url: 'https://api.myapp.com/webhooks/failures',
eventType: 'transaction.failed',
chainId: 137,
secret: 'whsec_failure_alerts',
});

Webhook 载荷

当事件匹配时,Engine 发送包含以下 JSON 正文的 POST 请求:

interface WebhookPayload {
id: string; // 唯一投递 ID
webhookId: string; // webhook 订阅 ID
eventType: WebhookEventType;
timestamp: string; // ISO-8601
data: {
chainId: number;
contractAddress: string;
transactionHash: string;
blockNumber: number;
eventName: string; // 如 'Transfer'
args: Record<string, unknown>; // 解码后的事件参数
};
}

ERC-20 Transfer 的示例载荷:

{
"id": "dlv_abc123",
"webhookId": "whk_xyz789",
"eventType": "contract.transfer",
"timestamp": "2025-03-15T10:30:00.000Z",
"data": {
"chainId": 1,
"contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"transactionHash": "0x1234...abcd",
"blockNumber": 19500000,
"eventName": "Transfer",
"args": {
"from": "0xSender...",
"to": "0xRecipient...",
"value": "1000000"
}
}
}

验证 Webhook 签名

当你设置了 secret 后,Engine 会在请求头中包含一个 X-One-Signature,其中包含请求正文的 HMAC-SHA256 签名。你应始终在处理程序中验证此签名,以确保载荷的真实性。

import crypto from 'node:crypto';

function verifyWebhookSignature(
body: string,
signature: string,
secret: string,
): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
);
}

// Express 示例
app.post('/webhooks/transfers', (req, res) => {
const signature = req.headers['x-one-signature'] as string;
const rawBody = JSON.stringify(req.body);

if (!verifyWebhookSignature(rawBody, signature, 'whsec_my_shared_secret_123')) {
return res.status(401).send('Invalid signature');
}

const payload = req.body;
console.log(`Transfer on chain ${payload.data.chainId}:`,
payload.data.args.from, '->', payload.data.args.to,
'amount:', payload.data.args.value,
);

res.status(200).send('OK');
});

管理 Webhook

Engine 还提供了列出、更新和删除 webhook 的方法:

// 列出所有 webhook
const list = await client.getWebhooks();
// list.data -> Webhook[]

// 更新 webhook(切换激活状态)
const updated = await client.updateWebhook('whk_xyz789', {
active: false,
});

// 删除 webhook
await client.deleteWebhook('whk_xyz789');

Node.js / Edge 运行时

import { OneEngineClient } from '@one_deploy/sdk';

const client = new OneEngineClient({
apiKey: process.env.ONE_API_KEY!,
projectId: process.env.ONE_PROJECT_ID!,
});

const res = await client.createWebhook({
url: 'https://api.myapp.com/webhooks/transfers',
eventType: 'contract.transfer',
contractAddress: '0xA0b8...3E7a',
chainId: 1,
eventName: 'Transfer',
secret: 'whsec_server_side',
});

console.log('Webhook ID:', res.data.id);
提示

如需完整的 webhook API 参考(包括重试策略、投递日志和批量订阅),请参阅 Webhook API 参考

下一步