UNPKG

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
# Anchor SDK [![npm version](https://badge.fury.io/js/%40keccak256-evg%2Fanchor-sdk.svg)](https://badge.fury.io/js/%40keccak256-evg%2Fanchor-sdk) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](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)