anchor-sdk 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +735 -0
  3. package/dist/AnchorApiClient.d.ts +203 -0
  4. package/dist/AnchorApiClient.js +279 -0
  5. package/dist/AnchorApiClientV2.d.ts +270 -0
  6. package/dist/AnchorApiClientV2.js +424 -0
  7. package/dist/AnchorERC1155Client.d.ts +85 -0
  8. package/dist/AnchorERC1155Client.js +280 -0
  9. package/dist/AnchorPayClient.d.ts +79 -0
  10. package/dist/AnchorPayClient.js +217 -0
  11. package/dist/abi/AnchorERC1155.d.ts +1359 -0
  12. package/dist/abi/AnchorERC1155.js +1122 -0
  13. package/dist/abi/AnchorPay.json +452 -0
  14. package/dist/api/AnchorApiHttpClient.d.ts +210 -0
  15. package/dist/api/AnchorApiHttpClient.js +411 -0
  16. package/dist/api/types.d.ts +764 -0
  17. package/dist/api/types.js +2 -0
  18. package/dist/constants.d.ts +49 -0
  19. package/dist/constants.js +221 -0
  20. package/dist/generated/Api.d.ts +1083 -0
  21. package/dist/generated/Api.js +571 -0
  22. package/dist/index.d.ts +341 -0
  23. package/dist/index.js +1377 -0
  24. package/dist/react/AnchorReactSDK.d.ts +59 -0
  25. package/dist/react/AnchorReactSDK.js +181 -0
  26. package/dist/react/index.d.ts +1 -0
  27. package/dist/react/index.js +8 -0
  28. package/dist/typechain/AnchorERC1155.d.ts +999 -0
  29. package/dist/typechain/AnchorERC1155.js +2 -0
  30. package/dist/typechain/AnchorPay.d.ts +242 -0
  31. package/dist/typechain/AnchorPay.js +2 -0
  32. package/dist/typechain/common.d.ts +50 -0
  33. package/dist/typechain/common.js +2 -0
  34. package/dist/typechain/factories/AnchorERC1155__factory.d.ts +1365 -0
  35. package/dist/typechain/factories/AnchorERC1155__factory.js +1766 -0
  36. package/dist/typechain/factories/AnchorPay__factory.d.ts +358 -0
  37. package/dist/typechain/factories/AnchorPay__factory.js +469 -0
  38. package/dist/typechain/factories/index.d.ts +2 -0
  39. package/dist/typechain/factories/index.js +10 -0
  40. package/dist/typechain/index.d.ts +5 -0
  41. package/dist/typechain/index.js +41 -0
  42. package/dist/types.d.ts +109 -0
  43. package/dist/types.js +2 -0
  44. package/package.json +87 -0
