anchor-sdk 0.1.41-internal.4 → 0.1.41-internal.6

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.
@@ -1,5 +1,13 @@
1
1
  import { Account, Address, Chain, Hex, PublicClient, TransactionReceipt, WalletClient } from "viem";
2
2
  import { PaymentOptions } from "./types";
3
+ /**
4
+ * 批量转账参数
5
+ */
6
+ export interface BatchTransferItem {
7
+ recipient: Address;
8
+ amount: bigint;
9
+ data?: Hex;
10
+ }
3
11
  /**
4
12
  * AnchorPay 客户端
5
13
  * 用于与 AnchorPay 合约交互
@@ -76,4 +84,90 @@ export declare class AnchorPayClient {
76
84
  data: string;
77
85
  value: bigint;
78
86
  };
87
+ /**
88
+ * 查询账户在 AnchorPay 合约中的代币余额
89
+ * @param account 账户地址
90
+ * @param token 代币地址(使用 NATIVE_TOKEN_ADDRESS 表示原生代币)
91
+ * @returns 余额
92
+ */
93
+ balanceOf(account: Address, token: Address): Promise<bigint>;
94
+ /**
95
+ * 为指定地址存入 ETH 到 AnchorPay 合约
96
+ * @param recipient 接收者地址
97
+ * @param amount 金额(单位:wei)
98
+ * @param options 交易选项
99
+ * @returns 交易收据或交易数据
100
+ */
101
+ depositETH(recipient: Address, amount: bigint, options?: PaymentOptions): Promise<TransactionReceipt | {
102
+ to: string;
103
+ data: string;
104
+ value: bigint;
105
+ }>;
106
+ /**
107
+ * 为指定地址存入 ERC20 代币到 AnchorPay 合约
108
+ * @param recipient 接收者地址
109
+ * @param tokenAddress ERC20 代币地址
110
+ * @param amount 金额
111
+ * @param options 交易选项
112
+ * @returns 交易收据或交易数据
113
+ */
114
+ depositERC20(recipient: Address, tokenAddress: Address, amount: bigint, options?: PaymentOptions): Promise<TransactionReceipt | {
115
+ to: string;
116
+ data: string;
117
+ value: bigint;
118
+ }>;
119
+ /**
120
+ * 从 AnchorPay 合约提取代币
121
+ * @param tokenAddress 代币地址(使用 NATIVE_TOKEN_ADDRESS 表示原生代币)
122
+ * @param amount 金额
123
+ * @param options 交易选项
124
+ * @returns 交易收据
125
+ */
126
+ withdraw(tokenAddress: Address, amount: bigint, options?: PaymentOptions): Promise<TransactionReceipt>;
127
+ /**
128
+ * 批量存入 ETH(使用 Multicall3)
129
+ * @param deposits 批量存款参数数组,每项包含接收者地址和金额
130
+ * @param options 交易选项
131
+ * @returns 交易收据或交易数据
132
+ */
133
+ batchDepositETH(deposits: BatchTransferItem[], options?: PaymentOptions): Promise<TransactionReceipt | {
134
+ to: string;
135
+ data: string;
136
+ value: bigint;
137
+ }>;
138
+ /**
139
+ * 批量存入 ERC20 代币(使用 Multicall3)
140
+ * @param tokenAddress ERC20 代币地址
141
+ * @param deposits 批量存款参数数组
142
+ * @param options 交易选项
143
+ * @returns 交易收据或交易数据
144
+ */
145
+ batchDepositERC20(tokenAddress: Address, deposits: BatchTransferItem[], options?: PaymentOptions): Promise<TransactionReceipt | {
146
+ to: string;
147
+ data: string;
148
+ value: bigint;
149
+ }>;
150
+ /**
151
+ * 批量发送 ETH(使用 Multicall3)
152
+ * @param transfers 批量转账参数数组
153
+ * @param options 交易选项
154
+ * @returns 交易收据或交易数据
155
+ */
156
+ batchSendETH(transfers: BatchTransferItem[], options?: PaymentOptions): Promise<TransactionReceipt | {
157
+ to: string;
158
+ data: string;
159
+ value: bigint;
160
+ }>;
161
+ /**
162
+ * 批量发送 ERC20 代币(使用 Multicall3)
163
+ * @param tokenAddress ERC20 代币地址
164
+ * @param transfers 批量转账参数数组
165
+ * @param options 交易选项
166
+ * @returns 交易收据或交易数据
167
+ */
168
+ batchSendERC20(tokenAddress: Address, transfers: BatchTransferItem[], options?: PaymentOptions): Promise<TransactionReceipt | {
169
+ to: string;
170
+ data: string;
171
+ value: bigint;
172
+ }>;
79
173
  }
@@ -271,5 +271,638 @@ class AnchorPayClient {
271
271
  value: 0n, // ERC20 批准不需要发送 ETH
272
272
  };
273
273
  }
