Skip to content

Commit 85df303

Browse files
committed
7702 sponsored execution progress push
1 parent d24392c commit 85df303

File tree

3 files changed

+115
-37
lines changed

3 files changed

+115
-37
lines changed

Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/AATypes.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,12 @@ public class EthGetUserOperationReceiptResponse
240240
public ThirdwebTransactionReceipt Receipt { get; set; }
241241
}
242242

243+
public class TwExecuteResponse
244+
{
245+
[JsonProperty("queueId")]
246+
public string QueueId { get; set; }
247+
}
248+
243249
public class EntryPointWrapper
244250
{
245251
[JsonProperty("entryPoint")]
@@ -606,18 +612,33 @@ public class Call
606612
[Parameter("bytes", "data", 3)]
607613
[JsonProperty("data")]
608614
public virtual byte[] Data { get; set; }
615+
616+
public object EncodeForHttp()
617+
{
618+
return new
619+
{
620+
target = this.Target,
621+
value = this.Value,
622+
data = this.Data != null ? this.Data.BytesToHex() : "0x"
623+
};
624+
}
609625
}
610626

611627
[Struct("WrappedCalls")]
612628
public class WrappedCalls
613629
{
614-
[Parameter("tuple[]", "calls", 1)]
630+
[Parameter("tuple[]", "calls", 1, structTypeName: "Call[]")]
615631
[JsonProperty("calls")]
616632
public virtual List<Call> Calls { get; set; }
617633

618634
[Parameter("bytes32", "uid", 2)]
619635
[JsonProperty("uid")]
620636
public virtual byte[] Uid { get; set; }
637+
638+
public object EncodeForHttp()
639+
{
640+
return new { calls = this.Calls != null ? this.Calls.Select(c => c.EncodeForHttp()).ToList() : new List<object>(), uid = this.Uid.BytesToHex() };
641+
}
621642
}
622643

623644
#endregion

Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/BundlerClient.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,41 @@ namespace Thirdweb.AccountAbstraction;
55

