anchor-sdk
Version:
TypeScript SDK for interacting with Anchor ecosystem - badge minting, payment processing, and ERC1155 token management
736 lines (587 loc) • 20.6 kB
Markdown
# Anchor SDK
[](https://badge.fury.io/js/%40keccak256-evg%2Fanchor-sdk)
[](https://opensource.org/licenses/MIT)
[](http://www.typescriptlang.org/)
Anchor SDK 是一个用于与 Anchor 生态系统交互的 TypeScript 开发工具包,支持徽章铸造、支付处理和 ERC1155 代币管理。
## ✨ 特性
- 🌐 **多网络支持** - 支持以太坊主网、Saigon、Ronin、Base Sepolia、Bera 等多个网络
- 🔐 **双模式支持** - 同时支持账户抽象(AA)和外部拥有账户(EOA)模式
- 🎯 **简单易用** - 提供直观的 API 接口进行徽章铸造和查询
- 💰 **多种支付方式** - 支持使用 ETH 或 ERC20 代币购买徽章
- ✍️ **自定义签名** - 支持使用自定义签名购买徽章
- 🆓 **免费铸造** - 支持铸造免费徽章
- 📝 **完全类型化** - 提供完整的 TypeScript 类型支持,确保良好的开发体验
- ⚡ **批量操作** - 支持批量铸造和 multicall 操作
- 🔄 **React 支持** - 提供 React Hook 和组件
## 📦 安装
### 使用 npm
```bash
npm install anchor-sdk
```
### 使用 yarn
```bash
yarn add anchor-sdk
```
### 使用 pnpm
```bash
pnpm add anchor-sdk
```
## 📋 依赖要求
Anchor SDK 需要以下依赖:
- **Node.js**: >= 16.0.0
- **TypeScript**: >= 4.5.0 (如果使用 TypeScript)
- **viem**: ^2.37.6
- **ethers**: ^6.15.0
### 可选依赖
- **React**: ^19.0.0 (如果使用 React 组件)
- **@types/react**: ^19.0.10 (如果使用 TypeScript 和 React)
## 🚀 快速开始
### 1. 初始化 SDK
```typescript
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
import { AnchorSDK, NETWORKS, Environment } from "anchor-sdk";
// 创建客户端
const rpcUrl = "https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY";
const privateKey = "0xYOUR_PRIVATE_KEY";
const publicClient = createPublicClient({
chain: mainnet,
transport: http(rpcUrl),
});
const account = privateKeyToAccount(
`0x${
privateKey.startsWith("0x") ? privateKey.slice(2) : privateKey
}` as `0x${string}`
);
const walletClient = createWalletClient({
account,
chain: mainnet,
transport: http(rpcUrl),
});
// 创建 SDK 实例
const sdk = new AnchorSDK({
publicClient,
walletClient,
network: NETWORKS.mainnet,
// 可以指定环境,支持 DEV、BETA 和 PROD
env: Environment.DEV,
// 也可以直接指定 API URL,这将覆盖环境配置
// apiBaseUrl: "https://api.example.com",
authToken: "YOUR_JWT_TOKEN",
clientId: "YOUR_CLIENT_ID",
});
```
### 2. 使用 ETH 购买徽章(EOA 模式)
```typescript
// 直接发送交易
const txHash = await sdk.buyBadgeWithETH(
"0xRECIPIENT_ADDRESS", // 接收徽章的地址
"0xCONTRACT_ADDRESS", // 合约地址
1, // Badge ID
1, // 数量
"0xRECEIPT_ADDRESS", // 接收支付的地址
{
value: "0.1", // 支付金额(ETH)
gas: 300000n, // 可选:指定 gas 限制
sendTransaction: true, // 明确指定直接发送交易
}
);
console.log(`交易已发送: ${txHash}`);
```
### 3. 使用 ETH 购买徽章(AA 模式)
```typescript
// 只返回交易数据,不发送交易
const txData = await sdk.buyBadgeWithETH(
"0xRECIPIENT_ADDRESS", // 接收徽章的地址
"0xCONTRACT_ADDRESS", // 合约地址
1, // Badge ID
1, // 数量
"0xRECEIPT_ADDRESS", // 接收支付的地址
{
value: "0.1", // 支付金额(ETH)
sendTransaction: false, // 指定不发送交易,只返回交易数据
}
);
console.log(`交易数据: ${txData}`);
// 使用你的 AA 钱包发送此交易数据
```
### 4. 铸造免费徽章
```typescript
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { saigon, baseSepolia } from "viem/chains";
import { AnchorSDK, NETWORKS, Environment } from "anchor-sdk";
// 创建客户端
const privateKey = "0xYOUR_PRIVATE_KEY";
// 这里使用 saigon 测试网,也可以使用 baseSepolia 或其他支持的网络
const publicClient = createPublicClient({
chain: saigon,
transport: http(),
});
const account = privateKeyToAccount(
`0x${
privateKey.startsWith("0x") ? privateKey.slice(2) : privateKey
}` as `0x${string}`
);
const walletClient = createWalletClient({
account,
chain: saigon,
transport: http(),
});
// 创建 SDK 实例
const sdk = new AnchorSDK({
publicClient,
walletClient,
env: Environment.DEV,
network: NETWORKS.saigon,
authToken: "YOUR_JWT_TOKEN",
clientId: "YOUR_CLIENT_ID",
});
try {
// 使用封装的 mintFreeBadge 方法(推荐)
// 这个方法会自动从后端获取签名请求并执行铸造
const customerAddress = account.address;
const contractAddress =
"0x23Da7778F6152D7B91C20808bEF7163c5EC2C470" as `0x${string}`; // 替换为合约地址
const claimableIds = ["CLAIMABLE_ID"]; // 替换为实际的可领取 ID
const txHash = await sdk.mintFreeBadge(
customerAddress,
contractAddress,
claimableIds,
{ sendTransaction: true } // 明确指定直接发送交易
);
console.log(`免费徽章铸造交易已提交:${txHash}`);
// 检查铸造结果
const receipt = await publicClient.waitForTransactionReceipt({
hash: txHash,
});
console.log(`铸造成功: ${receipt.status === "success"}`);
} catch (error) {
console.error("铸造免费徽章失败:", error);
}
```
### 5. 使用纯支付功能
```typescript
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { saigon } from "viem/chains";
import { AnchorSDK, Environment } from "anchor-sdk";
// 创建客户端
const privateKey = "0xYOUR_PRIVATE_KEY";
const publicClient = createPublicClient({
chain: saigon,
transport: http(),
});
const account = privateKeyToAccount(
`0x${
privateKey.startsWith("0x") ? privateKey.slice(2) : privateKey
}` as `0x${string}`
);
const walletClient = createWalletClient({
account,
chain: saigon,
transport: http(),
});
// 创建 SDK 实例
const sdk = new AnchorSDK({
publicClient,
walletClient,
env: Environment.DEV,
network: NETWORKS.saigon,
});
try {
// EOA 模式 - 直接发送交易
const txHash = await sdk.pay(
"order-123", // 订单 ID
"0.01", // 支付金额(ETH)
"0xRECEIPT_ADDRESS", // 接收地址
{ sendTransaction: true } // 明确指定直接发送交易
);
console.log(`交易已提交:${txHash}`);
// AA 模式 - 只返回交易数据,不发送交易
const txData = await sdk.pay(
"order-456", // 订单 ID
"0.05", // 支付金额(ETH)
"0xRECEIPT_ADDRESS", // 接收地址
{ sendTransaction: false } // 指定不发送交易,只返回交易数据
);
console.log("交易数据:", txData);
// 使用你的 AA 钱包发送此交易数据
} catch (error) {
console.error("支付失败:", error);
}
```
### 6. 使用自定义签名购买徽章
```typescript
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet, saigon } from "viem/chains";
import { AnchorSDK, NETWORKS, Environment } from "anchor-sdk";
import MugenApiClient from "./MugenApiClient";
// 创建客户端
const rpcUrl = "https://saigon-testnet.roninchain.com/rpc";
const privateKey = "0xYOUR_PRIVATE_KEY";
const publicClient = createPublicClient({
chain: saigon,
transport: http(rpcUrl),
});
const account = privateKeyToAccount(
`0x${
privateKey.startsWith("0x") ? privateKey.slice(2) : privateKey
}` as `0x${string}`
);
const userAddress = account.address;
const walletClient = createWalletClient({
account,
chain: saigon,
transport: http(rpcUrl),
});
// 创建 SDK 实例
const sdk = new AnchorSDK({
publicClient,
walletClient,
network: NETWORKS.saigon,
env: Environment.BETA,
});
// 从 Mugen API 获取签名数据
const mugenApiClient = new MugenApiClient(
"https://api.mugen.com/",
"YOUR_JWT_TOKEN"
);
try {
// 创建订单
const orderResult = await mugenApiClient.orderCreate(
"PRODUCT_CODE",
"PRODUCT_ID",
userAddress
);
console.log("创建订单结果:", orderResult);
if (orderResult.success && orderResult.obj) {
// 使用 SDK 的自定义签名方法购买徽章
const signedRequest = {
request: {
to: userAddress,
tokenId: "TOKEN_ID",
amount: 1,
price: "0.01",
currency: "0x0000000000000000000000000000000000000000", // ETH的地址是零地址
deadline: Math.floor(Date.now() / 1000) + 3600, // 1小时后过期
quantity: 1,
},
signature: orderResult.obj.signature,
};
const txHash = await sdk.buyBadgeWithETHWithSignedRequest(
signedRequest,
userAddress, // 接收地址
{ value: "0.01", sendTransaction: true }
);
console.log("交易已发送:", txHash);
} else {
console.error("创建订单失败:", orderResult);
}
} catch (error) {
console.error("购买徽章失败:", error);
}
```
## 🎯 DApp 函数调用场景
在 dApp 中,您可以在以下特定情况下调用这些函数来与 Anchor SDK 交互:
- **buyBadgeWithETH**: 当用户想要使用 ETH 购买徽章时调用此函数,在 EOA 模式下直接发送交易,或在 AA 模式下获取交易数据而不发送。
- **buyBadgeWithETHWithSignedRequest**: 用于使用预签名请求购买徽章,通常在集成外部 API 进行自定义签名时使用。
- **buyBadgeWithERC20**: 当用户使用 ERC20 代币购买徽章时调用此函数,处理代币批准和转账。
- **buyBadgeWithERC20WithCustomSignature**: 用于需要签名请求的 ERC20 购买,例如来自后端的验证订单场景。
- **pay**: 用于一般支付,如使用 ETH 处理订单或为 AA 钱包获取交易数据。
- **mintFreeBadge**: 为符合条件的用户铸造免费徽章时调用此函数,通常在从 API 获取可领取 ID 后使用。
- **其他函数**: 对于批量操作或高级交互,请参考 batchMintBadge 等方法在单个交易中进行多次铸造。
## 🌐 支持的网络
SDK 支持多种网络,包括:
| 网络 | 类型 | 链 ID | 说明 |
| ------------------ | ------ | ----- | ------------------- |
| `mainnet` | 主网 | 1 | 以太坊主网 |
| `saigon` | 测试网 | 2021 | Saigon 测试网 |
| `ronin` | 主网 | 2020 | Ronin 网络 |
| `baseSepolia` | 测试网 | 84532 | Base Sepolia 测试网 |
| `berachain` | 主网 | 80094 | Bera 主网 |
| `berachainBepolia` | 测试网 | 80085 | Bera 测试网 |
| `abstractTestnet` | 测试网 | 11124 | Abstract 测试网 |
| `base` | 主网 | 8453 | Base 主网 |
| `abstract` | 主网 | 11111 | Abstract 主网 |
## 🔐 API 认证
使用 Anchor API 功能时,需要提供以下认证参数:
- `authToken` - JWT 令牌,格式为 "your-jwt-token"
- `clientId` - 客户端标识
您可以在初始化 SDK 时提供这些参数,也可以在运行时更新:
```typescript
// 初始化时设置
const sdk = new AnchorSDK({
// 其他配置...
apiBaseUrl: "https://api.example.com",
authToken: "YOUR_JWT_TOKEN",
clientId: "YOUR_CLIENT_ID",
});
// 运行时更新
sdk.anchorApi.setAuthToken("NEW_JWT_TOKEN");
sdk.anchorApi.setClientId("NEW_CLIENT_ID");
```
### Token 过期处理
SDK 提供了 token 过期回调机制,当 API 调用遇到 token 过期错误时,会自动调用回调函数:
```typescript
// 初始化时设置 token 过期回调
const sdk = new AnchorSDK({
// 其他配置...
apiBaseUrl: "https://api.example.com",
authToken: "YOUR_JWT_TOKEN",
clientId: "YOUR_CLIENT_ID",
onTokenExpired: (error: Error) => {
console.log("Token 已过期:", error.message);
// 处理 token 过期逻辑
// 例如:刷新 token、重新登录、通知用户等
refreshToken().then((newToken) => {
sdk.anchorApi?.setAuthToken(newToken);
console.log("Token 已刷新");
});
},
});
// 运行时设置 token 过期回调
sdk.setTokenExpiredCallback((error: Error) => {
console.log("Token 已过期:", error.message);
// 处理 token 过期逻辑
});
```
回调函数会在以下情况下触发:
- 响应体中的 `code` 字段为 `params.jwt.check.invalid`(即使 HTTP 状态码为 200)
### 错误码说明
当 API 返回 `code: "params.jwt.check.invalid"` 时,表示 JWT token 验证失败,可能的原因包括:
- Token 已过期
- Token 格式不正确
- Token 签名无效
- Token 被撤销
**注意**:即使 token 过期,API 也可能返回 HTTP 状态码 200,SDK 会检查响应体中的错误码来判断 token 是否有效。
SDK 会自动检测这种错误码并调用回调函数,让您可以执行相应的处理逻辑。
## 📚 API 文档
### 核心类
#### `AnchorSDK`
主要的 SDK 类,提供与 Anchor 生态系统交互的所有功能。
```typescript
class AnchorSDK {
constructor(config: AnchorSDKConfig);
// 徽章购买方法
buyBadgeWithETH(
customerAddress: string,
contractAddress: string,
tokenId: string | number | bigint,
quantity: string | number | bigint,
receiptAddress: string,
options?: PaymentOptions
): Promise<TransactionReceipt | { to: string; data: string; value: bigint }>;
buyBadgeWithETHWithSignedRequest(
signedRequest: SignedMintRequest,
receiptAddress: string,
options?: PaymentOptions
): Promise<TransactionReceipt | { to: string; data: string; value: bigint }>;
buyBadgeWithERC20(
customerAddress: string,
contractAddress: string,
tokenId: string | number | bigint,
quantity: string | number | bigint,
currency: string,
receiptAddress: string,
options?: PaymentOptions
): Promise<TransactionReceipt | { to: string; data: string; value: bigint }>;
// 免费铸造方法
mintFreeBadge(
customerAddress: string,
contractAddress: string,
tokenIds: string[],
options?: { sendTransaction?: boolean }
): Promise<
| TransactionReceipt
| { to: string; data: string; value: bigint }
| { to: string; data: string; value: bigint }[]
>;
// 批量操作
batchMintBadge(
customerAddress: string,
contractAddress: string,
tokenIds: string[],
options?: { sendTransaction?: boolean }
): Promise<
TransactionReceipt | { to: string; data: string; value: bigint }[]
>;
batchMintBadgeWithMulticall(
customerAddress: string,
contractAddress: string,
tokenIds: string[],
options?: { sendTransaction?: boolean }
): Promise<TransactionReceipt | { to: string; data: string; value: bigint }>;
// 支付方法
pay(
orderId: string,
amount: string | number | bigint,
receiptAddress: string,
options?: PaymentOptions
): Promise<TransactionReceipt | { to: string; data: string; value: bigint }>;
sendPaymentWithNativeToken(params: {
callData: `0x${string}`;
amount: string | number | bigint;
receiptAddress: string;
contractName?: string;
options?: PaymentOptions;
}): Promise<TransactionReceipt | { to: string; data: string; value: bigint }>;
sendPaymentWithERC20(params: {
callData: `0x${string}`;
amount: string | number | bigint;
receiptAddress: string;
tokenAddress: string;
contractName?: string;
options?: PaymentOptions;
}): Promise<TransactionReceipt | { to: string; data: string; value: bigint }>;
// 工具方法
setWalletClient(signerOrWalletClient: any): AnchorSDK;
setTokenExpiredCallback(callback: (error: Error) => void): void;
processTransactionHash(txHash: string): Promise<any>;
getERC20ApprovalData(
tokenAddress: Address,
amount: bigint,
spender?: Address
): { to: string; data: string; value: bigint };
}
```
### 类型定义
#### `AnchorSDKConfig`
SDK 配置接口:
```typescript
interface AnchorSDKConfig {
provider?: string | any; // Provider URL 或 Web3 Provider
signer?: any; // 可选的签名者或钱包
network: {
// 网络信息
id: number;
name: string;
[key: string]: any;
};
env: Environment; // 环境枚举
apiBaseUrl?: string; // API 基础 URL
authToken?: string; // 认证令牌
projectId?: string; // 项目 ID
anchorPayAddress?: string; // AnchorPay 合约地址
anchorERC1155Address?: string; // AnchorERC1155 合约地址
publicClient?: PublicClient; // 公共客户端
walletClient?: WalletClient; // 钱包客户端
account?: Account; // 账户信息
onTokenExpired?: (error: Error) => void; // Token 过期回调
}
```
#### `PaymentOptions`
支付选项接口:
```typescript
interface PaymentOptions {
autoApprove?: boolean; // 是否自动批准 ERC20 代币
gas?: bigint; // Gas 限制
maxFeePerGas?: bigint; // 最大 Gas 费用
maxPriorityFeePerGas?: bigint; // 最大优先费用
value?: bigint; // 交易值
nonce?: number; // 交易 nonce
sendTransaction?: boolean; // 是否直接发送交易
}
```
#### `SignedMintRequest`
签名铸造请求接口:
```typescript
interface SignedMintRequest {
request: IMintRequest; // 铸造请求
signature: string; // 签名
}
interface IMintRequest {
to: Address; // 接收者地址
tokenId: number | bigint; // 代币 ID
quantity: number | bigint; // 数量
price: bigint; // 价格
currency: Address; // 货币地址
validityStartTimestamp?: number | bigint; // 有效期开始时间戳
validityEndTimestamp?: number | bigint; // 有效期结束时间戳
uid: Hex; // 唯一 ID
chainId: bigint; // 链 ID
contractAddress?: string; // 合约地址
projectHandle?: string; // 项目句柄
}
```
### 环境配置
#### `Environment`
环境枚举:
```typescript
enum Environment {
DEV = "dev", // 开发环境
BETA = "beta", // 测试环境
PROD = "prod", // 生产环境
}
```
### React 支持
SDK 还提供了 React Hook 和组件支持:
```typescript
import { useAnchorSDK, AnchorProvider } from "anchor-sdk/react";
// 使用 Hook
const { sdk, loading, error } = useAnchorSDK();
// 使用 Provider
<AnchorProvider config={sdkConfig}>
<YourApp />
</AnchorProvider>;
```
## 🛠️ 开发指南
### 本地开发
1. 克隆仓库:
```bash
git clone https://github.com/keccak256-evg/anchor-sdk.git
cd anchor-sdk
```
2. 安装依赖:
```bash
npm install
```
3. 构建项目:
```bash
npm run build
```
4. 运行测试:
```bash
npm test
```
### 项目结构
```
src/
├── index.ts # 主入口文件
├── AnchorSDK.ts # 核心 SDK 类
├── AnchorPayClient.ts # AnchorPay 客户端
├── AnchorERC1155Client.ts # AnchorERC1155 客户端
├── AnchorApiClientV2.ts # API 客户端
├── constants.ts # 常量定义
├── types.ts # 类型定义
├── abi/ # 合约 ABI
├── generated/ # 生成的 API 代码
├── react/ # React 组件和 Hook
└── utils/ # 工具函数
```
## 🤝 贡献
我们欢迎社区贡献!请遵循以下步骤:
1. Fork 这个仓库
2. 创建您的特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交您的更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 打开一个 Pull Request
### 贡献指南
- 请确保您的代码遵循项目的 TypeScript 配置
- 添加适当的测试覆盖
- 更新相关文档
- 遵循现有的代码风格
## 📄 许可证
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
## 🔗 相关链接
- [项目主页](https://github.com/keccak256-evg/anchor-sdk)
- [npm 包](https://www.npmjs.com/package/anchor-sdk)
- [文档](https://github.com/keccak256-evg/anchor-sdk/tree/main/docs)
- [示例代码](https://github.com/keccak256-evg/anchor-sdk/tree/main/examples)
## 📞 支持
如果您遇到任何问题或有疑问,请:
1. 查看 [文档](https://github.com/keccak256-evg/anchor-sdk/tree/main/docs)
2. 搜索 [Issues](https://github.com/keccak256-evg/anchor-sdk/issues)
3. 创建新的 [Issue](https://github.com/keccak256-evg/anchor-sdk/issues/new)