274
+ /**
275
+ * 查询账户在 AnchorPay 合约中的代币余额
276
+ * @param account 账户地址
277
+ * @param token 代币地址(使用 NATIVE_TOKEN_ADDRESS 表示原生代币)
278
+ * @returns 余额
279
+ */
280
+ async balanceOf(account, token) {
281
+ return (await this.publicClient.readContract({
282
+ address: this.contracts?.anchorPay || "",
283
+ abi: AnchorPay_json_1.default,
284
+ functionName: "balanceOf",
285
+ args: [account, token],
286
+ }));
287
+ }
288
+ /**
289
+ * 为指定地址存入 ETH 到 AnchorPay 合约
290
+ * @param recipient 接收者地址
291
+ * @param amount 金额(单位:wei)
292
+ * @param options 交易选项
293
+ * @returns 交易收据或交易数据
294
+ */
295
+ async depositETH(recipient, amount, options) {
296
+ // 如果需要发送交易,但没有钱包客户端或账户
297
+ if (options?.sendTransaction !== false &&
298
+ (!this.walletClient || !this.account)) {
299
+ throw new Error("Wallet client and account are required to send transactions");
300
+ }
301
+ // 准备交易数据
302
+ const calldata = (0, viem_1.encodeFunctionData)({
303
+ abi: AnchorPay_json_1.default,
304
+ functionName: "depositFor",
305
+ args: [recipient, constants_1.NATIVE_TOKEN_ADDRESS, amount],
306
+ });
307
+ // 如果不需要发送交易,只返回交易数据(AA 模式)
308
+ if (options?.sendTransaction === false) {
309
+ return {
310
+ to: this.contracts?.anchorPay || "",
311
+ data: calldata,
312
+ value: amount,
313
+ };
314
+ }
315
+ // 估算 gas 和费用
316
+ const { maxFeePerGas, maxPriorityFeePerGas } = await this.publicClient.estimateFeesPerGas();
317
+ const gas = await this.publicClient.estimateGas({
318
+ to: this.contracts?.anchorPay || "",
319
+ data: calldata,
320
+ value: amount,
321
+ account: this.account,
322
+ });
323
+ // 发送交易(EOA 模式)
324
+ if (!this.walletClient || !this.account) {
325
+ throw new Error("Wallet client and account are required to send transactions");
326
+ }
327
+ const txHash = await this.walletClient.sendTransaction({
328
+ account: this.account,
329
+ to: this.contracts?.anchorPay || "",
330
+ data: calldata,
331
+ value: amount,
332
+ maxFeePerGas: options?.maxFeePerGas || maxFeePerGas,
333
+ maxPriorityFeePerGas: options?.maxPriorityFeePerGas || maxPriorityFeePerGas,
334
+ gas: options?.gas || gas,
335
+ chain: this.publicClient.chain,
336
+ });
337
+ // 等待交易确认并返回收据
338
+ const receipt = await this.publicClient.waitForTransactionReceipt({
339
+ hash: txHash,
340
+ });
341
+ return receipt;
342
+ }
343
+ /**
344
+ * 为指定地址存入 ERC20 代币到 AnchorPay 合约
345
+ * @param recipient 接收者地址
346
+ * @param tokenAddress ERC20 代币地址
347
+ * @param amount 金额
348
+ * @param options 交易选项
349
+ * @returns 交易收据或交易数据
350
+ */
351
+ async depositERC20(recipient, tokenAddress, amount, options) {
352
+ // 如果需要发送交易,但没有钱包客户端或账户
353
+ if (options?.sendTransaction !== false &&
354
+ (!this.walletClient || !this.account)) {
355
+ throw new Error("Wallet client and account are required to send transactions");
356
+ }
357
+ // 如果需要自动批准,则先批准代币(默认开启自动批准)
358
+ if (options?.autoApprove !== false && this.walletClient && this.account) {
359
+ try {
360
+ console.log(`Checking ERC20 token approval status: ${tokenAddress}`);
361
+ // 检查授权
362
+ const allowance = (await this.publicClient.readContract({
363
+ address: tokenAddress,
364
+ abi: constants_1.ERC20_ABI,
365
+ functionName: "allowance",
366
+ args: [this.account.address, this.contracts?.anchorPay],
367
+ }));
368
+ console.log(`Current allowance: ${allowance}, Required amount: ${amount}, AnchorPay contract address: ${this.contracts?.anchorPay}`);
369
+ if (allowance < amount) {
370
+ console.log(`Approving ${amount} tokens to AnchorPay contract...`);
371
+ // 批准代币 - 使用最大可能值进行授权,避免多次授权
372
+ const maxApproveAmount = 2n ** 256n - 1n;
373
+ // 估算 gas 和费用
374
+ const { maxFeePerGas: approveMaxFeePerGas, maxPriorityFeePerGas: approveMaxPriorityFeePerGas, } = await this.publicClient.estimateFeesPerGas();
375
+ const approveCalldata = (0, viem_1.encodeFunctionData)({
376
+ abi: constants_1.ERC20_ABI,
377
+ functionName: "approve",
378
+ args: [this.contracts?.anchorPay, maxApproveAmount],
379
+ });
380
+ const approveGas = await this.publicClient.estimateGas({
381
+ to: tokenAddress,
382
+ data: approveCalldata,
383
+ value: 0n,
384
+ account: this.account,
385
+ });
386
+ const approveTxHash = await this.walletClient.writeContract({
387
+ account: this.account,
388
+ address: tokenAddress,
389
+ abi: constants_1.ERC20_ABI,
390
+ functionName: "approve",
391
+ args: [this.contracts?.anchorPay, maxApproveAmount],
392
+ chain: this.publicClient.chain,
393
+ maxFeePerGas: approveMaxFeePerGas,
394
+ maxPriorityFeePerGas: approveMaxPriorityFeePerGas,
395
+ gas: approveGas,
396
+ });
397
+ console.log(`Approval transaction sent, hash: ${approveTxHash}`);
398
+ // 等待交易确认
399
+ await this.publicClient.waitForTransactionReceipt({
400
+ hash: approveTxHash,
401
+ });
402
+ console.log(`Approval transaction confirmed`);
403
+ }
404
+ else {
405
+ console.log(`Sufficient allowance already exists, no need for additional approval`);
406
+ }
407
+ }
408
+ catch (error) {
409
+ console.error(`Approval failed:`, error);
410
+ throw new Error(`ERC20 approval failed: ${error instanceof Error ? error.message : String(error)}`);
411
+ }
412
+ }
413
+ // 准备交易数据
414
+ const calldata = (0, viem_1.encodeFunctionData)({
415
+ abi: AnchorPay_json_1.default,
416
+ functionName: "depositFor",
417
+ args: [recipient, tokenAddress, amount],
418
+ });
419
+ // 如果不需要发送交易,只返回交易数据(AA 模式)
420
+ if (options?.sendTransaction === false) {
421
+ return {
422
+ to: this.contracts?.anchorPay || "",
423
+ data: calldata,
424
+ value: 0n,
425
+ };
426
+ }
427
+ // 估算 gas 和费用
428
+ const { maxFeePerGas, maxPriorityFeePerGas } = await this.publicClient.estimateFeesPerGas();
429
+ const gas = await this.publicClient.estimateGas({
430
+ to: this.contracts?.anchorPay || "",
431
+ data: calldata,
432
+ value: 0n,
433
+ account: this.account,
434
+ });
435
+ // 发送交易(EOA 模式)
436
+ if (!this.walletClient || !this.account) {
437
+ throw new Error("Wallet client and account are required to send transactions");
438
+ }
439
+ const txHash = await this.walletClient.sendTransaction({
440
+ account: this.account,
441
+ to: this.contracts?.anchorPay || "",
442
+ data: calldata,
443
+ maxFeePerGas: options?.maxFeePerGas || maxFeePerGas,
444
+ maxPriorityFeePerGas: options?.maxPriorityFeePerGas || maxPriorityFeePerGas,
445
+ gas: options?.gas || gas,
446
+ chain: this.publicClient.chain,
447
+ });
448
+ // 等待交易确认并返回收据
449
+ const receipt = await this.publicClient.waitForTransactionReceipt({
450
+ hash: txHash,
451
+ });
452
+ return receipt;
453
+ }
454
+ /**
455
+ * 从 AnchorPay 合约提取代币
456
+ * @param tokenAddress 代币地址(使用 NATIVE_TOKEN_ADDRESS 表示原生代币)
457
+ * @param amount 金额
458
+ * @param options 交易选项
459
+ * @returns 交易收据
460
+ */
461
+ async withdraw(tokenAddress, amount, options) {
462
+ if (!this.walletClient || !this.account) {
463
+ throw new Error("Wallet client and account are required to send transactions");
464
+ }
465
+ // 估算 gas 和费用
466
+ const { maxFeePerGas, maxPriorityFeePerGas } = await this.publicClient.estimateFeesPerGas();
467
+ const calldata = (0, viem_1.encodeFunctionData)({
468
+ abi: AnchorPay_json_1.default,
469
+ functionName: "withdraw",
470
+ args: [tokenAddress, amount],
471
+ });
472
+ const gas = await this.publicClient.estimateGas({
473
+ to: this.contracts?.anchorPay || "",
474
+ data: calldata,
475
+ value: 0n,
476
+ account: this.account,
477
+ });
478
+ const txHash = await this.walletClient.sendTransaction({
479
+ account: this.account,
480
+ to: this.contracts?.anchorPay || "",
481
+ data: calldata,
482
+ maxFeePerGas: options?.maxFeePerGas || maxFeePerGas,
483
+ maxPriorityFeePerGas: options?.maxPriorityFeePerGas || maxPriorityFeePerGas,
484
+ gas: options?.gas || gas,
485
+ chain: this.publicClient.chain,
486
+ });
487
+ // 等待交易确认并返回收据
488
+ const receipt = await this.publicClient.waitForTransactionReceipt({
489
+ hash: txHash,
490
+ });
491
+ return receipt;
492
+ }
493
+ /**
494
+ * 批量存入 ETH(使用 Multicall3)
495
+ * @param deposits 批量存款参数数组,每项包含接收者地址和金额
496
+ * @param options 交易选项
497
+ * @returns 交易收据或交易数据
498
+ */
499
+ async batchDepositETH(deposits, options) {
500
+ if (deposits.length === 0) {
501
+ throw new Error("No deposits provided");
502
+ }
503
+ // 如果需要发送交易,但没有钱包客户端或账户
504
+ if (options?.sendTransaction !== false &&
505
+ (!this.walletClient || !this.account)) {
506
+ throw new Error("Wallet client and account are required to send transactions");
507
+ }
508
+ // 准备批量调用数据
509
+ const calls = deposits.map((deposit) => {
510
+ const calldata = (0, viem_1.encodeFunctionData)({
511
+ abi: AnchorPay_json_1.default,
512
+ functionName: "depositFor",
513
+ args: [deposit.recipient, constants_1.NATIVE_TOKEN_ADDRESS, deposit.amount],
514
+ });
515
+ return {
516
+ target: this.contracts?.anchorPay || "",
517
+ allowFailure: false,
518
+ value: deposit.amount,
519
+ callData: calldata,
520
+ };
521
+ });
522
+ // 计算总金额
523
+ const totalValue = deposits.reduce((sum, deposit) => sum + deposit.amount, 0n);
524
+ // 编码 Multicall3 调用
525
+ const multicallData = (0, viem_1.encodeFunctionData)({
526
+ abi: constants_1.MULTICALL3_ABI,
527
+ functionName: "aggregate3Value",
528
+ args: [calls],
529
+ });
530
+ // 如果不需要发送交易,只返回交易数据(AA 模式)
531
+ if (options?.sendTransaction === false) {
532
+ return {
533
+ to: constants_1.MULTICALL3_ADDRESS,
534
+ data: multicallData,
535
+ value: totalValue,
536
+ };
537
+ }
538
+ // 估算 gas 和费用
539
+ const { maxFeePerGas, maxPriorityFeePerGas } = await this.publicClient.estimateFeesPerGas();
540
+ const gas = await this.publicClient.estimateGas({
541
+ to: constants_1.MULTICALL3_ADDRESS,
542
+ data: multicallData,
543
+ value: totalValue,
544
+ account: this.account,
545
+ });
546
+ // 发送交易(EOA 模式)
547
+ if (!this.walletClient || !this.account) {
548
+ throw new Error("Wallet client and account are required to send transactions");
549
+ }
550
+ const txHash = await this.walletClient.sendTransaction({
551
+ account: this.account,
552
+ to: constants_1.MULTICALL3_ADDRESS,
553
+ data: multicallData,
554
+ value: totalValue,
555
+ maxFeePerGas: options?.maxFeePerGas || maxFeePerGas,
556
+ maxPriorityFeePerGas: options?.maxPriorityFeePerGas || maxPriorityFeePerGas,
557
+ gas: options?.gas || gas,
558
+ chain: this.publicClient.chain,
559
+ });
560
+ // 等待交易确认并返回收据
561
+ const receipt = await this.publicClient.waitForTransactionReceipt({
562
+ hash: txHash,
563
+ });
564
+ return receipt;
565
+ }
566
+ /**
567
+ * 批量存入 ERC20 代币(使用 Multicall3)
568
+ * @param tokenAddress ERC20 代币地址
569
+ * @param deposits 批量存款参数数组
570
+ * @param options 交易选项
571
+ * @returns 交易收据或交易数据
572
+ */
573
+ async batchDepositERC20(tokenAddress, deposits, options) {
574
+ if (deposits.length === 0) {
575
+ throw new Error("No deposits provided");
576
+ }
577
+ // 如果需要发送交易,但没有钱包客户端或账户
578
+ if (options?.sendTransaction !== false &&
579
+ (!this.walletClient || !this.account)) {
580
+ throw new Error("Wallet client and account are required to send transactions");
581
+ }
582
+ // 计算总金额(用于批准检查)
583
+ const totalAmount = deposits.reduce((sum, deposit) => sum + deposit.amount, 0n);
584
+ // 如果需要自动批准,则先批准代币(默认开启自动批准)
585
+ if (options?.autoApprove !== false && this.walletClient && this.account) {
586
+ try {
587
+ console.log(`Checking ERC20 token approval status: ${tokenAddress}`);
588
+ // 检查授权
589
+ const allowance = (await this.publicClient.readContract({
590
+ address: tokenAddress,
591
+ abi: constants_1.ERC20_ABI,
592
+ functionName: "allowance",
593
+ args: [this.account.address, this.contracts?.anchorPay],
594
+ }));
595
+ console.log(`Current allowance: ${allowance}, Required amount: ${totalAmount}, AnchorPay contract address: ${this.contracts?.anchorPay}`);
596
+ if (allowance < totalAmount) {
597
+ console.log(`Approving ${totalAmount} tokens to AnchorPay contract...`);
598
+ // 批准代币 - 使用最大可能值进行授权,避免多次授权
599
+ const maxApproveAmount = 2n ** 256n - 1n;
600
+ // 估算 gas 和费用
601
+ const { maxFeePerGas: approveMaxFeePerGas, maxPriorityFeePerGas: approveMaxPriorityFeePerGas, } = await this.publicClient.estimateFeesPerGas();
602
+ const approveCalldata = (0, viem_1.encodeFunctionData)({
603
+ abi: constants_1.ERC20_ABI,
604
+ functionName: "approve",
605
+ args: [this.contracts?.anchorPay, maxApproveAmount],
606
+ });
607
+ const approveGas = await this.publicClient.estimateGas({
608
+ to: tokenAddress,
609
+ data: approveCalldata,
610
+ value: 0n,
611
+ account: this.account,
612
+ });
613
+ const approveTxHash = await this.walletClient.writeContract({
614
+ account: this.account,
615
+ address: tokenAddress,
616
+ abi: constants_1.ERC20_ABI,
617
+ functionName: "approve",
618
+ args: [this.contracts?.anchorPay, maxApproveAmount],
619
+ chain: this.publicClient.chain,
620
+ maxFeePerGas: approveMaxFeePerGas,
621
+ maxPriorityFeePerGas: approveMaxPriorityFeePerGas,
622
+ gas: approveGas,
623
+ });
624
+ console.log(`Approval transaction sent, hash: ${approveTxHash}`);
625
+ // 等待交易确认
626
+ await this.publicClient.waitForTransactionReceipt({
627
+ hash: approveTxHash,
628
+ });
629
+ console.log(`Approval transaction confirmed`);
630
+ }
631
+ else {
632
+ console.log(`Sufficient allowance already exists, no need for additional approval`);
633
+ }
634
+ }
635
+ catch (error) {
636
+ console.error(`Approval failed:`, error);
637
+ throw new Error(`ERC20 approval failed: ${error instanceof Error ? error.message : String(error)}`);
638
+ }
639
+ }
640
+ // 准备批量调用数据
641
+ const calls = deposits.map((deposit) => {
642
+ const calldata = (0, viem_1.encodeFunctionData)({
643
+ abi: AnchorPay_json_1.default,
644
+ functionName: "depositFor",
645
+ args: [deposit.recipient, tokenAddress, deposit.amount],
646
+ });
647
+ return {
648
+ target: this.contracts?.anchorPay || "",
649
+ allowFailure: false,
650
+ value: 0n,
651
+ callData: calldata,
652
+ };
653
+ });
654
+ // 编码 Multicall3 调用
655
+ const multicallData = (0, viem_1.encodeFunctionData)({
656
+ abi: constants_1.MULTICALL3_ABI,
657
+ functionName: "aggregate3Value",
658
+ args: [calls],
659
+ });
660
+ // 如果不需要发送交易,只返回交易数据(AA 模式)
661
+ if (options?.sendTransaction === false) {
662
+ return {
663
+ to: constants_1.MULTICALL3_ADDRESS,
664
+ data: multicallData,
665
+ value: 0n,
666
+ };
667
+ }
668
+ // 估算 gas 和费用
669
+ const { maxFeePerGas, maxPriorityFeePerGas } = await this.publicClient.estimateFeesPerGas();
670
+ const gas = await this.publicClient.estimateGas({
671
+ to: constants_1.MULTICALL3_ADDRESS,
672
+ data: multicallData,
673
+ value: 0n,
674
+ account: this.account,
675
+ });
676
+ // 发送交易(EOA 模式)
677
+ if (!this.walletClient || !this.account) {
678
+ throw new Error("Wallet client and account are required to send transactions");
679
+ }
680
+ const txHash = await this.walletClient.sendTransaction({
681
+ account: this.account,
682
+ to: constants_1.MULTICALL3_ADDRESS,
683
+ data: multicallData,
684
+ maxFeePerGas: options?.maxFeePerGas || maxFeePerGas,
685
+ maxPriorityFeePerGas: options?.maxPriorityFeePerGas || maxPriorityFeePerGas,
686
+ gas: options?.gas || gas,
687
+ chain: this.publicClient.chain,
688
+ });
689
+ // 等待交易确认并返回收据
690
+ const receipt = await this.publicClient.waitForTransactionReceipt({
691
+ hash: txHash,
692
+ });
693
+ return receipt;
694
+ }
695
+ /**
696
+ * 批量发送 ETH(使用 Multicall3)
697
+ * @param transfers 批量转账参数数组
698
+ * @param options 交易选项
699
+ * @returns 交易收据或交易数据
700
+ */
701
+ async batchSendETH(transfers, options) {
702
+ if (transfers.length === 0) {
703
+ throw new Error("No transfers provided");
704
+ }
705
+ // 如果需要发送交易,但没有钱包客户端或账户
706
+ if (options?.sendTransaction !== false &&
707
+ (!this.walletClient || !this.account)) {
708
+ throw new Error("Wallet client and account are required to send transactions");
709
+ }
710
+ // 准备批量调用数据
711
+ const calls = transfers.map((transfer) => {
712
+ const calldata = (0, viem_1.encodeFunctionData)({
713
+ abi: AnchorPay_json_1.default,
714
+ functionName: "send",
715
+ args: [
716
+ transfer.recipient,
717
+ constants_1.NATIVE_TOKEN_ADDRESS,
718
+ transfer.amount,
719
+ transfer.data || "0x",
720
+ ],
721
+ });
722
+ return {
723
+ target: this.contracts?.anchorPay || "",
724
+ allowFailure: false,
725
+ value: transfer.amount,
726
+ callData: calldata,
727
+ };
728
+ });
729
+ // 计算总金额
730
+ const totalValue = transfers.reduce((sum, transfer) => sum + transfer.amount, 0n);
731
+ // 编码 Multicall3 调用
732
+ const multicallData = (0, viem_1.encodeFunctionData)({
733
+ abi: constants_1.MULTICALL3_ABI,
734
+ functionName: "aggregate3Value",
735
+ args: [calls],
736
+ });
737
+ // 如果不需要发送交易,只返回交易数据(AA 模式)
738
+ if (options?.sendTransaction === false) {
739
+ return {
740
+ to: constants_1.MULTICALL3_ADDRESS,
741
+ data: multicallData,
742
+ value: totalValue,
743
+ };
744
+ }
745
+ // 估算 gas 和费用
746
+ const { maxFeePerGas, maxPriorityFeePerGas } = await this.publicClient.estimateFeesPerGas();
747
+ const gas = await this.publicClient.estimateGas({
748
+ to: constants_1.MULTICALL3_ADDRESS,
749
+ data: multicallData,
750
+ value: totalValue,
751
+ account: this.account,
752
+ });
753
+ // 发送交易(EOA 模式)
754
+ if (!this.walletClient || !this.account) {
755
+ throw new Error("Wallet client and account are required to send transactions");
756
+ }
757
+ const txHash = await this.walletClient.sendTransaction({
758
+ account: this.account,
759
+ to: constants_1.MULTICALL3_ADDRESS,
760
+ data: multicallData,
761
+ value: totalValue,
762
+ maxFeePerGas: options?.maxFeePerGas || maxFeePerGas,
763
+ maxPriorityFeePerGas: options?.maxPriorityFeePerGas || maxPriorityFeePerGas,
764
+ gas: options?.gas || gas,
765
+ chain: this.publicClient.chain,
766
+ });
767
+ // 等待交易确认并返回收据
768
+ const receipt = await this.publicClient.waitForTransactionReceipt({
769
+ hash: txHash,
770
+ });
771
+ return receipt;
772
+ }
773
+ /**
774
+ * 批量发送 ERC20 代币(使用 Multicall3)
775
+ * @param tokenAddress ERC20 代币地址
776
+ * @param transfers 批量转账参数数组
777
+ * @param options 交易选项
778
+ * @returns 交易收据或交易数据
779
+ */
780
+ async batchSendERC20(tokenAddress, transfers, options) {
781
+ if (transfers.length === 0) {
782
+ throw new Error("No transfers provided");
783
+ }
784
+ // 如果需要发送交易,但没有钱包客户端或账户
785
+ if (options?.sendTransaction !== false &&
786
+ (!this.walletClient || !this.account)) {
787
+ throw new Error("Wallet client and account are required to send transactions");
788
+ }
789
+ // 计算总金额(用于批准检查)
790
+ const totalAmount = transfers.reduce((sum, transfer) => sum + transfer.amount, 0n);
791
+ // 如果需要自动批准,则先批准代币(默认开启自动批准)
792
+ if (options?.autoApprove !== false && this.walletClient && this.account) {
793
+ try {
794
+ console.log(`Checking ERC20 token approval status: ${tokenAddress}`);
795
+ // 检查授权
796
+ const allowance = (await this.publicClient.readContract({
797
+ address: tokenAddress,
798
+ abi: constants_1.ERC20_ABI,
799
+ functionName: "allowance",
800
+ args: [this.account.address, this.contracts?.anchorPay],
801
+ }));
802
+ console.log(`Current allowance: ${allowance}, Required amount: ${totalAmount}, AnchorPay contract address: ${this.contracts?.anchorPay}`);
803
+ if (allowance < totalAmount) {
804
+ console.log(`Approving ${totalAmount} tokens to AnchorPay contract...`);
805
+ // 批准代币 - 使用最大可能值进行授权,避免多次授权
806
+ const maxApproveAmount = 2n ** 256n - 1n;
807
+ // 估算 gas 和费用
808
+ const { maxFeePerGas: approveMaxFeePerGas, maxPriorityFeePerGas: approveMaxPriorityFeePerGas, } = await this.publicClient.estimateFeesPerGas();
809
+ const approveCalldata = (0, viem_1.encodeFunctionData)({
810
+ abi: constants_1.ERC20_ABI,
811
+ functionName: "approve",
812
+ args: [this.contracts?.anchorPay, maxApproveAmount],
813
+ });
814
+ const approveGas = await this.publicClient.estimateGas({
815
+ to: tokenAddress,
816
+ data: approveCalldata,
817
+ value: 0n,
818
+ account: this.account,
819
+ });
820
+ const approveTxHash = await this.walletClient.writeContract({
821
+ account: this.account,
822
+ address: tokenAddress,
823
+ abi: constants_1.ERC20_ABI,
824
+ functionName: "approve",
825
+ args: [this.contracts?.anchorPay, maxApproveAmount],
826
+ chain: this.publicClient.chain,
827
+ maxFeePerGas: approveMaxFeePerGas,
828
+ maxPriorityFeePerGas: approveMaxPriorityFeePerGas,
829
+ gas: approveGas,
830
+ });
831
+ console.log(`Approval transaction sent, hash: ${approveTxHash}`);
832
+ // 等待交易确认
833
+ await this.publicClient.waitForTransactionReceipt({
834
+ hash: approveTxHash,
835
+ });
836
+ console.log(`Approval transaction confirmed`);
837
+ }
838
+ else {
839
+ console.log(`Sufficient allowance already exists, no need for additional approval`);
840
+ }
841
+ }
842
+ catch (error) {
843
+ console.error(`Approval failed:`, error);
844
+ throw new Error(`ERC20 approval failed: ${error instanceof Error ? error.message : String(error)}`);
845
+ }
846
+ }
847
+ // 准备批量调用数据
848
+ const calls = transfers.map((transfer) => {
849
+ const calldata = (0, viem_1.encodeFunctionData)({
850
+ abi: AnchorPay_json_1.default,
851
+ functionName: "send",
852
+ args: [
853
+ transfer.recipient,
854
+ tokenAddress,
855
+ transfer.amount,
856
+ transfer.data || "0x",
857
+ ],
858
+ });
859
+ return {
860
+ target: this.contracts?.anchorPay || "",
861
+ allowFailure: false,
862
+ value: 0n,
863
+ callData: calldata,
864
+ };
865
+ });
866
+ // 编码 Multicall3 调用
867
+ const multicallData = (0, viem_1.encodeFunctionData)({
868
+ abi: constants_1.MULTICALL3_ABI,
869
+ functionName: "aggregate3Value",
870
+ args: [calls],
871
+ });
872
+ // 如果不需要发送交易,只返回交易数据(AA 模式)
873
+ if (options?.sendTransaction === false) {
874
+ return {
875
+ to: constants_1.MULTICALL3_ADDRESS,
876
+ data: multicallData,
877
+ value: 0n,
878
+ };
879
+ }
880
+ // 估算 gas 和费用
881
+ const { maxFeePerGas, maxPriorityFeePerGas } = await this.publicClient.estimateFeesPerGas();
882
+ const gas = await this.publicClient.estimateGas({
883
+ to: constants_1.MULTICALL3_ADDRESS,
884
+ data: multicallData,
885
+ value: 0n,
886
+ account: this.account,
887
+ });
888
+ // 发送交易(EOA 模式)
889
+ if (!this.walletClient || !this.account) {
890
+ throw new Error("Wallet client and account are required to send transactions");
891
+ }
892
+ const txHash = await this.walletClient.sendTransaction({
893
+ account: this.account,
894
+ to: constants_1.MULTICALL3_ADDRESS,
895
+ data: multicallData,
896
+ maxFeePerGas: options?.maxFeePerGas || maxFeePerGas,
897
+ maxPriorityFeePerGas: options?.maxPriorityFeePerGas || maxPriorityFeePerGas,
898
+ gas: options?.gas || gas,
899
+ chain: this.publicClient.chain,
900
+ });
901
+ // 等待交易确认并返回收据
902
+ const receipt = await this.publicClient.waitForTransactionReceipt({
903
+ hash: txHash,
904
+ });
905
+ return receipt;
906
+ }
274
907
  }
