@@ -38,6 +38,8 @@ public class SmartWallet
38
38
private bool _deployed ;
39
39
private bool _deploying ;
40
40
private bool _initialized ;
41
+ private bool _approved ;
42
+ private bool _approving ;
41
43
42
44
public List < string > Accounts { get ; internal set ; }
43
45
public string PersonalAddress { get ; internal set ; }
@@ -53,6 +55,8 @@ public SmartWallet(Web3 personalWeb3, ThirdwebSDK.SmartWalletConfig config)
53
55
{
54
56
factoryAddress = config . factoryAddress ,
55
57
gasless = config . gasless ,
58
+ erc20PaymasterAddress = config . erc20PaymasterAddress ,
59
+ erc20TokenAddress = config . erc20TokenAddress ,
56
60
bundlerUrl = string . IsNullOrEmpty ( config . bundlerUrl ) ? $ "https://{ ThirdwebManager . Instance . SDK . session . CurrentChainData . chainName } .bundler.thirdweb.com" : config . bundlerUrl ,
57
61
paymasterUrl = string . IsNullOrEmpty ( config . paymasterUrl ) ? $ "https://{ ThirdwebManager . Instance . SDK . session . CurrentChainData . chainName } .bundler.thirdweb.com" : config . paymasterUrl ,
58
62
entryPointAddress = string . IsNullOrEmpty ( config . entryPointAddress ) ? Constants . DEFAULT_ENTRYPOINT_ADDRESS : config . entryPointAddress ,
@@ -61,6 +65,8 @@ public SmartWallet(Web3 personalWeb3, ThirdwebSDK.SmartWalletConfig config)
61
65
_deployed = false ;
62
66
_initialized = false ;
63
67
_deploying = false ;
68
+ _approved = false ;
69
+ _approving = false ;
64
70
}
65
71
66
72
internal async Task < string > GetPersonalAddress ( )
@@ -197,8 +203,6 @@ private async Task<RpcResponseMessage> CreateUserOpAndSend(RpcRequestMessage req
197
203
var transactionInput = JsonConvert . DeserializeObject < TransactionInput > ( JsonConvert . SerializeObject ( paramList [ 0 ] ) ) ;
198
204
var dummySig = Constants . DUMMY_SIG ;
199
205
200
- var ( initCode , gas ) = await GetInitCode ( ) ;
201
-
202
206
var executeFn = new AccountContract . ExecuteFunction
203
207
{
204
208
Target = transactionInput . To ,
@@ -208,8 +212,37 @@ private async Task<RpcResponseMessage> CreateUserOpAndSend(RpcRequestMessage req
208
212
} ;
209
213
var executeInput = executeFn . CreateTransactionInput ( Accounts [ 0 ] ) ;
210
214
215
+ // Approve ERC20 tokens if any
216
+
217
+ if ( ! string . IsNullOrEmpty ( Config . erc20PaymasterAddress ) && ! _approved && ! _approving )
218
+ {
219
+ try
220
+ {
221
+ _approving = true ;
222
+ var tokenContract = ThirdwebManager . Instance . SDK . GetContract ( Config . erc20TokenAddress ) ;
223
+ var approvedAmount = await tokenContract . ERC20 . AllowanceOf ( Accounts [ 0 ] , Config . erc20PaymasterAddress ) ;
224
+ if ( BigInteger . Parse ( approvedAmount . value ) == 0 )
225
+ {
226
+ ThirdwebDebug . Log ( $ "Approving tokens for ERC20Paymaster spending") ;
227
+ _deploying = false ;
228
+ await tokenContract . ERC20 . SetAllowance ( Config . erc20PaymasterAddress , ( BigInteger . Pow ( 2 , 96 ) - 1 ) . ToString ( ) . ToEth ( ) ) ;
229
+ }
230
+ _approved = true ;
231
+ _approving = false ;
232
+ await UpdateDeploymentStatus ( ) ;
233
+ }
234
+ catch ( Exception e )
235
+ {
236
+ _approving = false ;
237
+ _approved = false ;
238
+ throw new Exception ( $ "Approving tokens for ERC20Paymaster spending failed: { e . Message } ") ;
239
+ }
240
+ }
241
+
211
242
// Create the user operation and its safe (hexified) version
212
243
244
+ var ( initCode , gas ) = await GetInitCode ( ) ;
245
+
213
246
var gasPrices = await Utils . GetGasPriceAsync ( ThirdwebManager . Instance . SDK . session . ChainId ) ;
214
247
215
248
var partialUserOp = new EntryPointContract . UserOperation ( )
@@ -235,7 +268,9 @@ private async Task<RpcResponseMessage> CreateUserOpAndSend(RpcRequestMessage req
235
268
236
269
var gasEstimates = await BundlerClient . EthEstimateUserOperationGas ( Config . bundlerUrl , apiKey , requestMessage . Id , partialUserOp . EncodeUserOperation ( ) , Config . entryPointAddress ) ;
237
270
partialUserOp . CallGasLimit = 50000 + new HexBigInteger ( gasEstimates . CallGasLimit ) . Value ;
238
- partialUserOp . VerificationGasLimit = new HexBigInteger ( gasEstimates . VerificationGas ) . Value ;
271
+ partialUserOp . VerificationGasLimit = string . IsNullOrEmpty ( Config . erc20PaymasterAddress )
272
+ ? new HexBigInteger ( gasEstimates . VerificationGas ) . Value
273
+ : new HexBigInteger ( gasEstimates . VerificationGas ) . Value * 3 ;
239
274
partialUserOp . PreVerificationGas = new HexBigInteger ( gasEstimates . PreVerificationGas ) . Value ;
240
275
241
276
// Update paymaster data if any
@@ -258,8 +293,8 @@ private async Task<RpcResponseMessage> CreateUserOpAndSend(RpcRequestMessage req
258
293
string txHash = null ;
259
294
while ( txHash == null )
260
295
{
261
- var getUserOpResponse = await BundlerClient . EthGetUserOperationByHash ( Config . bundlerUrl , apiKey , requestMessage . Id , userOpHash ) ;
262
- txHash = getUserOpResponse ? . transactionHash ;
296
+ var userOpReceipt = await BundlerClient . EthGetUserOperationReceipt ( Config . bundlerUrl , apiKey , requestMessage . Id , userOpHash ) ;
297
+ txHash = userOpReceipt ? . receipt ? . TransactionHash ;
263
298
await new WaitForSecondsRealtime ( 1f ) ;
264
299
}
265
300
ThirdwebDebug . Log ( "Tx Hash: " + txHash ) ;
@@ -297,9 +332,19 @@ private async Task<BigInteger> GetNonce()
297
332
298
333
private async Task < byte [ ] > GetPaymasterAndData ( object requestId , UserOperationHexified userOp , string apiKey )
299
334
{
300
- return Config . gasless
301
- ? ( await BundlerClient . PMSponsorUserOperation ( Config . paymasterUrl , apiKey , requestId , userOp , Config . entryPointAddress ) ) . paymasterAndData . HexStringToByteArray ( )
302
- : new byte [ ] { } ;
335
+ if ( ! string . IsNullOrEmpty ( Config . erc20PaymasterAddress ) && ! _approving )
336
+ {
337
+ return Config . erc20PaymasterAddress . HexToByteArray ( ) ;
338
+ }
339
+ else if ( Config . gasless )
340
+ {
341
+ var paymasterAndData = await BundlerClient . PMSponsorUserOperation ( Config . paymasterUrl , apiKey , requestId , userOp , Config . entryPointAddress ) ;
342
+ return paymasterAndData . paymasterAndData . HexToByteArray ( ) ;
343
+ }
344
+ else
345
+ {
346
+ return new byte [ ] { } ;
347
+ }
303
348
}
304
349
}
305
350
}
0 commit comments