package/dist/index.js ADDED
@@ -0,0 +1,1377 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.AnchorERC1155Client = exports.AnchorApiClientV2 = exports.AnchorPayClient = exports.AnchorSDK = void 0;
21
+ const viem_1 = require("viem");
22
+ const AnchorPayClient_1 = require("./AnchorPayClient");
23
+ const AnchorERC1155Client_1 = require("./AnchorERC1155Client");
24
+ const AnchorApiClientV2_1 = require("./AnchorApiClientV2");
25
+ const constants_1 = require("./constants");
26
+ const AnchorPay_json_1 = __importDefault(require("./abi/AnchorPay.json"));
27
+ const AnchorERC1155_1 = require("./abi/AnchorERC1155");
28
+ /**
29
+ * Anchor SDK
30
+ * 用于与 AnchorPay 和 AnchorERC1155 合约交互的 SDK
31
+ */
32
+ class AnchorSDK {
33
+ /**
34
+ * 创建 Anchor SDK 实例
35
+ * @param config SDK 配置
36
+ */
37
+ constructor(config) {
38
+ this.config = config;
39
+ this.contracts = {};
40
+ // 检查是否使用旧版配置(向后兼容)
41
+ if ("publicClient" in config && config.publicClient) {
42
+ // 使用旧版配置
43
+ this.publicClient = config.publicClient;
44
+ this.walletClient = config.walletClient;
45
+ this.account = this.walletClient?.account;
46
+ this.network = this.createChainFromNetwork(config.network);
47
+ }
48
+ else {
49
+ // 使用新版配置
50
+ // Convert network to viem Chain
51
+ this.network = this.createChainFromNetwork(config.network);
52
+ // Initialize viem clients
53
+ this.publicClient = this.initializePublicClient(config.provider);
54
+ // 如果没有提供 signer,则使用 provider 作为 signer
55
+ const effectiveSigner = config.signer || config.provider;
56
+ this.walletClient = effectiveSigner
57
+ ? this.initializeWalletClient(effectiveSigner)
58
+ : undefined;
59
+ this.account =
60
+ this.walletClient?.account ||
61
+ this.getAccountFromSigner(effectiveSigner);
62
+ }
63
+ // 如果用户直接提供了合约地址,优先使用用户提供的地址
64
+ if (config.anchorPayAddress) {
65
+ this.contracts.anchorPay = config.anchorPayAddress;
66
+ }
67
+ if (config.anchorERC1155Address) {
68
+ this.contracts.anchorERC1155 = config.anchorERC1155Address;
69
+ }
70
+ // 创建客户端
71
+ this.anchorPay = new AnchorPayClient_1.AnchorPayClient(this.publicClient, this.network, this.walletClient, this.account, this.contracts);
72
+ this.anchorERC1155 = new AnchorERC1155Client_1.AnchorERC1155Client(this.publicClient, this.network, this.walletClient, this.account, this.contracts);
73
+ // 处理环境配置
74
+ let apiBaseUrl = config.apiBaseUrl;
75
+ // 如果指定了环境,先尝试从环境配置中获取 API URL 和合约地址
76
+ if (config.env && constants_1.ENVIRONMENTS[config.env]) {
77
+ // 如果没有指定 API URL,使用环境配置中的 URL
78
+ if (!apiBaseUrl) {
79
+ apiBaseUrl = constants_1.ENVIRONMENTS[config.env].apiBaseUrl;
80
+ }
81
+ // 从环境配置中获取当前网络的合约地址(仅当用户没有直接提供时)
82
+ if (this.network.id &&
83
+ constants_1.ENVIRONMENTS[config.env].contracts[this.network.id]) {
84
+ const networkContracts = constants_1.ENVIRONMENTS[config.env].contracts[this.network.id];
85
+ // 只有当用户没有直接提供合约地址时,才使用环境配置中的地址
86
+ if (networkContracts.anchorPay && !this.contracts.anchorPay) {
87
+ this.contracts.anchorPay = networkContracts.anchorPay;
88
+ }
89
+ if (networkContracts.anchorERC1155 && !this.contracts.anchorERC1155) {
90
+ this.contracts.anchorERC1155 = networkContracts.anchorERC1155;
91
+ }
92
+ // 重新初始化客户端
93
+ this.anchorPay = new AnchorPayClient_1.AnchorPayClient(this.publicClient, this.network, this.walletClient, this.account, this.contracts);
94
+ this.anchorERC1155 = new AnchorERC1155Client_1.AnchorERC1155Client(this.publicClient, this.network, this.walletClient, this.account, this.contracts);
95
+ }
96
+ }
97
+ // 如果有 API URL,创建 Anchor API 客户端
98
+ if (apiBaseUrl) {
99
+ this.anchorApi = new AnchorApiClientV2_1.AnchorApiClientV2({
100
+ apiBaseUrl: apiBaseUrl,
101
+ authToken: config.authToken,
102
+ projectId: config.projectId,
103
+ network: this.network,
104
+ onTokenExpired: config.onTokenExpired,
105
+ });
106
+ }
107
+ }
108
+ /**
109
+ * 设置 Token 过期回调
110
+ * @param callback Token 过期回调函数
111
+ */
112
+ setTokenExpiredCallback(callback) {
113
+ if (this.anchorApi) {
114
+ this.anchorApi.setTokenExpiredCallback(callback);
115
+ }
116
+ }
117
+ /**
118
+ * 设置钱包客户端
119
+ * 在 SDK 初始化后动态更新钱包客户端和账户
120
+ * @param walletClient 新的钱包客户端
121
+ */
122
+ setWalletClient(signerOrWalletClient) {
123
+ // 检查是否是 viem WalletClient(向后兼容)
124
+ if (signerOrWalletClient && "account" in signerOrWalletClient) {
125
+ // 使用旧版接口
126
+ this.walletClient = signerOrWalletClient;
127
+ this.account = signerOrWalletClient.account;
128
+ }
129
+ else {
130
+ // 使用新版接口
131
+ // 如果没有提供有效的签名者,则使用当前的 provider
132
+ const effectiveSigner = signerOrWalletClient || this.config.provider;
133
+ this.walletClient = this.initializeWalletClient(effectiveSigner);
134
+ this.account =
135
+ this.walletClient?.account ||
136
+ this.getAccountFromSigner(effectiveSigner);
137
+ }
138
+ // 重新初始化客户端
139
+ this.anchorPay = new AnchorPayClient_1.AnchorPayClient(this.publicClient, this.network, this.walletClient, this.account, this.contracts);
140
+ this.anchorERC1155 = new AnchorERC1155Client_1.AnchorERC1155Client(this.publicClient, this.network, this.walletClient, this.account, this.contracts);
141
+ console.log("SDK wallet client updated:", this.account);
142
+ return this;
143
+ }
144
+ /**
145
+ * 初始化公共客户端
146
+ * @param provider 提供者(URL或Web3提供者)
147
+ * @returns 公共客户端
148
+ */
149
+ initializePublicClient(provider) {
150
+ // If provider is a string URL
151
+ if (typeof provider === "string") {
152
+ return (0, viem_1.createPublicClient)({
153
+ chain: this.network,
154
+ transport: (0, viem_1.http)(provider),
155
+ });
156
+ }
157
+ // If provider is a Web3 Provider (like window.ethereum)
158
+ if (provider && typeof provider === "object") {
159
+ return (0, viem_1.createPublicClient)({
160
+ chain: this.network,
161
+ transport: (0, viem_1.custom)(provider),
162
+ });
163
+ }
164
+ throw new Error("Invalid provider. Please provide a valid RPC URL or Web3 Provider.");
165
+ }
166
+ /**
167
+ * 初始化钱包客户端
168
+ * @param signer 签名者
169
+ * @returns 钱包客户端
170
+ */
171
+ initializeWalletClient(signer) {
172
+ if (!signer)
173
+ return undefined;
174
+ // If signer is a Web3 Provider with accounts
175
+ if (signer && typeof signer === "object") {
176
+ // For providers like window.ethereum that have accounts
177
+ if (signer.selectedAddress) {
178
+ return (0, viem_1.createWalletClient)({
179
+ chain: this.network,
180
+ transport: (0, viem_1.custom)(signer),
181
+ account: signer.selectedAddress,
182
+ });
183
+ }
184
+ // For signers with an address property
185
+ if (signer.address) {
186
+ return (0, viem_1.createWalletClient)({
187
+ chain: this.network,
188
+ transport: (0, viem_1.custom)(signer.provider || signer),
189
+ account: signer.address,
190
+ });
191
+ }
192
+ }
193
+ return undefined;
194
+ }
195
+ /**
196
+ * 从签名者获取账户
197
+ * @param signer 签名者
198
+ * @returns 账户
199
+ */
200
+ getAccountFromSigner(signer) {
201
+ if (!signer)
202
+ return undefined;
203
+ // If signer has selectedAddress (like window.ethereum)
204
+ if (signer.selectedAddress) {
205
+ return {
206
+ address: signer.selectedAddress,
207
+ type: "json-rpc",
208
+ };
209
+ }
210
+ // If signer has address property
211
+ if (signer.address) {
212
+ return {
213
+ address: signer.address,
214
+ type: "json-rpc",
215
+ };
216
+ }
217
+ return undefined;
218
+ }
219
+ /**
220
+ * 从网络信息创建Chain对象
221
+ * @param network 网络信息
222
+ * @returns Chain对象
223
+ */
224
+ createChainFromNetwork(network) {
225
+ // Create a basic Chain object from the provided network info
226
+ return {
227
+ ...network,
228
+ nativeCurrency: network.nativeCurrency || {
229
+ name: "Ether",
230
+ symbol: "ETH",
231
+ decimals: 18,
232
+ },
233
+ rpcUrls: {
234
+ default: {
235
+ http: [
236
+ typeof this.config.provider === "string"
237
+ ? this.config.provider
238
+ : "",
239
+ ],
240
+ },
241
+ public: {
242
+ http: [
243
+ typeof this.config.provider === "string"
244
+ ? this.config.provider
245
+ : "",
246
+ ],
247
+ },
248
+ },
249
+ };
250
+ }
251
+ /**
252
+ * 购买 Badge(使用 ETH,直接使用签名请求)
253
+ * @param signedRequest 签名铸造请求
254
+ * @param receiptAddress 接收地址,如果提供,将作为 ERC20 转账的接收地址使用
255
+ * @param options 支付选项
256
+ * @returns 如果直接发送交易,返回交易收据;否则返回交易数据
257
+ */
258
+ async buyBadgeWithETHWithSignedRequest(signedRequest, receiptAddress, options) {
259
+ // 校验 signedRequest
260
+ if (!signedRequest || !signedRequest.request || !signedRequest.signature) {
261
+ throw new Error("signedRequest is required and must contain request and signature");
262
+ }
263
+ // 校验 receiptAddress
264
+ if (!receiptAddress || !(0, viem_1.isAddress)(receiptAddress)) {
265
+ throw new Error("receiptAddress is required and must be a valid Ethereum address");
266
+ }
267
+ // 如果需要发送交易,但没有钱包客户端或账户
268
+ if (options?.sendTransaction !== false &&
269
+ (!this.walletClient || !this.account)) {
270
+ throw new Error("Wallet client and account are required to send transactions");
271
+ }
272
+ // 检查请求是否有效
273
+ this.validateMintRequest(signedRequest.request);
274
+ // 检查货币是否为 ETH
275
+ if (signedRequest.request.currency !== constants_1.NATIVE_TOKEN_ADDRESS) {
276
+ throw new Error("Currency must be ETH");
277
+ }
278
+ // 计算总价
279
+ const totalPrice = BigInt(signedRequest.request.price) *
280
+ BigInt(signedRequest.request.quantity);
281
+ // 编码数据
282
+ const data = this.anchorERC1155.encodeTokenReceivedData(signedRequest);
283
+ // 发送交易
284
+ const result = await this.anchorPay.sendETH(receiptAddress, totalPrice, data, options);
285
+ // 如果是交易收据并且有 transactionHash,尝试加快交易处理
286
+ // 根据 viem 文档,TransactionReceipt 中的交易哈希字段是 transactionHash,而不是 hash
287
+ if (options?.sendTransaction !== false &&
288
+ result &&
289
+ typeof result === "object" &&
290
+ "transactionHash" in result) {
291
+ try {
292
+ await this.processTransactionHash(result.transactionHash);
293
+ }
294
+ catch (error) {
295
+ // 忽略错误,不影响主流程
296
+ console.warn(`Failed to process transaction hash: ${error instanceof Error ? error.message : String(error)}`);
297
+ }
298
+ }
299
+ return result;
300
+ }
301
+ /**
302
+ * 购买 Badge(使用 ERC20 代币,直接使用签名请求)
303
+ * @param signedRequest 签名铸造请求
304
+ * @param receiptAddress 接收地址,如果提供,将作为接收地址使用
305
+ * @param options 支付选项
306
+ * @returns 如果直接发送交易,返回交易收据;否则返回交易数据
307
+ */
308
+ async buyBadgeWithERC20WithSignedRequest(signedRequest, receiptAddress, options) {
309
+ // 校验 signedRequest
310
+ if (!signedRequest || !signedRequest.request || !signedRequest.signature) {
311
+ throw new Error("signedRequest is required and must contain request and signature");
312
+ }
313
+ // 校验 receiptAddress
314
+ if (!receiptAddress || !(0, viem_1.isAddress)(receiptAddress)) {
315
+ throw new Error("receiptAddress is required and must be a valid Ethereum address");
316
+ }
317
+ // 如果需要发送交易,但没有钱包客户端或账户
318
+ if (options?.sendTransaction !== false &&
319
+ (!this.walletClient || !this.account)) {
320
+ throw new Error("Wallet client and account are required to send transactions");
321
+ }
322
+ // 检查请求是否有效
323
+ this.validateMintRequest(signedRequest.request);
324
+ // 检查货币是否为 ERC20
325
+ if (signedRequest.request.currency === constants_1.NATIVE_TOKEN_ADDRESS) {
326
+ throw new Error("Currency cannot be native token");
327
+ }
328
+ // 计算总价
329
+ const totalPrice = BigInt(signedRequest.request.price) *
330
+ BigInt(signedRequest.request.quantity);
331
+ // 编码数据
332
+ const data = this.anchorERC1155.encodeTokenReceivedData(signedRequest);
333
+ // 发送交易
334
+ const result = await this.anchorPay.sendERC20(receiptAddress, signedRequest.request.currency, totalPrice, data, options);
335
+ // 如果是交易收据并且有 transactionHash,尝试加快交易处理
336
+ // 根据 viem 文档,TransactionReceipt 中的交易哈希字段是 transactionHash,而不是 hash
337
+ if (options?.sendTransaction !== false &&
338
+ result &&
339
+ typeof result === "object" &&
340
+ "transactionHash" in result) {
341
+ try {
342
+ await this.processTransactionHash(result.transactionHash);
343
+ }
344
+ catch (error) {
345
+ // 忽略错误,不影响主流程
346
+ console.warn(`Failed to process transaction hash: ${error instanceof Error ? error.message : String(error)}`);
347
+ }
348
+ }
349
+ return result;
350
+ }
351
+ /**
352
+ * 使用 ETH 购买徽章
353
+ * @param customerAddress 客户地址
354
+ * @param contractAddress NFT 地址
355
+ * @param tokenId 代币 ID
356
+ * @param quantity 数量
357
+ * @param receiptAddress 接收地址,如果提供,将作为接收地址使用
358
+ * @param options 支付选项
359
+ * @returns 如果直接发送交易,返回交易收据;否则返回交易数据
360
+ */
361
+ async buyBadgeWithETH(customerAddress, contractAddress, tokenId, quantity = 1, receiptAddress, options) {
362
+ // 校验地址参数
363
+ if (!customerAddress || !(0, viem_1.isAddress)(customerAddress)) {
364
+ throw new Error("customerAddress is required and must be a valid Ethereum address");
365
+ }
366
+ if (!contractAddress || !(0, viem_1.isAddress)(contractAddress)) {
367
+ throw new Error("contractAddress is required and must be a valid Ethereum address");
368
+ }
369
+ if (!receiptAddress || !(0, viem_1.isAddress)(receiptAddress)) {
370
+ throw new Error("receiptAddress is required and must be a valid Ethereum address");
371
+ }
372
+ // 校验 tokenId 和 quantity
373
+ if (tokenId === undefined || tokenId === "") {
374
+ throw new Error("tokenId is required");
375
+ }
376
+ const quantityBigInt = BigInt(quantity.toString());
377
+ if (quantityBigInt <= 0n) {
378
+ throw new Error("quantity must be greater than 0");
379
+ }
380
+ if (!this.anchorApi) {
381
+ throw new Error("Anchor API client not initialized, please check apiBaseUrl configuration");
382
+ }
383
+ // 如果需要发送交易,但没有钱包客户端或账户
384
+ if (options?.sendTransaction !== false &&
385
+ (!this.walletClient || !this.account)) {
386
+ throw new Error("Wallet client and account are required to send transactions");
387
+ }
388
+ // 使用 claimableIds 来标识要购买的徽章
389
+ const claimableIds = [`${tokenId.toString()}`];
390
+ // 从后端获取铸造请求
391
+ const mintResponse = await this.anchorApi.getBadgeClaimSignatures({
392
+ customerAddress,
393
+ badgeIds: claimableIds,
394
+ });
395
+ if (mintResponse.code !== "0000" ||
396
+ !mintResponse.obj ||
397
+ !mintResponse.obj.signatures ||
398
+ mintResponse.obj.signatures.length === 0) {
399
+ throw new Error("Failed to get mint request from backend");
400
+ }
401
+ const mintRequest = mintResponse.obj.signatures[0];
402
+ // 检查必要字段
403
+ if (!mintRequest.badgeId ||
404
+ !mintRequest.signature ||
405
+ !mintRequest.validityStartTimestamp ||
406
+ !mintRequest.validityEndTimestamp) {
407
+ throw new Error("Mint request missing required fields");
408
+ }
409
+ // 创建签名铸造请求
410
+ const signedRequest = {
411
+ request: {
412
+ to: customerAddress,
413
+ tokenId: BigInt(mintRequest.badgeId || tokenId),
414
+ quantity: BigInt(mintRequest.quantity || quantity),
415
+ price: BigInt(mintRequest.price || 0), // 从签名中获取价格信息
416
+ currency: mintRequest.currency, // 从签名中获取货币信息
417
+ validityStartTimestamp: BigInt(mintRequest.validityStartTimestamp || Math.floor(Date.now() / 1000)), // 立即生效
418
+ validityEndTimestamp: BigInt(mintRequest.validityEndTimestamp ||
419
+ Math.floor(Date.now() / 1000) + 3600), // 1小时后过期
420
+ uid: mintRequest.uid, // 从签名中获取 uid
421
+ chainId: BigInt(mintRequest.chainId || this.network.id), // 从签名中获取链 ID
422
+ projectHandle: mintRequest.projectHandle, // 从签名中获取项目句柄
423
+ },
424
+ signature: mintRequest.signature,
425
+ };
426
+ // 使用签名请求购买徽章(支持 AA 和 EOA 模式)
427
+ return await this.buyBadgeWithETHWithSignedRequest(signedRequest, receiptAddress, options);
428
+ }
429
+ /**
430
+ * 使用自定义签名数据购买徽章(ETH)
431
+ * @param signedRequest 用户自己组装的签名铸造请求
432
+ * @param receiptAddress 接收地址,如果提供,将作为接收地址使用
433
+ * @param options 支付选项
434
+ * @returns 如果直接发送交易,返回交易收据;否则返回交易数据
435
+ */
436
+ async buyBadgeWithETHWithCustomSignature(signedRequest, receiptAddress, options) {
437
+ // 校验 signedRequest
438
+ if (!signedRequest) {
439
+ throw new Error("signedRequest is required");
440
+ }
441
+ // 校验必要字段
442
+ if (!signedRequest.to || !(0, viem_1.isAddress)(signedRequest.to)) {
443
+ throw new Error("signedRequest.to is required and must be a valid Ethereum address");
444
+ }
445
+ if (signedRequest.tokenId === undefined || signedRequest.tokenId === "") {
446
+ throw new Error("signedRequest.tokenId is required");
447
+ }
448
+ if (signedRequest.quantity === undefined ||
449
+ BigInt(signedRequest.quantity.toString()) <= 0n) {
450
+ throw new Error("signedRequest.quantity is required and must be greater than 0");
451
+ }
452
+ if (signedRequest.price === undefined) {
453
+ throw new Error("signedRequest.price is required");
454
+ }
455
+ if (!signedRequest.uid) {
456
+ throw new Error("signedRequest.uid is required");
457
+ }
458
+ if (!signedRequest.signature) {
459
+ throw new Error("signedRequest.signature is required");
460
+ }
461
+ if (!signedRequest.chainId) {
462
+ throw new Error("signedRequest.chainId is required");
463
+ }
464
+ // 校验 receiptAddress
465
+ if (!receiptAddress || !(0, viem_1.isAddress)(receiptAddress)) {
466
+ throw new Error("receiptAddress is required and must be a valid Ethereum address");
467
+ }
468
+ // 将用户提供的数据转换为标准格式
469
+ const formattedRequest = {
470
+ request: {
471
+ to: signedRequest.to,
472
+ tokenId: BigInt(signedRequest.tokenId),
473
+ quantity: BigInt(signedRequest.quantity),
474
+ price: BigInt(signedRequest.price),
475
+ currency: signedRequest.currency,
476
+ validityStartTimestamp: BigInt(signedRequest.validityStartTimestamp ||
477
+ Math.floor(Date.now() / 1000) + 3600),
478
+ validityEndTimestamp: BigInt(signedRequest.validityEndTimestamp ||
479
+ Math.floor(Date.now() / 1000) + 3600),
480
+ uid: signedRequest.uid,
481
+ chainId: BigInt(signedRequest.chainId || this.network.id),
482
+ projectHandle: signedRequest.projectHandle,
483
+ },
484
+ signature: signedRequest.signature.startsWith("0x")
485
+ ? signedRequest.signature
486
+ : `0x${signedRequest.signature}`,
487
+ };
488
+ // 使用标准方法处理
489
+ return await this.buyBadgeWithETHWithSignedRequest(formattedRequest, receiptAddress, options);
490
+ }
491
+ /**
492
+ * 使用 ERC20 代币购买徽章
493
+ * @param customerAddress 客户地址
494
+ * @param contractAddress NFT 地址
495
+ * @param tokenId 代币 ID
496
+ * @param quantity 数量
497
+ * @param currency ERC20 代币地址
498
+ * @param receiptAddress 接收地址,如果提供,将作为接收地址使用
499
+ * @param options 支付选项
500
+ * @returns 如果直接发送交易,返回交易收据;否则返回交易数据
501
+ */
502
+ async buyBadgeWithERC20(customerAddress, contractAddress, tokenId, quantity = 1, currency, receiptAddress, options) {
503
+ if (!this.anchorApi) {
504
+ throw new Error("Anchor API client not initialized, please check apiBaseUrl configuration");
505
+ }
506
+ // 如果需要发送交易,但没有钱包客户端或账户
507
+ if (options?.sendTransaction !== false &&
508
+ (!this.walletClient || !this.account)) {
509
+ throw new Error("Wallet client and account are required to send transactions");
510
+ }
511
+ // 校验地址参数
512
+ if (!customerAddress || !(0, viem_1.isAddress)(customerAddress)) {
513
+ throw new Error("customerAddress is required and must be a valid Ethereum address");
514
+ }
515
+ if (!contractAddress || !(0, viem_1.isAddress)(contractAddress)) {
516
+ throw new Error("contractAddress is required and must be a valid Ethereum address");
517
+ }
518
+ if (!receiptAddress || !(0, viem_1.isAddress)(receiptAddress)) {
519
+ throw new Error("receiptAddress is required and must be a valid Ethereum address");
520
+ }
521
+ if (!currency || !(0, viem_1.isAddress)(currency)) {
522
+ throw new Error("currency is required and must be a valid ERC20 token address");
523
+ }
524
+ // 校验 tokenId 和 quantity
525
+ if (tokenId === undefined || tokenId === "") {
526
+ throw new Error("tokenId is required");
527
+ }
528
+ const quantityBigInt = BigInt(quantity.toString());
529
+ if (quantityBigInt <= 0n) {
530
+ throw new Error("quantity must be greater than 0");
531
+ }
532
+ // 使用 claimableIds 来标识要购买的徽章
533
+ const claimableIds = [`${tokenId.toString()}`];
534
+ // 从后端获取铸造请求
535
+ const mintResponse = await this.anchorApi.getBadgeClaimSignatures({
536
+ customerAddress,
537
+ badgeIds: claimableIds,
538
+ });
539
+ if (mintResponse.code !== "0000" ||
540
+ !mintResponse.obj ||
541
+ !mintResponse.obj.signatures ||
542
+ mintResponse.obj.signatures.length === 0) {
543
+ throw new Error("Failed to get mint request from backend");
544
+ }
545
+ const mintRequest = mintResponse.obj.signatures[0];
546
+ // 检查必要字段
547
+ if (!mintRequest.badgeId ||
548
+ !mintRequest.signature ||
549
+ !mintRequest.validityStartTimestamp ||
550
+ !mintRequest.validityEndTimestamp) {
551
+ throw new Error("Mint request missing required fields");
552
+ }
553
+ // 创建签名铸造请求
554
+ const signedRequest = {
555
+ request: {
556
+ to: customerAddress,
557
+ tokenId: BigInt(mintRequest.badgeId || tokenId),
558
+ quantity: BigInt(mintRequest.quantity || quantity),
559
+ price: BigInt(mintRequest.price || 0), // 从签名中获取价格信息
560
+ currency: mintRequest.currency, // 从签名中获取货币信息
561
+ validityStartTimestamp: BigInt(mintRequest.validityStartTimestamp || Math.floor(Date.now() / 1000)), // 立即生效
562
+ validityEndTimestamp: BigInt(mintRequest.validityEndTimestamp ||
563
+ Math.floor(Date.now() / 1000) + 3600), // 1小时后过期
564
+ uid: mintRequest.uid, // 从签名中获取 uid
565
+ chainId: BigInt(mintRequest.chainId || this.network.id), // 从签名中获取链 ID
566
+ projectHandle: mintRequest.projectHandle, // 从签名中获取项目句柄
567
+ },
568
+ signature: mintRequest.signature,
569
+ };
570
+ // 使用签名请求购买徽章(支持 AA 和 EOA 模式)
571
+ return await this.buyBadgeWithERC20WithSignedRequest(signedRequest, receiptAddress, options);
572
+ }
573
+ /**
574
+ * 使用自定义签名数据购买徽章(ERC20)
575
+ * @param signedRequest 用户自己组装的签名铸造请求
576
+ * @param receiptAddress 接收地址,如果提供,将作为 ERC20 转账的接收地址使用
577
+ * @param options 支付选项
578
+ * @returns 如果直接发送交易,返回交易收据;否则返回交易数据
579
+ */
580
+ async buyBadgeWithERC20WithCustomSignature(signedRequest, receiptAddress, options) {
581
+ // 校验 signedRequest
582
+ if (!signedRequest) {
583
+ throw new Error("signedRequest is required");
584
+ }
585
+ // 校验必要字段
586
+ if (!signedRequest.to || !(0, viem_1.isAddress)(signedRequest.to)) {
587
+ throw new Error("signedRequest.to is required and must be a valid Ethereum address");
588
+ }
589
+ if (signedRequest.tokenId === undefined || signedRequest.tokenId === "") {
590
+ throw new Error("signedRequest.tokenId is required");
591
+ }
592
+ if (signedRequest.quantity === undefined ||
593
+ BigInt(signedRequest.quantity.toString()) <= 0n) {
594
+ throw new Error("signedRequest.quantity is required and must be greater than 0");
595
+ }
596
+ if (signedRequest.price === undefined) {
597
+ throw new Error("signedRequest.price is required");
598
+ }
599
+ if (!signedRequest.uid) {
600
+ throw new Error("signedRequest.uid is required");
601
+ }
602
+ if (!signedRequest.signature) {
603
+ throw new Error("signedRequest.signature is required");
604
+ }
605
+ if (!signedRequest.chainId) {
606
+ throw new Error("signedRequest.chainId is required");
607
+ }
608
+ // 校验 receiptAddress
609
+ if (!receiptAddress || !(0, viem_1.isAddress)(receiptAddress)) {
610
+ throw new Error("receiptAddress is required and must be a valid Ethereum address");
611
+ }
612
+ // 将用户提供的数据转换为标准格式
613
+ const formattedRequest = {
614
+ request: {
615
+ to: signedRequest.to,
616
+ tokenId: BigInt(signedRequest.tokenId),
617
+ quantity: BigInt(signedRequest.quantity),
618
+ price: BigInt(signedRequest.price),
619
+ currency: signedRequest.currency,
620
+ validityStartTimestamp: BigInt(signedRequest.validityStartTimestamp ||
621
+ Math.floor(Date.now() / 1000) + 3600),
622
+ validityEndTimestamp: BigInt(signedRequest.validityEndTimestamp ||
623
+ Math.floor(Date.now() / 1000) + 3600),
624
+ uid: signedRequest.uid,
625
+ chainId: BigInt(signedRequest.chainId || this.network.id),
626
+ projectHandle: signedRequest.projectHandle,
627
+ },
628
+ signature: signedRequest.signature.startsWith("0x")
629
+ ? signedRequest.signature
630
+ : `0x${signedRequest.signature}`,
631
+ };
632
+ // 使用标准方法处理
633
+ return await this.buyBadgeWithERC20WithSignedRequest(formattedRequest, receiptAddress, options);
634
+ }
635
+ /**
636
+ * 支付方法,使用 ETH 进行支付
637
+ * @deprecated 请使用 sendPayment 方法代替。sendPayment 提供了更灵活的支付选项和更好的类型支持。
638
+ * @param orderId 订单 ID
639
+ * @param amount 支付金额
640
+ * @param receiptAddress 接收地址,如果提供,将作为接收地址使用
641
+ * @param options 支付选项
642
+ * @returns 如果直接发送交易,返回交易收据;否则返回交易数据
643
+ */
644
+ async pay(orderId, amount, receiptAddress, options) {
645
+ // 校验 orderId
646
+ if (!orderId || orderId.trim() === "") {
647
+ throw new Error("orderId is required and cannot be empty");
648
+ }
649
+ // 校验 receiptAddress
650
+ if (!receiptAddress || !(0, viem_1.isAddress)(receiptAddress)) {
651
+ throw new Error("receiptAddress is required and must be a valid Ethereum address");
652
+ }
653
+ // 如果需要发送交易,但没有钱包客户端或账户
654
+ if (options?.sendTransaction !== false &&
655
+ (!this.walletClient || !this.account)) {
656
+ throw new Error("Wallet client and account are required to send transactions");
657
+ }
658
+ // 将 ETH 金额转换为 Wei
659
+ const paymentAmount = typeof amount === "string" || typeof amount === "number"
660
+ ? (0, viem_1.parseEther)(amount.toString())
661
+ : amount;
662
+ // 使用 orderId 作为 userData
663
+ const data = this.encodeUserData(orderId);
664
+ const calldata = (0, viem_1.encodeFunctionData)({
665
+ abi: AnchorPay_json_1.default,
666
+ functionName: "send",
667
+ args: [receiptAddress, constants_1.NATIVE_TOKEN_ADDRESS, paymentAmount, data],
668
+ });
669
+ // 如果不需要发送交易,只返回交易数据(AA 模式)
670
+ if (options?.sendTransaction === false) {
671
+ return {
672
+ to: this.contracts?.anchorPay || "",
673
+ data: calldata,
674
+ value: paymentAmount,
675
+ };
676
+ }
677
+ // 发送交易(EOA 模式)
678
+ const txHash = await this.walletClient.sendTransaction({
679
+ account: this.account,
680
+ to: this.contracts?.anchorPay || "",
681
+ data: calldata,
682
+ value: paymentAmount,
683
+ });
684
+ // 等待交易确认并返回收据
685
+ const result = await this.publicClient.waitForTransactionReceipt({
686
+ hash: txHash,
687
+ });
688
+ // 如果是交易收据并且有 transactionHash,尝试加快交易处理
689
+ if (result && typeof result === "object" && "transactionHash" in result) {
690
+ try {
691
+ await this.processTransactionHash(result.transactionHash);
692
+ }
693
+ catch (error) {
694
+ // 忽略错误,不影响主流程
695
+ console.warn(`Failed to process transaction hash: ${error instanceof Error ? error.message : String(error)}`);
696
+ }
697
+ }
698
+ return result;
699
+ }
700
+ /**
701
+ * 支付方法,使用 ETH 进行支付
702
+ * @param params 支付参数
703
+ * @param params.callData 自定义数据
704
+ * @param params.amount 支付金额
705
+ * @param params.receiptAddress 接收地址
706
+ * @param params.contractName 合约名称(可选)
707
+ * @param params.options 支付选项(可选)
708
+ * @returns 如果直接发送交易,返回交易收据;否则返回交易数据
709
+ */
710
+ async sendPaymentWithNativeToken(params) {
711
+ const { callData, amount, receiptAddress, contractName, options } = params;
712
+ if (!callData || callData.trim() === "") {
713
+ throw new Error("callData is required and cannot be empty");
714
+ }
715
+ // 校验 receiptAddress
716
+ if (!receiptAddress || !(0, viem_1.isAddress)(receiptAddress)) {
717
+ throw new Error("receiptAddress is required and must be a valid Ethereum address");
718
+ }
719
+ // 如果需要发送交易,但没有钱包客户端或账户
720
+ if (options?.sendTransaction !== false &&
721
+ (!this.walletClient || !this.account)) {
722
+ throw new Error("Wallet client and account are required to send transactions");
723
+ }
724
+ // 将 ETH 金额转换为 Wei
725
+ const paymentAmount = typeof amount === "string" || typeof amount === "number"
726
+ ? (0, viem_1.parseEther)(amount.toString())
727
+ : amount;
728
+ const interfaceHash = (0, viem_1.keccak256)((0, viem_1.stringToHex)(contractName || ""));
729
+ const data = (0, viem_1.concat)([interfaceHash, callData]);
730
+ const calldata = (0, viem_1.encodeFunctionData)({
731
+ abi: AnchorPay_json_1.default,
732
+ functionName: "send",
733
+ args: [receiptAddress, constants_1.NATIVE_TOKEN_ADDRESS, paymentAmount, data],
734
+ });
735
+ // 如果不需要发送交易,只返回交易数据(AA 模式)
736
+ if (options?.sendTransaction === false) {
737
+ return {
738
+ to: this.contracts?.anchorPay || "",
739
+ data: calldata,
740
+ value: paymentAmount,
741
+ };
742
+ }
743
+ // 发送交易(EOA 模式)
744
+ const txHash = await this.walletClient.sendTransaction({
745
+ account: this.account,
746
+ to: this.contracts?.anchorPay || "",
747
+ data: calldata,
748
+ value: paymentAmount,
749
+ });
750
+ // 等待交易确认并返回收据
751
+ const result = await this.publicClient.waitForTransactionReceipt({
752
+ hash: txHash,
753
+ });
754
+ // 如果是交易收据并且有 transactionHash,尝试加快交易处理
755
+ if (result && typeof result === "object" && "transactionHash" in result) {
756
+ try {
757
+ await this.processTransactionHash(result.transactionHash);
758
+ }
759
+ catch (error) {
760
+ // 忽略错误,不影响主流程
761
+ console.warn(`Failed to process transaction hash: ${error instanceof Error ? error.message : String(error)}`);
762
+ }
763
+ }
764
+ return result;
765
+ }
766
+ /**
767
+ * 支付方法,使用 ERC20 代币进行支付
768
+ * @param params 支付参数
769
+ * @param params.callData 自定义数据
770
+ * @param params.amount 支付金额
771
+ * @param params.receiptAddress 接收地址
772
+ * @param params.tokenAddress ERC20 代币地址
773
+ * @param params.contractName 合约名称(可选)
774
+ * @param params.options 支付选项(可选)
775
+ * @returns 如果直接发送交易,返回交易收据;否则返回交易数据数组
776
+ */
777
+ async sendPaymentWithERC20(params) {
778
+ const { callData, amount, receiptAddress, tokenAddress, contractName, options, } = params;
779
+ if (!callData || callData.trim() === "") {
780
+ throw new Error("callData is required and cannot be empty");
781
+ }
782
+ // 校验 receiptAddress
783
+ if (!receiptAddress || !(0, viem_1.isAddress)(receiptAddress)) {
784
+ throw new Error("receiptAddress is required and must be a valid Ethereum address");
785
+ }
786
+ // 校验 tokenAddress
787
+ if (!tokenAddress || !(0, viem_1.isAddress)(tokenAddress)) {
788
+ throw new Error("tokenAddress is required and must be a valid Ethereum address");
789
+ }
790
+ // 获取代币的小数位数
791
+ const decimals = (await this.publicClient.readContract({
792
+ address: tokenAddress,
793
+ abi: constants_1.ERC20_ABI,
794
+ functionName: "decimals",
795
+ }));
796
+ // 根据代币的小数位数进行转换
797
+ const paymentAmount = typeof amount === "bigint"
798
+ ? amount
799
+ : BigInt(Math.floor(Number(amount) * 10 ** decimals));
800
+ const interfaceHash = (0, viem_1.keccak256)((0, viem_1.stringToHex)(contractName || ""));
801
+ console.log(`Interface hash: ${interfaceHash}`);
802
+ const data = (0, viem_1.concat)([interfaceHash, callData]);
803
+ console.log(`callData: ${callData}`);
804
+ console.log(`Data: ${data}`);
805
+ // 使用 AnchorPayClient 的 sendERC20 方法
806
+ const result = await this.anchorPay.sendERC20(receiptAddress, tokenAddress, paymentAmount, data, options);
807
+ // 如果是交易收据并且有 transactionHash,尝试加快交易处理
808
+ if (result && typeof result === "object" && "transactionHash" in result) {
809
+ try {
810
+ await this.processTransactionHash(result.transactionHash);
811
+ }
812
+ catch (error) {
813
+ console.warn(`Failed to process transaction hash ${result.transactionHash} with Anchor API: ${error}`);
814
+ }
815
+ }
816
+ return result;
817
+ }
818
+ /**
819
+ * 编码用户数据
820
+ * @param orderId 订单 ID
821
+ * @returns 编码后的数据
822
+ */
823
+ encodeUserData(orderId) {
824
+ // 使用 keccak256 哈希函数计算接口哈希
825
+ const interfaceHash = (0, viem_1.keccak256)((0, viem_1.stringToHex)(""));
826
+ console.log(interfaceHash, "interfaceHash");
827
+ // 编码订单 ID
828
+ const encodedOrderId = (0, viem_1.stringToHex)(orderId);
829
+ // 返回完整的编码数据
830
+ console.log((0, viem_1.concat)([interfaceHash, encodedOrderId]), "concat");
831
+ return (0, viem_1.concat)([interfaceHash, encodedOrderId]);
832
+ }
833
+ /**
834
+ * 铸造免费 Badge
835
+ * 从后端获取签名铸造请求,然后调用合约铸造
836
+ * @param customerAddress 客户地址
837
+ * @param contractAddress NFT 地址
838
+ * @param tokenIds badge 的 tokenId 数组
839
+ * @param options 选项,可以控制是否直接发送交易
840
+ * @returns 如果直接发送交易,返回交易收据;否则返回交易数据
841
+ */
842
+ async mintFreeBadge(customerAddress, contractAddress, tokenIds, options) {
843
+ // 校验地址参数
844
+ if (!customerAddress || !(0, viem_1.isAddress)(customerAddress)) {
845
+ throw new Error("customerAddress is required and must be a valid Ethereum address");
846
+ }
847
+ if (!contractAddress || !(0, viem_1.isAddress)(contractAddress)) {
848
+ throw new Error("contractAddress is required and must be a valid Ethereum address");
849
+ }
850
+ // 校验 tokenIds
851
+ if (!tokenIds || !Array.isArray(tokenIds) || tokenIds.length === 0) {
852
+ throw new Error("tokenIds is required and must be a non-empty array");
853
+ }
854
+ for (const id of tokenIds) {
855
+ if (!id || typeof id !== "string") {
856
+ throw new Error("Each tokenId must be a non-empty string");
857
+ }
858
+ }
859
+ if (!this.anchorApi) {
860
+ throw new Error("Anchor API client not initialized, please check apiBaseUrl configuration");
861
+ }
862
+ // 如果需要发送交易,但没有钱包客户端或账户
863
+ if (options?.sendTransaction !== false &&
864
+ (!this.walletClient || !this.account)) {
865
+ throw new Error("Wallet client and account are required to send transactions");
866
+ }
867
+ // 步骤 1: 从后端获取铸造请求
868
+ const mintResponse = await this.anchorApi.getBadgeClaimSignatures({
869
+ customerAddress,
870
+ badgeIds: tokenIds,
871
+ });
872
+ console.log(JSON.stringify(mintResponse));
873
+ if (mintResponse.code !== "0000" ||
874
+ !mintResponse.obj ||
875
+ !mintResponse.obj.signatures ||
876
+ mintResponse.obj.signatures.length === 0) {
877
+ throw new Error("Failed to get mint request from backend");
878
+ }
879
+ if (options?.sendTransaction === false) {
880
+ const mintRequests = mintResponse.obj.signatures; // 获取所有签名
881
+ // 创建批量签名铸造请求数组
882
+ const batchUserOp = await Promise.all(mintRequests.map(async (mintRequest) => {
883
+ // 检查必要字段
884
+ if (!mintRequest.badgeId ||
885
+ !mintRequest.signature ||
886
+ !mintRequest.validityStartTimestamp ||
887
+ !mintRequest.validityEndTimestamp) {
888
+ throw new Error("Mint request missing required fields");
889
+ }
890
+ // 创建签名铸造请求
891
+ const signedRequest = {
892
+ request: {
893
+ to: customerAddress,
894
+ tokenId: BigInt(mintRequest.badgeId),
895
+ quantity: BigInt(mintRequest.quantity || 1),
896
+ price: BigInt(mintRequest.price || 0), // 从签名中获取价格信息
897
+ currency: mintRequest.currency, // ETH
898
+ validityStartTimestamp: BigInt(mintRequest.validityStartTimestamp),
899
+ validityEndTimestamp: BigInt(mintRequest.validityEndTimestamp),
900
+ uid: mintRequest.uid,
901
+ chainId: BigInt(mintRequest.chainId || this.network.id),
902
+ projectHandle: mintRequest.projectHandle,
903
+ },
904
+ signature: mintRequest.signature,
905
+ };
906
+ // 检查请求是否有效
907
+ this.validateMintRequest(signedRequest.request);
908
+ // 检查价格是否为 0
909
+ if (BigInt(signedRequest.request.price) !== 0n) {
910
+ throw new Error("Free Badge price must be 0");
911
+ }
912
+ // 返回单个交易数据
913
+ return (await this.anchorERC1155.mintWithSignature(signedRequest, contractAddress, { sendTransaction: false }));
914
+ }));
915
+ // 返回批量操作数组
916
+ return batchUserOp;
917
+ }
918
+ // 处理所有铸造请求
919
+ let lastReceipt = null;
920
+ for (const mintRequest of mintResponse.obj.signatures) {
921
+ // 检查必要字段
922
+ if (!mintRequest.badgeId ||
923
+ !mintRequest.signature ||
924
+ !mintRequest.validityStartTimestamp ||
925
+ !mintRequest.validityEndTimestamp) {
926
+ console.warn("铸造请求缺失必要字段", mintRequest);
927
+ continue;
928
+ }
929
+ // 创建签名铸造请求
930
+ const signedRequest = {
931
+ request: {
932
+ to: customerAddress,
933
+ tokenId: BigInt(mintRequest.badgeId),
934
+ quantity: BigInt(mintRequest.quantity || 1),
935
+ price: BigInt(mintRequest.price || 0), // 从签名中获取价格信息
936
+ currency: mintRequest.currency, // 从签名中获取货币信息
937
+ validityStartTimestamp: BigInt(mintRequest.validityStartTimestamp),
938
+ validityEndTimestamp: BigInt(mintRequest.validityEndTimestamp),
939
+ uid: mintRequest.uid, // 从签名中获取 uid
940
+ chainId: BigInt(mintRequest.chainId || this.network.id), // 从签名中获取链 ID
941
+ projectHandle: mintRequest.projectHandle, // 从签名中获取项目句柄
942
+ },
943
+ signature: mintRequest.signature,
944
+ };
945
+ // 检查请求是否有效
946
+ this.validateMintRequest(signedRequest.request);
947
+ // 检查价格是否为 0
948
+ if (BigInt(signedRequest.request.price) !== 0n) {
949
+ throw new Error("Free Badge price must be 0");
950
+ }
951
+ console.log(signedRequest, "signedRequest");
952
+ // 调用合约铸造(始终发送交易)
953
+ const result = await this.anchorERC1155.mintWithSignature(signedRequest, contractAddress, { sendTransaction: true });
954
+ // 由于指定了 sendTransaction: true,返回的应该是交易收据
955
+ if ("transactionHash" in result) {
956
+ lastReceipt = result;
957
+ // 尝试加快交易处理
958
+ try {
959
+ await this.processTransactionHash(result.transactionHash);
960
+ }
961
+ catch (error) {
962
+ // 忽略错误,不影响主流程
963
+ console.warn(`Failed to process transaction hash: ${error instanceof Error ? error.message : String(error)}`);
964
+ }
965
+ }
966
+ else {
967
+ throw new Error("Expected transaction receipt, but received transaction data");
968
+ }
969
+ }
970
+ // 返回最后一个交易收据,或者抛出错误
971
+ if (!lastReceipt) {
972
+ throw new Error("No successful mint transaction");
973
+ }
974
+ return lastReceipt;
975
+ }
976
+ /**
977
+ * 铸造免费 Badge(直接使用签名请求)
978
+ * @param signedRequest 签名铸造请求
979
+ * @param options 选项,可以控制是否直接发送交易
980
+ * @returns 如果直接发送交易,返回交易收据;否则返回交易数据
981
+ */
982
+ async mintFreeBadgeWithSignedRequest(signedRequest, options) {
983
+ // 检查请求是否有效
984
+ this.validateMintRequest(signedRequest.request);
985
+ // 检查价格是否为 0
986
+ if (BigInt(signedRequest.request.price) !== 0n) {
987
+ throw new Error("Free Badge price must be 0");
988
+ }
989
+ // 调用 mintWithSignature,并传递选项
990
+ const result = await this.anchorERC1155.mintWithSignature(signedRequest, signedRequest?.request?.contractAddress, options);
991
+ // 如果是交易收据并且有 transactionHash,尝试加快交易处理
992
+ if (options?.sendTransaction !== false &&
993
+ result &&
994
+ typeof result === "object" &&
995
+ "transactionHash" in result) {
996
+ try {
997
+ await this.processTransactionHash(result.transactionHash);
998
+ }
999
+ catch (error) {
1000
+ // 忽略错误,不影响主流程
1001
+ console.warn(`Failed to process transaction hash: ${error instanceof Error ? error.message : String(error)}`);
1002
+ }
1003
+ }
1004
+ return result;
1005
+ }
1006
+ /**
1007
+ * 获取 ERC20 代币批准的交易数据(用于 AA 模式)
1008
+ * @param tokenAddress ERC20 代币地址
1009
+ * @param amount 批准金额
1010
+ * @param spender 花费者地址(默认为 AnchorPay 合约地址)
1011
+ * @returns 交易数据对象,包含 to、data 和 value 字段
1012
+ */
1013
+ getERC20ApprovalData(tokenAddress, amount, spender) {
1014
+ return this.anchorPay.getERC20ApprovalData(tokenAddress, amount, spender);
1015
+ }
1016
+ /**
1017
+ * 验证铸造请求
1018
+ * @param request 铸造请求
1019
+ */
1020
+ validateMintRequest(request) {
1021
+ // 检查接收者地址
1022
+ if (!(0, viem_1.isAddress)(request.to)) {
1023
+ throw new Error("Invalid recipient address");
1024
+ }
1025
+ // 检查有效期
1026
+ const now = Math.floor(Date.now() / 1000);
1027
+ const endTimestamp = request.validityEndTimestamp;
1028
+ if (endTimestamp && BigInt(endTimestamp) < BigInt(now)) {
1029
+ throw new Error("Mint request expired");
1030
+ }
1031
+ // 检查数量
1032
+ if (BigInt(request.quantity) <= 0n) {
1033
+ throw new Error("Quantity must be greater than 0");
1034
+ }
1035
+ }
1036
+ /**
1037
+ * 处理交易哈希
1038
+ * 在交易确认后调用该方法可加快交易处理
1039
+ * @param txHash 交易哈希
1040
+ * @returns 处理结果或 undefined(如果 API 客户端不可用)
1041
+ */
1042
+ async processTransactionHash(txHash) {
1043
+ try {
1044
+ if (this.anchorApi) {
1045
+ const result = await this.anchorApi.transactionHashProcess(txHash);
1046
+ console.log(`Transaction processing accelerated: ${txHash}`);
1047
+ return result;
1048
+ }
1049
+ }
1050
+ catch (error) {
1051
+ console.warn(`Failed to accelerate transaction processing: ${error instanceof Error ? error.message : String(error)}`);
1052
+ // 不影响主流程,只记录日志
1053
+ }
1054
+ return undefined;
1055
+ }
1056
+ /**
1057
+ * 批量铸造徽章(V2)
1058
+ * @param customerAddress 客户地址
1059
+ * @param contractAddress NFT 合约地址
1060
+ * @param tokenIds 代币 ID 列表
1061
+ * @param options 选项,可以控制是否直接发送交易
1062
+ * @returns 如果直接发送交易,返回最后一个交易收据;AA 模式返回所有交易数据数组
1063
+ */
1064
+ async batchMintBadge(customerAddress, contractAddress, tokenIds, options) {
1065
+ if (!this.anchorApi) {
1066
+ throw new Error("Anchor API client not initialized, please check apiBaseUrl configuration");
1067
+ }
1068
+ // 参数校验
1069
+ if (!customerAddress || !(0, viem_1.isAddress)(customerAddress)) {
1070
+ throw new Error("customerAddress is required and must be a valid Ethereum address");
1071
+ }
1072
+ if (!contractAddress || !(0, viem_1.isAddress)(contractAddress)) {
1073
+ throw new Error("contractAddress is required and must be a valid Ethereum address");
1074
+ }
1075
+ if (!Array.isArray(tokenIds) || tokenIds.length === 0) {
1076
+ throw new Error("tokenIds is required and must be a non-empty array");
1077
+ }
1078
+ for (const id of tokenIds) {
1079
+ if (!id || typeof id !== "string") {
1080
+ throw new Error("Each tokenId must be a non-empty string");
1081
+ }
1082
+ }
1083
+ // 如果需要发送交易,但没有钱包客户端或账户
1084
+ if (options?.sendTransaction !== false &&
1085
+ (!this.walletClient || !this.account)) {
1086
+ throw new Error("Wallet client and account are required to send transactions");
1087
+ }
1088
+ // 步骤 1: 从后端获取批量铸造请求
1089
+ // 使用新的 API 设计,一次性获取所有徽章的签名
1090
+ const batchMintResponse = await this.anchorApi.getBadgeClaimSignatures({
1091
+ customerAddress,
1092
+ badgeIds: tokenIds,
1093
+ });
1094
+ if (batchMintResponse.code !== "0000" ||
1095
+ !batchMintResponse.obj ||
1096
+ !batchMintResponse.obj.signatures ||
1097
+ batchMintResponse.obj.signatures.length === 0) {
1098
+ throw new Error("Failed to get batch mint request from backend");
1099
+ }
1100
+ const mintRequests = batchMintResponse.obj.signatures;
1101
+ // AA 模式:只返回所有交易数据
1102
+ if (options?.sendTransaction === false) {
1103
+ const batchUserOp = await Promise.all(mintRequests.map(async (mintRequest) => {
1104
+ // 检查必要字段
1105
+ if (!mintRequest.badgeId ||
1106
+ !mintRequest.signature ||
1107
+ !mintRequest.validityStartTimestamp ||
1108
+ !mintRequest.validityEndTimestamp) {
1109
+ throw new Error("Mint request missing required fields");
1110
+ }
1111
+ // 创建签名铸造请求
1112
+ const signedRequest = {
1113
+ request: {
1114
+ to: customerAddress,
1115
+ tokenId: BigInt(mintRequest.badgeId),
1116
+ quantity: BigInt(mintRequest.quantity || 1), // 默认数量为1
1117
+ price: BigInt(mintRequest.price || 0), // 从签名中获取价格信息
1118
+ currency: mintRequest.currency, // 从签名中获取货币信息
1119
+ validityStartTimestamp: BigInt(mintRequest.validityStartTimestamp),
1120
+ validityEndTimestamp: BigInt(mintRequest.validityEndTimestamp),
1121
+ uid: mintRequest.uid, // 从签名中获取 uid
1122
+ chainId: BigInt(mintRequest.chainId || this.network.id), // 从签名中获取链 ID
1123
+ projectHandle: mintRequest.projectHandle, // 从签名中获取项目句柄
1124
+ },
1125
+ signature: mintRequest.signature,
1126
+ };
1127
+ this.validateMintRequest(signedRequest.request);
1128
+ return (await this.anchorERC1155.mintWithSignature(signedRequest, contractAddress, { sendTransaction: false }));
1129
+ }));
1130
+ return batchUserOp;
1131
+ }
1132
+ // EOA 模式:链上批量 mint,返回最后一个收据
1133
+ let lastReceipt = null;
1134
+ for (const mintRequest of mintRequests) {
1135
+ if (!mintRequest.badgeId ||
1136
+ !mintRequest.signature ||
1137
+ !mintRequest.validityStartTimestamp ||
1138
+ !mintRequest.validityEndTimestamp) {
1139
+ console.warn("铸造请求缺失必要字段", mintRequest);
1140
+ continue;
1141
+ }
1142
+ const signedRequest = {
1143
+ request: {
1144
+ to: customerAddress,
1145
+ tokenId: BigInt(mintRequest.badgeId),
1146
+ quantity: BigInt(mintRequest.quantity || 1), // 默认数量为1
1147
+ price: BigInt(mintRequest.price || 0), // 从签名中获取价格信息
1148
+ currency: mintRequest.currency, // 从签名中获取货币信息
1149
+ validityStartTimestamp: BigInt(mintRequest.validityStartTimestamp),
1150
+ validityEndTimestamp: BigInt(mintRequest.validityEndTimestamp),
1151
+ uid: mintRequest.uid, // 从签名中获取 uid
1152
+ chainId: BigInt(mintRequest.chainId || this.network.id), // 从签名中获取链 ID
1153
+ projectHandle: mintRequest.projectHandle, // 从签名中获取项目句柄
1154
+ },
1155
+ signature: mintRequest.signature,
1156
+ };
1157
+ this.validateMintRequest(signedRequest.request);
1158
+ const result = await this.anchorERC1155.mintWithSignature(signedRequest, contractAddress, { sendTransaction: true });
1159
+ if ("transactionHash" in result) {
1160
+ lastReceipt = result;
1161
+ try {
1162
+ await this.processTransactionHash(result.transactionHash);
1163
+ }
1164
+ catch (error) {
1165
+ console.warn(`Failed to process transaction hash: ${error instanceof Error ? error.message : String(error)}`);
1166
+ }
1167
+ }
1168
+ else {
1169
+ throw new Error("Expected transaction receipt, but received transaction data");
1170
+ }
1171
+ }
1172
+ if (!lastReceipt) {
1173
+ throw new Error("No successful mint transaction");
1174
+ }
1175
+ return lastReceipt;
1176
+ }
1177
+ /**
1178
+ * 批量铸造徽章(Multicall V2)
1179
+ * @param customerAddress 客户地址
1180
+ * @param contractAddress NFT 合约地址
1181
+ * @param tokenIds 代币 ID 列表
1182
+ * @param options 选项,可以控制是否直接发送交易
1183
+ * @returns 如果直接发送交易,返回交易收据;AA 模式返回 multicall 交易数据
1184
+ */
1185
+ async batchMintBadgeWithMulticall(customerAddress, contractAddress, tokenIds, options) {
1186
+ if (!this.anchorApi) {
1187
+ throw new Error("Anchor API client not initialized, please check apiBaseUrl configuration");
1188
+ }
1189
+ // 参数校验
1190
+ if (!customerAddress || !(0, viem_1.isAddress)(customerAddress)) {
1191
+ throw new Error("customerAddress is required and must be a valid Ethereum address");
1192
+ }
1193
+ if (!contractAddress || !(0, viem_1.isAddress)(contractAddress)) {
1194
+ throw new Error("contractAddress is required and must be a valid Ethereum address");
1195
+ }
1196
+ if (!Array.isArray(tokenIds) || tokenIds.length === 0) {
1197
+ throw new Error("tokenIds is required and must be a non-empty array");
1198
+ }
1199
+ // 如果需要发送交易,但没有钱包客户端或账户
1200
+ if (options?.sendTransaction !== false &&
1201
+ (!this.walletClient || !this.account)) {
1202
+ throw new Error("Wallet client and account are required to send transactions");
1203
+ }
1204
+ // 从后端获取批量铸造请求
1205
+ // 使用新的 API 设计,一次性获取所有徽章的签名
1206
+ const batchMintResponse = await this.anchorApi.getBadgeClaimSignatures({
1207
+ customerAddress,
1208
+ badgeIds: tokenIds,
1209
+ });
1210
+ if (batchMintResponse.code !== "0000" ||
1211
+ !batchMintResponse.obj ||
1212
+ !batchMintResponse.obj.signatures ||
1213
+ batchMintResponse.obj.signatures.length === 0) {
1214
+ throw new Error("Failed to get batch mint request from backend");
1215
+ }
1216
+ const mintRequests = batchMintResponse.obj.signatures;
1217
+ // 编码所有 mintWithSignature 调用的 calldata
1218
+ const calldatas = mintRequests.map((mintRequest) => {
1219
+ if (!mintRequest.badgeId ||
1220
+ !mintRequest.signature ||
1221
+ !mintRequest.validityStartTimestamp ||
1222
+ !mintRequest.validityEndTimestamp) {
1223
+ throw new Error("Mint request missing required fields");
1224
+ }
1225
+ const signedRequest = {
1226
+ request: {
1227
+ to: customerAddress,
1228
+ tokenId: BigInt(mintRequest.badgeId),
1229
+ quantity: BigInt(mintRequest.quantity || 1), // 默认数量为1
1230
+ price: BigInt(mintRequest.price || 0), // 从签名中获取价格信息
1231
+ currency: mintRequest.currency, // 从签名中获取货币信息
1232
+ validityStartTimestamp: BigInt(mintRequest.validityStartTimestamp),
1233
+ validityEndTimestamp: BigInt(mintRequest.validityEndTimestamp),
1234
+ uid: mintRequest.uid, // 从签名中获取 uid
1235
+ chainId: BigInt(mintRequest.chainId || this.network.id), // 从签名中获取链 ID
1236
+ projectHandle: mintRequest.projectHandle, // 从签名中获取项目句柄
1237
+ },
1238
+ signature: mintRequest.signature,
1239
+ };
1240
+ this.validateMintRequest(signedRequest.request);
1241
+ // 注意:这里我们假设所有 mint 都发生在同一个 ERC1155 合约上
1242
+ // 并且这个合约地址是在 AnchorERC1155Client 中配置的
1243
+ return (0, viem_1.encodeFunctionData)({
1244
+ abi: AnchorERC1155_1.AnchorERC1155ABI,
1245
+ functionName: "mintWithSignature",
1246
+ args: [
1247
+ this.anchorERC1155["formatMintRequest"](signedRequest.request),
1248
+ signedRequest.signature,
1249
+ ],
1250
+ });
1251
+ });
1252
+ // 调用 multicall
1253
+ const result = await this.anchorERC1155.multicall(calldatas, options);
1254
+ if (options?.sendTransaction !== false &&
1255
+ result &&
1256
+ typeof result === "object" &&
1257
+ "transactionHash" in result) {
1258
+ try {
1259
+ await this.processTransactionHash(result.transactionHash);
1260
+ }
1261
+ catch (error) {
1262
+ console.warn(`Failed to process transaction hash: ${error instanceof Error ? error.message : String(error)}`);
1263
+ }
1264
+ }
1265
+ return result;
1266
+ }
1267
+ /**
1268
+ * 批量铸造免费徽章(Multicall)
1269
+ * 从后端获取签名铸造请求,然后使用 multicall 批量铸造
1270
+ * @param customerAddress 客户地址
1271
+ * @param contractAddress NFT 合约地址
1272
+ * @param tokenIds badge 的 tokenId 数组
1273
+ * @param options 选项,可以控制是否直接发送交易
1274
+ * @returns 如果直接发送交易,返回交易收据;AA 模式返回 multicall 交易数据
1275
+ */
1276
+ async mintFreeBadgeWithMulticall(customerAddress, contractAddress, tokenIds, options) {
1277
+ if (!this.anchorApi) {
1278
+ throw new Error("Anchor API client not initialized, please check apiBaseUrl configuration");
1279
+ }
1280
+ // 参数校验
1281
+ if (!customerAddress || !(0, viem_1.isAddress)(customerAddress)) {
1282
+ throw new Error("customerAddress is required and must be a valid Ethereum address");
1283
+ }
1284
+ if (!contractAddress || !(0, viem_1.isAddress)(contractAddress)) {
1285
+ throw new Error("contractAddress is required and must be a valid Ethereum address");
1286
+ }
1287
+ if (!Array.isArray(tokenIds) || tokenIds.length === 0) {
1288
+ throw new Error("tokenIds is required and must be a non-empty array");
1289
+ }
1290
+ for (const id of tokenIds) {
1291
+ if (!id || typeof id !== "string") {
1292
+ throw new Error("Each tokenId must be a non-empty string");
1293
+ }
1294
+ }
1295
+ // 如果需要发送交易,但没有钱包客户端或账户
1296
+ if (options?.sendTransaction !== false &&
1297
+ (!this.walletClient || !this.account)) {
1298
+ throw new Error("Wallet client and account are required to send transactions");
1299
+ }
1300
+ // 从后端获取免费铸造请求
1301
+ const mintResponse = await this.anchorApi.getBadgeClaimSignatures({
1302
+ customerAddress,
1303
+ badgeIds: tokenIds,
1304
+ });
1305
+ if (mintResponse.code !== "0000" ||
1306
+ !mintResponse.obj ||
1307
+ !mintResponse.obj.signatures ||
1308
+ mintResponse.obj.signatures.length === 0) {
1309
+ throw new Error("Failed to get mint request from backend");
1310
+ }
1311
+ // 编码所有 mintWithSignature 调用的 calldata
1312
+ const calldatas = mintResponse.obj.signatures.map((mintRequest) => {
1313
+ if (!mintRequest.badgeId ||
1314
+ !mintRequest.signature ||
1315
+ !mintRequest.validityStartTimestamp ||
1316
+ !mintRequest.validityEndTimestamp) {
1317
+ throw new Error("Mint request missing required fields");
1318
+ }
1319
+ const signedRequest = {
1320
+ request: {
1321
+ to: customerAddress,
1322
+ tokenId: BigInt(mintRequest.badgeId),
1323
+ quantity: BigInt(mintRequest.quantity || 1), // 默认数量为1
1324
+ price: BigInt(mintRequest.price || 0), // 从签名中获取价格信息
1325
+ currency: mintRequest.currency, // 从签名中获取货币信息
1326
+ validityStartTimestamp: BigInt(mintRequest.validityStartTimestamp),
1327
+ validityEndTimestamp: BigInt(mintRequest.validityEndTimestamp),
1328
+ uid: mintRequest.uid, // 从签名中获取 uid
1329
+ chainId: BigInt(mintRequest.chainId || this.network.id), // 从签名中获取链 ID
1330
+ projectHandle: mintRequest.projectHandle, // 从签名中获取项目句柄
1331
+ },
1332
+ signature: mintRequest.signature,
1333
+ };
1334
+ this.validateMintRequest(signedRequest.request);
1335
+ // 检查价格是否为 0
1336
+ if (BigInt(signedRequest.request.price) !== 0n) {
1337
+ throw new Error("Free Badge price must be 0");
1338
+ }
1339
+ // 编码 mintWithSignature 调用
1340
+ return (0, viem_1.encodeFunctionData)({
1341
+ abi: AnchorERC1155_1.AnchorERC1155ABI,
1342
+ functionName: "mintWithSignature",
1343
+ args: [
1344
+ this.anchorERC1155["formatMintRequest"](signedRequest.request),
1345
+ signedRequest.signature,
1346
+ ],
1347
+ });
1348
+ });
1349
+ // 调用 multicall
1350
+ const result = await this.anchorERC1155.multicall(calldatas, options);
1351
+ if (options?.sendTransaction !== false &&
1352
+ result &&
1353
+ typeof result === "object" &&
1354
+ "transactionHash" in result) {
1355
+ try {
1356
+ await this.processTransactionHash(result.transactionHash);
1357
+ }
1358
+ catch (error) {
1359
+ console.warn(`Failed to process transaction hash: ${error instanceof Error ? error.message : String(error)}`);
1360
+ }
1361
+ }
1362
+ return result;
1363
+ }
1364
+ }
1365
+ exports.AnchorSDK = AnchorSDK;
1366
+ // 导出类型和常量
1367
+ __exportStar(require("./types"), exports);
1368
+ __exportStar(require("./constants"), exports);
1369
+ var AnchorPayClient_2 = require("./AnchorPayClient");
1370
+ Object.defineProperty(exports, "AnchorPayClient", { enumerable: true, get: function () { return AnchorPayClient_2.AnchorPayClient; } });
1371
+ var AnchorApiClientV2_2 = require("./AnchorApiClientV2");
1372
+ Object.defineProperty(exports, "AnchorApiClientV2", { enumerable: true, get: function () { return AnchorApiClientV2_2.AnchorApiClientV2; } });
1373
+ var AnchorERC1155Client_2 = require("./AnchorERC1155Client");
1374
+ Object.defineProperty(exports, "AnchorERC1155Client", { enumerable: true, get: function () { return AnchorERC1155Client_2.AnchorERC1155Client; } });
1375
+ // 导出 React SDK
1376
+ __exportStar(require("./react"), exports);
1377
+ __exportStar(require("./generated/Api"), exports);