275
908
  exports.AnchorPayClient = AnchorPayClient;
@@ -24,6 +24,53 @@
24
24
  "outputs": [],
25
25
  "stateMutability": "nonpayable"
26
26
  },
27
+ {
28
+ "type": "function",
29
+ "name": "balanceOf",
30
+ "inputs": [
31
+ {
32
+ "name": "account",
33
+ "type": "address",
34
+ "internalType": "address"
35
+ },
36
+ {
37
+ "name": "token",
38
+ "type": "address",
39
+ "internalType": "address"
40
+ }
41
+ ],
42
+ "outputs": [
43
+ {
44
+ "name": "",
45
+ "type": "uint256",
46
+ "internalType": "uint256"
47
+ }
48
+ ],
49
+ "stateMutability": "view"
50
+ },
51
+ {
52
+ "type": "function",
53
+ "name": "depositFor",
54
+ "inputs": [
55
+ {
56
+ "name": "recipient",
57
+ "type": "address",
58
+ "internalType": "address"
59
+ },
60
+ {
61
+ "name": "token",
62
+ "type": "address",
63
+ "internalType": "address"
64
+ },
65
+ {
66
+ "name": "amount",
67
+ "type": "uint256",
68
+ "internalType": "uint256"
69
+ }
70
+ ],
71
+ "outputs": [],
72
+ "stateMutability": "payable"
73
+ },
27
74
  {
28
75
  "type": "function",
29
76
  "name": "getInterfaceImplementer",
@@ -203,6 +250,55 @@
203
250
  "outputs": [],
204
251
  "stateMutability": "payable"
205
252
  },
