Skip to content

Commit 9f26659

Browse files
committed
Support 3rd party executors, fix sign typed data
1 parent 42cd677 commit 9f26659

File tree

5 files changed

+136
-23
lines changed

5 files changed

+136
-23
lines changed

Thirdweb.Console/Program.Types.cs

Lines changed: 0 additions & 16 deletions
This file was deleted.

Thirdweb.Console/Program.cs

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,18 +154,18 @@
154154
// --------------------------------------------------------------------------
155155

156156
var chainWith7702 = 911867;
157-
var delegationContractAddress = "0x08e47c0d38feb3d849abc01e2b7fb5d3d0d626e9"; // MinimalAccount
157+
var delegationContractAddress = "0xb012446cba783d0f7723daf96cf4c49005022307"; // MinimalAccount
158158

159159
// Required environment variables
160-
var executorWalletAddress = Environment.GetEnvironmentVariable("ENGINE_EXECUTOR_WALLET_ADDRESS") ?? throw new Exception("ENGINE_EXECUTOR_WALLET_ADDRESS is required");
160+
var backendWalletAddress = Environment.GetEnvironmentVariable("ENGINE_BACKEND_WALLET_ADDRESS") ?? throw new Exception("ENGINE_BACKEND_WALLET_ADDRESS is required");
161161
var engineUrl = Environment.GetEnvironmentVariable("ENGINE_URL") ?? throw new Exception("ENGINE_URL is required");
162162
var engineAccessToken = Environment.GetEnvironmentVariable("ENGINE_ACCESS_TOKEN") ?? throw new Exception("ENGINE_ACCESS_TOKEN is required");
163163

164164
// --------------------------------------------------------------------------
165165
// Initialize Engine Wallet
166166
// --------------------------------------------------------------------------
167167

168-
var engineWallet = await EngineWallet.Create(client, engineUrl, engineAccessToken, executorWalletAddress, 15);
168+
var engineWallet = await EngineWallet.Create(client, engineUrl, engineAccessToken, backendWalletAddress, 15);
169169

