t402 1.9.0__py3-none-any.whl → 1.9.1__py3-none-any.whl

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 (100) hide show
  1. t402/__init__.py +2 -1
  2. t402/bridge/client.py +13 -5
  3. t402/bridge/constants.py +3 -1
  4. t402/bridge/router.py +1 -1
  5. t402/bridge/scan.py +3 -1
  6. t402/chains.py +268 -1
  7. t402/cli.py +31 -9
  8. t402/common.py +2 -0
  9. t402/cosmos_paywall_template.py +2 -0
  10. t402/encoding.py +9 -3
  11. t402/erc4337/accounts.py +56 -51
  12. t402/erc4337/bundlers.py +105 -99
  13. t402/erc4337/paymasters.py +100 -109
  14. t402/erc4337/types.py +39 -26
  15. t402/evm_paywall_template.py +1 -1
  16. t402/fastapi/middleware.py +1 -3
  17. t402/mcp/server.py +79 -46
  18. t402/near_paywall_template.py +2 -0
  19. t402/networks.py +34 -1
  20. t402/paywall.py +1 -3
  21. t402/schemes/__init__.py +124 -0
  22. t402/schemes/aptos/__init__.py +70 -0
  23. t402/schemes/aptos/constants.py +349 -0
  24. t402/schemes/aptos/exact_direct/__init__.py +44 -0
  25. t402/schemes/aptos/exact_direct/client.py +202 -0
  26. t402/schemes/aptos/exact_direct/facilitator.py +426 -0
  27. t402/schemes/aptos/exact_direct/server.py +272 -0
  28. t402/schemes/aptos/types.py +237 -0
  29. t402/schemes/evm/__init__.py +46 -1
  30. t402/schemes/evm/exact/__init__.py +11 -0
  31. t402/schemes/evm/exact/client.py +3 -1
  32. t402/schemes/evm/exact/facilitator.py +894 -0
  33. t402/schemes/evm/exact/server.py +1 -1
  34. t402/schemes/evm/exact_legacy/__init__.py +38 -0
  35. t402/schemes/evm/exact_legacy/client.py +291 -0
  36. t402/schemes/evm/exact_legacy/facilitator.py +777 -0
  37. t402/schemes/evm/exact_legacy/server.py +231 -0
  38. t402/schemes/evm/upto/__init__.py +12 -0
  39. t402/schemes/evm/upto/client.py +6 -2
  40. t402/schemes/evm/upto/facilitator.py +625 -0
  41. t402/schemes/evm/upto/server.py +243 -0
  42. t402/schemes/evm/upto/types.py +3 -1
  43. t402/schemes/interfaces.py +6 -2
  44. t402/schemes/near/__init__.py +112 -0
  45. t402/schemes/near/constants.py +189 -0
  46. t402/schemes/near/exact_direct/__init__.py +21 -0
  47. t402/schemes/near/exact_direct/client.py +204 -0
  48. t402/schemes/near/exact_direct/facilitator.py +455 -0
  49. t402/schemes/near/exact_direct/server.py +303 -0
  50. t402/schemes/near/types.py +419 -0
  51. t402/schemes/polkadot/__init__.py +72 -0
  52. t402/schemes/polkadot/constants.py +155 -0
  53. t402/schemes/polkadot/exact_direct/__init__.py +43 -0
  54. t402/schemes/polkadot/exact_direct/client.py +235 -0
  55. t402/schemes/polkadot/exact_direct/facilitator.py +428 -0
  56. t402/schemes/polkadot/exact_direct/server.py +292 -0
  57. t402/schemes/polkadot/types.py +385 -0
  58. t402/schemes/registry.py +6 -2
  59. t402/schemes/stacks/__init__.py +68 -0
  60. t402/schemes/stacks/constants.py +122 -0
  61. t402/schemes/stacks/exact_direct/__init__.py +43 -0
  62. t402/schemes/stacks/exact_direct/client.py +222 -0
  63. t402/schemes/stacks/exact_direct/facilitator.py +424 -0
  64. t402/schemes/stacks/exact_direct/server.py +292 -0
  65. t402/schemes/stacks/types.py +380 -0
  66. t402/schemes/svm/__init__.py +29 -0
  67. t402/schemes/svm/exact/__init__.py +35 -0
  68. t402/schemes/svm/exact/client.py +23 -0
  69. t402/schemes/svm/exact/facilitator.py +24 -0
  70. t402/schemes/svm/exact/server.py +20 -0
  71. t402/schemes/tezos/__init__.py +84 -0
  72. t402/schemes/tezos/constants.py +372 -0
  73. t402/schemes/tezos/exact_direct/__init__.py +22 -0
  74. t402/schemes/tezos/exact_direct/client.py +226 -0
  75. t402/schemes/tezos/exact_direct/facilitator.py +491 -0
  76. t402/schemes/tezos/exact_direct/server.py +277 -0
  77. t402/schemes/tezos/types.py +220 -0
  78. t402/schemes/ton/__init__.py +9 -2
  79. t402/schemes/ton/exact/__init__.py +7 -0
  80. t402/schemes/ton/exact/facilitator.py +730 -0
  81. t402/schemes/ton/exact/server.py +1 -1
  82. t402/schemes/tron/__init__.py +11 -2
  83. t402/schemes/tron/exact/__init__.py +9 -0
  84. t402/schemes/tron/exact/facilitator.py +673 -0
  85. t402/schemes/tron/exact/server.py +1 -1
  86. t402/stacks_paywall_template.py +2 -0
  87. t402/svm.py +45 -11
  88. t402/svm_paywall_template.py +1 -1
  89. t402/ton.py +5 -1
  90. t402/ton_paywall_template.py +1 -192
  91. t402/tron.py +2 -0
  92. t402/tron_paywall_template.py +2 -0
  93. t402/types.py +3 -1
  94. t402/wdk/errors.py +15 -5
  95. t402/wdk/signer.py +11 -2
  96. {t402-1.9.0.dist-info → t402-1.9.1.dist-info}/METADATA +42 -1
  97. t402-1.9.1.dist-info/RECORD +125 -0
  98. t402-1.9.0.dist-info/RECORD +0 -72
  99. {t402-1.9.0.dist-info → t402-1.9.1.dist-info}/WHEEL +0 -0
  100. {t402-1.9.0.dist-info → t402-1.9.1.dist-info}/entry_points.txt +0 -0