253
+ {
254
+ "type": "function",
255
+ "name": "withdraw",
256
+ "inputs": [
257
+ {
258
+ "name": "token",
259
+ "type": "address",
260
+ "internalType": "address"
261
+ },
262
+ {
263
+ "name": "amount",
264
+ "type": "uint256",
265
+ "internalType": "uint256"
266
+ }
267
+ ],
268
+ "outputs": [],
269
+ "stateMutability": "nonpayable"
270
+ },
271
+ {
272
+ "type": "event",
273
+ "name": "Deposited",
274
+ "inputs": [
275
+ {
276
+ "name": "from",
277
+ "type": "address",
278
+ "indexed": true,
279
+ "internalType": "address"
280
+ },
281
+ {
282
+ "name": "recipient",
283
+ "type": "address",
284
+ "indexed": true,
285
+ "internalType": "address"
286
+ },
287
+ {
288
+ "name": "token",
289
+ "type": "address",
290
+ "indexed": true,
291
+ "internalType": "address"
292
+ },
293
+ {
294
+ "name": "amount",
295
+ "type": "uint256",
296
+ "indexed": false,
297
+ "internalType": "uint256"
298
+ }
299
+ ],
300
+ "anonymous": false
301
+ },
206
302
  {
207
303
  "type": "event",
208
304
  "name": "Initialized",
@@ -323,6 +419,31 @@
323
419
  ],
