t402 1.7.1__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 (102) 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 +164 -1
  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 +67 -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 +70 -0
  39. t402/schemes/evm/upto/client.py +244 -0
  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 +307 -0
  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/schemes/upto/__init__.py +80 -0
  87. t402/schemes/upto/types.py +376 -0
  88. t402/stacks_paywall_template.py +2 -0
  89. t402/svm.py +45 -11
  90. t402/svm_paywall_template.py +1 -1
  91. t402/ton.py +5 -1
  92. t402/ton_paywall_template.py +1 -192
  93. t402/tron.py +2 -0
  94. t402/tron_paywall_template.py +2 -0
  95. t402/types.py +4 -2
  96. t402/wdk/errors.py +15 -5
  97. t402/wdk/signer.py +11 -2
  98. {t402-1.7.1.dist-info → t402-1.9.1.dist-info}/METADATA +42 -1
  99. t402-1.9.1.dist-info/RECORD +125 -0
  100. t402-1.7.1.dist-info/RECORD +0 -67
  101. {t402-1.7.1.dist-info → t402-1.9.1.dist-info}/WHEEL +0 -0
  102. {t402-1.7.1.dist-info → t402-1.9.1.dist-info}/entry_points.txt +0 -0
@@ -34,20 +34,14 @@ class PaymasterClient(ABC):
34
34
 
35
35
  @abstractmethod
36
36
  def get_paymaster_data(
37
- self,
38
- user_op: UserOperation,
39
- chain_id: int,
40
- entry_point: str
37
+ self, user_op: UserOperation, chain_id: int, entry_point: str
41
38
  ) -> PaymasterData:
42
39
  """Get paymaster data for a UserOperation."""
43
40
  pass
44
41
 
45
42
  @abstractmethod
46
43
  def will_sponsor(
47
- self,
48
- user_op: UserOperation,
49
- chain_id: int,
50
- entry_point: str
44
+ self, user_op: UserOperation, chain_id: int, entry_point: str
51
45
  ) -> bool:
52
46
  """Check if the paymaster will sponsor this operation."""
53
47
  pass
@@ -60,13 +54,27 @@ def _pack_user_op_for_paymaster(user_op: UserOperation) -> Dict[str, Any]:
60
54
  "nonce": hex(user_op.nonce),
61
55
  "initCode": "0x" + user_op.init_code.hex() if user_op.init_code else "0x",
62
56
  "callData": "0x" + user_op.call_data.hex() if user_op.call_data else "0x",
63
- "verificationGasLimit": hex(user_op.verification_gas_limit or DEFAULT_GAS_LIMITS.verification_gas_limit),
64
- "callGasLimit": hex(user_op.call_gas_limit or DEFAULT_GAS_LIMITS.call_gas_limit),
65
- "preVerificationGas": hex(user_op.pre_verification_gas or DEFAULT_GAS_LIMITS.pre_verification_gas),
66
- "maxFeePerGas": hex(user_op.max_fee_per_gas) if user_op.max_fee_per_gas else "0x0",
67
- "maxPriorityFeePerGas": hex(user_op.max_priority_fee_per_gas) if user_op.max_priority_fee_per_gas else "0x0",
68
- "paymasterAndData": "0x" + user_op.paymaster_and_data.hex() if user_op.paymaster_and_data else "0x",
69
- "signature": "0x" + user_op.signature.hex() if user_op.signature else "0x" + get_dummy_signature().hex(),
57
+ "verificationGasLimit": hex(
58
+ user_op.verification_gas_limit or DEFAULT_GAS_LIMITS.verification_gas_limit
59
+ ),
60
+ "callGasLimit": hex(
61
+ user_op.call_gas_limit or DEFAULT_GAS_LIMITS.call_gas_limit
62
+ ),
63
+ "preVerificationGas": hex(
64
+ user_op.pre_verification_gas or DEFAULT_GAS_LIMITS.pre_verification_gas
65
+ ),
66
+ "maxFeePerGas": hex(user_op.max_fee_per_gas)
67
+ if user_op.max_fee_per_gas
68
+ else "0x0",
69
+ "maxPriorityFeePerGas": hex(user_op.max_priority_fee_per_gas)
70
+ if user_op.max_priority_fee_per_gas
71
+ else "0x0",
72
+ "paymasterAndData": "0x" + user_op.paymaster_and_data.hex()
73
+ if user_op.paymaster_and_data
74
+ else "0x",
75
+ "signature": "0x" + user_op.signature.hex()
76
+ if user_op.signature
77
+ else "0x" + get_dummy_signature().hex(),
70
78
  }