t402/erc4337/bundlers.py CHANGED
@@ -47,34 +47,35 @@ class GenericBundlerClient:
47
47
  def send_user_operation(self, user_op: UserOperation) -> str:
48
48
  """Submit a UserOperation to the bundler."""
49
49
  packed = user_op.to_packed_dict()
50
- result = self._rpc_call(
51
- "eth_sendUserOperation",
52
- [packed, self.entry_point]
53
- )
50
+ result = self._rpc_call("eth_sendUserOperation", [packed, self.entry_point])
54
51
  return result
55
52
 
56
53
  def estimate_user_operation_gas(self, user_op: UserOperation) -> GasEstimate:
57
54
  """Estimate gas for a UserOperation."""
58
55
  packed = user_op.to_packed_dict()
59
56
  result = self._rpc_call(
60
- "eth_estimateUserOperationGas",
61
- [packed, self.entry_point]
57
+ "eth_estimateUserOperationGas", [packed, self.entry_point]
62
58
  )
63
59
 
64
60
  return GasEstimate(
65
61
  verification_gas_limit=int(result.get("verificationGasLimit", "0x0"), 16),
66
62
  call_gas_limit=int(result.get("callGasLimit", "0x0"), 16),
67
63
  pre_verification_gas=int(result.get("preVerificationGas", "0x0"), 16),
68
- paymaster_verification_gas_limit=int(result.get("paymasterVerificationGasLimit", "0x0"), 16) if result.get("paymasterVerificationGasLimit") else None,
69
- paymaster_post_op_gas_limit=int(result.get("paymasterPostOpGasLimit", "0x0"), 16) if result.get("paymasterPostOpGasLimit") else None,
64
+ paymaster_verification_gas_limit=int(
65
+ result.get("paymasterVerificationGasLimit", "0x0"), 16
66
+ )
67
+ if result.get("paymasterVerificationGasLimit")
68
+ else None,
69
+ paymaster_post_op_gas_limit=int(
70
+ result.get("paymasterPostOpGasLimit", "0x0"), 16
71
+ )
72
+ if result.get("paymasterPostOpGasLimit")
73
+ else None,
70
74
  )
71
75
 
