Skip to content

[Beta] EIP7702 Unmanaged/Managed Execution for In-App & Ecosystem Wallets, turn your EOAs into Smart EOAs with a simple flag. #138

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
May 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 37 additions & 101 deletions Thirdweb.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Nethereum.ABI;
using Nethereum.Hex.HexConvertors.Extensions;
using Nethereum.Hex.HexTypes;
using Nethereum.Util;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Thirdweb;
Expand All @@ -26,10 +27,10 @@
var privateKey = Environment.GetEnvironmentVariable("PRIVATE_KEY");

// Fetch timeout options are optional, default is 120000ms
var client = ThirdwebClient.Create(secretKey: secretKey, fetchTimeoutOptions: new TimeoutOptions(storage: 120000, rpc: 120000, other: 120000));
var client = ThirdwebClient.Create(secretKey: secretKey);

// Create a private key wallet
var privateKeyWallet = await PrivateKeyWallet.Generate(client: client);
// Create a private key wallet
var privateKeyWallet = await PrivateKeyWallet.Generate(client);

// var walletAddress = await privateKeyWallet.GetAddress();
// Console.WriteLine($"PK Wallet address: {walletAddress}");
Expand Down Expand Up @@ -326,113 +327,48 @@

#region EIP-7702

// // --------------------------------------------------------------------------
// // Configuration
// // --------------------------------------------------------------------------
// var chain = 11155111; // sepolia

// var chainWith7702 = 911867;
// var delegationContractAddress = "0xb012446cba783d0f7723daf96cf4c49005022307"; // MinimalAccount

// // Required environment variables
// var backendWalletAddress = Environment.GetEnvironmentVariable("ENGINE_BACKEND_WALLET_ADDRESS") ?? throw new Exception("ENGINE_BACKEND_WALLET_ADDRESS is required");
// var engineUrl = Environment.GetEnvironmentVariable("ENGINE_URL") ?? throw new Exception("ENGINE_URL is required");
// var engineAccessToken = Environment.GetEnvironmentVariable("ENGINE_ACCESS_TOKEN") ?? throw new Exception("ENGINE_ACCESS_TOKEN is required");

// // --------------------------------------------------------------------------
// // Initialize Engine Wallet
// // --------------------------------------------------------------------------

// var engineWallet = await EngineWallet.Create(client, engineUrl, engineAccessToken, backendWalletAddress, 15);

// // --------------------------------------------------------------------------
// // Delegation Contract Implementation
// // --------------------------------------------------------------------------

// var delegationContract = await ThirdwebContract.Create(client, delegationContractAddress, chainWith7702);

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

// // Sign the authorization to point to the delegation contract
// var authorization = await eoaWallet.SignAuthorization(chainWith7702, delegationContractAddress, willSelfExecute: false);
// Console.WriteLine($"Authorization: {JsonConvert.SerializeObject(authorization, Formatting.Indented)}");

// // Sign message for session key
// var sessionKeyParams = new SessionKeyParams_7702()
// // Connect to EOA
// var smartEoa = await InAppWallet.Create(client, authProvider: AuthProvider.Google, executionMode: ExecutionMode.EIP7702Sponsored);
// if (!await smartEoa.IsConnected())
// {
// Signer = backendWalletAddress,
// NativeTokenLimitPerTransaction = 0,
// StartTimestamp = 0,
// EndTimestamp = Utils.GetUnixTimeStampNow() + (3600 * 24),
// ApprovedTargets = new List<string> { Constants.ADDRESS_ZERO },
// Uid = Guid.NewGuid().ToByteArray()
// };
// var sessionKeySig = await EIP712.GenerateSignature_SmartAccount_7702("MinimalAccount", "1", chainWith7702, eoaWalletAddress, sessionKeyParams, eoaWallet);

// // Create call data for the session key
// var sessionKeyCallData = delegationContract.CreateCallData("createSessionKeyWithSig", sessionKeyParams, sessionKeySig.HexToBytes());

// // Execute the delegation & session key creation in one go, from the backend!
// var delegationReceipt = await engineWallet.ExecuteTransaction(new ThirdwebTransactionInput(chainId: chainWith7702, to: eoaWalletAddress, data: sessionKeyCallData, authorization: authorization));
// Console.WriteLine($"Delegation Execution Receipt: {JsonConvert.SerializeObject(delegationReceipt, Formatting.Indented)}");

