Skip to content

Commit fef804a

Browse files
authored
Smart Wallet Native Transaction Queuing (#115)
1 parent e0f34b8 commit fef804a

File tree

4 files changed

+71
-38
lines changed

4 files changed

+71
-38
lines changed

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

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
using Nethereum.Contracts;
1515
using Nethereum.Hex.HexConvertors.Extensions;
1616
using Thirdweb.Redcode.Awaiting;
17+
using System.Threading;
18+
using System.Collections.Concurrent;
1719

1820
namespace Thirdweb.AccountAbstraction
1921
{
@@ -34,24 +36,19 @@ public class UserOperationHexified
3436

3537
public class SmartWallet
3638
{
39+
private bool _deployed;
40+
private bool _deploying;
41+
private bool _initialized;
42+
3743
public List<string> Accounts { get; internal set; }
3844
public string PersonalAddress { get; internal set; }
3945
public Web3 PersonalWeb3 { get; internal set; }
4046
public ThirdwebSDK.SmartWalletConfig Config { get; internal set; }
47+
public bool IsDeployed => _deployed;
48+
public bool IsDeploying => _deploying;
4149

42-
private bool _deployed;
43-
public bool IsDeployed
44-
{
45-
get { return _deployed; }
46-
}
47-
48-
private bool _deploying;
49-
public bool IsDeploying
50-
{
51-
get { return _deploying; }
52-
}
53-
54-
private bool _initialized;
50+
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
51+
private readonly ConcurrentQueue<TaskCompletionSource<RpcResponseMessage>> _responseQueue = new ConcurrentQueue<TaskCompletionSource<RpcResponseMessage>>();
5552

5653
public SmartWallet(Web3 personalWeb3, ThirdwebSDK.SmartWalletConfig config)
5754
{
@@ -141,14 +138,36 @@ internal async Task<RpcResponseMessage> Request(RpcRequestMessage requestMessage
141138
{
142139
ThirdwebDebug.Log("Requesting: " + requestMessage.Method + "...");
143140

144-
if (requestMessage.Method == "eth_chainId")
141+
if (requestMessage.Method == "eth_sendTransaction")
145142
{
146-
var chainId = await PersonalWeb3.Eth.ChainId.SendRequestAsync();
147-
return new RpcResponseMessage(requestMessage.Id, chainId.HexValue);
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;
148166
}
149-
else if (requestMessage.Method == "eth_sendTransaction")
167+
else if (requestMessage.Method == "eth_chainId")
150168
{
151-
return await CreateUserOpAndSend(requestMessage);
169+
var chainId = await PersonalWeb3.Eth.ChainId.SendRequestAsync();
170+
return new RpcResponseMessage(requestMessage.Id, chainId.HexValue);
152171
}
153172
else
154173
{
@@ -227,22 +246,19 @@ private async Task<RpcResponseMessage> CreateUserOpAndSend(RpcRequestMessage req
227246
}
228247
ThirdwebDebug.Log("Tx Hash: " + txHash);
229248

230-
// // Check if successful
231-
232-
// var receipt = await new Web3(ThirdwebManager.Instance.SDK.session.RPC).Eth.Transactions.GetTransactionReceipt.SendRequestAsync(txHash);
233-
// var decodedEvents = receipt.DecodeAllEvents<EntryPointContract.UserOperationEventEventDTO>();
234-
// if (decodedEvents[0].Event.Success == false)
235-
// {
236-
// ThirdwebDebug.Log("Transaction not successful, checking reason...");
237-
// var reason = await new Web3(ThirdwebManager.Instance.SDK.session.RPC).Eth.GetContractTransactionErrorReason.SendRequestAsync(txHash);
238-
// throw new Exception($"Transaction {txHash} reverted with reason: {reason}");
239-
// }
240-
// else
241-
// {
242-
// ThirdwebDebug.Log("Transaction successful");
243-
// _deployed = true;
244-
// }
249+
// Check if successful
245250

251+
var receipt = await Transaction.WaitForTransactionResultRaw(txHash);
252+
var decodedEvents = receipt.DecodeAllEvents<EntryPointContract.UserOperationEventEventDTO>();
253+
if (decodedEvents[0].Event.Success == false)
254+
{
255+
throw new Exception($"Transaction {txHash} execution reverted");
256+
}
257+
else
258+
{
259+
ThirdwebDebug.Log("Transaction successful");
260+
_deployed = true;
261+
}
246262
return new RpcResponseMessage(requestMessage.Id, txHash);
247263
}
248264

Assets/Thirdweb/Core/Scripts/Transaction.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,13 +361,30 @@ public async Task<TransactionResult> SendAndWaitForTransactionResult(bool? gasle
361361
/// <param name="txHash">The transaction hash to wait for.</param>
362362
/// <returns>The transaction result as a <see cref="TransactionResult"/> object.</returns>
363363
public static async Task<TransactionResult> WaitForTransactionResult(string txHash)
364+
{
365+
var receipt = await WaitForTransactionResultRaw(txHash);
366+
return receipt.ToTransactionResult();
367+
}
368+
369+
/// <summary>
370+
/// Waits for the transaction result asynchronously.
371+
/// </summary>
372+
/// <param name="txHash">The transaction hash to wait for.</param>
373+
/// <returns>The transaction result as a <see cref="TransactionResult"/> object.</returns>
374+
public static async Task<TransactionReceipt> WaitForTransactionResultRaw(string txHash)
364375
{
365376
if (Utils.IsWebGLBuild())
366377
throw new UnityException("WaitForTransactionResult is not supported in WebGL builds.");
367378

368-
var receiptPoller = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
369-
var receipt = await receiptPoller.TransactionReceiptPolling.PollForReceiptAsync(txHash);
370-
return receipt.ToTransactionResult();
379+
var web3 = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
380+
var receipt = await web3.TransactionReceiptPolling.PollForReceiptAsync(txHash);
381+
if (receipt.Failed())
382+
{
383+
var reason = await web3.Eth.GetContractTransactionErrorReason.SendRequestAsync(txHash);
384+
if (!string.IsNullOrEmpty(reason))
385+
throw new UnityException($"Transaction failed: {reason}");
386+
}
387+
return receipt;
371388
}
372389

373390
private async Task<string> Send()

Assets/Thirdweb/Core/Scripts/TransactionManager.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,7 @@ public static async Task<TransactionReceipt> ThirdwebWriteRawResult<TWFunction>(
147147
}
148148
}
149149
ThirdwebDebug.Log("txHash: " + txHash);
150-
var receiptPoller = new Web3(ThirdwebManager.Instance.SDK.session.RPC);
151-
return await receiptPoller.TransactionReceiptPolling.PollForReceiptAsync(txHash);
150+
return await Transaction.WaitForTransactionResultRaw(txHash);
152151
}
153152
}
154153

Assets/Thirdweb/Core/Scripts/Wallets/ThirdwebSmartWallet.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public async Task<string> Connect(WalletConnection walletConnection, string rpc)
4040
public Task Disconnect()
4141
{
4242
_web3 = null;
43+
_personalWallet.Disconnect();
4344
return Task.CompletedTask;
4445
}
4546

0 commit comments

Comments
 (0)