71
79
 
72
80
 
@@ -79,10 +87,12 @@ class PimlicoPaymaster(PaymasterClient):
79
87
  chain_id: int,
80
88
  paymaster_url: Optional[str] = None,
81
89
  entry_point: str = ENTRYPOINT_V07_ADDRESS,
82
- sponsorship_policy_id: Optional[str] = None
90
+ sponsorship_policy_id: Optional[str] = None,
83
91
  ):
84
92
  network = PIMLICO_NETWORKS.get(chain_id, str(chain_id))
85
- self.paymaster_url = paymaster_url or f"https://api.pimlico.io/v2/{network}/rpc?apikey={api_key}"
93
+ self.paymaster_url = (
94
+ paymaster_url or f"https://api.pimlico.io/v2/{network}/rpc?apikey={api_key}"
95
+ )
86
96
  self.api_key = api_key
87
97
  self.chain_id = chain_id
88
98
  self.entry_point = entry_point
@@ -91,19 +101,13 @@ class PimlicoPaymaster(PaymasterClient):
91
101
  self._client = httpx.Client(timeout=30.0)
92
102
 
93
103
  def get_paymaster_data(
94
- self,
95
- user_op: UserOperation,
96
- chain_id: int,
97
- entry_point: str
104
+ self, user_op: UserOperation, chain_id: int, entry_point: str
98
105
  ) -> PaymasterData:
99
106
  """Get paymaster data for sponsorship."""
100
107
  return self.sponsor_user_operation(user_op)
101
108
 
102
109
  def will_sponsor(
103
- self,
104
- user_op: UserOperation,
105
- chain_id: int,
106
- entry_point: str
110
+ self, user_op: UserOperation, chain_id: int, entry_point: str
107
111
  ) -> bool:
108
112
  """Check if the paymaster will sponsor this operation."""
109
113
  try:
@@ -125,27 +129,26 @@ class PimlicoPaymaster(PaymasterClient):
125
129
  return self._parse_paymaster_response(result)
126
130
 
127
131
  def get_token_quotes(
128
- self,
129
- user_op: UserOperation,
130
- tokens: List[str]
132
+ self, user_op: UserOperation, tokens: List[str]
131
133
  ) -> List[TokenQuote]:
132
134
  """Get quotes for paying gas with tokens."""
133
135
  packed = _pack_user_op_for_paymaster(user_op)
134
136
 
135
137
  result = self._rpc_call(
136
- "pimlico_getTokenQuotes",
137
- [packed, self.entry_point, tokens]
138
+ "pimlico_getTokenQuotes", [packed, self.entry_point, tokens]
138
139
  )
139
140
 
140
141
  quotes = []
141
142
  for r in result:
142
- quotes.append(TokenQuote(
143
- token=r.get("token", ""),
144
- symbol=r.get("symbol", ""),
145
- decimals=r.get("decimals", 18),
146
- fee=int(r.get("fee", "0x0"), 16),
147
- exchange_rate=int(r.get("exchangeRate", "0x0"), 16),
148
- ))
143
+ quotes.append(
144
+ TokenQuote(
145
+ token=r.get("token", ""),
146
+ symbol=r.get("symbol", ""),
147
+ decimals=r.get("decimals", 18),
148
+ fee=int(r.get("fee", "0x0"), 16),
149
+ exchange_rate=int(r.get("exchangeRate", "0x0"), 16),
150
+ )
151
+ )
149
152
 