72
76
  def get_user_operation_by_hash(self, user_op_hash: str) -> Optional[UserOperation]:
73
77
  """Retrieve a UserOperation by hash."""
74
- result = self._rpc_call(
75
- "eth_getUserOperationByHash",
76
- [user_op_hash]
77
- )
78
+ result = self._rpc_call("eth_getUserOperationByHash", [user_op_hash])
78
79
 
79
80
  if not result or not result.get("userOperation"):
80
81
  return None
@@ -94,12 +95,11 @@ class GenericBundlerClient:
94
95
  signature=bytes.fromhex(op.get("signature", "0x")[2:]),
95
96
  )
96
97
 
97
- def get_user_operation_receipt(self, user_op_hash: str) -> Optional[UserOperationReceipt]:
98
+ def get_user_operation_receipt(
99
+ self, user_op_hash: str
100
+ ) -> Optional[UserOperationReceipt]:
98
101
  """Retrieve the receipt for a UserOperation."""
99
- result = self._rpc_call(
100
- "eth_getUserOperationReceipt",
101
- [user_op_hash]
102
- )
102
+ result = self._rpc_call("eth_getUserOperationReceipt", [user_op_hash])
103
103
 
104
104
  if not result or not result.get("userOpHash"):
105
105
  return None
@@ -115,7 +115,9 @@ class GenericBundlerClient:
115
115
  success=result.get("success", False),
116
116
  reason=result.get("reason"),
117
117
  transaction_hash=receipt.get("transactionHash"),
118
- block_number=int(receipt.get("blockNumber", "0x0"), 16) if receipt.get("blockNumber") else None,
118
+ block_number=int(receipt.get("blockNumber", "0x0"), 16)
119
+ if receipt.get("blockNumber")
120
+ else None,
119
121
  block_hash=receipt.get("blockHash"),
120
122
  )
121
123
 
@@ -125,10 +127,7 @@ class GenericBundlerClient:
125
127
  return result or []
126
128
 
127
129
  def wait_for_receipt(
128
- self,
129
- user_op_hash: str,
130
- timeout: float = 60.0,
131
- polling_interval: float = 2.0
130
+ self, user_op_hash: str, timeout: float = 60.0, polling_interval: float = 2.0
132
131
  ) -> UserOperationReceipt:
133
132
  """Wait for a UserOperation receipt with polling."""
134
133
  deadline = time.time() + timeout
@@ -153,9 +152,7 @@ class GenericBundlerClient:
153
152
  }
154
153
 
155
154
  response = self._client.post(
156
- self.bundler_url,
157
- json=request,
158
- headers={"Content-Type": "application/json"}
155
+ self.bundler_url, json=request, headers={"Content-Type": "application/json"}
159
156
  )
160
157
 
161
158
  if response.status_code != 200:
@@ -168,7 +165,7 @@ class GenericBundlerClient:
168
165
  raise BundlerError(
169
166
  error.get("message", "Unknown error"),
170
167
  code=error.get("code"),
171
- data=error.get("data")
168
+ data=error.get("data"),
172
169
  )
173
170
 
174
171
  return data.get("result")
@@ -177,6 +174,7 @@ class GenericBundlerClient:
177
174
  @dataclass
178
175
  class PimlicoGasPrice:
179
176
  """Gas price estimates from Pimlico."""
177
+
180
178
  slow_max_fee: int
181
179
  slow_priority_fee: int
182
180
  standard_max_fee: int
@@ -193,16 +191,14 @@ class PimlicoBundlerClient(GenericBundlerClient):
193
191
  api_key: str,
194
192
  chain_id: int,
195
193
  bundler_url: Optional[str] = None,
196
- entry_point: str = ENTRYPOINT_V07_ADDRESS
194
+ entry_point: str = ENTRYPOINT_V07_ADDRESS,
197
195
  ):
198
196
  network = PIMLICO_NETWORKS.get(chain_id, str(chain_id))
199
197
  url = bundler_url or f"https://api.pimlico.io/v2/{network}/rpc?apikey={api_key}"
200
198
 
201
- super().__init__(BundlerConfig(
202
- bundler_url=url,
203
- chain_id=chain_id,
204
- entry_point=entry_point
205
- ))
199
+ super().__init__(
200
+ BundlerConfig(bundler_url=url, chain_id=chain_id, entry_point=entry_point)
201
+ )
206
202
 