170170
// --------------------------------------------------------------------------
171171
// Delegation Contract Implementation
@@ -185,7 +185,7 @@
185185
// Sign message for session key
186186
var sessionKeyParams = new SessionKeyParams_7702()
187187
{
188-
Signer = executorWalletAddress,
188+
Signer = backendWalletAddress,
189189
NativeTokenLimitPerTransaction = 0,
190190
StartTimestamp = 0,
191191
EndTimestamp = Utils.GetUnixTimeStampNow() + (3600 * 24),
@@ -210,7 +210,7 @@
210210
var eoaContract = await ThirdwebContract.Create(client, eoaWalletAddress, chainWith7702, delegationContract.Abi);
211211

212212
// --------------------------------------------------------------------------
213-
// Mint Tokens (DropERC20) to the EOA Using the Executor
213+
// Mint Tokens (DropERC20) to the EOA Using the backend session key
214214
// --------------------------------------------------------------------------
215215

216216
var erc20ContractAddress = "0xAA462a5BE0fc5214507FDB4fB2474a7d5c69065b"; // DropERC20
@@ -225,7 +225,7 @@
225225
"execute",
226226
new object[]
227227
{
228-
new List<Thirdweb.Console.Call>
228+
new List<Call>
229229
{
230230
new()
231231
{
@@ -257,6 +257,57 @@
257257
var eoaBalanceAfter = await erc20Contract.ERC20_BalanceOf(eoaWalletAddress);
258258
Console.WriteLine($"EOA balance after: {eoaBalanceAfter}");
259259

260+
// --------------------------------------------------------------------------
261+
// Mint Tokens (DropERC20) to the EOA Using an alternative executor
262+
// --------------------------------------------------------------------------
263+
264+
// Executor wallet (managed)
265+
var executorWallet = await PrivateKeyWallet.Create(client, privateKey);
266+
267+
// Log ERC20 balance before mint
268+
eoaBalanceBefore = await erc20Contract.ERC20_BalanceOf(eoaWalletAddress);
269+
Console.WriteLine($"EOA balance before: {eoaBalanceBefore}");
270+
271+
// Sign wrapped calls 712 using an authorized session key (backend wallet in this case)
272+
var wrappedCalls = new WrappedCalls()
273+
{
274+
Calls = new List<Call>
275+
{
276+
new()
277+
{
278+
Data = erc20Contract
279+
.CreateCallData(
280+
"claim",
281+
new object[]
282+
{
283+
eoaWalletAddress, // receiver
284+
100, // quantity
285+
Constants.NATIVE_TOKEN_ADDRESS, // currency
286+
0, // pricePerToken
287+
new object[] { Array.Empty<byte>(), BigInteger.Zero, BigInteger.Zero, Constants.ADDRESS_ZERO }, // allowlistProof
288+
Array.Empty<byte>() // data
289+
}
290+
)
291+
.HexToBytes(),
292+
To = erc20ContractAddress,
293+
Value = BigInteger.Zero
294+
}
295+
},
296+
Uid = Guid.NewGuid().ToByteArray().BytesToHex().HexToBytes32()
297+
};
298+
var wrappedCallsSig = await EIP712.GenerateSignature_SmartAccount_7702_WrappedCalls("MinimalAccount", "1", chainWith7702, eoaWalletAddress, wrappedCalls, engineWallet);
299+
300+
// Create execution call data, this time in a way that can be broadcast by anyone
301+
executeCallData = eoaContract.CreateCallData("executeWithSig", wrappedCalls, wrappedCallsSig.HexToBytes());
302+
303+
var executeTx = await ThirdwebTransaction.Create(wallet: executorWallet, txInput: new ThirdwebTransactionInput(chainId: chainWith7702, to: eoaWalletAddress, data: executeCallData));
304+
executeReceipt = await ThirdwebTransaction.SendAndWaitForTransactionReceipt(executeTx);
305+
Console.WriteLine($"Execute receipt: {JsonConvert.SerializeObject(executeReceipt, Formatting.Indented)}");
306+
307+
// Log ERC20 balance after mint
308+
eoaBalanceAfter = await erc20Contract.ERC20_BalanceOf(eoaWalletAddress);
309+
Console.WriteLine($"EOA balance after: {eoaBalanceAfter}");
310+
260311
#endregion
261312

262313
#region Smart Ecosystem Wallet

Thirdweb/Thirdweb.Wallets/EIP712.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,28 @@ public static class EIP712
1414
{
1515
#region Generation
1616

17+
/// <summary>
18+
/// Generates a signature for a 7702 smart account wrapped calls request.
19+
/// </summary>
20+
/// <param name="domainName">The domain name.</param>
21+
/// <param name="version">The version.</param>
22+
/// <param name="chainId">The chain ID.</param>
23+
/// <param name="verifyingContract">The verifying contract.</param>
24+
/// <param name="wrappedCalls">The wrapped calls request.</param>
25+
/// <param name="signer">The wallet signer.</param>
26+
public static async Task<string> GenerateSignature_SmartAccount_7702_WrappedCalls(
27+
string domainName,
28+
string version,
29+
BigInteger chainId,
30+
string verifyingContract,
31+
AccountAbstraction.WrappedCalls wrappedCalls,
32+
IThirdwebWallet signer
33+
)
34+
{
35+
var typedData = GetTypedDefinition_SmartAccount_7702_WrappedCalls(domainName, version, chainId, verifyingContract);
36+
return await signer.SignTypedDataV4(wrappedCalls, typedData);
37+
}
38+
1739
/// <summary>
1840
/// Generates a signature for a 7702 smart account session key.
1941
/// </summary>
@@ -203,6 +225,30 @@ IThirdwebWallet signer
203225

204226
#region Typed Definitions
205227

228+
/// <summary>
229+
/// Gets the typed data definition for a 7702 smart account wrapped calls request.
230+
/// </summary>
231+
/// <param name="domainName">The domain name.</param>
232+
/// <param name="version">The version.</param>
233+
/// <param name="chainId">The chain ID.</param>
234+
/// <param name="verifyingContract">The verifying contract.</param>
235+
/// <returns>The typed data definition.</returns>
236+
public static TypedData<Domain> GetTypedDefinition_SmartAccount_7702_WrappedCalls(string domainName, string version, BigInteger chainId, string verifyingContract)
237+
{
238+
return new TypedData<Domain>
239+
{
240+
Domain = new Domain
241+
{
242+
Name = domainName,
243+
Version = version,
244+
ChainId = chainId,
245+
VerifyingContract = verifyingContract,
246+
},
247+
Types = MemberDescriptionFactory.GetTypesMemberDescription(typeof(Domain), typeof(AccountAbstraction.WrappedCalls), typeof(AccountAbstraction.Call)),
248+
PrimaryType = "WrappedCalls",
249+
};
250+
}
251+
206252
/// <summary>
207253
/// Gets the typed data definition for a 7702 smart account session key.
208254
/// </summary>

Thirdweb/Thirdweb.Wallets/EngineWallet/EngineWallet.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,11 @@ public async Task<string> SignTypedDataV4(string json)
246246
}
247247

248248
var processedJson = Utils.PreprocessTypedDataJson(json);
249+
// TODO: remove this sanitization when engine is upgraded to match spec
250+
processedJson = processedJson.Replace("message", "value");
251+
var tempObj = JObject.Parse(processedJson);
252+
_ = tempObj["types"].Value<JObject>().Remove("EIP712Domain");
253+
processedJson = tempObj.ToString();
249254

250255
var url = $"{this._engineUrl}/backend-wallet/sign-typed-data";
251256

@@ -307,7 +312,6 @@ public async Task<string> SendTransaction(ThirdwebTransactionInput transaction)
307312
var url = $"{this._engineUrl}/backend-wallet/{transaction.ChainId.Value}/send-transaction";
308313

309314
var requestContent = new StringContent(JsonConvert.SerializeObject(payload, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }), Encoding.UTF8, "application/json");
310-
Console.WriteLine(JsonConvert.SerializeObject(payload, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }));
311315

312316
var response = await this._engineClient.PostAsync(url, requestContent).ConfigureAwait(false);
313317
_ = response.EnsureSuccessStatusCode();

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,3 +513,31 @@ public class SessionKeyParams_7702
513513
[JsonProperty("uid")]
514514
public byte[] Uid { get; set; }
515515
}
516+
517+
[Struct("Call")]
518+
public class Call
519+
{
520+
[Parameter("bytes", "data", 1)]
521+
[JsonProperty("data")]
522+
public byte[] Data { get; set; }
523+
524+
[Parameter("address", "to", 2)]
525+
[JsonProperty("to")]
526+
public string To { get; set; }
527+
528+
[Parameter("uint256", "value", 3)]
529+
[JsonProperty("value")]
530+
public BigInteger Value { get; set; }
531+
}
532+
533+
[Struct("WrappedCalls")]
534+
public class WrappedCalls
535+
{
536+
[Parameter("tuple[]", "calls", 1, "Call[]")]
537+
[JsonProperty("calls")]
538+
public List<Call> Calls { get; set; }
539+
540+
[Parameter("bytes32", "uid", 2)]
541+
[JsonProperty("uid")]
542+
public byte[] Uid { get; set; }
543+
}

0 commit comments

Comments
 (0)