150
153
  return quotes
151
154
 
@@ -155,9 +158,15 @@ class PimlicoPaymaster(PaymasterClient):
155
158
  if result.get("paymaster"):
156
159
  return PaymasterData(
157
160
  paymaster=result["paymaster"],
158
- paymaster_verification_gas_limit=int(result.get("paymasterVerificationGasLimit", "0x0"), 16),
159
- paymaster_post_op_gas_limit=int(result.get("paymasterPostOpGasLimit", "0x0"), 16),
160
- paymaster_data=bytes.fromhex(result.get("paymasterData", "0x")[2:]) if result.get("paymasterData") else b"",
161
+ paymaster_verification_gas_limit=int(
162
+ result.get("paymasterVerificationGasLimit", "0x0"), 16
163
+ ),
164
+ paymaster_post_op_gas_limit=int(
165
+ result.get("paymasterPostOpGasLimit", "0x0"), 16
166
+ ),
167
+ paymaster_data=bytes.fromhex(result.get("paymasterData", "0x")[2:])
168
+ if result.get("paymasterData")
169
+ else b"",
161
170
  )
162
171
 
163
172
  # Fall back to v0.6 format (packed)
@@ -186,7 +195,7 @@ class PimlicoPaymaster(PaymasterClient):
186
195
  response = self._client.post(
187
196
  self.paymaster_url,
188
197
  json=request,
189
- headers={"Content-Type": "application/json"}
198
+ headers={"Content-Type": "application/json"},
190
199
  )
191
200
 
192
201
  if response.status_code != 200:
@@ -199,7 +208,7 @@ class PimlicoPaymaster(PaymasterClient):
199
208
  raise PaymasterError(
200
209
  error.get("message", "Unknown error"),
201
210
  code=error.get("code"),
202
- data=error.get("data")
211
+ data=error.get("data"),
203
212
  )
204
213
 
205
214
  return data.get("result")
@@ -213,7 +222,7 @@ class BiconomyPaymaster(PaymasterClient):
213
222
  api_key: str,
214
223
  chain_id: int,
215
224
  paymaster_url: str,
216
- mode: str = "sponsored" # "sponsored" or "erc20"
225
+ mode: str = "sponsored", # "sponsored" or "erc20"
217
226
  ):
218
227
  self.api_key = api_key
219
228
  self.chain_id = chain_id
@@ -223,10 +232,7 @@ class BiconomyPaymaster(PaymasterClient):
223
232
  self._client = httpx.Client(timeout=30.0)
224
233
 
225
234
  def get_paymaster_data(
226
- self,
227
- user_op: UserOperation,
228
- chain_id: int,
229
- entry_point: str
235
+ self, user_op: UserOperation, chain_id: int, entry_point: str
230
236
  ) -> PaymasterData:
231
237
  """Get paymaster data for sponsorship."""
232
238
  packed = _pack_user_op_for_paymaster(user_op)
@@ -244,10 +250,7 @@ class BiconomyPaymaster(PaymasterClient):
244
250
  return self._parse_paymaster_response(result)
245
251
 
246
252
  def will_sponsor(
247
- self,
248
- user_op: UserOperation,
249
- chain_id: int,
250
- entry_point: str
253
+ self, user_op: UserOperation, chain_id: int, entry_point: str
251
254
  ) -> bool:
252
255
  """Check if the paymaster will sponsor this operation."""
253
256
  try:
@@ -257,9 +260,7 @@ class BiconomyPaymaster(PaymasterClient):
257
260
  return False
258
261
 
259
262
  def get_fee_quotes(
260
- self,
261
- user_op: UserOperation,
262
- tokens: List[str]
263
+ self, user_op: UserOperation, tokens: List[str]
263
264
  ) -> List[TokenQuote]:
264
265
  """Get fee quotes for ERC20 token payment."""
265
266
  packed = _pack_user_op_for_paymaster(user_op)
@@ -268,21 +269,19 @@ class BiconomyPaymaster(PaymasterClient):
268
269
 
269
270
  quotes = []
270
271
  for r in result:
271
- quotes.append(TokenQuote(
272
- token=r.get("token", ""),
273
- symbol=r.get("symbol", ""),
274
- decimals=r.get("decimals", 18),
275
- fee=int(r.get("fee", "0x0"), 16),
276
- exchange_rate=int(r.get("exchangeRate", "0x0"), 16),
277
- ))
272
+ quotes.append(
273
+ TokenQuote(
274
+ token=r.get("token", ""),
275
+ symbol=r.get("symbol", ""),
276
+ decimals=r.get("decimals", 18),
277
+ fee=int(r.get("fee", "0x0"), 16),
278
+ exchange_rate=int(r.get("exchangeRate", "0x0"), 16),
279
+ )
280
+ )
278
281
 
279
282
  return quotes
280
283
 
281
- def check_sponsorship(
282
- self,
283
- user_op: UserOperation,
284
- entry_point: str
285
- ) -> bool:
284
+ def check_sponsorship(self, user_op: UserOperation, entry_point: str) -> bool:
286
285
  """Check if the operation can be sponsored."""
287
286
  packed = _pack_user_op_for_paymaster(user_op)
288
287
 
@@ -297,9 +296,15 @@ class BiconomyPaymaster(PaymasterClient):
297
296
  if result.get("paymaster"):
298
297
  return PaymasterData(
299
298
  paymaster=result["paymaster"],
300
- paymaster_verification_gas_limit=int(result.get("paymasterVerificationGasLimit", "0x0"), 16),
301
- paymaster_post_op_gas_limit=int(result.get("paymasterPostOpGasLimit", "0x0"), 16),
302
- paymaster_data=bytes.fromhex(result.get("paymasterData", "0x")[2:]) if result.get("paymasterData") else b"",
299
+ paymaster_verification_gas_limit=int(
300
+ result.get("paymasterVerificationGasLimit", "0x0"), 16
301
+ ),
302
+ paymaster_post_op_gas_limit=int(
303
+ result.get("paymasterPostOpGasLimit", "0x0"), 16
304
+ ),
305
+ paymaster_data=bytes.fromhex(result.get("paymasterData", "0x")[2:])
306
+ if result.get("paymasterData")
307
+ else b"",
303
308
  )
304
309
 
305
310
  paymaster_and_data = result.get("paymasterAndData", "0x")
@@ -330,7 +335,7 @@ class BiconomyPaymaster(PaymasterClient):
330
335
  headers={
331
336
  "Content-Type": "application/json",
332
337
  "x-api-key": self.api_key,
333
- }
338
+ },
334
339
  )
335
340
 
336
341
  if response.status_code != 200:
@@ -343,7 +348,7 @@ class BiconomyPaymaster(PaymasterClient):
343
348
  raise PaymasterError(
344
349
  error.get("message", "Unknown error"),
345
350
  code=error.get("code"),
346
- data=error.get("data")
351
+ data=error.get("data"),
347
352
  )
348
353
 
349
354
  return data.get("result")
@@ -357,7 +362,7 @@ class StackupPaymaster(PaymasterClient):
357
362
  api_key: str,
358
363
  chain_id: int,
359
364
  paymaster_url: str,
360
- paymaster_type: Optional[str] = None
365
+ paymaster_type: Optional[str] = None,
361
366
  ):
362
367
  self.api_key = api_key
363
368
  self.chain_id = chain_id
@@ -367,10 +372,7 @@ class StackupPaymaster(PaymasterClient):
367
372
  self._client = httpx.Client(timeout=30.0)
