Relay TypeScript SDK
Relay是PBJ Protocol的生产TypeScript SDK,用于Solana上的USDC里程碑托管。它帮助应用程序、代理机构、客户和独立团队定义可交付成果,在工作开始之前确保项目预算,保存交付证据,批准发布,并通过明确的规则解决争议。
- 端到端打字 严格的TypeScript模型、错误和服务api
- 没有签字 - Relay准备事务;你的钱包会告诉他们
- 开发和主网 -明确的网络选择与主网保障
- 证据准备好了 - GitHub, Figma,谷歌驱动器,url和校验和
- 安全的运输 -超时、中止信号、幂等、重试控制和编校
[!警告) Relay执行资助、证据、批准和发布规则。它不能独立地决定主观的创造性或软件工作是否满足期望。定义可衡量的里程碑标准,并在资助之前选择争议解决者。
安装
npm i @pbjpro/relay
当前版本: v1.3.0
要求
- Node.js 20或更新版本,或使用
fetch和AbortController - 用于经过身份验证的服务器操作的中继API密钥
- 一个兼容的Solana钱包,用于签署资金和发布交易
- 所有令牌数量的十进制字符串——绝不是JavaScript浮点数
这个包包括ESM、CommonJS、声明文件和源映射。
快速入门
import { RelayClient } from "@pbjpro/relay";
const relay = new RelayClient({
apiKey: process.env.RELAY_API_KEY!,
baseUrl: process.env.RELAY_API_URL,
network: "devnet",
});
将API密钥保存在受信任的服务器上。不要在浏览器包中包含特权中继或集成凭据。
环境
RELAY_API_KEY=relay_test_replace_me
RELAY_API_URL=https://api.0xpbj.pro
RELAY_NETWORK=devnet
RELAY_CONFIRM_MAINNET=false
看到 .env.example 获取完整的配置模板。
创建一个托管项目
const project = await relay.escrows.create({
name: "Brand redesign",
clientWallet: "CLIENT_SOLANA_ADDRESS",
freelancerWallet: "FREELANCER_SOLANA_ADDRESS",
resolverWallet: "RESOLVER_SOLANA_ADDRESS",
currency: "USDC",
milestones: [
{
title: "Creative direction",
amount: "1500.00",
description: "Approved research and creative direction",
dueAt: "2027-01-31T00:00:00.000Z",
},
{
title: "Final delivery",
amount: "3500.00",
description: "Approved final files and documented handoff",
dueAt: "2027-02-28T00:00:00.000Z",
},
],
disputePolicy: {
resolverType: "human",
inactivityTimeoutHours: 168,
evidenceWindowHours: 72,
},
idempotencyKey: "brand-redesign-001",
});
与USDC的资金托管
Relay准备事务。客户端钱包在本地审核并签名。
const funding = await relay.escrows.prepareFunding({
projectId: project.id,
amount: "5000.00",
});
const signature = await wallet.sendTransaction(funding.transaction);
await relay.escrows.confirmFunding({
projectId: project.id,
signature,
});
Relay不需要钱包种子短语或私钥。
管理资金工作流程
高级工作流准备事务,将签名委托给您的钱包,并确认提交的签名。私钥永远不会进入SDK。
const confirmation = await relay.escrows.fund({
projectId: project.id,
amount: "5000.00",
signTransaction: (transaction) => wallet.sendTransaction(transaction),
onProgress: ({ stage, signature }) => {
console.log(stage, signature ?? "");
},
});
提交里程碑证据
尽可能固定特定的、不可变的版本。活动分支、设计文件或驱动链接在提交后可能会更改。
const submission = await relay.milestones.submit({
milestoneId: "milestone_id",
summary: "Final files and handoff documentation",
evidence: [
{
type: "github",
repository: "pbjprotocol/client-project",
commitSha: "FULL_COMMIT_SHA",
},
{
type: "figma",
fileKey: "FIGMA_FILE_KEY",
versionId: "FIGMA_VERSION_ID",
},
{
type: "google_drive",
fileId: "GOOGLE_DRIVE_FILE_ID",
checksum: "SHA256_CHECKSUM",
},
],
idempotencyKey: "delivery-001",
});
核实交货证据
const report = await relay.evidence.verify("evidence_id");
if (!report.valid) {
console.error("Evidence verification failed", report.checks);
}
批准和放行
const prepared = await relay.milestones.prepareRelease({
milestoneId: submission.milestoneId,
});
const signature = await wallet.sendTransaction(prepared.transaction);
await relay.milestones.confirmRelease({
milestoneId: submission.milestoneId,
signature,
});
签名的区块链事务确认永远不会自动重试。
等效的管理发布工作流可作为 relay.milestones.release():
await relay.milestones.release({
milestoneId: submission.milestoneId,
signTransaction: (transaction) => wallet.sendTransaction(transaction),
});
解决争议
const dispute = await relay.disputes.create({
milestoneId: "milestone_id",
reason: "delivery_does_not_match_scope",
statement: "The submitted package omits the agreed mobile layouts.",
evidenceIds: ["evidence_1", "evidence_2"],
idempotencyKey: "dispute-001",
});
await relay.disputes.resolve({
disputeId: dispute.id,
outcome: "split",
statement: "Desktop scope delivered; mobile scope remains incomplete.",
clientAmount: "1800.00",
freelancerAmount: "1200.00",
});
人则
在处理webhook之前,始终验证准确的、未修改的请求体。验证包括HMAC认证和时间戳重放保护。
const event = await relay.webhooks.verify({
payload: rawRequestBody,
signature: request.headers["x-relay-signature"],
secret: process.env.RELAY_WEBHOOK_SECRET!,
});
switch (event.type) {
case "milestone.submitted":
console.log(event.data.submission.id);
break;
case "dispute.opened":
console.log(event.data.id);
break;
}
使用 relay.webhooks.idempotent(store, handler) 使用数据库或缓存来防止重复传递处理。只有在处理程序成功后才会记录事件。
开发和主网
Devnet是开发过程中的默认和推荐环境。
const relay = new RelayClient({
apiKey: process.env.RELAY_API_KEY!,
network: "devnet",
});
主网需要明确的确认,永远不会无声地退回到devnet。
const relay = new RelayClient({
apiKey: process.env.RELAY_API_KEY!,
network: "mainnet",
confirmMainnet: true,
});
集成
| 集成 | 交付的证据 |
|---|---|
| GitHub | 存储库、不可变提交sha、交付活动 |
| Figma | 文件版本,冻结预览,审查意见 |
| 谷歌驱动 | 文件、文件夹、版本元数据、校验和 |
集成令牌应该使用工作流支持的最窄权限。
分页
const page = await relay.milestones.list({
projectId: project.id,
status: ["submitted", "in_dispute"],
page: 1,
limit: 20,
});
for await (const milestone of relay.milestones.iterate({
projectId: project.id,
})) {
console.log(milestone.id, milestone.status);
}
事务监控
for await (const update of relay.solana.watchTransaction(signature, {
timeoutMs: 60_000,
signal: abortController.signal,
})) {
console.log(update.status);
}
迭代器发出状态变化,并在确认时停止。失败的事务和超时会产生输入错误。
输入错误
import { RelayAuthenticationError, RelayTransactionError } from "@pbjpro/relay";
try {
await createFundedProject();
} catch (error) {
if (error instanceof RelayAuthenticationError) {
console.error("Check RELAY_API_KEY");
} else if (error instanceof RelayTransactionError) {
console.error("Transaction failed", error.signature);
} else {
throw error;
}
}
所有中继错误暴露 code, message, retryable,加上可选 statusCode, requestId, cause,并有结构 details.
公共服务
| 服务 | 方法 |
|---|---|
relay.escrows | create, get, list, prepareFunding, confirmFunding, cancel, getBalance, getActivity |
relay.milestones | create, get, list, iterate, submit, prepareRelease, confirmRelease, requestChanges |
relay.evidence | create, get, list, refresh, getFrozenPreview, verifyChecksum |
relay.disputes | create, get, list, addEvidence, addStatement, resolve, getTimeline |
relay.integrations | GitHub, Figma和谷歌驱动证据服务 |
relay.solana | 网络、RPC、USDC铸币厂、交易状态、确认和钱包验证 |
relay.webhooks | HMAC验证,重放保护,重复检测和幂等处理程序 |
测试工具
可选的测试入口点提供了不需要网络访问的确定性装置和模拟中继传输。
import { createRelayMock, projectFixture } from "@pbjpro/relay/testing";
const project = projectFixture();
const mock = createRelayMock([
{ path: `/v1/escrows/${project.id}`, body: project },
]);
const result = await mock.client.escrows.get(project.id);
它也输出 milestoneFixture, evidenceFixture, webhookFixture, signWebhookEvent.
安全检查表
- 将API和集成凭据保持在服务器端。
- 永远不要在Relay配置中存储钱包私钥。
- 在签名之前验证网络、钱包地址、USDC铸币厂、里程碑总数和解析器。
- 对不可变证据使用完整提交sha、冻结设计版本和校验和。
- 对创建和提交工作流使用唯一的等幂键。
- 从应用程序日志中编辑授权标头和敏感主体。
- 签署前审查主网交易。
示例
中提供可运行的集成 examples:
- Node.js项目创建、资助、提交、发布和争议流
- js路由处理程序集成,将API密钥保存在服务器端
- 快速服务器集成与类型化错误处理
开发
npm ci
npm run typecheck
npm run lint
npm test
npm run build
其他脚本包括 test:coverage, format, format:check.
文档和支持
出版
- 更新软件包版本和
CHANGELOG.md. - 运行
npm ci和npm run prepublishOnly. - 检查包装
npm pack --dry-run. - 发布与
npm publish --access public从授权的NPM帐户。
许可证
MIT©PBJ协议。看到 许可证.