@swimmingkiim/pay-sdk 0.1.21 → 0.1.23

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,25 @@ 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 to ensure smooth execution even for new accounts.
108
+
109
+ #### How it works:
110
+ 1. **Check**: Before sending a UserOperation, the SDK checks if the Smart Account has enough USDC to cover the Paymaster fee (default fee or custom amount).
111
+ 2. **Deposit**: If funds are insufficient, it automatically triggers a standard ETH transaction from the signer's EOA (Externally Owned Account) to the Smart Account to transfer the missing USDC.
112
+ 3. **Execute**: Once the deposit transaction is confirmed on-chain, it proceeds with the Paymaster sponsored transaction.
113
+
114
+ #### ⚠️ Important Requirements:
115
+ * **Signer EOA Funds**: Your private key's wallet (EOA) MUST have:
116
+ * **ETH**: To pay gas for the standard deposit transaction (this step is NOT sponsored).
117
+ * **USDC**: Sufficient balance to transfer to the Smart Account.
118
+ * **Permissions**: The EOA must be an owner of the Smart Account (handled automatically during creation).
119
+
104
120
  ## 🛡️ Error Handling
105
121
 
106
122
  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;IAmC9C,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"}
@@ -27,14 +27,20 @@ export class SmartAccountManager {
27
27
  },
28
28
  saltNonce,
29
29
  });
30
+ const apiKey = this.paymaster?.getApiKey();
31
+ const fetchOptions = apiKey ? { headers: { 'x-api-key': apiKey } } : undefined;
30
32
  this.client = createSmartAccountClient({
31
33
  account: this.account,
32
34
  chain: this.signer.chain,
33
- bundlerTransport: http(this.rpcUrl),
35
+ bundlerTransport: http(this.rpcUrl, { fetchOptions }),
34
36
  paymaster: this.paymaster ? this.paymaster.getClient() : undefined,
35
37
  userOperation: {
36
38
  estimateFeesPerGas: async () => {
37
- return (await this.publicClient.estimateFeesPerGas());
39
+ const fees = await this.publicClient.estimateFeesPerGas();
40
+ return {
41
+ maxFeePerGas: (fees.maxFeePerGas * 20n) / 10n, // 2x (safety margin)
42
+ maxPriorityFeePerGas: (fees.maxPriorityFeePerGas * 20n) / 10n
43
+ };
38
44
  }
39
45
  }
40
46
  }).extend(erc7579Actions());