368
373
 
369
374
  def get_paymaster_data(
370
- self,
371
- user_op: UserOperation,
372
- chain_id: int,
373
- entry_point: str
375
+ self, user_op: UserOperation, chain_id: int, entry_point: str
374
376
  ) -> PaymasterData:
375
377
  """Get paymaster data for sponsorship."""
376
378
  packed = _pack_user_op_for_paymaster(user_op)
@@ -379,20 +381,14 @@ class StackupPaymaster(PaymasterClient):
379
381
  if self.paymaster_type:
380
382
  context["type"] = self.paymaster_type
381
383
 
382
- result = self._rpc_call("pm_getPaymasterStubData", [
383
- packed,
384
- entry_point,
385
- hex(chain_id),
386
- context
387
- ])
384
+ result = self._rpc_call(
385
+ "pm_getPaymasterStubData", [packed, entry_point, hex(chain_id), context]
386
+ )
388
387
 
389
388
  return self._parse_paymaster_response(result)
390
389
 
391
390
  def will_sponsor(
392
- self,
393
- user_op: UserOperation,
394
- chain_id: int,
395
- entry_point: str
391
+ self, user_op: UserOperation, chain_id: int, entry_point: str
396
392
  ) -> bool:
397
393
  """Check if the paymaster will sponsor this operation."""
398
394
  try:
@@ -406,9 +402,15 @@ class StackupPaymaster(PaymasterClient):
406
402
  if result.get("paymaster"):
407
403
  return PaymasterData(
408
404
  paymaster=result["paymaster"],
409
- paymaster_verification_gas_limit=int(result.get("paymasterVerificationGasLimit", "0x0"), 16),
410
- paymaster_post_op_gas_limit=int(result.get("paymasterPostOpGasLimit", "0x0"), 16),
411
- paymaster_data=bytes.fromhex(result.get("paymasterData", "0x")[2:]) if result.get("paymasterData") else b"",
405
+ paymaster_verification_gas_limit=int(
406
+ result.get("paymasterVerificationGasLimit", "0x0"), 16
407
+ ),
408
+ paymaster_post_op_gas_limit=int(
409
+ result.get("paymasterPostOpGasLimit", "0x0"), 16
410
+ ),
411
+ paymaster_data=bytes.fromhex(result.get("paymasterData", "0x")[2:])
412
+ if result.get("paymasterData")
413
+ else b"",
412
414
  )
413
415
 
414
416
  paymaster_and_data = result.get("paymasterAndData", "0x")
@@ -439,7 +441,7 @@ class StackupPaymaster(PaymasterClient):
439
441
  headers={
440
442
  "Content-Type": "application/json",
441
443
  "Authorization": f"Bearer {self.api_key}",
442
- }
444
+ },
443
445
  )
444
446
 
445
447
  if response.status_code != 200:
@@ -452,17 +454,14 @@ class StackupPaymaster(PaymasterClient):
452
454
  raise PaymasterError(
453
455
  error.get("message", "Unknown error"),
454
456
  code=error.get("code"),
455
- data=error.get("data")
457
+ data=error.get("data"),
456
458
  )
457
459
 
458
460
  return data.get("result")
459
461
 
460
462
 
461
463
  def create_paymaster(
462
- provider: str,
463
- api_key: str,
464
- chain_id: int,
465
- **kwargs
464
+ provider: str, api_key: str, chain_id: int, **kwargs
466
465
  ) -> PaymasterClient:
467
466
  """Factory function to create a paymaster client."""
468
467
  if provider == "pimlico":
@@ -471,21 +470,21 @@ def create_paymaster(
471
470
  chain_id=chain_id,
472
471
  paymaster_url=kwargs.get("paymaster_url"),
473
472
  entry_point=kwargs.get("entry_point", ENTRYPOINT_V07_ADDRESS),
474
- sponsorship_policy_id=kwargs.get("sponsorship_policy_id")
473
+ sponsorship_policy_id=kwargs.get("sponsorship_policy_id"),
475
474
  )