324
420
  "anonymous": false
325
421
  },
422
+ {
423
+ "type": "event",
424
+ "name": "Withdrawn",
425
+ "inputs": [
426
+ {
427
+ "name": "recipient",
428
+ "type": "address",
429
+ "indexed": true,
430
+ "internalType": "address"
431
+ },
432
+ {
433
+ "name": "token",
434
+ "type": "address",
435
+ "indexed": true,
436
+ "internalType": "address"
437
+ },
438
+ {
439
+ "name": "amount",
440
+ "type": "uint256",
441
+ "indexed": false,
442
+ "internalType": "uint256"
443
+ }
444
+ ],
445
+ "anonymous": false
446
+ },
326
447
  {
327
448
  "type": "error",
328
449
  "name": "AddressEmptyCode",
@@ -360,6 +481,11 @@
360
481
  "name": "FailedCall",
361
482
  "inputs": []
362
483
  },
484
+ {
485
+ "type": "error",
486
+ "name": "InsufficientBalance",
487
+ "inputs": []
488
+ },
363
489
  {
364
490
  "type": "error",
365
491
  "name": "InsufficientPayment",
@@ -47,3 +47,114 @@ export declare const INTERFACE_HASHES: {
47
47
  TOKEN_RECEIVED: string;
48
48
  };
49
49
  export declare const NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
50
+ /**
51
+ * Multicall3 合约地址(跨链通用)
52
+ */
53
+ export declare const MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
54
+ /**
55
+ * Multicall3 合约 ABI
56
+ */
57
+ export declare const MULTICALL3_ABI: readonly [{
58
+ readonly inputs: readonly [{
59
+ readonly components: readonly [{
60
+ readonly internalType: "address";
61
+ readonly name: "target";
62
+ readonly type: "address";
63
+ }, {
64
+ readonly internalType: "bytes";
65
+ readonly name: "callData";
66
+ readonly type: "bytes";
67
+ }];
68
+ readonly internalType: "struct Multicall3.Call[]";
69
+ readonly name: "calls";
70
+ readonly type: "tuple[]";
71
+ }];
72
+ readonly name: "aggregate";
73
+ readonly outputs: readonly [{
74
+ readonly internalType: "uint256";
75
+ readonly name: "blockNumber";
76
+ readonly type: "uint256";
77
+ }, {
78
+ readonly internalType: "bytes[]";
79
+ readonly name: "returnData";
80
+ readonly type: "bytes[]";
81
+ }];
82
+ readonly stateMutability: "payable";
83
+ readonly type: "function";
84
+ }, {
85
+ readonly inputs: readonly [{
86
+ readonly components: readonly [{
87
+ readonly internalType: "address";
88
+ readonly name: "target";
89
+ readonly type: "address";
90
+ }, {
91
+ readonly internalType: "bool";
92
+ readonly name: "allowFailure";
93
+ readonly type: "bool";
94
+ }, {
95
+ readonly internalType: "bytes";
96
+ readonly name: "callData";
97
+ readonly type: "bytes";
98
+ }];
99
+ readonly internalType: "struct Multicall3.Call3[]";
100
+ readonly name: "calls";
101
+ readonly type: "tuple[]";
102
+ }];
103
+ readonly name: "aggregate3";
104
+ readonly outputs: readonly [{
105
+ readonly components: readonly [{
106
+ readonly internalType: "bool";
107
+ readonly name: "success";
108
+ readonly type: "bool";
109
+ }, {
110
+ readonly internalType: "bytes";
111
+ readonly name: "returnData";
112
+ readonly type: "bytes";
113
+ }];
114
+ readonly internalType: "struct Multicall3.Result[]";
115
+ readonly name: "returnData";
116
+ readonly type: "tuple[]";
117
+ }];
118
+ readonly stateMutability: "payable";
119
+ readonly type: "function";
120
+ }, {
121
+ readonly inputs: readonly [{
122
+ readonly components: readonly [{
123
+ readonly internalType: "address";
124
+ readonly name: "target";
125
+ readonly type: "address";
126
+ }, {
127
+ readonly internalType: "bool";
128
+ readonly name: "allowFailure";
129
+ readonly type: "bool";
130
+ }, {
131
+ readonly internalType: "uint256";
132
+ readonly name: "value";
133
+ readonly type: "uint256";
134
+ }, {
135
+ readonly internalType: "bytes";
136
+ readonly name: "callData";
137
+ readonly type: "bytes";
138
+ }];
139
+ readonly internalType: "struct Multicall3.Call3Value[]";
140
+ readonly name: "calls";
141
+ readonly type: "tuple[]";
142
+ }];
143
+ readonly name: "aggregate3Value";
144
+ readonly outputs: readonly [{
145
+ readonly components: readonly [{
146
+ readonly internalType: "bool";
147
+ readonly name: "success";
148
+ readonly type: "bool";
149
+ }, {
150
+ readonly internalType: "bytes";
151
+ readonly name: "returnData";
152
+ readonly type: "bytes";
153
+ }];
154
+ readonly internalType: "struct Multicall3.Result[]";
155
+ readonly name: "returnData";
156
+ readonly type: "tuple[]";
157
+ }];
158
+ readonly stateMutability: "payable";
159
+ readonly type: "function";
160
+ }];
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NATIVE_TOKEN_ADDRESS = exports.INTERFACE_HASHES = exports.ENVIRONMENTS = exports.ERC20_ABI = exports.Environment = void 0;
3
+ exports.MULTICALL3_ABI = exports.MULTICALL3_ADDRESS = exports.NATIVE_TOKEN_ADDRESS = exports.INTERFACE_HASHES = exports.ENVIRONMENTS = exports.ERC20_ABI = exports.Environment = void 0;
4
4
  const chains_1 = require("viem/chains");
5
5
  /**
6
6
  * 环境枚举
@@ -219,3 +219,149 @@ exports.INTERFACE_HASHES = {
219
219
  TOKEN_RECEIVED: "0x8b2cb2c7b7e2282e2e4b29a6a8c2b2db4218c6a5f8e8fcb3d5598f3e1e6c59d9",
220
220
  };
221
221
  exports.NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
222
+ /**
223
+ * Multicall3 合约地址(跨链通用)
224
+ */
225
+ exports.MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
226
+ /**
227
+ * Multicall3 合约 ABI
228
+ */
229
+ exports.MULTICALL3_ABI = [
230
+ {
231
+ inputs: [
232
+ {
233
+ components: [
234
+ {
235
+ internalType: "address",
236
+ name: "target",
237
+ type: "address",
238
+ },
239
+ {
240
+ internalType: "bytes",
241
+ name: "callData",
242
+ type: "bytes",
243
+ },
244
+ ],
245
+ internalType: "struct Multicall3.Call[]",
246
+ name: "calls",
247
+ type: "tuple[]",
248
+ },
249
+ ],
250
+ name: "aggregate",
251
+ outputs: [
252
+ {
253
+ internalType: "uint256",
254
+ name: "blockNumber",
255
+ type: "uint256",
256
+ },
257
+ {
258
+ internalType: "bytes[]",
259
+ name: "returnData",
260
+ type: "bytes[]",
261
+ },
262
+ ],
263
+ stateMutability: "payable",
264
+ type: "function",
265
+ },
266
+ {
267
+ inputs: [
268
+ {
269
+ components: [
270
+ {
271
+ internalType: "address",
272
+ name: "target",
273
+ type: "address",
274
+ },
275
+ {
276
+ internalType: "bool",
277
+ name: "allowFailure",
278
+ type: "bool",
279
+ },
280
+ {
281
+ internalType: "bytes",
282
+ name: "callData",
283
+ type: "bytes",
284
+ },
285
+ ],
286
+ internalType: "struct Multicall3.Call3[]",
287
+ name: "calls",
288
+ type: "tuple[]",
289
+ },
290
+ ],
291
+ name: "aggregate3",
292
+ outputs: [
293
+ {
294
+ components: [
295
+ {
296
+ internalType: "bool",
297
+ name: "success",
298
+ type: "bool",
299
+ },
300
+ {
301
+ internalType: "bytes",
302
+ name: "returnData",
303
+ type: "bytes",
304
+ },
305
+ ],
306
+ internalType: "struct Multicall3.Result[]",
307
+ name: "returnData",
308
+ type: "tuple[]",
309
+ },
310
+ ],
311
+ stateMutability: "payable",
312
+ type: "function",
313
+ },
314
+ {
315
+ inputs: [
316
+ {
317
+ components: [
318
+ {
319
+ internalType: "address",
320
+ name: "target",
321
+ type: "address",
322
+ },
323
+ {
324
+ internalType: "bool",
325
+ name: "allowFailure",
326
+ type: "bool",
327
+ },
328
+ {
329
+ internalType: "uint256",
330
+ name: "value",
331
+ type: "uint256",
332
+ },
333
+ {
334
+ internalType: "bytes",
335
+ name: "callData",
336
+ type: "bytes",
337
+ },
338
+ ],
339
+ internalType: "struct Multicall3.Call3Value[]",
340
+ name: "calls",
341
+ type: "tuple[]",
342
+ },
343
+ ],
344
+ name: "aggregate3Value",
345
+ outputs: [
346
+ {
347
+ components: [
348
+ {
349
+ internalType: "bool",
350
+ name: "success",
351
+ type: "bool",
352
+ },
353
+ {
354
+ internalType: "bytes",
355
+ name: "returnData",
356
+ type: "bytes",
357
+ },
358
+ ],
359
+ internalType: "struct Multicall3.Result[]",
360
+ name: "returnData",
361
+ type: "tuple[]",
362
+ },
363
+ ],
364
+ stateMutability: "payable",
365
+ type: "function",
366
+ },
367
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anchor-sdk",
3
- "version": "0.1.41-internal.4",
3
+ "version": "0.1.41-internal.6",
4
4
  "description": "TypeScript SDK for interacting with Anchor ecosystem - badge minting, payment processing, and ERC1155 token management",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",