@@ -26,6 +26,12 @@ public class SmartWallet : IThirdwebWallet
26
26
private BigInteger _chainId ;
27
27
private string _bundlerUrl ;
28
28
private string _paymasterUrl ;
29
+ private string _erc20PaymasterAddress ;
30
+ private string _erc20PaymasterToken ;
31
+ private bool _isApproving ;
32
+ private bool _isApproved ;
33
+
34
+ private bool UseERC20Paymaster => ! string . IsNullOrEmpty ( _erc20PaymasterAddress ) && ! string . IsNullOrEmpty ( _erc20PaymasterToken ) ;
29
35
30
36
protected SmartWallet (
31
37
IThirdwebWallet personalAccount ,
@@ -35,7 +41,9 @@ protected SmartWallet(
35
41
string paymasterUrl ,
36
42
ThirdwebContract entryPointContract ,
37
43
ThirdwebContract factoryContract ,
38
- ThirdwebContract accountContract
44
+ ThirdwebContract accountContract ,
45
+ string erc20PaymasterAddress ,
46
+ string erc20PaymasterToken
39
47
)
40
48
{
41
49
Client = personalAccount . Client ;
@@ -48,6 +56,8 @@ ThirdwebContract accountContract
48
56
_entryPointContract = entryPointContract ;
49
57
_factoryContract = factoryContract ;
50
58
_accountContract = accountContract ;
59
+ _erc20PaymasterAddress = erc20PaymasterAddress ;
60
+ _erc20PaymasterToken = erc20PaymasterToken ;
51
61
}
52
62
53
63
public static async Task < SmartWallet > Create (
@@ -58,7 +68,9 @@ public static async Task<SmartWallet> Create(
58
68
string accountAddressOverride = null ,
59
69
string entryPoint = null ,
60
70
string bundlerUrl = null ,
61
- string paymasterUrl = null
71
+ string paymasterUrl = null ,
72
+ string erc20PaymasterAddress = null ,
73
+ string erc20PaymasterToken = null
62
74
)
63
75
{
64
76
if ( ! await personalWallet . IsConnected ( ) )
@@ -98,7 +110,7 @@ public static async Task<SmartWallet> Create(
98
110
) ;
99
111
}
100
112
101
- return new SmartWallet ( personalWallet , gasless , chainId , bundlerUrl , paymasterUrl , entryPointContract , factoryContract , accountContract ) ;
113
+ return new SmartWallet ( personalWallet , gasless , chainId , bundlerUrl , paymasterUrl , entryPointContract , factoryContract , accountContract , erc20PaymasterAddress , erc20PaymasterToken ) ;
102
114
}
103
115
104
116
public async Task < bool > IsDeployed ( )
@@ -191,6 +203,31 @@ private async Task<UserOperation> SignUserOp(ThirdwebTransactionInput transactio
191
203
{
192
204
requestId ??= 1 ;
193
205
206
+ // Approve tokens if ERC20Paymaster
207
+ if ( UseERC20Paymaster && ! _isApproving && ! _isApproved && ! simulation )
208
+ {
209
+ try
210
+ {
211
+ _isApproving = true ;
212
+ var tokenContract = await ThirdwebContract . Create ( Client , _erc20PaymasterToken , _chainId ) ;
213
+ var approvedAmount = await tokenContract . ERC20_Allowance ( _accountContract . Address , _erc20PaymasterAddress ) ;
214
+ if ( approvedAmount == 0 )
215
+ {
216
+ _ = await tokenContract . ERC20_Approve ( this , _erc20PaymasterAddress , BigInteger . Pow ( 2 , 96 ) - 1 ) ;
217
+ }
218
+ _isApproved = true ;
219
+ }
220
+ catch ( Exception e )
221
+ {
222
+ _isApproved = false ;
223
+ throw new Exception ( $ "Approving tokens for ERC20Paymaster spending failed: { e . Message } ") ;
224
+ }
225
+ finally
226
+ {
227
+ _isApproving = false ;
228
+ }
229
+ }
230
+
194
231
var initCode = await GetInitCode ( ) ;
195
232
196
233
// Wait until deployed to avoid double initCode
@@ -241,7 +278,7 @@ private async Task<UserOperation> SignUserOp(ThirdwebTransactionInput transactio
241
278
242
279
// Update paymaster data if any
243
280
244
- partialUserOp . PaymasterAndData = await GetPaymasterAndData ( requestId , EncodeUserOperation ( partialUserOp ) ) ;
281
+ partialUserOp . PaymasterAndData = await GetPaymasterAndData ( requestId , EncodeUserOperation ( partialUserOp ) , simulation ) ;
245
282
246
283
// Estimate gas
247
284
@@ -252,7 +289,7 @@ private async Task<UserOperation> SignUserOp(ThirdwebTransactionInput transactio
252
289
253
290
// Update paymaster data if any
254
291
255
- partialUserOp . PaymasterAndData = await GetPaymasterAndData ( requestId , EncodeUserOperation ( partialUserOp ) ) ;
292
+ partialUserOp . PaymasterAndData = await GetPaymasterAndData ( requestId , EncodeUserOperation ( partialUserOp ) , simulation ) ;
256
293
257
294
// Hash, sign and encode the user operation
258
295
@@ -310,9 +347,13 @@ private async Task<string> ZkBroadcastTransaction(object transactionInput)
310
347
return result . transactionHash ;
311
348
}
312
349
313
- private async Task < byte [ ] > GetPaymasterAndData ( object requestId , UserOperationHexified userOp )
350
+ private async Task < byte [ ] > GetPaymasterAndData ( object requestId , UserOperationHexified userOp , bool simulation = false )
314
351
{
315
- if ( _gasless )
352
+ if ( UseERC20Paymaster && ! _isApproving && ! simulation )
353
+ {
354
+ return Utils . HexConcat ( _erc20PaymasterAddress , _erc20PaymasterToken ) . HexToByteArray ( ) ;
355
+ }
356
+ else if ( _gasless )
316
357
{
317
358
var paymasterAndData = await BundlerClient . PMSponsorUserOperation ( Client , _paymasterUrl , requestId , userOp , _entryPointContract . Address ) ;
318
359
return paymasterAndData . paymasterAndData . HexToByteArray ( ) ;
0 commit comments