476
475
  elif provider == "biconomy":
477
476
  return BiconomyPaymaster(
478
477
  api_key=api_key,
479
478
  chain_id=chain_id,
480
479
  paymaster_url=kwargs.get("paymaster_url", ""),
481
- mode=kwargs.get("mode", "sponsored")
480
+ mode=kwargs.get("mode", "sponsored"),
482
481
  )
483
482
  elif provider == "stackup":
484
483
  return StackupPaymaster(
485
484
  api_key=api_key,
486
485
  chain_id=chain_id,
487
486
  paymaster_url=kwargs.get("paymaster_url", ""),
488
- paymaster_type=kwargs.get("paymaster_type")
487
+ paymaster_type=kwargs.get("paymaster_type"),
489
488
  )
490
489
  else:
491
490
  raise PaymasterError(f"Unknown paymaster provider: {provider}")
@@ -498,10 +497,7 @@ class UnifiedPaymaster:
498
497
  self.paymasters = paymasters
499
498
 
500
499
  def get_paymaster_data(
501
- self,
502
- user_op: UserOperation,
503
- chain_id: int,
504
- entry_point: str
500
+ self, user_op: UserOperation, chain_id: int, entry_point: str
505
501
  ) -> PaymasterData:
506
502
  """Try each paymaster until one succeeds."""
507
503
  last_error: Optional[Exception] = None
@@ -513,15 +509,10 @@ class UnifiedPaymaster:
513
509
  last_error = e
514
510
  continue
515
511
 
516
- raise PaymasterError(
517
- f"All paymasters failed. Last error: {last_error}"
518
- )
512
+ raise PaymasterError(f"All paymasters failed. Last error: {last_error}")
519
513
 
520
514
  def will_sponsor(
521
- self,
522
- user_op: UserOperation,
523
- chain_id: int,
524
- entry_point: str
515
+ self, user_op: UserOperation, chain_id: int, entry_point: str
525
516
  ) -> bool:
526
517
  """Check if any paymaster will sponsor."""
527
518
  for paymaster in self.paymasters:
t402/erc4337/types.py CHANGED
@@ -26,6 +26,7 @@ SAFE_4337_ADDRESSES = {
26
26
 
27
27
  class PaymasterType(str, Enum):
28
28
  """Type of paymaster for gas sponsorship."""
29
+
29
30
  NONE = "none"
30
31
  VERIFYING = "verifying"
31
32
  TOKEN = "token"
@@ -39,6 +40,7 @@ class UserOperation:
39
40
 
40
41
  This is the format used before packing for on-chain submission.
41
42
  """
43
+
42
44
  sender: str
43
45
  nonce: int = 0
44
46
  init_code: bytes = field(default_factory=bytes)
@@ -63,20 +65,18 @@ class UserOperation:
63
65
  "preVerificationGas": hex(self.pre_verification_gas),
64
66
  "maxPriorityFeePerGas": hex(self.max_priority_fee_per_gas),
65
67
  "maxFeePerGas": hex(self.max_fee_per_gas),
66
- "paymasterAndData": "0x" + self.paymaster_and_data.hex() if self.paymaster_and_data else "0x",
68
+ "paymasterAndData": "0x" + self.paymaster_and_data.hex()
69
+ if self.paymaster_and_data
70
+ else "0x",
67
71
  "signature": "0x" + self.signature.hex() if self.signature else "0x",
68
72
  }
69
73
 
70
74
  def to_packed_dict(self) -> dict:
71
75
  """Convert to packed format for v0.7 submission."""
72
76
  account_gas_limits = pack_account_gas_limits(
73
- self.verification_gas_limit,
74
- self.call_gas_limit
75
- )
76
- gas_fees = pack_gas_fees(
77
- self.max_priority_fee_per_gas,
78
- self.max_fee_per_gas
77
+ self.verification_gas_limit, self.call_gas_limit
79
78
  )
79
+ gas_fees = pack_gas_fees(self.max_priority_fee_per_gas, self.max_fee_per_gas)
80
80
 
81
81
  return {
82
82
  "sender": self.sender,
@@ -86,7 +86,9 @@ class UserOperation:
86
86
  "accountGasLimits": "0x" + account_gas_limits.hex(),
87
87
  "preVerificationGas": hex(self.pre_verification_gas),
88
88
  "gasFees": "0x" + gas_fees.hex(),
89
- "paymasterAndData": "0x" + self.paymaster_and_data.hex() if self.paymaster_and_data else "0x",
89
+ "paymasterAndData": "0x" + self.paymaster_and_data.hex()
90
+ if self.paymaster_and_data
91
+ else "0x",
90
92
  "signature": "0x" + self.signature.hex() if self.signature else "0x",
91
93
  }
92
94
 
@@ -98,6 +100,7 @@ class PackedUserOperation:
98
100
 
99
101
  Gas fields are packed into bytes32 for efficiency.
100
102
  """
103
+
101
104
  sender: str
102
105
  nonce: int
103
106
  init_code: bytes
@@ -112,6 +115,7 @@ class PackedUserOperation:
112
115
  @dataclass
113
116
  class PaymasterData:
114
117
  """Paymaster information for gas sponsorship."""
118
+
115
119
  paymaster: str
116
120
  paymaster_verification_gas_limit: int = 50000
117
121
  paymaster_post_op_gas_limit: int = 50000
@@ -120,14 +124,17 @@ class PaymasterData:
120
124
  def to_bytes(self) -> bytes:
121
125
  """Pack into paymasterAndData format."""
122
126
  paymaster_bytes = bytes.fromhex(self.paymaster[2:])
123
- verification_bytes = self.paymaster_verification_gas_limit.to_bytes(16, 'big')
124
- post_op_bytes = self.paymaster_post_op_gas_limit.to_bytes(16, 'big')
125
- return paymaster_bytes + verification_bytes + post_op_bytes + self.paymaster_data
127
+ verification_bytes = self.paymaster_verification_gas_limit.to_bytes(16, "big")
128
+ post_op_bytes = self.paymaster_post_op_gas_limit.to_bytes(16, "big")
129
+ return (
130
+ paymaster_bytes + verification_bytes + post_op_bytes + self.paymaster_data
131
+ )
126
132
 
127
133
 
128
134
  @dataclass
129
135
  class GasEstimate:
130
136
  """Gas estimation results from the bundler."""
137
+
131
138
  verification_gas_limit: int
132
139
  call_gas_limit: int
133
140
  pre_verification_gas: int
@@ -138,6 +145,7 @@ class GasEstimate:
138
145
  @dataclass
139
146
  class UserOperationReceipt:
140
147
  """Receipt after UserOperation execution."""
148
+
141
149
  user_op_hash: str
142
150
  sender: str
143
151
  nonce: int
@@ -154,6 +162,7 @@ class UserOperationReceipt:
154
162
  @dataclass
155
163
  class BundlerConfig:
156
164
  """Configuration for bundler client."""
165
+
157
166
  bundler_url: str
158
167
  chain_id: int
159
168
  entry_point: str = ENTRYPOINT_V07_ADDRESS
@@ -162,6 +171,7 @@ class BundlerConfig:
162
171
  @dataclass
163
172
  class PaymasterConfig:
164
173
  """Configuration for paymaster integration."""
174
+
165
175
  address: str
166
176
  url: Optional[str] = None
167
177
  paymaster_type: PaymasterType = PaymasterType.SPONSORING
@@ -170,6 +180,7 @@ class PaymasterConfig:
170
180
  @dataclass
171
181
  class TokenQuote:
172
182
  """Quote for token paymaster."""
183
+
173
184
  token: str
174
185
  symbol: str
175
186
  decimals: int
@@ -180,6 +191,7 @@ class TokenQuote:
180
191
  @dataclass
181
192
  class AssetChange:
182
193
  """Asset change from simulation."""
194
+
183
195
  asset_type: str # native, erc20, erc721, erc1155
184
196
  change_type: str # transfer_in, transfer_out
185
197
  from_address: str
@@ -195,6 +207,7 @@ class AssetChange:
195
207
  @dataclass
196
208
  class SimulationResult:
197
209
  """Simulation result for UserOperation."""
210
+
198
211
  success: bool
199
212
  error: Optional[str] = None
200
213
  changes: List[AssetChange] = field(default_factory=list)
@@ -223,13 +236,13 @@ BUNDLER_METHODS = {
223
236
 
224
237
  # Supported chains for ERC-4337
225
238
  SUPPORTED_CHAINS = [
226
- 1, # Ethereum Mainnet
227
- 11155111, # Ethereum Sepolia
228
- 8453, # Base
229
- 84532, # Base Sepolia
230
- 10, # Optimism
231
- 42161, # Arbitrum One
232
- 137, # Polygon
239
+ 1, # Ethereum Mainnet
240
+ 11155111, # Ethereum Sepolia
241
+ 8453, # Base
242
+ 84532, # Base Sepolia
243
+ 10, # Optimism
244
+ 42161, # Arbitrum One
245
+ 137, # Polygon
233
246
  ]
234
247
 
235
248
 
@@ -265,29 +278,29 @@ PIMLICO_NETWORKS = {
265
278
 
266
279
  def pack_account_gas_limits(verification_gas_limit: int, call_gas_limit: int) -> bytes:
267
280
  """Pack verification and call gas limits into bytes32."""
268
- verification_bytes = verification_gas_limit.to_bytes(16, 'big')
269
- call_bytes = call_gas_limit.to_bytes(16, 'big')
281
+ verification_bytes = verification_gas_limit.to_bytes(16, "big")
282
+ call_bytes = call_gas_limit.to_bytes(16, "big")
270
283
  return verification_bytes + call_bytes
271
284
 
272
285
 
273
286
  def unpack_account_gas_limits(packed: bytes) -> tuple[int, int]:
274
287
  """Unpack account gas limits from bytes32."""
275
- verification_gas_limit = int.from_bytes(packed[:16], 'big')
276
- call_gas_limit = int.from_bytes(packed[16:], 'big')
288
+ verification_gas_limit = int.from_bytes(packed[:16], "big")
289
+ call_gas_limit = int.from_bytes(packed[16:], "big")
277
290
  return verification_gas_limit, call_gas_limit
278
291
 
279
292
 
280
293
  def pack_gas_fees(max_priority_fee_per_gas: int, max_fee_per_gas: int) -> bytes:
281
294
  """Pack max priority fee and max fee per gas into bytes32."""
282
- priority_bytes = max_priority_fee_per_gas.to_bytes(16, 'big')
283
- max_bytes = max_fee_per_gas.to_bytes(16, 'big')
295
+ priority_bytes = max_priority_fee_per_gas.to_bytes(16, "big")
296
+ max_bytes = max_fee_per_gas.to_bytes(16, "big")
284
297
  return priority_bytes + max_bytes
285
298
 
286
299
 
287
300
  def unpack_gas_fees(packed: bytes) -> tuple[int, int]:
288
301
  """Unpack gas fees from bytes32."""
289
- max_priority_fee_per_gas = int.from_bytes(packed[:16], 'big')
290
- max_fee_per_gas = int.from_bytes(packed[16:], 'big')
302
+ max_priority_fee_per_gas = int.from_bytes(packed[:16], "big")
303
+ max_fee_per_gas = int.from_bytes(packed[16:], "big")
291
304
  return max_priority_fee_per_gas, max_fee_per_gas
292
305
 
293
306