207
203
  self.api_key = api_key
208
204
 
@@ -220,39 +216,32 @@ class PimlicoBundlerClient(GenericBundlerClient):
220
216
  )
221
217
 
222
218
  def send_compressed_user_operation(
223
- self,
224
- compressed_calldata: bytes,
225
- inflator_address: str
219
+ self, compressed_calldata: bytes, inflator_address: str
226
220
  ) -> str:
227
221
  """Send a compressed UserOperation for gas savings."""
228
222
  result = self._rpc_call(
229
223
  "pimlico_sendCompressedUserOperation",
230
- [
231
- "0x" + compressed_calldata.hex(),
232
- inflator_address,
233
- self.entry_point
234
- ]
224
+ ["0x" + compressed_calldata.hex(), inflator_address, self.entry_point],
235
225
  )
236
226
  return result
237
227
 
238
228
  def get_user_operation_status(self, user_op_hash: str) -> Dict[str, Any]:
239
229
  """Get the status of a UserOperation."""
240
- result = self._rpc_call(
241
- "pimlico_getUserOperationStatus",
242
- [user_op_hash]
243
- )
230
+ result = self._rpc_call("pimlico_getUserOperationStatus", [user_op_hash])
244
231
  return result
245
232
 
246
233
 
247
234
  @dataclass
248
235
  class AlchemyPolicyConfig:
249
236
  """Alchemy policy configuration for gas sponsorship."""
237
+
250
238
  policy_id: str
251
239
 
252
240
 
253
241
  @dataclass
254
242
  class GasAndPaymasterResult:
255
243
  """Combined gas and paymaster data from Alchemy."""
244
+
256
245
  gas_estimate: GasEstimate
257
246
  paymaster_data: Optional[PaymasterData]
258
247
  max_fee_per_gas: int
@@ -268,7 +257,7 @@ class AlchemyBundlerClient(GenericBundlerClient):
268
257
  chain_id: int,
269
258
  bundler_url: Optional[str] = None,
270
259
  entry_point: str = ENTRYPOINT_V07_ADDRESS,
271
- policy: Optional[AlchemyPolicyConfig] = None
260
+ policy: Optional[AlchemyPolicyConfig] = None,
272
261
  ):
273
262
  network = ALCHEMY_NETWORKS.get(chain_id)
274
263
  if not network:
@@ -276,19 +265,15 @@ class AlchemyBundlerClient(GenericBundlerClient):
276
265
 
277
266
  url = bundler_url or f"https://{network}.g.alchemy.com/v2/{api_key}"
278
267
 
279
- super().__init__(BundlerConfig(
280
- bundler_url=url,
281
- chain_id=chain_id,
282
- entry_point=entry_point
283
- ))
268
+ super().__init__(
269
+ BundlerConfig(bundler_url=url, chain_id=chain_id, entry_point=entry_point)
270
+ )
284
271
 
285
272
  self.api_key = api_key
286
273
  self.policy = policy
287
274
 
288
275
  def request_gas_and_paymaster_and_data(
289
- self,
290
- user_op: UserOperation,
291
- overrides: Optional[Dict[str, int]] = None
276
+ self, user_op: UserOperation, overrides: Optional[Dict[str, int]] = None
292
277
  ) -> GasAndPaymasterResult:
293
278
  """Request gas estimates and paymaster data in a single call."""
294
279
  if not self.policy:
@@ -304,33 +289,42 @@ class AlchemyBundlerClient(GenericBundlerClient):
304
289
  }
305
290
 
306
291
  if overrides:
307
- request_params["overrides"] = {
308
- k: hex(v) for k, v in overrides.items()
309
- }
292
+ request_params["overrides"] = {k: hex(v) for k, v in overrides.items()}
310
293
 
311
294
  result = self._rpc_call(
312
- "alchemy_requestGasAndPaymasterAndData",
313
- [request_params]
295
+ "alchemy_requestGasAndPaymasterAndData", [request_params]
314
296
  )
315
297
 
316
298
  # Parse paymaster data
317
299
  paymaster_data = None
318
300
  paymaster_and_data = result.get("paymasterAndData", "0x")