// // Verify contract code deployed to the EOA
// var rpc = ThirdwebRPC.GetRpcInstance(client, chainWith7702);
// var code = await rpc.SendRequestAsync<string>("eth_getCode", eoaWalletAddress, "latest");
// Console.WriteLine($"EOA code: {code}");

// // The EOA is now a contract
// var eoaContract = await ThirdwebContract.Create(client, eoaWalletAddress, chainWith7702, delegationContract.Abi);
// _ = await smartEoa.LoginWithOauth(
// isMobile: false,
// (url) =>
// {
// var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
// _ = Process.Start(psi);
// }
// );
// }
// var smartEoaAddress = await smartEoa.GetAddress();
// Console.WriteLine($"User Wallet address: {await smartEoa.GetAddress()}");

// // --------------------------------------------------------------------------
// // Mint Tokens (DropERC20) to the EOA Using the backend session key
// // --------------------------------------------------------------------------
// // Upgrade EOA - This wallet explicitly uses EIP-7702 delegation to the thirdweb MinimalAccount (will delegate upon first tx)

// var erc20ContractAddress = "0xAA462a5BE0fc5214507FDB4fB2474a7d5c69065b"; // DropERC20
// var erc20Contract = await ThirdwebContract.Create(client, erc20ContractAddress, chainWith7702);
// // Transact, will upgrade EOA
// var receipt = await smartEoa.Transfer(chainId: chain, toAddress: await Utils.GetAddressFromENS(client, "vitalik.eth"), weiAmount: 0);
// Console.WriteLine($"Transfer Receipt: {receipt.TransactionHash}");

// // Log ERC20 balance before mint
// var eoaBalanceBefore = await erc20Contract.ERC20_BalanceOf(eoaWalletAddress);
// Console.WriteLine($"EOA balance before: {eoaBalanceBefore}");
// // Double check that it was upgraded
// var isDelegated = await Utils.IsDelegatedAccount(client, chain, smartEoaAddress);
// Console.WriteLine($"Is delegated: {isDelegated}");

// // Create execution call data (calling 'claim' on the DropERC20)
// var executeCallData = eoaContract.CreateCallData(
// "execute",
// new object[]
// // Create a session key
// var sessionKeyReceipt = await smartEoa.CreateSessionKey(
// chain,
// new SessionSpec()
// {
// new List<Call>
// {
// new()
// {
// Data = erc20Contract
// .CreateCallData(
// "claim",
// new object[]
// {
// eoaWalletAddress, // receiver
// 100, // quantity
// Constants.NATIVE_TOKEN_ADDRESS, // currency
// 0, // pricePerToken
// new object[] { Array.Empty<byte>(), BigInteger.Zero, BigInteger.Zero, Constants.ADDRESS_ZERO }, // allowlistProof
// Array.Empty<byte>() // data
// }
// )
// .HexToBytes(),
// To = erc20ContractAddress,
// Value = BigInteger.Zero
// }
// }
// Signer = await Utils.GetAddressFromENS(client, "0xfirekeeper.eth"),
// IsWildcard = true,
// ExpiresAt = Utils.GetUnixTimeStampNow() + 86400, // 1 day
// CallPolicies = new List<CallSpec>(),
// TransferPolicies = new List<TransferSpec>(),
// Uid = Guid.NewGuid().ToByteArray().PadTo32Bytes()
// }
// );

// var executeReceipt = await engineWallet.ExecuteTransaction(new ThirdwebTransactionInput(chainId: chainWith7702, to: eoaWalletAddress, data: executeCallData));
// Console.WriteLine($"Execute receipt: {JsonConvert.SerializeObject(executeReceipt, Formatting.Indented)}");

// // Log ERC20 balance after mint
// var eoaBalanceAfter = await erc20Contract.ERC20_BalanceOf(eoaWalletAddress);
// Console.WriteLine($"EOA balance after: {eoaBalanceAfter}");
// Console.WriteLine($"Session key receipt: {sessionKeyReceipt.TransactionHash}");

#endregion

