11
11
12
12
namespace Thirdweb ;
13
13
14
+ public enum TokenPaymaster
15
+ {
16
+ NONE ,
17
+ BASE_USDC ,
18
+ }
19
+
14
20
public class SmartWallet : IThirdwebWallet
15
21
{
16
22
public ThirdwebClient Client
@@ -35,9 +41,42 @@ public bool IsDeploying
35
41
private readonly string _paymasterUrl ;
36
42
private readonly string _erc20PaymasterAddress ;
37
43
private readonly string _erc20PaymasterToken ;
44
+ private readonly BigInteger _erc20PaymasterStorageSlot ;
38
45
private bool _isApproving ;
39
46
private bool _isApproved ;
40
47
48
+ private struct TokenPaymasterConfig ( )
49
+ {
50
+ public BigInteger ChainId ;
51
+ public string PaymasterAddress ;
52
+ public string TokenAddress ;
53
+ public BigInteger BalanceStorageSlot ;
54
+ }
55
+
56
+ private static readonly Dictionary < TokenPaymaster , TokenPaymasterConfig > _tokenPaymasterConfig = new ( )
57
+ {
58
+ {
59
+ TokenPaymaster . NONE ,
60
+ new TokenPaymasterConfig ( )
61
+ {
62
+ ChainId = 0 ,
63
+ PaymasterAddress = null ,
64
+ TokenAddress = null ,
65
+ BalanceStorageSlot = 0
66
+ }
67
+ } ,
68
+ {
69
+ TokenPaymaster . BASE_USDC ,
70
+ new TokenPaymasterConfig ( )
71
+ {
72
+ ChainId = 8453 ,
73
+ PaymasterAddress = "0x0c6199eE133EB4ff8a6bbD03370336C5A5d9D536" ,
74
+ TokenAddress = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" ,
75
+ BalanceStorageSlot = 9
76
+ }
77
+ }
78
+ } ;
79
+
41
80
private bool UseERC20Paymaster => ! string . IsNullOrEmpty ( this . _erc20PaymasterAddress ) && ! string . IsNullOrEmpty ( this . _erc20PaymasterToken ) ;
42
81
43
82
protected SmartWallet (
@@ -50,7 +89,8 @@ protected SmartWallet(
50
89
ThirdwebContract factoryContract ,
51
90
ThirdwebContract accountContract ,
52
91
string erc20PaymasterAddress ,
53
- string erc20PaymasterToken
92
+ string erc20PaymasterToken ,
93
+ BigInteger erc20PaymasterStorageSlot
54
94
)
55
95
{
56
96
this . Client = personalAccount . Client ;
@@ -65,6 +105,7 @@ string erc20PaymasterToken
65
105
this . _accountContract = accountContract ;
66
106
this . _erc20PaymasterAddress = erc20PaymasterAddress ;
67
107
this . _erc20PaymasterToken = erc20PaymasterToken ;
108
+ this . _erc20PaymasterStorageSlot = erc20PaymasterStorageSlot ;
68
109
}
69
110
70
111
public static async Task < SmartWallet > Create (
@@ -76,8 +117,7 @@ public static async Task<SmartWallet> Create(
76
117
string entryPoint = null ,
77
118
string bundlerUrl = null ,
78
119
string paymasterUrl = null ,
79
- string erc20PaymasterAddress = null ,
80
- string erc20PaymasterToken = null
120
+ TokenPaymaster tokenPaymaster = TokenPaymaster . NONE
81
121
)
82
122
{
83
123
if ( ! await personalWallet . IsConnected ( ) )
@@ -112,7 +152,21 @@ public static async Task<SmartWallet> Create(
112
152
accountContract = await ThirdwebContract . Create ( personalWallet . Client , accountAddress , chainId , accountAbi ) ;
113
153
}
114
154
115
- return new SmartWallet ( personalWallet , gasless , chainId , bundlerUrl , paymasterUrl , entryPointContract , factoryContract , accountContract , erc20PaymasterAddress , erc20PaymasterToken ) ;
155
+ var erc20PmInfo = _tokenPaymasterConfig [ tokenPaymaster ] ;
156
+
157
+ if ( tokenPaymaster != TokenPaymaster . NONE )
158
+ {
159
+ if ( entryPointVersion != 7 )
160
+ {
161
+ throw new InvalidOperationException ( "Token paymasters are only supported in entry point version 7." ) ;
162
+ }
163
+ if ( erc20PmInfo . ChainId != chainId )
164
+ {
165
+ throw new InvalidOperationException ( "Token paymaster chain ID does not match the smart account chain ID." ) ;
166
+ }
167
+ }
168
+
169
+ return new SmartWallet ( personalWallet , gasless , chainId , bundlerUrl , paymasterUrl , entryPointContract , factoryContract , accountContract , erc20PmInfo . PaymasterAddress , erc20PmInfo . TokenAddress , erc20PmInfo . BalanceStorageSlot ) ;
116
170
}
117
171
118
172
public async Task < bool > IsDeployed ( )
@@ -330,56 +384,42 @@ private async Task<object> SignUserOp(ThirdwebTransactionInput transactionInput,
330
384
Signature = Constants . DUMMY_SIG . HexToBytes ( ) ,
331
385
} ;
332
386
333
- // Update paymaster data if any
334
-
335
- var res = await this . GetPaymasterAndData ( requestId , EncodeUserOperation ( partialUserOp ) , true ) ;
336
- partialUserOp . Paymaster = res . Paymaster ;
337
- partialUserOp . PaymasterData = res . PaymasterData ? . HexToBytes ( ) ?? Array . Empty < byte > ( ) ;
338
- partialUserOp . PreVerificationGas = new HexBigInteger ( res . PreVerificationGas ?? "0" ) . Value ;
339
- partialUserOp . VerificationGasLimit = new HexBigInteger ( res . VerificationGasLimit ?? "0" ) . Value ;
340
- partialUserOp . CallGasLimit = new HexBigInteger ( res . CallGasLimit ?? "0" ) . Value ;
341
- partialUserOp . PaymasterVerificationGasLimit = new HexBigInteger ( res . PaymasterVerificationGasLimit ?? "0" ) . Value ;
342
- partialUserOp . PaymasterPostOpGasLimit = new HexBigInteger ( res . PaymasterPostOpGasLimit ?? "0" ) . Value ;
343
-
344
- // Estimate gas
387
+ // Update Paymaster Data / Estimate gas
345
388
346
389
if (
347
- ( this . UseERC20Paymaster && ! this . _isApproving )
348
- || partialUserOp . PreVerificationGas . IsZero
349
- || partialUserOp . VerificationGasLimit . IsZero
350
- || partialUserOp . CallGasLimit . IsZero
351
- || partialUserOp . PaymasterVerificationGasLimit . IsZero
352
- || partialUserOp . PaymasterPostOpGasLimit . IsZero
390
+ this . UseERC20Paymaster && ! this . _isApproving
353
391
)
354
392
{
355
- Dictionary < string , object > stateDict = null ;
356
- if ( this . UseERC20Paymaster && ! this . _isApproving )
357
- {
358
- var abiEncoder = new ABIEncode ( ) ;
359
- var slotBytes = abiEncoder . GetABIEncoded ( new ABIValue ( "address" , this . _accountContract . Address ) , new ABIValue ( "uint256" , new BigInteger ( 9 ) ) ) ;
360
- var desiredBalance = BigInteger . Pow ( 2 , 96 ) - 1 ;
361
- var storageDict = new Dictionary < string , string >
393
+ var abiEncoder = new ABIEncode ( ) ;
394
+ var slotBytes = abiEncoder . GetABIEncoded ( new ABIValue ( "address" , this . _accountContract . Address ) , new ABIValue ( "uint256" , this . _erc20PaymasterStorageSlot ) ) ;
395
+ var desiredBalance = BigInteger . Pow ( 2 , 96 ) - 1 ;
396
+ var storageDict = new Dictionary < string , string >
362
397
{
363
398
{ new Sha3Keccack ( ) . CalculateHash ( slotBytes ) . BytesToHex ( ) . ToString ( ) , desiredBalance . ToHexBigInteger ( ) . HexValue . HexToBytes32 ( ) . BytesToHex ( ) }
364
399
} ;
365
- stateDict = new Dictionary < string , object > { { this . _erc20PaymasterToken , new { stateDiff = storageDict } } } ;
366
- res = await this . GetPaymasterAndData ( requestId , EncodeUserOperation ( partialUserOp ) , simulation ) ;
367
- partialUserOp . Paymaster = res . Paymaster ;
368
- partialUserOp . PaymasterData = res . PaymasterData . HexToBytes ( ) ;
369
- }
400
+ var stateDict = new Dictionary < string , object > { { this . _erc20PaymasterToken , new { stateDiff = storageDict } } } ;
401
+ var res = await this . GetPaymasterAndData ( requestId , EncodeUserOperation ( partialUserOp ) , simulation ) ;
402
+ partialUserOp . Paymaster = res . Paymaster ;
403
+ partialUserOp . PaymasterData = res . PaymasterData . HexToBytes ( ) ;
370
404
371
405
var gasEstimates = await BundlerClient . EthEstimateUserOperationGas ( this . Client , this . _bundlerUrl , requestId , EncodeUserOperation ( partialUserOp ) , this . _entryPointContract . Address , stateDict ) ;
372
406
partialUserOp . CallGasLimit = 21000 + new HexBigInteger ( gasEstimates . CallGasLimit ) . Value ;
373
407
partialUserOp . VerificationGasLimit = new HexBigInteger ( gasEstimates . VerificationGasLimit ) . Value ;
374
408
partialUserOp . PreVerificationGas = new HexBigInteger ( gasEstimates . PreVerificationGas ) . Value ;
375
409
partialUserOp . PaymasterVerificationGasLimit = new HexBigInteger ( gasEstimates . PaymasterVerificationGasLimit ) . Value ;
376
410
partialUserOp . PaymasterPostOpGasLimit = new HexBigInteger ( gasEstimates . PaymasterPostOpGasLimit ) . Value ;
411
+ }
412
+ else
413
+ {
377
414
378
- // Update paymaster data if any
379
-
380
- res = await this . GetPaymasterAndData ( requestId , EncodeUserOperation ( partialUserOp ) , simulation ) ;
415
+ var res = await this . GetPaymasterAndData ( requestId , EncodeUserOperation ( partialUserOp ) , true ) ;
381
416
partialUserOp . Paymaster = res . Paymaster ;
382
- partialUserOp . PaymasterData = res . PaymasterData . HexToBytes ( ) ;
417
+ partialUserOp . PaymasterData = res . PaymasterData ? . HexToBytes ( ) ?? Array . Empty < byte > ( ) ;
418
+ partialUserOp . PreVerificationGas = new HexBigInteger ( res . PreVerificationGas ?? "0" ) . Value ;
419
+ partialUserOp . VerificationGasLimit = new HexBigInteger ( res . VerificationGasLimit ?? "0" ) . Value ;
420
+ partialUserOp . CallGasLimit = new HexBigInteger ( res . CallGasLimit ?? "0" ) . Value ;
421
+ partialUserOp . PaymasterVerificationGasLimit = new HexBigInteger ( res . PaymasterVerificationGasLimit ?? "0" ) . Value ;
422
+ partialUserOp . PaymasterPostOpGasLimit = new HexBigInteger ( res . PaymasterPostOpGasLimit ?? "0" ) . Value ;
383
423
}
384
424
385
425
// Hash, sign and encode the user operation
0 commit comments