319
- if paymaster_and_data and paymaster_and_data != "0x" and len(paymaster_and_data) >= 106:
301
+ if (
302
+ paymaster_and_data
303
+ and paymaster_and_data != "0x"
304
+ and len(paymaster_and_data) >= 106
305
+ ):
320
306
  paymaster_data = PaymasterData(
321
307
  paymaster="0x" + paymaster_and_data[2:42],
322
308
  paymaster_verification_gas_limit=int(paymaster_and_data[42:74], 16),
323
309
  paymaster_post_op_gas_limit=int(paymaster_and_data[74:106], 16),
324
- paymaster_data=bytes.fromhex(paymaster_and_data[106:]) if len(paymaster_and_data) > 106 else b"",
310
+ paymaster_data=bytes.fromhex(paymaster_and_data[106:])
311
+ if len(paymaster_and_data) > 106
312
+ else b"",
325
313
  )
326
314
 
327
315
  return GasAndPaymasterResult(
328
316
  gas_estimate=GasEstimate(
329
- verification_gas_limit=int(result.get("verificationGasLimit", "0x0"), 16),
317
+ verification_gas_limit=int(
318
+ result.get("verificationGasLimit", "0x0"), 16
319
+ ),
330
320
  call_gas_limit=int(result.get("callGasLimit", "0x0"), 16),
331
321
  pre_verification_gas=int(result.get("preVerificationGas", "0x0"), 16),
332
- paymaster_verification_gas_limit=paymaster_data.paymaster_verification_gas_limit if paymaster_data else None,
333
- paymaster_post_op_gas_limit=paymaster_data.paymaster_post_op_gas_limit if paymaster_data else None,
322
+ paymaster_verification_gas_limit=paymaster_data.paymaster_verification_gas_limit
323
+ if paymaster_data
324
+ else None,
325
+ paymaster_post_op_gas_limit=paymaster_data.paymaster_post_op_gas_limit
326
+ if paymaster_data
327
+ else None,
334
328
  ),
335
329
  paymaster_data=paymaster_data,
336
330
  max_fee_per_gas=int(result.get("maxFeePerGas", "0x0"), 16),
@@ -338,8 +332,7 @@ class AlchemyBundlerClient(GenericBundlerClient):
338
332
  )
339
333
 
340
334
  def simulate_user_operation_asset_changes(
341
- self,
342
- user_op: UserOperation
335
+ self, user_op: UserOperation
343
336
  ) -> SimulationResult:
344
337
  """Simulate asset changes from a UserOperation."""
345
338
  packed = user_op.to_packed_dict()
@@ -347,26 +340,34 @@ class AlchemyBundlerClient(GenericBundlerClient):
347
340
  try:
348
341
  result = self._rpc_call(
349
342
  "alchemy_simulateUserOperationAssetChanges",
350
- [{
351
- "entryPoint": self.entry_point,
352
- "userOperation": packed,
353
- }]
343
+ [
344
+ {
345
+ "entryPoint": self.entry_point,
346
+ "userOperation": packed,
347
+ }
348
+ ],
354
349
  )
355
350
 
356
351
  changes = []
357
352
  for change in result.get("changes", []):
358
- changes.append(AssetChange(
359
- asset_type=change.get("assetType", ""),
360
- change_type=change.get("changeType", ""),
361
- from_address=change.get("from", ""),
362
- to_address=change.get("to", ""),
363
- amount=int(change["amount"], 16) if change.get("amount") else None,
364
- token_id=int(change["tokenId"], 16) if change.get("tokenId") else None,
365
- contract_address=change.get("contractAddress"),
366
- symbol=change.get("symbol"),
367
- name=change.get("name"),
368
- decimals=change.get("decimals"),
369
- ))
353
+ changes.append(
354
+ AssetChange(
355
+ asset_type=change.get("assetType", ""),
356
+ change_type=change.get("changeType", ""),
357
+ from_address=change.get("from", ""),
358
+ to_address=change.get("to", ""),
359
+ amount=int(change["amount"], 16)
360
+ if change.get("amount")
361
+ else None,
362
+ token_id=int(change["tokenId"], 16)
363
+ if change.get("tokenId")
364
+ else None,
365
+ contract_address=change.get("contractAddress"),
366
+ symbol=change.get("symbol"),
367
+ name=change.get("name"),
368
+ decimals=change.get("decimals"),
369
+ )
370
+ )
370
371
 
371
372
  return SimulationResult(success=True, changes=changes)
372
373
 