Expand Down
56 changes: 23 additions & 33 deletions Thirdweb.Tests/Thirdweb.AI/Thirdweb.AI.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Thirdweb.Tests.AI;

public class NebulaTests : BaseTests
{
private const string NEBULA_TEST_USDC_ADDRESS = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238";
// private const string NEBULA_TEST_USDC_ADDRESS = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238";

private const string NEBULA_TEST_CONTRACT = "0xe2cb0eb5147b42095c2FfA6F7ec953bb0bE347D8";

Expand Down Expand Up @@ -46,16 +46,6 @@ public async Task Chat_Single_ReturnsResponse()
Assert.Contains("CAT", response.Message);
}

[Fact(Timeout = 120000)]
public async Task Chat_Single_NoContext_ReturnsResponse()
{
var nebula = await ThirdwebNebula.Create(this.Client);
var response = await nebula.Chat(message: $"What's the symbol of this contract: {NEBULA_TEST_CONTRACT} (Sepolia)?");
Assert.NotNull(response);
Assert.NotNull(response.Message);
Assert.Contains("CAT", response.Message);
}

[Fact(Timeout = 120000)]
public async Task Chat_UnderstandsWalletContext()
{
Expand All @@ -68,26 +58,26 @@ public async Task Chat_UnderstandsWalletContext()
Assert.Contains(expectedAddress, response.Message);
}

[Fact(Timeout = 120000)]
public async Task Execute_ReturnsMessageAndReceipt()
{
var signer = await PrivateKeyWallet.Generate(this.Client);
var wallet = await SmartWallet.Create(signer, NEBULA_TEST_CHAIN);
var nebula = await ThirdwebNebula.Create(this.Client);
var response = await nebula.Execute(
new List<NebulaChatMessage>
{
new("What's the address of vitalik.eth", NebulaChatRole.User),
new("The address of vitalik.eth is 0xd8dA6BF26964aF8E437eEa5e3616511D7G3a3298", NebulaChatRole.Assistant),
new($"Approve 1 USDC (this contract: {NEBULA_TEST_USDC_ADDRESS}) to them", NebulaChatRole.User),
},
wallet: wallet
);
Assert.NotNull(response);
Assert.NotNull(response.Message);
Assert.NotNull(response.TransactionReceipts);
Assert.NotEmpty(response.TransactionReceipts);
Assert.NotNull(response.TransactionReceipts[0].TransactionHash);
Assert.True(response.TransactionReceipts[0].TransactionHash.Length == 66);
}
// [Fact(Timeout = 120000)]
// public async Task Execute_ReturnsMessageAndReceipt()
// {
// var signer = await PrivateKeyWallet.Generate(this.Client);
// var wallet = await SmartWallet.Create(signer, NEBULA_TEST_CHAIN);
// var nebula = await ThirdwebNebula.Create(this.Client);
// var response = await nebula.Execute(
// new List<NebulaChatMessage>
// {
// new("What's the address of vitalik.eth", NebulaChatRole.User),
// new("The address of vitalik.eth is 0xd8dA6BF26964aF8E437eEa5e3616511D7G3a3298", NebulaChatRole.Assistant),
// new($"Approve 1 USDC (this contract: {NEBULA_TEST_USDC_ADDRESS}) to them", NebulaChatRole.User),
// },
// wallet: wallet
// );
// Assert.NotNull(response);
// Assert.NotNull(response.Message);
// Assert.NotNull(response.TransactionReceipts);
// Assert.NotEmpty(response.TransactionReceipts);
// Assert.NotNull(response.TransactionReceipts[0].TransactionHash);
// Assert.True(response.TransactionReceipts[0].TransactionHash.Length == 66);
// }
}
2 changes: 1 addition & 1 deletion Thirdweb/Thirdweb.Transactions/ThirdwebTransactionInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public EIP7702Authorization(BigInteger chainId, string address, BigInteger nonce
this.ChainId = new HexBigInteger(chainId).HexValue;
this.Address = address;
this.Nonce = new HexBigInteger(nonce).HexValue;
this.YParity = yParity.BytesToHex();
this.YParity = yParity.BytesToHex() == "0x00" ? "0x0" : "0x1";
this.R = r.BytesToHex();
this.S = s.BytesToHex();
}
Expand Down
Loading