@swimmingkiim/pay-sdk 0.1.21 → 0.1.22

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.
@@ -98,9 +98,19 @@ const txHash = await smartAccount.executeBatch([
98
98
  }
99
99
  ]);
100
100
 
101
+
101
102
  console.log("Transaction Hash:", txHash);
102
103
  ```
103
104
 
105
+ ### 5. Auto-Deposit Feature
106
+
107
+ The SDK includes a built-in **Auto-Deposit** mechanism.
108
+ 1. **Check**: Before sending, it checks if the Smart Account has enough USDC for the Paymaster fee (default 0.1 USDC).
109
+ 2. **Deposit**: If funds are insufficient, it automatically triggers a deposit transaction from the signer's EOA (Externally Owned Account) to the Smart Account.
110
+ 3. **Execute**: Once the deposit is confirmed, it proceeds with the Paymaster transaction.
111
+
112
+ **Note**: This requires your EOA to have sufficient USDC and ETH (for the deposit gas) if a top-up is needed.
113
+
104
114
  ## 🛡️ Error Handling
105
115
 
106
116
  If the Paymaster service is unavailable or rejects the request (e.g., due to rate limiting or insufficient funds), the SDK may throw an error. It is good practice to wrap your execution logic in a try-catch block.
@@ -15,5 +15,6 @@ export declare class SmartAccountManager {
15
15
  value: bigint;
16
16
  data: `0x${string}`;
17
17
  }[]): Promise<any>;
18
+ ensureGasFunds(requiredAmount: bigint): Promise<void>;
18
19
  }
19
20
  //# sourceMappingURL=smart-account.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"smart-account.d.ts","sourceRoot":"","sources":["../../src/account/smart-account.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAQ,MAAM,MAAM,CAAA;AAIzH,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAE5D,qBAAa,mBAAmB;IAKxB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,SAAS,CAAC;IAPf,MAAM,EAAE,GAAG,CAAA;IACX,OAAO,EAAE,GAAG,CAAA;gBAGP,MAAM,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,EAC/C,YAAY,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,EAC5C,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,gBAAgB,YAAA;IAGlC,iBAAiB,CAAC,SAAS,GAAE,MAAW;IA4B9C,UAAU,IAAI,OAAO;IAIf,YAAY,CAAC,KAAK,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,KAAK,MAAM,EAAE,CAAA;KAAE,EAAE;CAUlF"}
1
+ {"version":3,"file":"smart-account.d.ts","sourceRoot":"","sources":["../../src/account/smart-account.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAQ,MAAM,MAAM,CAAA;AAIzH,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAE5D,qBAAa,mBAAmB;IAKxB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,SAAS,CAAC;IAPf,MAAM,EAAE,GAAG,CAAA;IACX,OAAO,EAAE,GAAG,CAAA;gBAGP,MAAM,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,EAC/C,YAAY,EAAE,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,EAC5C,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,gBAAgB,YAAA;IAGlC,iBAAiB,CAAC,SAAS,GAAE,MAAW;IA4B9C,UAAU,IAAI,OAAO;IAIf,YAAY,CAAC,KAAK,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,KAAK,MAAM,EAAE,CAAA;KAAE,EAAE;IAgBzE,cAAc,CAAC,cAAc,EAAE,MAAM;CAqE9C"}
@@ -46,11 +46,71 @@ export class SmartAccountManager {
46
46
  async executeBatch(calls) {
47
47
  if (!this.client)
48
48
  throw new Error("Account not initialized");
49
+ // [Fee Logic] Ensure Smart Account has enough USDC for the fee
50
+ // We assume 0.1 USDC is required per transaction for now (as per Paymaster config)
51
+ await this.ensureGasFunds(100000n);
49
52
  const txHash = await this.client.sendTransaction({
50
53
  calls: calls,
51
54
  account: this.account
52
55
  });
53
56
  return txHash;
54
57
  }
58
+ // New Helper: Check and Deposit Funds if needed
59
+ async ensureGasFunds(requiredAmount) {
60
+ const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
61
+ const ERC20_ABI = [{
62
+ name: 'balanceOf',
63
+ type: 'function',
64
+ stateMutability: 'view',
65
+ inputs: [{ name: 'account', type: 'address' }],
66
+ outputs: [{ name: '', type: 'uint256' }]
67
+ }, {
68
+ name: 'transfer',
69
+ type: 'function',
70
+ stateMutability: 'nonpayable',
71
+ inputs: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }],
72
+ outputs: [{ name: '', type: 'bool' }]
73
+ }];
74
+ console.log(`[SmartAccount] Checking Gas Funds (Required: ${requiredAmount})...`);
75
+ // 1. Check Smart Account Balance
76
+ const saBalance = await this.publicClient.readContract({
77
+ address: USDC_ADDRESS,
78
+ abi: ERC20_ABI,
79
+ functionName: 'balanceOf',
80
+ args: [this.account.address]
81
+ });
82
+ console.log(`[SmartAccount] Current Balance: ${saBalance}`);
83
+ if (saBalance >= requiredAmount) {
84
+ console.log(`[SmartAccount] ✅ Sufficient funds.`);
85
+ return;
86
+ }
87
+ const shortage = requiredAmount - saBalance;
88
+ console.log(`[SmartAccount] ⚠️ Insufficient funds. Shortage: ${shortage}`);
89
+ // 2. Check EOA Balance
90
+ const eoaAddress = this.signer.account.address;
91
+ const eoaBalance = await this.publicClient.readContract({
92
+ address: USDC_ADDRESS,
93
+ abi: ERC20_ABI,
94
+ functionName: 'balanceOf',
95
+ args: [eoaAddress]
96
+ });
97
+ console.log(`[SmartAccount] EOA Balance: ${eoaBalance}`);
98
+ if (eoaBalance < shortage) {
99
+ throw new Error(`Insufficient funds in both Smart Account (${saBalance}) and EOA (${eoaBalance}). Required: ${requiredAmount}`);
100
+ }
101
+ // 3. Deposit from EOA
102
+ console.log(`[SmartAccount] 🔄 Auto-depositing ${shortage} USDC from EOA...`);
103
+ const hash = await this.signer.writeContract({
104
+ address: USDC_ADDRESS,
105
+ abi: ERC20_ABI,
106
+ functionName: 'transfer',
107
+ args: [this.account.address, shortage],
108
+ chain: this.signer.chain,
109
+ account: this.signer.account
110
+ });
111
+ console.log(`[SmartAccount] Deposit Tx Sent: ${hash}. Waiting for confirmation...`);
112
+ await this.publicClient.waitForTransactionReceipt({ hash });
113
+ console.log(`[SmartAccount] ✅ Deposit confirmed. Proceeding with UserOp.`);
114
+ }
55
115
  }
56
116
  //# sourceMappingURL=smart-account.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"smart-account.js","sourceRoot":"","sources":["../../src/account/smart-account.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAEzD,OAAO,EAAgG,IAAI,EAAE,MAAM,MAAM,CAAA;AACzH,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAI/D,MAAM,OAAO,mBAAmB;IAKhB;IACA;IACA;IACA;IAPL,MAAM,CAAK;IACX,OAAO,CAAK;IAEnB,YACY,MAA+C,EAC/C,YAA4C,EAC5C,MAAc,EACd,SAA4B;QAH5B,WAAM,GAAN,MAAM,CAAyC;QAC/C,iBAAY,GAAZ,YAAY,CAAgC;QAC5C,WAAM,GAAN,MAAM,CAAQ;QACd,cAAS,GAAT,SAAS,CAAmB;IACpC,CAAC;IAEL,KAAK,CAAC,iBAAiB,CAAC,YAAoB,EAAE;QAC1C,sCAAsC;QACtC,IAAI,CAAC,OAAO,GAAG,MAAM,kBAAkB,CAAC;YACpC,MAAM,EAAE,IAAI,CAAC,YAAY;YACzB,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;YAC7B,OAAO,EAAE,OAAO;YAChB,UAAU,EAAE;gBACR,OAAO,EAAE,4CAA4C,EAAE,iBAAiB;gBACxE,OAAO,EAAE,KAAK;aACjB;YACD,SAAS;SACZ,CAAC,CAAA;QAEF,IAAI,CAAC,MAAM,GAAG,wBAAwB,CAAC;YACnC,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;YACxB,gBAAgB,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;YACnC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS;YAClE,aAAa,EAAE;gBACX,kBAAkB,EAAE,KAAK,IAAI,EAAE;oBAC3B,OAAO,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,kBAAkB,EAAE,CAAQ,CAAA;gBAChE,CAAC;aACJ;SACJ,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAA;QAE3B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAA;IAC/B,CAAC;IAED,UAAU;QACN,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAA;IAC/B,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAA4D;QAC3E,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;QAE5D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;YAC7C,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,IAAI,CAAC,OAAO;SACxB,CAAC,CAAA;QAEF,OAAO,MAAM,CAAA;IACjB,CAAC;CACJ"}
1
+ {"version":3,"file":"smart-account.js","sourceRoot":"","sources":["../../src/account/smart-account.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swimmingkiim/pay-sdk",
3
- "version": "0.1.21",
3
+ "version": "0.1.22",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/swimmingkiim/a2a-project.git"
@@ -53,6 +53,10 @@ export class SmartAccountManager {
53
53
  async executeBatch(calls: { to: Address, value: bigint, data: `0x${string}` }[]) {
54
54
  if (!this.client) throw new Error("Account not initialized")
55
55
 
56
+ // [Fee Logic] Ensure Smart Account has enough USDC for the fee
57
+ // We assume 0.1 USDC is required per transaction for now (as per Paymaster config)
58
+ await this.ensureGasFunds(100000n);
59
+
56
60
  const txHash = await this.client.sendTransaction({
57
61
  calls: calls,
58
62
  account: this.account
@@ -60,4 +64,75 @@ export class SmartAccountManager {
60
64
 
61
65
  return txHash
62
66
  }
67
+
68
+ // New Helper: Check and Deposit Funds if needed
69
+ async ensureGasFunds(requiredAmount: bigint) {
70
+ const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
71
+ const ERC20_ABI = [{
72
+ name: 'balanceOf',
73
+ type: 'function',
74
+ stateMutability: 'view',
75
+ inputs: [{ name: 'account', type: 'address' }],
76
+ outputs: [{ name: '', type: 'uint256' }]
77
+ }, {
78
+ name: 'transfer',
79
+ type: 'function',
80
+ stateMutability: 'nonpayable',
81
+ inputs: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }],
82
+ outputs: [{ name: '', type: 'bool' }]
83
+ }] as const;
84
+
85
+ console.log(`[SmartAccount] Checking Gas Funds (Required: ${requiredAmount})...`);
86
+
87
+ // 1. Check Smart Account Balance
88
+ const saBalance = await this.publicClient.readContract({
89
+ address: USDC_ADDRESS,
90
+ abi: ERC20_ABI,
91
+ functionName: 'balanceOf',
92
+ args: [this.account.address]
93
+ });
94
+
95
+ console.log(`[SmartAccount] Current Balance: ${saBalance}`);
96
+
97
+ if (saBalance >= requiredAmount) {
98
+ console.log(`[SmartAccount] ✅ Sufficient funds.`);
99
+ return;
100
+ }
101
+
102
+ const shortage = requiredAmount - saBalance;
103
+ console.log(`[SmartAccount] ⚠️ Insufficient funds. Shortage: ${shortage}`);
104
+
105
+ // 2. Check EOA Balance
106
+ const eoaAddress = this.signer.account.address;
107
+ const eoaBalance = await this.publicClient.readContract({
108
+ address: USDC_ADDRESS,
109
+ abi: ERC20_ABI,
110
+ functionName: 'balanceOf',
111
+ args: [eoaAddress]
112
+ });
113
+
114
+ console.log(`[SmartAccount] EOA Balance: ${eoaBalance}`);
115
+
116
+ if (eoaBalance < shortage) {
117
+ throw new Error(`Insufficient funds in both Smart Account (${saBalance}) and EOA (${eoaBalance}). Required: ${requiredAmount}`);
118
+ }
119
+
120
+ // 3. Deposit from EOA
121
+ console.log(`[SmartAccount] 🔄 Auto-depositing ${shortage} USDC from EOA...`);
122
+
123
+ const hash = await this.signer.writeContract({
124
+ address: USDC_ADDRESS,
125
+ abi: ERC20_ABI,
126
+ functionName: 'transfer',
127
+ args: [this.account.address, shortage],
128
+ chain: this.signer.chain,
129
+ account: this.signer.account
130
+ });
131
+
132
+ console.log(`[SmartAccount] Deposit Tx Sent: ${hash}. Waiting for confirmation...`);
133
+
134
+ await this.publicClient.waitForTransactionReceipt({ hash });
135
+
136
+ console.log(`[SmartAccount] ✅ Deposit confirmed. Proceeding with UserOp.`);
137
+ }
63
138
  }
@@ -0,0 +1,96 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
2
+ import { SmartAccountManager } from '../src/account/smart-account.js'
3
+ import { privateKeyToAccount } from 'viem/accounts'
4
+ import { baseSepolia } from 'viem/chains'
5
+
6
+ describe('SmartAccountManager: Auto Deposit', () => {
7
+ let sa: SmartAccountManager;
8
+ let mockPublicClient: any;
9
+ let mockWalletClient: any;
10
+ let mockSigner: any;
11
+
12
+ beforeEach(() => {
13
+ mockSigner = privateKeyToAccount('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80');
14
+
15
+ mockWalletClient = {
16
+ account: mockSigner,
17
+ chain: baseSepolia,
18
+ writeContract: vi.fn().mockResolvedValue('0xdepositHash')
19
+ };
20
+
21
+ mockPublicClient = {
22
+ readContract: vi.fn(),
23
+ waitForTransactionReceipt: vi.fn().mockResolvedValue({ status: 'success' }),
24
+ estimateFeesPerGas: vi.fn().mockResolvedValue({ maxFeePerGas: 10n, maxPriorityFeePerGas: 1n })
25
+ };
26
+
27
+ sa = new SmartAccountManager(
28
+ mockWalletClient,
29
+ mockPublicClient,
30
+ 'http://localhost:8545'
31
+ );
32
+
33
+ // Mock initialized account manually since we are not calling createSafeAccount
34
+ sa.account = { address: '0xSmartAccountAddress' };
35
+ // Mock the permissionless client
36
+ sa.client = {
37
+ sendTransaction: vi.fn().mockResolvedValue('0xuserOpHash')
38
+ };
39
+ });
40
+
41
+ it('should proceed if Smart Account has sufficient funds', async () => {
42
+ // Mock Balance: SA = 1 USDC (1000000), Required = 0.1 USDC (100000)
43
+ // First call is checking SA balance
44
+ mockPublicClient.readContract.mockResolvedValueOnce(1000000n);
45
+
46
+ await sa.executeBatch([]);
47
+
48
+ expect(mockPublicClient.readContract).toHaveBeenCalledTimes(1); // Only checked SA balance
49
+ expect(mockWalletClient.writeContract).not.toHaveBeenCalled(); // No deposit
50
+ expect(sa.client.sendTransaction).toHaveBeenCalled();
51
+ });
52
+
53
+ it('should deposit from EOA if Smart Account has insufficient funds', async () => {
54
+ // First call: SA Balance = 0
55
+ mockPublicClient.readContract.mockResolvedValueOnce(0n);
56
+ // Second call: EOA Balance = 1 USDC
57
+ mockPublicClient.readContract.mockResolvedValueOnce(1000000n);
58
+
59
+ await sa.executeBatch([]);
60
+
61
+ expect(mockPublicClient.readContract).toHaveBeenCalledTimes(2); // Checked both
62
+ expect(mockWalletClient.writeContract).toHaveBeenCalledWith(expect.objectContaining({
63
+ functionName: 'transfer',
64
+ args: ['0xSmartAccountAddress', 100000n] // Shortage is full amount (100000 - 0)
65
+ }));
66
+ expect(mockPublicClient.waitForTransactionReceipt).toHaveBeenCalled();
67
+ expect(sa.client.sendTransaction).toHaveBeenCalled();
68
+ });
69
+
70
+ it('should throw if both have insufficient funds', async () => {
71
+ // First call: SA Balance = 0
72
+ mockPublicClient.readContract.mockResolvedValueOnce(0n);
73
+ // Second call: EOA Balance = 0
74
+ mockPublicClient.readContract.mockResolvedValueOnce(0n);
75
+
76
+ await expect(sa.executeBatch([])).rejects.toThrow(/Insufficient funds/);
77
+
78
+ expect(mockWalletClient.writeContract).not.toHaveBeenCalled();
79
+ expect(sa.client.sendTransaction).not.toHaveBeenCalled();
80
+ });
81
+
82
+ it('should deposit partial shortage if partial funds exist', async () => {
83
+ // Required: 100000
84
+ // SA Balance: 40000
85
+ // Shortage: 60000
86
+
87
+ mockPublicClient.readContract.mockResolvedValueOnce(40000n); // SA
88
+ mockPublicClient.readContract.mockResolvedValueOnce(1000000n); // EOA
89
+
90
+ await sa.executeBatch([]);
91
+
92
+ expect(mockWalletClient.writeContract).toHaveBeenCalledWith(expect.objectContaining({
93
+ args: ['0xSmartAccountAddress', 60000n]
94
+ }));
95
+ });
96
+ });
@@ -58,9 +58,15 @@ describe('a2pay: Smart Accounts & Sessions', () => {
58
58
  getClient: () => ({ sponsorUserOperation: vi.fn() })
59
59
  } as any
60
60
 
61
+ // Mock Public Client to handle ensureGasFunds check
62
+ const mockPublicClientForTest = {
63
+ readContract: vi.fn().mockResolvedValue(1000000n), // Sufficient balance
64
+ estimateFeesPerGas: vi.fn().mockResolvedValue({ maxFeePerGas: 10n, maxPriorityFeePerGas: 1n })
65
+ } as any
66
+
61
67
  const sa = new SmartAccountManager(
62
68
  walletClient,
63
- publicClient,
69
+ mockPublicClientForTest,
64
70
  'https://rpc.url',
65
71
  mockPaymaster
66
72
  )