Skip to content

Commit c58f47a

Browse files
authored
Engine Wallet & 7702 Session Keys (#126)
1 parent 869e48c commit c58f47a

File tree

8 files changed

+687
-77
lines changed

8 files changed

+687
-77
lines changed

Thirdweb.Console/Program.Types.cs

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

Thirdweb.Console/Program.cs

Lines changed: 99 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33

44
using System.Diagnostics;
55
using System.Numerics;
6+
using System.Text;
67
using dotenv.net;
8+
using Nethereum.ABI;
9+
using Nethereum.Hex.HexConvertors.Extensions;
710
using Nethereum.Hex.HexTypes;
811
using Newtonsoft.Json;
912
using Newtonsoft.Json.Linq;
1013
using Thirdweb;
14+
using Thirdweb.AccountAbstraction;
1115
using Thirdweb.AI;
1216
using Thirdweb.Pay;
1317

@@ -143,89 +147,133 @@
143147

144148
#endregion
145149

150+
#region Engine Wallet
151+
152+
// // EngineWallet is compatible with IThirdwebWallet and can be used with any SDK method/extension
153+
// var engineWallet = await EngineWallet.Create(
154+
// client: client,
155+
// engineUrl: Environment.GetEnvironmentVariable("ENGINE_URL"),
156+
// authToken: Environment.GetEnvironmentVariable("ENGINE_ACCESS_TOKEN"),
157+
// walletAddress: Environment.GetEnvironmentVariable("ENGINE_BACKEND_WALLET_ADDRESS"),
158+
// timeoutSeconds: null, // no timeout
159+
// additionalHeaders: null // can set things like x-account-address if using basic session keys
160+
// );
161+
162+
// // Simple self transfer
163+
// var receipt = await engineWallet.Transfer(chainId: 11155111, toAddress: await engineWallet.GetAddress(), weiAmount: 0);
164+
// Console.WriteLine($"Receipt: {receipt}");
165+
166+
#endregion
167+
146168
#region EIP-7702
147169

148-
// // Chain and contract addresses
170+
// // --------------------------------------------------------------------------
171+
// // Configuration
172+
// // --------------------------------------------------------------------------
173+
149174
// var chainWith7702 = 911867;
150-
// var erc20ContractAddress = "0xAA462a5BE0fc5214507FDB4fB2474a7d5c69065b"; // Fake ERC20
151-
// var delegationContractAddress = "0x654F42b74885EE6803F403f077bc0409f1066c58"; // BatchCallDelegation
175+
// var delegationContractAddress = "0xb012446cba783d0f7723daf96cf4c49005022307"; // MinimalAccount
176+
177+
// // Required environment variables
178+
// var backendWalletAddress = Environment.GetEnvironmentVariable("ENGINE_BACKEND_WALLET_ADDRESS") ?? throw new Exception("ENGINE_BACKEND_WALLET_ADDRESS is required");
179+
// var engineUrl = Environment.GetEnvironmentVariable("ENGINE_URL") ?? throw new Exception("ENGINE_URL is required");
180+
// var engineAccessToken = Environment.GetEnvironmentVariable("ENGINE_ACCESS_TOKEN") ?? throw new Exception("ENGINE_ACCESS_TOKEN is required");
181+
182+
// // --------------------------------------------------------------------------
183+
// // Initialize Engine Wallet
184+
// // --------------------------------------------------------------------------
185+
186+
// var engineWallet = await EngineWallet.Create(client, engineUrl, engineAccessToken, backendWalletAddress, 15);
187+
188+
// // --------------------------------------------------------------------------
189+
// // Delegation Contract Implementation
190+
// // --------------------------------------------------------------------------
152191

153-
// // Initialize contracts normally
154-
// var erc20Contract = await ThirdwebContract.Create(client: client, address: erc20ContractAddress, chain: chainWith7702);
155-
// var delegationContract = await ThirdwebContract.Create(client: client, address: delegationContractAddress, chain: chainWith7702);
192+
// var delegationContract = await ThirdwebContract.Create(client, delegationContractAddress, chainWith7702);
156193

157194
// // Initialize a (to-be) 7702 EOA
158195
// var eoaWallet = await PrivateKeyWallet.Generate(client);
159196
// var eoaWalletAddress = await eoaWallet.GetAddress();
160197
// Console.WriteLine($"EOA address: {eoaWalletAddress}");
161198

162-
// // Initialize another wallet, the "executor" that will hit the eoa's (to-be) execute function
163-
// var executorWallet = await PrivateKeyWallet.Generate(client);
164-
// var executorWalletAddress = await executorWallet.GetAddress();
165-
// Console.WriteLine($"Executor address: {executorWalletAddress}");
199+
// // Sign the authorization to point to the delegation contract
200+
// var authorization = await eoaWallet.SignAuthorization(chainWith7702, delegationContractAddress, willSelfExecute: false);
201+
// Console.WriteLine($"Authorization: {JsonConvert.SerializeObject(authorization, Formatting.Indented)}");
166202

167-
// // Fund the executor wallet
168-
// var fundingWallet = await PrivateKeyWallet.Create(client, privateKey);
169-
// var fundingHash = (await fundingWallet.Transfer(chainWith7702, executorWalletAddress, BigInteger.Parse("0.001".ToWei()))).TransactionHash;
170-
// Console.WriteLine($"Funded Executor Wallet: {fundingHash}");
203+
// // Sign message for session key
204+
// var sessionKeyParams = new SessionKeyParams_7702()
205+
// {
206+
// Signer = backendWalletAddress,
207+
// NativeTokenLimitPerTransaction = 0,
208+
// StartTimestamp = 0,
209+
// EndTimestamp = Utils.GetUnixTimeStampNow() + (3600 * 24),
210+
// ApprovedTargets = new List<string> { Constants.ADDRESS_ZERO },
211+
// Uid = Guid.NewGuid().ToByteArray()
212+
// };
213+
// var sessionKeySig = await EIP712.GenerateSignature_SmartAccount_7702("MinimalAccount", "1", chainWith7702, eoaWalletAddress, sessionKeyParams, eoaWallet);
171214

172-
// // Sign the authorization to make it point to the delegation contract
173-
// var authorization = await eoaWallet.SignAuthorization(chainId: chainWith7702, contractAddress: delegationContractAddress, willSelfExecute: false);
174-
// Console.WriteLine($"Authorization: {JsonConvert.SerializeObject(authorization, Formatting.Indented)}");
215+
// // Create call data for the session key
216+
// var sessionKeyCallData = delegationContract.CreateCallData("createSessionKeyWithSig", sessionKeyParams, sessionKeySig.HexToBytes());
175217

176-
// // Execute the delegation
177-
// var tx = await ThirdwebTransaction.Create(executorWallet, new ThirdwebTransactionInput(chainId: chainWith7702, to: executorWalletAddress, authorization: authorization));
178-
// var hash = (await ThirdwebTransaction.SendAndWaitForTransactionReceipt(tx)).TransactionHash;
179-
// Console.WriteLine($"Authorization execution transaction hash: {hash}");
218+
// // Execute the delegation & session key creation in one go, from the backend!
219+
// var delegationReceipt = await engineWallet.ExecuteTransaction(new ThirdwebTransactionInput(chainId: chainWith7702, to: eoaWalletAddress, data: sessionKeyCallData, authorization: authorization));
220+
// Console.WriteLine($"Delegation Execution Receipt: {JsonConvert.SerializeObject(delegationReceipt, Formatting.Indented)}");
180221

181-
// // Prove that code has been deployed to the eoa
222+
// // Verify contract code deployed to the EOA
182223
// var rpc = ThirdwebRPC.GetRpcInstance(client, chainWith7702);
183224
// var code = await rpc.SendRequestAsync<string>("eth_getCode", eoaWalletAddress, "latest");
184225
// Console.WriteLine($"EOA code: {code}");
185226

186-
// // Log erc20 balance of executor before the claim
187-
// var executorBalanceBefore = await erc20Contract.ERC20_BalanceOf(executorWalletAddress);
188-
// Console.WriteLine($"Executor balance before: {executorBalanceBefore}");
227+
// // The EOA is now a contract
228+
// var eoaContract = await ThirdwebContract.Create(client, eoaWalletAddress, chainWith7702, delegationContract.Abi);
189229

190-
// // Prepare the claim call
191-
// var claimCallData = erc20Contract.CreateCallData(
192-
// "claim",
193-
// new object[]
194-
// {
195-
// executorWalletAddress, // receiver
196-
// 100, // quantity
197-
// Constants.NATIVE_TOKEN_ADDRESS, // currency
198-
// 0, // pricePerToken
199-
// new object[] { Array.Empty<byte>(), BigInteger.Zero, BigInteger.Zero, Constants.ADDRESS_ZERO }, // allowlistProof
200-
// Array.Empty<byte>() // data
201-
// }
202-
// );
230+
// // --------------------------------------------------------------------------
231+
// // Mint Tokens (DropERC20) to the EOA Using the backend session key
232+
// // --------------------------------------------------------------------------
233+
234+
// var erc20ContractAddress = "0xAA462a5BE0fc5214507FDB4fB2474a7d5c69065b"; // DropERC20
235+
// var erc20Contract = await ThirdwebContract.Create(client, erc20ContractAddress, chainWith7702);
203236

204-
// // Embed the claim call in the execute call
205-
// var executeCallData = delegationContract.CreateCallData(
206-
// method: "execute",
207-
// parameters: new object[]
237+
// // Log ERC20 balance before mint
238+
// var eoaBalanceBefore = await erc20Contract.ERC20_BalanceOf(eoaWalletAddress);
239+
// Console.WriteLine($"EOA balance before: {eoaBalanceBefore}");
240+
241+
// // Create execution call data (calling 'claim' on the DropERC20)
242+
// var executeCallData = eoaContract.CreateCallData(
243+
// "execute",
244+
// new object[]
208245
// {
209-
// new List<Thirdweb.Console.Call>
246+
// new List<Call>
210247
// {
211248
// new()
212249
// {
213-
// Data = claimCallData.HexToBytes(),
250+
// Data = erc20Contract
251+
// .CreateCallData(
252+
// "claim",
253+
// new object[]
254+
// {
255+
// eoaWalletAddress, // receiver
256+
// 100, // quantity
257+
// Constants.NATIVE_TOKEN_ADDRESS, // currency
258+
// 0, // pricePerToken
259+
// new object[] { Array.Empty<byte>(), BigInteger.Zero, BigInteger.Zero, Constants.ADDRESS_ZERO }, // allowlistProof
260+
// Array.Empty<byte>() // data
261+
// }
262+
// )
263+
// .HexToBytes(),
214264
// To = erc20ContractAddress,
215265
// Value = BigInteger.Zero
216266
// }
217267
// }
218268
// }
219269
// );
220270

221-
// // Execute from the executor wallet targeting the eoa which is pointing to the delegation contract
222-
// var tx2 = await ThirdwebTransaction.Create(executorWallet, new ThirdwebTransactionInput(chainId: chainWith7702, to: eoaWalletAddress, data: executeCallData));
223-
// var hash2 = (await ThirdwebTransaction.SendAndWaitForTransactionReceipt(tx2)).TransactionHash;
224-
// Console.WriteLine($"Token claim transaction hash: {hash2}");
271+
// var executeReceipt = await engineWallet.ExecuteTransaction(new ThirdwebTransactionInput(chainId: chainWith7702, to: eoaWalletAddress, data: executeCallData));
272+
// Console.WriteLine($"Execute receipt: {JsonConvert.SerializeObject(executeReceipt, Formatting.Indented)}");
225273

226-
// // Log erc20 balance of executor after the claim
227-
// var executorBalanceAfter = await erc20Contract.ERC20_BalanceOf(executorWalletAddress);
228-
// Console.WriteLine($"Executor balance after: {executorBalanceAfter}");
274+
// // Log ERC20 balance after mint
275+
// var eoaBalanceAfter = await erc20Contract.ERC20_BalanceOf(eoaWalletAddress);
276+
// Console.WriteLine($"EOA balance after: {eoaBalanceAfter}");
229277

230278
#endregion
231279

Thirdweb/Thirdweb.Contracts/ThirdwebContract.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ namespace Thirdweb;
1212
/// </summary>
1313
public class ThirdwebContract
1414
{
15-
internal ThirdwebClient Client { get; private set; }
16-
internal string Address { get; private set; }
17-
internal BigInteger Chain { get; private set; }
18-
internal string Abi { get; private set; }
15+
public ThirdwebClient Client { get; private set; }
16+
public string Address { get; private set; }
17+
public BigInteger Chain { get; private set; }
18+
public string Abi { get; private set; }
1919

2020
private static readonly Dictionary<string, string> _contractAbiCache = new();
2121
private static readonly object _cacheLock = new();

Thirdweb/Thirdweb.Extensions/ThirdwebExtensions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,7 +1235,9 @@ public static async Task<NFT> ERC721_GetNFT(this ThirdwebContract contract, BigI
12351235
}
12361236
catch (Exception e)
12371237
{
1238+
#pragma warning disable IDE0059 // Unnecessary assignment of a value
12381239
metadata = new NFTMetadata { Description = e.Message };
1240+
#pragma warning restore IDE0059 // Unnecessary assignment of a value
12391241
}
12401242
metadata.Id = tokenId.ToString();
12411243

@@ -1386,7 +1388,9 @@ public static async Task<NFT> ERC1155_GetNFT(this ThirdwebContract contract, Big
13861388
}
13871389
catch (Exception e)
13881390
{
1391+
#pragma warning disable IDE0059 // Unnecessary assignment of a value
13891392
metadata = new NFTMetadata { Description = e.Message };
1393+
#pragma warning restore IDE0059 // Unnecessary assignment of a value
13901394
}
13911395
metadata.Id = tokenId.ToString();
13921396

Thirdweb/Thirdweb.Utils/Utils.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,8 @@ public static bool IsEip1559Supported(string chainId)
755755
case "841":
756756
// Taraxa Testnet
757757
case "842":
758+
// Odyssey Testnet
759+
case "911867":
758760
return false;
759761
default:
760762
return true;
@@ -942,6 +944,13 @@ public static async Task<BigInteger> FetchGasPrice(ThirdwebClient client, BigInt
942944
return (gasPrice, gasPrice);
943945
}
944946

947+
// Arbitrum, Arbitrum Nova & Arbitrum Sepolia
948+
if (chainId == (BigInteger)42161 || chainId == (BigInteger)42170 || chainId == (BigInteger)421614)
949+
{
950+
var gasPrice = await FetchGasPrice(client, chainId, withBump).ConfigureAwait(false);
951+
return (gasPrice, gasPrice);
952+
}
953+
945954
try
946955
{
947956
var block = await rpc.SendRequestAsync<JObject>("eth_getBlockByNumber", "latest", true).ConfigureAwait(false);
@@ -1165,17 +1174,16 @@ public static List<EIP7702Authorization> DecodeAutorizationList(byte[] authoriza
11651174
foreach (var rlpElement in decodedList)
11661175
{
11671176
var decodedItem = (RLPCollection)rlpElement;
1177+
var signature = RLPSignedDataDecoder.DecodeSignature(decodedItem, 3);
11681178
var authorizationListItem = new EIP7702Authorization
11691179
{
11701180
ChainId = new HexBigInteger(decodedItem[0].RLPData.ToBigIntegerFromRLPDecoded()).HexValue,
11711181
Address = decodedItem[1].RLPData.BytesToHex().ToChecksumAddress(),
1172-
Nonce = new HexBigInteger(decodedItem[2].RLPData.ToBigIntegerFromRLPDecoded()).HexValue
1182+
Nonce = new HexBigInteger(decodedItem[2].RLPData.ToBigIntegerFromRLPDecoded()).HexValue,
1183+
YParity = signature.V.BytesToHex(),
1184+
R = signature.R.BytesToHex(),
1185+
S = signature.S.BytesToHex()
11731186
};
1174-
var signature = RLPSignedDataDecoder.DecodeSignature(decodedItem, 3);
1175-
authorizationListItem.YParity = signature.V.BytesToHex();
1176-
authorizationListItem.R = signature.R.BytesToHex();
1177-
authorizationListItem.S = signature.S.BytesToHex();
1178-
11791187
authorizationLists.Add(authorizationListItem);
11801188
}
11811189

0 commit comments

Comments
 (0)