Skip to content

Commit e183543

Browse files
committed
Add EIP-7702 and EOA execution options to ServerWallet
Introduces EIP7702ExecutionOptions and EOAExecutionOptions classes for ServerWallet execution modes. Updates ServerWallet logic to support these new execution options and improves type handling. Also comments out ServerWallet usage in the console example and fixes account parsing in ServerWallet creation.
1 parent ed105d4 commit e183543

File tree

3 files changed

+101
-72
lines changed

3 files changed

+101
-72
lines changed

Thirdweb.Console/Program.cs

Lines changed: 39 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
var privateKey = Environment.GetEnvironmentVariable("PRIVATE_KEY");
2828

2929
// Fetch timeout options are optional, default is 120000ms
30-
var client = ThirdwebClient.Create(secretKey: secretKey);
30+
var client = ThirdwebClient.Create(secretKey: "4qXoZMCqQo9SD8YkrdvO5Ci9gYKrgRADHSY84Q0wwKHZS53_R1QNcIs2XbFBWR0xE7HTQPER45T1sN1JvdFKlA");
3131

3232
// Create a private key wallet
3333
var privateKeyWallet = await PrivateKeyWallet.Generate(client);
@@ -342,52 +342,44 @@
342342

343343
#region Server Wallet
344344

345-
// You need only pass this if you are using a self-managed vault (check your dashboard Transactions tab)
346-
var myAccessToken = Environment.GetEnvironmentVariable("VAULT_ACCESS_TOKEN");
347-
348-
// ServerWallet is compatible with IThirdwebWallet and can be used with any SDK method/extension
349-
var serverWallet = await ServerWallet.Create(
350-
client: client,
351-
label: "Test",
352-
// Optional, defaults to Auto - we choose between EIP-7702, EIP-4337 or native zkSync AA execution
353-
executionOptions: new AutoExecutionOptions(),
354-
vaultAccessToken: myAccessToken
355-
);
356-
var serverWalletAddress = await serverWallet.GetAddress();
357-
Console.WriteLine($"Server Wallet address: {serverWalletAddress}");
358-
359-
var serverWalletPersonalSig = await serverWallet.PersonalSign("Hello, Thirdweb!");
360-
Console.WriteLine($"Server Wallet personal sign: {serverWalletPersonalSig}");
361-
362-
var json =
363-
/*lang=json,strict*/
364-
"{\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallet\",\"type\":\"address\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person\"},{\"name\":\"contents\",\"type\":\"string\"}]},\"primaryType\":\"Mail\",\"domain\":{\"name\":\"Ether Mail\",\"version\":\"1\",\"chainId\":84532,\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\"},\"message\":{\"from\":{\"name\":\"Cow\",\"wallet\":\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\"},\"to\":{\"name\":\"Bob\",\"wallet\":\"0xbBbBBBBbbBBBbbbBbbBbbBBbBbbBbBbBbBbbBBbB\"},\"contents\":\"Hello, Bob!\"}}";
365-
var serverWalletTypedDataSign = await serverWallet.SignTypedDataV4(json);
366-
Console.WriteLine($"Server Wallet typed data sign: {serverWalletTypedDataSign}");
367-
368-
// ServerWallet forcing ERC-4337 Execution Mode
369-
var smartServerWallet = await ServerWallet.Create(
370-
client: client,
371-
label: "Test",
372-
executionOptions: new ERC4337ExecutionOptions(chainId: 84532, signerAddress: serverWalletAddress),
373-
vaultAccessToken: myAccessToken
374-
);
375-
var smartServerWalletAddress = await smartServerWallet.GetAddress();
376-
Console.WriteLine($"Smart Server Wallet address: {smartServerWalletAddress}");
377-
378-
var smartServerWalletPersonalSig = await smartServerWallet.PersonalSign("Hello, Thirdweb!");
379-
Console.WriteLine($"Smart Server Wallet personal sign: {smartServerWalletPersonalSig}");
380-
381-
var smartServerWalletTypedDataSign = await smartServerWallet.SignTypedDataV4(json);
382-
Console.WriteLine($"Smart Server Wallet typed data sign: {smartServerWalletTypedDataSign}");
383-
384-
// Simple self transfer
385-
var serverWalletReceipt = await serverWallet.Transfer(chainId: 421614, toAddress: await serverWallet.GetAddress(), weiAmount: 0);
386-
Console.WriteLine($"Server Wallet Hash: {serverWalletReceipt.TransactionHash}");
387-
388-
// Simple self transfer
389-
var smartServerWalletReceipt = await smartServerWallet.Transfer(chainId: 421614, toAddress: await smartServerWallet.GetAddress(), weiAmount: 0);
390-
Console.WriteLine($"Server Wallet Hash: {smartServerWalletReceipt.TransactionHash}");
345+
// // ServerWallet is compatible with IThirdwebWallet and can be used with any SDK method/extension
346+
// var serverWallet = await ServerWallet.Create(
347+
// client: client,
348+
// label: "Test",
349+
// // Optional, defaults to Auto - we choose between EIP-7702, EIP-4337 or native zkSync AA execution / EOA is also available
350+
// executionOptions: new AutoExecutionOptions()
351+
// );
352+
353+
// var serverWalletAddress = await serverWallet.GetAddress();
354+
// Console.WriteLine($"Server Wallet address: {serverWalletAddress}");
355+
356+
// var serverWalletPersonalSig = await serverWallet.PersonalSign("Hello, Thirdweb!");
357+
// Console.WriteLine($"Server Wallet personal sign: {serverWalletPersonalSig}");
358+
359+
// var json =
360+
// /*lang=json,strict*/
361+
// "{\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallet\",\"type\":\"address\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person\"},{\"name\":\"contents\",\"type\":\"string\"}]},\"primaryType\":\"Mail\",\"domain\":{\"name\":\"Ether Mail\",\"version\":\"1\",\"chainId\":84532,\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\"},\"message\":{\"from\":{\"name\":\"Cow\",\"wallet\":\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\"},\"to\":{\"name\":\"Bob\",\"wallet\":\"0xbBbBBBBbbBBBbbbBbbBbbBBbBbbBbBbBbBbbBBbB\"},\"contents\":\"Hello, Bob!\"}}";
362+
// var serverWalletTypedDataSign = await serverWallet.SignTypedDataV4(json);
363+
// Console.WriteLine($"Server Wallet typed data sign: {serverWalletTypedDataSign}");
364+
365+
// // Simple self transfer
366+
// var serverWalletReceipt = await serverWallet.Transfer(chainId: 84532, toAddress: await serverWallet.GetAddress(), weiAmount: 0);
367+
// Console.WriteLine($"Server Wallet Hash: {serverWalletReceipt.TransactionHash}");
368+
369+
// // ServerWallet forcing ERC-4337 Execution Mode
370+
// var smartServerWallet = await ServerWallet.Create(client: client, label: "Test", executionOptions: new ERC4337ExecutionOptions(chainId: 84532, signerAddress: serverWalletAddress));
371+
// var smartServerWalletAddress = await smartServerWallet.GetAddress();
372+
// Console.WriteLine($"Smart Server Wallet address: {smartServerWalletAddress}");
373+
374+
// var smartServerWalletPersonalSig = await smartServerWallet.PersonalSign("Hello, Thirdweb!");
375+
// Console.WriteLine($"Smart Server Wallet personal sign: {smartServerWalletPersonalSig}");
376+
377+
// var smartServerWalletTypedDataSign = await smartServerWallet.SignTypedDataV4(json);
378+
// Console.WriteLine($"Smart Server Wallet typed data sign: {smartServerWalletTypedDataSign}");
379+
380+
// // Simple self transfer
381+
// var smartServerWalletReceipt = await smartServerWallet.Transfer(chainId: 84532, toAddress: await smartServerWallet.GetAddress(), weiAmount: 0);
382+
// Console.WriteLine($"Server Wallet Hash: {smartServerWalletReceipt.TransactionHash}");
391383