@@ -46,11 +52,71 @@ export class SmartAccountManager {
46
52
  async executeBatch(calls) {
47
53
  if (!this.client)
48
54
  throw new Error("Account not initialized");
55
+ // [Fee Logic] Ensure Smart Account has enough USDC for the fee
56
+ // We assume 0.6 USDC is required per transaction (matching Paymaster Policy)
57
+ await this.ensureGasFunds(600000n);
49
58
  const txHash = await this.client.sendTransaction({
50
59
  calls: calls,
51
60
  account: this.account
52
61
  });
53
62
  return txHash;
54
63
  }
64
+ // New Helper: Check and Deposit Funds if needed
65
+ async ensureGasFunds(requiredAmount) {
66
+ const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
67
+ const ERC20_ABI = [{
68
+ name: 'balanceOf',
69
+ type: 'function',
70
+ stateMutability: 'view',
71
+ inputs: [{ name: 'account', type: 'address' }],
72
+ outputs: [{ name: '', type: 'uint256' }]
73
+ }, {
74
+ name: 'transfer',
75
+ type: 'function',
76
+ stateMutability: 'nonpayable',
77
+ inputs: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }],
78
+ outputs: [{ name: '', type: 'bool' }]
79
+ }];
80
+ console.log(`[SmartAccount] Checking Gas Funds (Required: ${requiredAmount})...`);
81
+ // 1. Check Smart Account Balance
82
+ const saBalance = await this.publicClient.readContract({
83
+ address: USDC_ADDRESS,
84
+ abi: ERC20_ABI,
85
+ functionName: 'balanceOf',
86
+ args: [this.account.address]
87
+ });
88
+ console.log(`[SmartAccount] Current Balance: ${saBalance}`);
89
+ if (saBalance >= requiredAmount) {
90
+ console.log(`[SmartAccount] ✅ Sufficient funds.`);
91
+ return;
92
+ }
93
+ const shortage = requiredAmount - saBalance;
94
+ console.log(`[SmartAccount] ⚠️ Insufficient funds. Shortage: ${shortage}`);
95
+ // 2. Check EOA Balance
96
+ const eoaAddress = this.signer.account.address;
97
+ const eoaBalance = await this.publicClient.readContract({
98
+ address: USDC_ADDRESS,
99
+ abi: ERC20_ABI,
100
+ functionName: 'balanceOf',
101
+ args: [eoaAddress]
102
+ });
103
+ console.log(`[SmartAccount] EOA Balance: ${eoaBalance}`);
104
+ if (eoaBalance < shortage) {
105
+ throw new Error(`Insufficient funds in both Smart Account (${saBalance}) and EOA (${eoaBalance}). Required: ${requiredAmount}`);
106
+ }
107
+ // 3. Deposit from EOA
108
+ console.log(`[SmartAccount] 🔄 Auto-depositing ${shortage} USDC from EOA...`);
109
+ const hash = await this.signer.writeContract({
110
+ address: USDC_ADDRESS,
111
+ abi: ERC20_ABI,
112
+ functionName: 'transfer',
113
+ args: [this.account.address, shortage],
114
+ chain: this.signer.chain,
115
+ account: this.signer.account
116
+ });
117
+ console.log(`[SmartAccount] Deposit Tx Sent: ${hash}. Waiting for confirmation...`);
118
+ await this.publicClient.waitForTransactionReceipt({ hash });
119
+ console.log(`[SmartAccount] ✅ Deposit confirmed. Proceeding with UserOp.`);
120
+ }
55
121
  }
56
122
  //# 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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"}
