Skip to content

Commit 5c37e2a

Browse files
authored
Parallelized UserOps with multidimensional nonce (#122)
1 parent 87a5aa9 commit 5c37e2a

File tree

2 files changed

+49
-38
lines changed

2 files changed

+49
-38
lines changed

Assets/Thirdweb/Core/Scripts/AccountAbstraction/Core/SmartWallet.cs

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,6 @@ public class SmartWallet
4747
public bool IsDeployed => _deployed;
4848
public bool IsDeploying => _deploying;
4949

50-
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
51-
private readonly ConcurrentQueue<TaskCompletionSource<RpcResponseMessage>> _responseQueue = new ConcurrentQueue<TaskCompletionSource<RpcResponseMessage>>();
52-
5350
public SmartWallet(Web3 personalWeb3, ThirdwebSDK.SmartWalletConfig config)
5451
{
5552
PersonalWeb3 = personalWeb3;
@@ -140,29 +137,7 @@ internal async Task<RpcResponseMessage> Request(RpcRequestMessage requestMessage
140137

141138
if (requestMessage.Method == "eth_sendTransaction")
142139
{
143-
var tcs = new TaskCompletionSource<RpcResponseMessage>();
144-
_responseQueue.Enqueue(tcs);
145-
146-
await _semaphore.WaitAsync();
147-
148-
if (_responseQueue.TryDequeue(out var dequeuedTcs) && dequeuedTcs == tcs)
149-
{
150-
try
151-
{
152-
var response = await CreateUserOpAndSend(requestMessage);
153-
tcs.SetResult(response);
154-
}
155-
catch (Exception ex)
156-
{
157-
tcs.SetException(ex);
158-
}
159-
finally
160-
{
161-
_semaphore.Release();
162-
}
163-
}
164-
165-
return await tcs.Task;
140+
return await CreateUserOpAndSend(requestMessage);
166141
}
167142
else if (requestMessage.Method == "eth_chainId")
168143
{
@@ -177,6 +152,14 @@ internal async Task<RpcResponseMessage> Request(RpcRequestMessage requestMessage
177152

178153
private async Task<RpcResponseMessage> CreateUserOpAndSend(RpcRequestMessage requestMessage)
179154
{
155+
await new WaitUntil(() => !_deploying);
156+
157+
await UpdateDeploymentStatus();
158+
if (!_deployed)
159+
{
160+
_deploying = true;
161+
}
162+
180163
string apiKey = ThirdwebManager.Instance.SDK.session.Options.clientId;
181164

182165
// Deserialize the transaction input from the request message
@@ -188,7 +171,6 @@ private async Task<RpcResponseMessage> CreateUserOpAndSend(RpcRequestMessage req
188171
for (int i = 0; i < Constants.DUMMY_SIG_LENGTH; i++)
189172
dummySig[i] = 0x01;
190173

191-
await UpdateDeploymentStatus();
192174
var (initCode, gas) = await GetInitCode();
193175

194176
var executeFn = new AccountContract.ExecuteFunction
@@ -248,24 +230,33 @@ private async Task<RpcResponseMessage> CreateUserOpAndSend(RpcRequestMessage req
248230

249231
// Check if successful
250232

251-
var receipt = await Transaction.WaitForTransactionResultRaw(txHash);
252-
var decodedEvents = receipt.DecodeAllEvents<EntryPointContract.UserOperationEventEventDTO>();
253-
if (decodedEvents[0].Event.Success == false)
233+
if (!_deployed)
254234
{
255-
throw new Exception($"Transaction {txHash} execution reverted");
256-
}
257-
else
258-
{
259-
ThirdwebDebug.Log("Transaction successful");
260-
_deployed = true;
235+
var receipt = await Transaction.WaitForTransactionResultRaw(txHash);
236+
var decodedEvents = receipt.DecodeAllEvents<EntryPointContract.UserOperationEventEventDTO>();
237+
if (decodedEvents[0].Event.Success == false)
238+
{
239+
throw new Exception($"Transaction {txHash} execution reverted");
240+
}
241+
else
242+
{
243+
ThirdwebDebug.Log("Transaction successful");
244+
_deployed = true;
245+
}
261246
}
247+
248+
_deploying = false;
249+
262250
return new RpcResponseMessage(requestMessage.Id, txHash);
263251
}
264252

265253
private async Task<BigInteger> GetNonce()
266254
{
267-
var nonce = await TransactionManager.ThirdwebRead<AccountContract.GetNonceFunction, AccountContract.GetNonceOutputDTO>(Accounts[0], new AccountContract.GetNonceFunction() { });
268-
return nonce.ReturnValue1;
255+
var nonce = await TransactionManager.ThirdwebRead<EntryPointContract.GetNonceFunction, EntryPointContract.GetNonceOutputDTO>(
256+
Config.entryPointAddress,
257+
new EntryPointContract.GetNonceFunction() { Sender = Accounts[0], Key = UserOpUtils.GetRandomInt192() }
258+
);
259+
return nonce.Nonce;
269260
}
270261

271262
private async Task<byte[]> GetPaymasterAndData(object requestId, UserOperationHexified userOp, string apiKey)

Assets/Thirdweb/Core/Scripts/AccountAbstraction/Core/UserOpUtils.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,32 @@
1+
using System.Numerics;
12
using System.Threading.Tasks;
23
using Nethereum.Hex.HexTypes;
34
using Nethereum.Signer;
5+
using System.Security.Cryptography;
46
using EntryPointContract = Thirdweb.Contracts.EntryPoint.ContractDefinition;
57

68
namespace Thirdweb.AccountAbstraction
79
{
810
public static class UserOpUtils
911
{
12+
public static BigInteger GetRandomInt192()
13+
{
14+
byte[] randomBytes = GetRandomBytes(24); // 192 bits = 24 bytes
15+
BigInteger randomInt = new(randomBytes);
16+
randomInt = BigInteger.Abs(randomInt) % (BigInteger.One << 192);
17+
return randomInt;
18+
}
19+
20+
private static byte[] GetRandomBytes(int byteCount)
21+
{
22+
using (RNGCryptoServiceProvider rng = new())
23+
{
24+
byte[] randomBytes = new byte[byteCount];
25+
rng.GetBytes(randomBytes);
26+
return randomBytes;
27+
}
28+
}
29+
1030
public static async Task<byte[]> HashAndSignUserOp(this EntryPointContract.UserOperation userOp, string entryPoint)
1131
{
1232
var userOpHash = await TransactionManager.ThirdwebRead<EntryPointContract.GetUserOpHashFunction, EntryPointContract.GetUserOpHashOutputDTO>(

0 commit comments

Comments
 (0)