392384
#endregion
393385

Thirdweb/Thirdweb.Wallets/ServerWallet/ServerWallet.Types.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,32 @@ public class AutoExecutionOptions : ExecutionOptions
2929
public string From { get; set; }
3030
}
3131

32+
/// <summary>
33+
/// Externally Owned Account (EOA) execution options
34+
/// </summary>
35+
[JsonObject]
36+
public class EIP7702ExecutionOptions : ExecutionOptions
37+
{
38+
[JsonProperty("type")]
39+
public string Type { get; set; } = "EIP7702";
40+
41+
[JsonProperty("from")]
42+
public string From { get; set; }
43+
}
44+
45+
/// <summary>
46+
/// Externally Owned Account (EOA) execution options
47+
/// </summary>
48+
[JsonObject]
49+
public class EOAExecutionOptions : ExecutionOptions
50+
{
51+
[JsonProperty("type")]
52+
public string Type { get; set; } = "EOA";
53+
54+
[JsonProperty("from")]
55+
public string From { get; set; }
56+
}
57+
3258
/// <summary>
3359
/// ERC-4337 execution options
3460
/// </summary>

Thirdweb/Thirdweb.Wallets/ServerWallet/ServerWallet.cs

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public partial class ServerWallet : IThirdwebWallet
2121
private readonly IThirdwebHttpClient _engineClient;
2222
private readonly ExecutionOptions _executionOptions;
2323

24-
private readonly JsonSerializerSettings _jsonSerializerSettings = new() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.Indented, };
24+
private readonly JsonSerializerSettings _jsonSerializerSettings = new() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.Indented };
2525