@@ -375,10 +376,7 @@ class AlchemyBundlerClient(GenericBundlerClient):
375
376
 
376
377
  def get_fee_history(self) -> Dict[str, int]:
377
378
  """Get fee history for gas estimation."""
378
- result = self._rpc_call(
379
- "eth_feeHistory",
380
- ["0x5", "latest", [25, 50, 75]]
381
- )
379
+ result = self._rpc_call("eth_feeHistory", ["0x5", "latest", [25, 50, 75]])
382
380
 
383
381
  base_fee_per_gas = result.get("baseFeePerGas", [])
384
382
  rewards = result.get("reward", [])
@@ -390,7 +388,12 @@ class AlchemyBundlerClient(GenericBundlerClient):
390
388
  if rewards:
391
389
  mid_idx = len(rewards) // 2
392
390
  if rewards[mid_idx]:
393
- median_priority_fee = int(rewards[mid_idx][1] if len(rewards[mid_idx]) > 1 else rewards[mid_idx][0], 16)
391
+ median_priority_fee = int(
392
+ rewards[mid_idx][1]
393
+ if len(rewards[mid_idx]) > 1
394
+ else rewards[mid_idx][0],
395
+ 16,
396
+ )
394
397
 
395
398
  max_fee_per_gas = latest_base_fee * 2 + median_priority_fee
396
399
 
@@ -407,8 +410,12 @@ class AlchemyBundlerClient(GenericBundlerClient):
407
410
  "nonce": hex(user_op.nonce),
408
411
  "initCode": "0x" + user_op.init_code.hex() if user_op.init_code else "0x",
409
412
  "callData": "0x" + user_op.call_data.hex() if user_op.call_data else "0x",
410
- "paymasterAndData": "0x" + user_op.paymaster_and_data.hex() if user_op.paymaster_and_data else "0x",
411
- "signature": "0x" + user_op.signature.hex() if user_op.signature else "0x" + get_dummy_signature().hex(),
413
+ "paymasterAndData": "0x" + user_op.paymaster_and_data.hex()
414
+ if user_op.paymaster_and_data
415
+ else "0x",
416
+ "signature": "0x" + user_op.signature.hex()
417
+ if user_op.signature
418
+ else "0x" + get_dummy_signature().hex(),
412
419
  }
413
420
 
414
421
  if user_op.verification_gas_limit:
@@ -426,10 +433,7 @@ class AlchemyBundlerClient(GenericBundlerClient):
426
433
 
427
434
 
428
435
  def create_bundler_client(
429
- provider: str,
430
- api_key: str,
431
- chain_id: int,
432
- **kwargs
436
+ provider: str, api_key: str, chain_id: int, **kwargs
433
437
  ) -> Union[GenericBundlerClient, PimlicoBundlerClient, AlchemyBundlerClient]:
434
438
  """Factory function to create a bundler client."""
435
439
  if provider == "pimlico":
@@ -437,7 +441,7 @@ def create_bundler_client(
437
441
  api_key=api_key,
438
442
  chain_id=chain_id,
439
443
  bundler_url=kwargs.get("bundler_url"),
440
- entry_point=kwargs.get("entry_point", ENTRYPOINT_V07_ADDRESS)
444
+ entry_point=kwargs.get("entry_point", ENTRYPOINT_V07_ADDRESS),
441
445
  )
442
446
  elif provider == "alchemy":
443
447
  policy = None
@@ -448,11 +452,13 @@ def create_bundler_client(
448
452
  chain_id=chain_id,
449
453
  bundler_url=kwargs.get("bundler_url"),
450
454
  entry_point=kwargs.get("entry_point", ENTRYPOINT_V07_ADDRESS),
451
- policy=policy
455
+ policy=policy,
452
456
  )
453
457
  else:
454
- return GenericBundlerClient(BundlerConfig(
455
- bundler_url=kwargs.get("bundler_url", ""),
456
- chain_id=chain_id,
457
- entry_point=kwargs.get("entry_point", ENTRYPOINT_V07_ADDRESS)
458
- ))
458
+ return GenericBundlerClient(
459
+ BundlerConfig(
460
+ bundler_url=kwargs.get("bundler_url", ""),
461
+ chain_id=chain_id,
462
+ entry_point=kwargs.get("entry_point", ENTRYPOINT_V07_ADDRESS),
463
+ )
464
+ )