@@ -1,7 +1,9 @@
1
1
  export declare class PaymasterManager {
2
2
  private client;
3
+ private apiKey?;
3
4
  constructor(rpcUrl?: string, apiKey?: string);
4
5
  getClient(): any;
6
+ getApiKey(): string | undefined;
5
7
  getStubPaymasterData(userOp: any): Promise<any>;
6
8
  /**
7
9
  * Appends a fee transfer transaction to the list of calls.
@@ -1 +1 @@
1
- {"version":3,"file":"paymaster.d.ts","sourceRoot":"","sources":["../../src/paymaster/paymaster.ts"],"names":[],"mappings":"AAMA,qBAAa,gBAAgB;IACzB,OAAO,CAAC,MAAM,CAAK;gBAEP,MAAM,GAAE,MAA6C,EAAE,MAAM,CAAC,EAAE,MAAM;IAYlF,SAAS;IAIH,oBAAoB,CAAC,MAAM,EAAE,GAAG;IAMtC;;;;;OAKG;IACH,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;CAwBxG"}
1
+ {"version":3,"file":"paymaster.d.ts","sourceRoot":"","sources":["../../src/paymaster/paymaster.ts"],"names":[],"mappings":"AAMA,qBAAa,gBAAgB;IACzB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAC,CAAQ;gBAEX,MAAM,GAAE,MAA6C,EAAE,MAAM,CAAC,EAAE,MAAM;IAalF,SAAS;IAIT,SAAS;IAIH,oBAAoB,CAAC,MAAM,EAAE,GAAG;IAMtC;;;;;OAKG;IACH,MAAM,CAAC,gBAAgB,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;CAwBxG"}
@@ -3,7 +3,9 @@ import { http, encodeFunctionData, parseAbi } from "viem";
3
3
  const ERC20_ABI = parseAbi(['function transfer(address to, uint256 amount) returns (bool)']);
4
4
  export class PaymasterManager {
5
5
  client;
6
+ apiKey;
6
7
  constructor(rpcUrl = "http://localhost:8080/v1/paymaster", apiKey) {
8
+ this.apiKey = apiKey;
7
9
  const fetchOptions = apiKey ? { headers: { 'x-api-key': apiKey } } : undefined;
8
10
  this.client = createPimlicoClient({
9
11
  transport: http(rpcUrl, { fetchOptions }),
@@ -16,6 +18,9 @@ export class PaymasterManager {
16
18
  getClient() {
17
19
  return this.client;
18
20
  }
21
+ getApiKey() {
22
+ return this.apiKey;
23
+ }
19
24
  async getStubPaymasterData(userOp) {
20
25
  return this.client.sponsorUserOperation({
21
26
  userOperation: userOp
@@ -1 +1 @@
1
- {"version":3,"file":"paymaster.js","sourceRoot":"","sources":["../../src/paymaster/paymaster.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA;AACpE,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAO,MAAM,MAAM,CAAA;AAE9D,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,8DAA8D,CAAC,CAAC,CAAC;AAG7F,MAAM,OAAO,gBAAgB;IACjB,MAAM,CAAK;IAEnB,YAAY,SAAiB,oCAAoC,EAAE,MAAe;QAC9E,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAE/E,IAAI,CAAC,MAAM,GAAG,mBAAmB,CAAC;YAC9B,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,YAAY,EAAE,CAAC;YACzC,UAAU,EAAE;gBACR,OAAO,EAAE,4CAA4C;gBACrD,OAAO,EAAE,KAAK;aACjB;SACJ,CAAC,CAAA;IACN,CAAC;IAED,SAAS;QACL,OAAO,IAAI,CAAC,MAAM,CAAA;IACtB,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,MAAW;QAClC,OAAO,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;YACpC,aAAa,EAAE,MAAM;SACxB,CAAC,CAAA;IACN,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,gBAAgB,CAAC,KAAY,EAAE,SAA+D;QACjG,yCAAyC;QACzC,MAAM,MAAM,GAAG;YACX,QAAQ,EAAE,SAAS,EAAE,QAAQ,IAAI,4CAA4C,EAAE,sBAAsB;YACrG,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,OAAO,EAAE,WAAW;YACjD,KAAK,EAAE,SAAS,EAAE,KAAK,IAAI,4CAA4C,CAAC,eAAe;SAC1F,CAAC;QAEF,IAAI,MAAM,CAAC,QAAQ,KAAK,4CAA4C,EAAE,CAAC;YACnE,OAAO,CAAC,IAAI,CAAC,uFAAuF,CAAC,CAAC;QAC1G,CAAC;QAED,MAAM,OAAO,GAAG;YACZ,EAAE,EAAE,MAAM,CAAC,KAAY;YACvB,KAAK,EAAE,EAAE;YACT,IAAI,EAAE,kBAAkB,CAAC;gBACrB,GAAG,EAAE,SAAS;gBACd,YAAY,EAAE,UAAU;gBACxB,IAAI,EAAE,CAAC,MAAM,CAAC,QAAe,EAAE,MAAM,CAAC,MAAM,CAAC;aAChD,CAAC;SACL,CAAC;QAEF,OAAO,CAAC,GAAG,KAAK,EAAE,OAAO,CAAC,CAAC;IAC/B,CAAC;CACJ"}
1
+ {"version":3,"file":"paymaster.js","sourceRoot":"","sources":["../../src/paymaster/paymaster.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA;AACpE,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAO,MAAM,MAAM,CAAA;AAE9D,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,8DAA8D,CAAC,CAAC,CAAC;AAG7F,MAAM,OAAO,gBAAgB;IACjB,MAAM,CAAK;IACX,MAAM,CAAS;IAEvB,YAAY,SAAiB,oCAAoC,EAAE,MAAe;QAC9E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAE/E,IAAI,CAAC,MAAM,GAAG,mBAAmB,CAAC;YAC9B,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,YAAY,EAAE,CAAC;YACzC,UAAU,EAAE;gBACR,OAAO,EAAE,4CAA4C;gBACrD,OAAO,EAAE,KAAK;aACjB;SACJ,CAAC,CAAA;IACN,CAAC;IAED,SAAS;QACL,OAAO,IAAI,CAAC,MAAM,CAAA;IACtB,CAAC;IAED,SAAS;QACL,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,MAAW;QAClC,OAAO,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;YACpC,aAAa,EAAE,MAAM;SACxB,CAAC,CAAA;IACN,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,gBAAgB,CAAC,KAAY,EAAE,SAA+D;QACjG,yCAAyC;QACzC,MAAM,MAAM,GAAG;YACX,QAAQ,EAAE,SAAS,EAAE,QAAQ,IAAI,4CAA4C,EAAE,sBAAsB;YACrG,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,OAAO,EAAE,WAAW;YACjD,KAAK,EAAE,SAAS,EAAE,KAAK,IAAI,4CAA4C,CAAC,eAAe;SAC1F,CAAC;QAEF,IAAI,MAAM,CAAC,QAAQ,KAAK,4CAA4C,EAAE,CAAC;YACnE,OAAO,CAAC,IAAI,CAAC,uFAAuF,CAAC,CAAC;QAC1G,CAAC;QAED,MAAM,OAAO,GAAG;YACZ,EAAE,EAAE,MAAM,CAAC,KAAY;YACvB,KAAK,EAAE,EAAE;YACT,IAAI,EAAE,kBAAkB,CAAC;gBACrB,GAAG,EAAE,SAAS;gBACd,YAAY,EAAE,UAAU;gBACxB,IAAI,EAAE,CAAC,MAAM,CAAC,QAAe,EAAE,MAAM,CAAC,MAAM,CAAC;aAChD,CAAC;SACL,CAAC;QAEF,OAAO,CAAC,GAAG,KAAK,EAAE,OAAO,CAAC,CAAC;IAC/B,CAAC;CACJ"}
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.23",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/swimmingkiim/a2a-project.git"
@@ -31,14 +31,21 @@ export class SmartAccountManager {
31
31
  saltNonce,
32
32
  })
33
33
 
34
+ const apiKey = this.paymaster?.getApiKey();
35
+ const fetchOptions = apiKey ? { headers: { 'x-api-key': apiKey } } : undefined;
36
+
34
37
  this.client = createSmartAccountClient({
35
38
  account: this.account,
36
39
  chain: this.signer.chain,
37
- bundlerTransport: http(this.rpcUrl),
40
+ bundlerTransport: http(this.rpcUrl, { fetchOptions }),
38
41
  paymaster: this.paymaster ? this.paymaster.getClient() : undefined,
39
42
  userOperation: {
40
43
  estimateFeesPerGas: async () => {
41
- return (await this.publicClient.estimateFeesPerGas()) as any
44
+ const fees = await this.publicClient.estimateFeesPerGas();
45
+ return {
46
+ maxFeePerGas: (fees.maxFeePerGas * 20n) / 10n, // 2x (safety margin)
47
+ maxPriorityFeePerGas: (fees.maxPriorityFeePerGas * 20n) / 10n
48
+ } as any;
42
49
  }
43
50
  }
44
51
  }).extend(erc7579Actions())
@@ -53,6 +60,10 @@ export class SmartAccountManager {
53
60
  async executeBatch(calls: { to: Address, value: bigint, data: `0x${string}` }[]) {
54
61
  if (!this.client) throw new Error("Account not initialized")
55
62
 
63
+ // [Fee Logic] Ensure Smart Account has enough USDC for the fee
64
+ // We assume 0.6 USDC is required per transaction (matching Paymaster Policy)
65
+ await this.ensureGasFunds(600000n);
66
+
56
67
  const txHash = await this.client.sendTransaction({
57
68
  calls: calls,
58
69
  account: this.account
@@ -60,4 +71,75 @@ export class SmartAccountManager {
60
71
 
61
72
  return txHash
62
73
  }
74
+
75
+ // New Helper: Check and Deposit Funds if needed
76
+ async ensureGasFunds(requiredAmount: bigint) {
77
+ const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
78
+ const ERC20_ABI = [{
79
+ name: 'balanceOf',
80
+ type: 'function',
81
+ stateMutability: 'view',
82
+ inputs: [{ name: 'account', type: 'address' }],
83
+ outputs: [{ name: '', type: 'uint256' }]
84
+ }, {
85
+ name: 'transfer',
86
+ type: 'function',
87
+ stateMutability: 'nonpayable',
88
+ inputs: [{ name: 'to', type: 'address' }, { name: 'amount', type: 'uint256' }],
89
+ outputs: [{ name: '', type: 'bool' }]
90
+ }] as const;
91
+
92
+ console.log(`[SmartAccount] Checking Gas Funds (Required: ${requiredAmount})...`);
93
+
94
+ // 1. Check Smart Account Balance
95
+ const saBalance = await this.publicClient.readContract({
96
+ address: USDC_ADDRESS,
97
+ abi: ERC20_ABI,
98
+ functionName: 'balanceOf',
99
+ args: [this.account.address]
100
+ });
101
+
102
+ console.log(`[SmartAccount] Current Balance: ${saBalance}`);
103
+
104
+ if (saBalance >= requiredAmount) {
105
+ console.log(`[SmartAccount] ✅ Sufficient funds.`);
106
+ return;
107
+ }
108
+
109
+ const shortage = requiredAmount - saBalance;
110
+ console.log(`[SmartAccount] ⚠️ Insufficient funds. Shortage: ${shortage}`);
111
+
112
+ // 2. Check EOA Balance
113
+ const eoaAddress = this.signer.account.address;
114
+ const eoaBalance = await this.publicClient.readContract({
115
+ address: USDC_ADDRESS,
116
+ abi: ERC20_ABI,
117
+ functionName: 'balanceOf',
118
+ args: [eoaAddress]
119
+ });
120
+
121
+ console.log(`[SmartAccount] EOA Balance: ${eoaBalance}`);
122
+
123
+ if (eoaBalance < shortage) {
124
+ throw new Error(`Insufficient funds in both Smart Account (${saBalance}) and EOA (${eoaBalance}). Required: ${requiredAmount}`);
125
+ }
126
+
127
+ // 3. Deposit from EOA
128
+ console.log(`[SmartAccount] 🔄 Auto-depositing ${shortage} USDC from EOA...`);
129
+
130
+ const hash = await this.signer.writeContract({
131
+ address: USDC_ADDRESS,
132
+ abi: ERC20_ABI,
133
+ functionName: 'transfer',
134
+ args: [this.account.address, shortage],
135
+ chain: this.signer.chain,
136
+ account: this.signer.account
137
+ });
138
+
139
+ console.log(`[SmartAccount] Deposit Tx Sent: ${hash}. Waiting for confirmation...`);
140
+
141
+ await this.publicClient.waitForTransactionReceipt({ hash });
142
+
143
+ console.log(`[SmartAccount] ✅ Deposit confirmed. Proceeding with UserOp.`);
144
+ }
63
145
  }
@@ -6,8 +6,10 @@ const ERC20_ABI = parseAbi(['function transfer(address to, uint256 amount) retur
6
6
 
7
7
  export class PaymasterManager {
8
8
  private client: any
9
+ private apiKey?: string
9
10
 
10
11
  constructor(rpcUrl: string = "http://localhost:8080/v1/paymaster", apiKey?: string) {
12
+ this.apiKey = apiKey;
11
13
  const fetchOptions = apiKey ? { headers: { 'x-api-key': apiKey } } : undefined;
12
14
 
13
15
  this.client = createPimlicoClient({
@@ -23,6 +25,10 @@ export class PaymasterManager {
23
25
  return this.client
24
26
  }
25
27
 
28
+ getApiKey() {
29
+ return this.apiKey;
30
+ }
31
+
26
32
  async getStubPaymasterData(userOp: any) {
27
33
  return this.client.sponsorUserOperation({
28
34
  userOperation: userOp
@@ -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
  )