2626
internal ServerWallet(ThirdwebClient client, IThirdwebHttpClient engineClient, string walletAddress, ExecutionOptions executionOptions)
2727
{
@@ -65,7 +65,7 @@ public static async Task<ServerWallet> Create(ThirdwebClient client, string labe
6565
var content = await serverWalletListResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
6666

6767
var responseObj = JObject.Parse(content);
68-
var accounts = responseObj["result"]?.ToObject<JArray>();
68+
var accounts = responseObj["result"]?["accounts"]?.ToObject<JArray>(); // TODO: Support pagination
6969

7070
if (accounts == null || accounts.Count == 0)
7171
{
@@ -86,14 +86,28 @@ public static async Task<ServerWallet> Create(ThirdwebClient client, string labe
8686
}
8787

8888
executionOptions ??= new AutoExecutionOptions { IdempotencyKey = Guid.NewGuid().ToString(), From = signerWalletAddress.ToChecksumAddress() };
89-
if (executionOptions is AutoExecutionOptions autoExecutionOptions)
89+
if (executionOptions is ERC4337ExecutionOptions erc4337ExecutionOptions)
90+
{
91+
erc4337ExecutionOptions.SmartAccountAddress = smartWalletAddress;
92+
erc4337ExecutionOptions.SignerAddress = signerWalletAddress;
93+
}
94+
else if (executionOptions is EIP7702ExecutionOptions eip7702ExecutionOptions)
95+
{
96+
eip7702ExecutionOptions.From = signerWalletAddress.ToChecksumAddress();
97+
}
98+
else if (executionOptions is EOAExecutionOptions eoaExecutionOptions)
99+
{
100+
eoaExecutionOptions.From = signerWalletAddress.ToChecksumAddress();
101+
}
102+
else if (executionOptions is AutoExecutionOptions autoExecutionOptions)
90103
{
91104
autoExecutionOptions.From ??= signerWalletAddress.ToChecksumAddress();
92105
}
93-
else if (executionOptions is ERC4337ExecutionOptions erc4337ExecutionOptions)
106+
else
94107
{
95-
erc4337ExecutionOptions.SmartAccountAddress = smartWalletAddress;
96-
erc4337ExecutionOptions.SignerAddress = signerWalletAddress;
108+
throw new InvalidOperationException(
109+
$"Unsupported execution options type: {executionOptions.GetType().Name}. Supported types are AutoExecutionOptions, EIP7702ExecutionOptions, EOAExecutionOptions, and ERC4337ExecutionOptions."
110+
);
97111
}
98112

99113
var wallet = new ServerWallet(client, engineClient, smartWalletAddress ?? signerWalletAddress, executionOptions);
@@ -149,22 +163,19 @@ private object ToEngineTransaction(ThirdwebTransactionInput transaction)
149163
data = transaction.Data ?? "0x",
150164
value = transaction.Value?.HexValue ?? "0x00",
151165
authorizationList = transaction.AuthorizationList != null && transaction.AuthorizationList.Count > 0
152-
? transaction.AuthorizationList
153-
.Select(
154-
authorization =>
155-
new
156-
{
157-
chainId = authorization.ChainId.HexToNumber(),
158-
address = authorization.Address,
159-
nonce = authorization.Nonce.HexToNumber(),
160-
yParity = authorization.YParity.HexToNumber(),
161-
r = authorization.R,
162-
s = authorization.S
163-
}
164-
)
166+
? transaction
167+
.AuthorizationList.Select(authorization => new
168+
{
169+
chainId = authorization.ChainId.HexToNumber(),
170+
address = authorization.Address,
171+
nonce = authorization.Nonce.HexToNumber(),
172+
yParity = authorization.YParity.HexToNumber(),
173+
r = authorization.R,
174+
s = authorization.S,
175+
})
165176
.ToArray()
166177
: null,
167-
}
178+
},
168179
},
169180
};
170181
}
@@ -222,9 +233,9 @@ public async Task<string> PersonalSign(byte[] rawMessage)
222233
{
223234
type = "auto",
224235
from = address,
225-
chainId = this._executionOptions.ChainId
236+
chainId = this._executionOptions.ChainId,
226237
},
227-
@params = new[] { new { message = rawMessage.BytesToHex(), format = "hex" } }
238+
@params = new[] { new { message = rawMessage.BytesToHex(), format = "hex" } },
228239
};
229240

230241
var requestContent = new StringContent(JsonConvert.SerializeObject(payload, this._jsonSerializerSettings), Encoding.UTF8, "application/json");
@@ -253,9 +264,9 @@ public async Task<string> PersonalSign(string message)
253264
{
254265
type = "auto",
255266
from = address,
256-
chainId = this._executionOptions.ChainId
267+
chainId = this._executionOptions.ChainId,
257268
},
258-
@params = new[] { new { message, format = "text" } }
269+
@params = new[] { new { message, format = "text" } },
259270
};
260271

261272
var requestContent = new StringContent(JsonConvert.SerializeObject(payload, this._jsonSerializerSettings), Encoding.UTF8, "application/json");
@@ -288,7 +299,7 @@ public async Task<string> SignTypedDataV4(string json)
288299
from = address,
289300
chainId = BigInteger.Parse(JObject.Parse(processedJson)["domain"]?["chainId"]?.Value<string>()),
290301
},
291-
@params = new[] { processedJson }
302+
@params = new[] { processedJson },
292303
};
293304
var requestContent = new StringContent(JsonConvert.SerializeObject(payload, this._jsonSerializerSettings), Encoding.UTF8, "application/json");
294305

0 commit comments

Comments
 (0)