66
public static class BundlerClient
77
{
8+
// EIP 7702 requests
9+
public static async Task<TwExecuteResponse> TwExecute(
10+
ThirdwebClient client,
11+
string url,
12+
object requestId,
13+
string eoaAddress,
14+
WrappedCalls wrappedCalls,
15+
string signature,
16+
EIP7702Authorization? authorization
17+
)
18+
{
19+
var response = await BundlerRequest(
20+
client,
21+
url,
22+
requestId,
23+
"tw_execute",
24+
eoaAddress,
25+
wrappedCalls.EncodeForHttp(),
26+
signature,
27+
authorization == null
28+
? null
29+
: new
30+
{
31+
chainId = authorization?.ChainId.HexToNumber(),
32+
address = authorization?.Address,
33+
nonce = authorization?.Nonce.HexToNumber(),
34+
yParity = authorization?.YParity.HexToNumber(),
35+
r = authorization?.R,
36+
s = authorization?.S
37+
}
38+
)
39+
.ConfigureAwait(false);
40+
return JsonConvert.DeserializeObject<TwExecuteResponse>(response.Result.ToString());
41+
}
42+
843
// Bundler requests
944

1045
public static async Task<EthGetUserOperationReceiptResponse> EthGetUserOperationReceipt(ThirdwebClient client, string bundlerUrl, object requestId, string userOpHash)

Thirdweb/Thirdweb.Wallets/SmarterWallet.cs

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Numerics;
22
using Nethereum.ABI.EIP712;
3+
using Nethereum.Util;
34
using Thirdweb.AccountAbstraction;
45

56
namespace Thirdweb;
@@ -42,9 +43,7 @@ public static async Task<SmarterWallet> Create(ThirdwebClient client, BigInteger
4243
var userWalletAddress = await userWallet.GetAddress();
4344
var userContract = await ThirdwebContract.Create(client, userWalletAddress, chainId, Constants.MINIMAL_ACCOUNT_7702_ABI);
4445
var needsDelegation = !await Utils.IsDelegatedAccount(client, chainId, userWalletAddress);
45-
EIP7702Authorization? authorization = needsDelegation
46-
? await userWallet.SignAuthorization(chainId, Constants.MINIMAL_ACCOUNT_7702, willSelfExecute: executionMode != ExecutionMode.EIP7702)
47-
: null;
46+
EIP7702Authorization? authorization = needsDelegation ? await userWallet.SignAuthorization(chainId, Constants.MINIMAL_ACCOUNT_7702, willSelfExecute: executionMode == ExecutionMode.EOA) : null;
4847
var wallet = new SmarterWallet(client, chainId, userWallet, userContract, authorization, executionMode);
4948
Utils.TrackConnection(wallet);
5049
return wallet;
@@ -129,53 +128,76 @@ public Task<string> SignTransaction(ThirdwebTransactionInput transaction)
129128

130129
public async Task<string> SendTransaction(ThirdwebTransactionInput transaction)
131130
{
132-
ThirdwebTransaction finalTx;
131+
var userWalletAddress = await this.UserWallet.GetAddress();
132+
133+
if (this.Authorization != null && await Utils.IsDelegatedAccount(this.Client, this.ChainId, userWalletAddress))
134+
{
135+
this.Authorization = null;
136+
}
137+
138+
var calls = new List<Call>
139+
{
140+
new()
141+
{
142+
Target = transaction.To,
143+
Value = transaction.Value?.Value ?? BigInteger.Zero,
144+
Data = transaction.Data.HexToBytes()
145+
}
146+
};
147+
133148
switch (this.ExecutionMode)
134149
{
135150
case ExecutionMode.EIP7702:
136-
throw new NotImplementedException("EIP7702 Sponsored Execution mode is not yet implemented.");
137-
// 1. Create payload with eoa address, wrapped calls, signature and optional authorizationList
138-
// 2. Send to https://{chainId}.bundler.thirdweb.com as RpcRequest w/ method tw_execute
139-
// 3. Retrieve tx hash or queue id from response
140-
// 4. Return tx hash
151+
var wrappedCalls = new WrappedCalls() { Calls = calls, Uid = Guid.NewGuid().ToByteArray().PadTo32Bytes() };
152+
var signature = await EIP712.GenerateSignature_SmartAccount_7702_WrappedCalls("MinimalAccount", "1", this.ChainId, userWalletAddress, wrappedCalls, this.UserWallet);
153+
var response = await BundlerClient.TwExecute(
154+
client: this.Client,
155+
// url: $"{this.ChainId}.bundler.thirdweb.com",
156+
url: "http://localhost:8787?chain=11155111",
157+
requestId: 7702,
158+
eoaAddress: userWalletAddress,
159+
wrappedCalls: wrappedCalls,
160+
signature: signature,
161+
authorization: this.Authorization != null && !await Utils.IsDelegatedAccount(this.Client, this.ChainId, userWalletAddress) ? this.Authorization : null
162+
);
163+
throw new NotImplementedException($"EIP-7702 transaction execution is not done, here's the queue id: {response.QueueId}");
164+
// string txHash = null;
165+
// var ct = new CancellationTokenSource(this.Client.FetchTimeoutOptions.GetTimeout(TimeoutType.Other));
166+
// try
167+
// {
168+
// while (txHash == null)
169+
// {
170+
// ct.Token.ThrowIfCancellationRequested();
171+
172+
// var txReceipt = await BundlerClient.TwGetTransactionReceipt(client: this.Client, url: $"{this.ChainId}.bundler.thirdweb.com", requestId: 7702, queueId).ConfigureAwait(false);
173+
174+
// txHash = txReceipt?.Receipt?.TransactionHash;
175+
// await ThirdwebTask.Delay(100, ct.Token).ConfigureAwait(false);
176+
// }
177+
// }
178+
// catch (OperationCanceledException)
179+
// {
180+
// throw new Exception($"EIP-7702 sponsored transaction timed out with queue id: {queueId}");
181+
// }
182+
// break;
141183
case ExecutionMode.EOA:
142-
// Direct Call struct
143-
var calls = new List<Call>
144-
{
145-
new()
146-
{
147-
Target = transaction.To,
148-
Value = transaction.Value?.Value ?? BigInteger.Zero,
149-
Data = transaction.Data.HexToBytes()
150-
}
151-
};
152184
// Add up values of all calls
153185
BigInteger totalValue = 0;
154186
foreach (var call in calls)
155187
{
156188
totalValue += call.Value;
157189
}
158190
// Prepare a tx using the user wallet as the executor
159-
finalTx = await this.UserContract.Prepare(wallet: this.UserWallet, method: "execute", weiValue: totalValue, parameters: new object[] { calls });
160-
break;
191+
var finalTx = await this.UserContract.Prepare(wallet: this.UserWallet, method: "execute", weiValue: totalValue, parameters: new object[] { calls });
192+
finalTx.Input.AuthorizationList = this.Authorization != null ? new List<EIP7702Authorization>() { this.Authorization.Value } : null;
193+
194+
// Append authorization if not delegated yet
195+
196+
// Send the transaction and return the
197+
return await ThirdwebTransaction.Send(finalTx);
161198
default:
162199
throw new NotImplementedException($"Execution mode {this.ExecutionMode} is not supported.");
163200
}
164-
165-
// Append authorization if not delegated yet
166-
if (this.Authorization != null)
167-
{
168-
if (!await Utils.IsDelegatedAccount(this.Client, this.ChainId, await this.UserWallet.GetAddress()))
169-
{
170-
finalTx.Input.AuthorizationList = new List<EIP7702Authorization>() { this.Authorization.Value };
171-
}
172-
else
173-
{
174-
this.Authorization = null;
175-
}
176-
}
177-
// Send the transaction and return the
178-
return await ThirdwebTransaction.Send(finalTx);
179201
}
180202

181203
public async Task<ThirdwebTransactionReceipt> ExecuteTransaction(ThirdwebTransactionInput transaction)

0 commit comments

Comments
 (0)