Skip to content

Commit 234e7e9

Browse files
committed
Full working example
1 parent 0196f47 commit 234e7e9

File tree

7 files changed

+177
-124
lines changed

7 files changed

+177
-124
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.PHONY: run
2+
3+
run:
4+
dotnet run --project Thirdweb.Console

Thirdweb.Console/Program.Types.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Numerics;
2+
using Nethereum.ABI.FunctionEncoding.Attributes;
3+
4+
namespace Thirdweb.Console;
5+
6+
public class Call
7+
{
8+
[Parameter("bytes", "data", 1)]
9+
public required byte[] Data { get; set; }
10+
11+
[Parameter("address", "to", 2)]
12+
public required string To { get; set; }
13+
14+
[Parameter("uint256", "value", 3)]
15+
public required BigInteger Value { get; set; }
16+
}

Thirdweb.Console/Program.cs

Lines changed: 103 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -18,86 +18,20 @@
1818
// Do not use private keys client side, use InAppWallet/SmartWallet instead
1919
var privateKey = Environment.GetEnvironmentVariable("PRIVATE_KEY");
2020

21-
// Initialize client
22-
var client = ThirdwebClient.Create(secretKey: secretKey);
23-
24-
// Chain and contract addresses
25-
var chainWith7702 = 7078815900;
26-
var erc20ContractAddress = "0x852e8247A55C49dc3b7e6f8788347813e562F597"; // Mekong Token
27-
var delegationContractAddress = "0x7B9E7AFd452666302352D82161B083dF792f7Cf4"; // BatchCallDelegation
28-
29-
// Initialize contracts normally
30-
var erc20Contract = await ThirdwebContract.Create(client: client, address: erc20ContractAddress, chain: chainWith7702);
31-
var delegationContract = await ThirdwebContract.Create(
32-
client: client,
33-
address: delegationContractAddress,
34-
chain: chainWith7702,
35-
abi: /*lang=json,strict*/
36-
"[{\"anonymous\": false,\"inputs\": [{\"indexed\": true,\"internalType\": \"address\",\"name\": \"to\",\"type\": \"address\"},{\"indexed\": false,\"internalType\": \"uint256\",\"name\": \"value\",\"type\": \"uint256\"},{\"indexed\": false,\"internalType\": \"bytes\",\"name\": \"data\",\"type\": \"bytes\"}],\"name\": \"Executed\",\"type\": \"event\"},{\"inputs\": [{\"components\": [{\"internalType\": \"bytes\",\"name\": \"data\",\"type\": \"bytes\"},{\"internalType\": \"address\",\"name\": \"to\",\"type\": \"address\"},{\"internalType\": \"uint256\",\"name\": \"value\",\"type\": \"uint256\"}],\"internalType\": \"struct BatchCallDelegation.Call[]\",\"name\": \"calls\",\"type\": \"tuple[]\"}],\"name\": \"execute\",\"outputs\": [],\"stateMutability\": \"payable\",\"type\": \"function\"}]"
37-
);
38-
39-
// Initialize a 7702 EOA
40-
var eoaWallet = await PrivateKeyWallet.Generate(client);
41-
var eoaWalletAddress = await eoaWallet.GetAddress();
42-
Console.WriteLine($"EOA address: {eoaWalletAddress}");
43-
44-
// // Temporary - fund eoa wallet
45-
// var fundingWallet = await PrivateKeyWallet.Create(client, privateKey);
46-
// var fundingHash = (
47-
// await ThirdwebTransaction.SendAndWaitForTransactionReceipt(
48-
// await ThirdwebTransaction.Create(fundingWallet, new ThirdwebTransactionInput(chainId: chainWith7702, to: eoaWalletAddress, value: BigInteger.Parse("0.01".ToWei())))
49-
// )
50-
// ).TransactionHash;
51-
// Console.WriteLine($"Funding hash: {fundingHash}");
52-
53-
// Sign the authorization to make it point to the delegation contract
54-
var authorization = await eoaWallet.SignAuthorization(chainId: chainWith7702, contractAddress: delegationContractAddress, willSelfExecute: false);
55-
Console.WriteLine($"Authorization: {JsonConvert.SerializeObject(authorization, Formatting.Indented)}");
56-
57-
// Initialize another wallet, the "executor" that will hit the eoa's execute function
58-
var executorWallet = await PrivateKeyWallet.Create(client, privateKey);
59-
var executorWalletAddress = await executorWallet.GetAddress();
60-
Console.WriteLine($"Executor address: {executorWalletAddress}");
61-
62-
// Execute the delegation
63-
var tx = await ThirdwebTransaction.Create(executorWallet, new ThirdwebTransactionInput(chainId: chainWith7702, to: eoaWalletAddress, authorization: authorization));
64-
var hash = (await ThirdwebTransaction.SendAndWaitForTransactionReceipt(tx)).TransactionHash;
65-
Console.WriteLine($"Transaction hash: {hash}");
66-
67-
// Log erc20 balance of executor before the claim
68-
var executorBalanceBefore = await erc20Contract.ERC20_BalanceOf(executorWalletAddress);
69-
Console.WriteLine($"Executor balance before: {executorBalanceBefore}");
70-
71-
// Prepare the claim call
72-
var claimCallData = erc20Contract.CreateCallData(
73-
"claim",
74-
new object[]
75-
{
76-
executorWalletAddress, // receiver
77-
100, // quantity
78-
Constants.NATIVE_TOKEN_ADDRESS, // currency
79-
0, // pricePerToken
80-
new object[] { Array.Empty<byte>(), BigInteger.Zero, BigInteger.Zero, Constants.ADDRESS_ZERO }, // allowlistProof
81-
Array.Empty<byte>() // data
82-
}
83-
);
84-
85-
// Embed the claim call in the execute call
86-
var executeCallData = delegationContract.CreateCallData("execute", new object[] { new object[] { claimCallData, eoaWalletAddress, BigInteger.Zero } });
87-
88-
// Execute from the executor wallet targeting the eoa which is pointing to the delegation contract
89-
var tx2 = await ThirdwebTransaction.Create(executorWallet, new ThirdwebTransactionInput(chainId: chainWith7702, to: eoaWalletAddress, data: executeCallData));
90-
var hash2 = (await ThirdwebTransaction.SendAndWaitForTransactionReceipt(tx2)).TransactionHash;
91-
Console.WriteLine($"Transaction hash: {hash2}");
92-
93-
// Log erc20 balance of executor after the claim
94-
var executorBalanceAfter = await erc20Contract.ERC20_BalanceOf(executorWalletAddress);
95-
Console.WriteLine($"Executor balance after: {executorBalanceAfter}");
21+
// Fetch timeout options are optional, default is 120000ms
22+
var client = ThirdwebClient.Create(secretKey: secretKey, fetchTimeoutOptions: new TimeoutOptions(storage: 120000, rpc: 120000, other: 120000));
23+
24+
// Create a private key wallet
25+
var privateKeyWallet = await PrivateKeyWallet.Generate(client: client);
26+
27+
// var walletAddress = await privateKeyWallet.GetAddress();
28+
// Console.WriteLine($"PK Wallet address: {walletAddress}");
9629

9730
#region Contract Interaction
9831

9932
// var contract = await ThirdwebContract.Create(client: client, address: "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d", chain: 1);
100-
// var result = await contract.
33+
// var nfts = await contract.ERC721_GetAllNFTs();
34+
// Console.WriteLine($"NFTs: {JsonConvert.SerializeObject(nfts, Formatting.Indented)}");
10135

10236
#endregion
10337

@@ -110,7 +44,13 @@
11044

11145
#region AA 0.6
11246

113-
// var smartWallet06 = await SmartWallet.Create(personalWallet: privateKeyWallet, chainId: 421614);
47+
// var smartWallet06 = await SmartWallet.Create(
48+
// personalWallet: privateKeyWallet,
49+
// chainId: 421614,
50+
// gasless: true,
51+
// factoryAddress: "0xa8deE7854fb1eA8c13b713585C81d91ea86dAD84",
52+
// entryPoint: Constants.ENTRYPOINT_ADDRESS_V06
53+
// );
11454

11555
// var receipt06 = await smartWallet06.ExecuteTransaction(new ThirdwebTransactionInput(chainId: 421614, to: await smartWallet06.GetAddress(), value: 0, data: "0x"));
11656

@@ -151,6 +91,92 @@
15191

15292
#endregion
15393

94+
#region EIP-7702
95+
96+
// // Chain and contract addresses
97+
// var chainWith7702 = 911867;
98+
// var erc20ContractAddress = "0xAA462a5BE0fc5214507FDB4fB2474a7d5c69065b"; // Fake ERC20
99+
// var delegationContractAddress = "0x654F42b74885EE6803F403f077bc0409f1066c58"; // BatchCallDelegation
100+
101+
// // Initialize contracts normally
102+
// var erc20Contract = await ThirdwebContract.Create(client: client, address: erc20ContractAddress, chain: chainWith7702);
103+
// var delegationContract = await ThirdwebContract.Create(client: client, address: delegationContractAddress, chain: chainWith7702);
104+
105+
// // Initialize a (to-be) 7702 EOA
106+
// var eoaWallet = await PrivateKeyWallet.Generate(client);
107+
// var eoaWalletAddress = await eoaWallet.GetAddress();
108+
// Console.WriteLine($"EOA address: {eoaWalletAddress}");
109+
110+
// // Initialize another wallet, the "executor" that will hit the eoa's (to-be) execute function
111+
// var executorWallet = await PrivateKeyWallet.Generate(client);
112+
// var executorWalletAddress = await executorWallet.GetAddress();
113+
// Console.WriteLine($"Executor address: {executorWalletAddress}");
114+
115+
// // Fund the executor wallet
116+
// var fundingWallet = await PrivateKeyWallet.Create(client, privateKey);
117+
// var fundingHash = (await fundingWallet.Transfer(chainWith7702, executorWalletAddress, BigInteger.Parse("0.001".ToWei()))).TransactionHash;
118+
// Console.WriteLine($"Funded Executor Wallet: {fundingHash}");
119+
120+
// // Sign the authorization to make it point to the delegation contract
121+
// var authorization = await eoaWallet.SignAuthorization(chainId: chainWith7702, contractAddress: delegationContractAddress, willSelfExecute: false);
122+
// Console.WriteLine($"Authorization: {JsonConvert.SerializeObject(authorization, Formatting.Indented)}");
123+
124+
// // Execute the delegation
125+
// var tx = await ThirdwebTransaction.Create(executorWallet, new ThirdwebTransactionInput(chainId: chainWith7702, to: executorWalletAddress, authorization: authorization));
126+
// var hash = (await ThirdwebTransaction.SendAndWaitForTransactionReceipt(tx)).TransactionHash;
127+
// Console.WriteLine($"Authorization execution transaction hash: {hash}");
128+
129+
// // Prove that code has been deployed to the eoa
130+
// var rpc = ThirdwebRPC.GetRpcInstance(client, chainWith7702);
131+
// var code = await rpc.SendRequestAsync<string>("eth_getCode", eoaWalletAddress, "latest");
132+
// Console.WriteLine($"EOA code: {code}");
133+
134+
// // Log erc20 balance of executor before the claim
135+
// var executorBalanceBefore = await erc20Contract.ERC20_BalanceOf(executorWalletAddress);
136+
// Console.WriteLine($"Executor balance before: {executorBalanceBefore}");
137+
138+
// // Prepare the claim call
139+
// var claimCallData = erc20Contract.CreateCallData(
140+
// "claim",
141+
// new object[]
142+
// {
143+
// executorWalletAddress, // receiver
144+
// 100, // quantity
145+
// Constants.NATIVE_TOKEN_ADDRESS, // currency
146+
// 0, // pricePerToken
147+
// new object[] { Array.Empty<byte>(), BigInteger.Zero, BigInteger.Zero, Constants.ADDRESS_ZERO }, // allowlistProof
148+
// Array.Empty<byte>() // data
149+
// }
150+
// );
151+
152+
// // Embed the claim call in the execute call
153+
// var executeCallData = delegationContract.CreateCallData(
154+
// method: "execute",
155+
// parameters: new object[]
156+
// {
157+
// new List<Thirdweb.Console.Call>
158+
// {
159+
// new()
160+
// {
161+
// Data = claimCallData.HexToBytes(),
162+
// To = erc20ContractAddress,
163+
// Value = BigInteger.Zero
164+
// }
165+
// }
166+
// }
167+
// );
168+
169+
// // Execute from the executor wallet targeting the eoa which is pointing to the delegation contract
170+
// var tx2 = await ThirdwebTransaction.Create(executorWallet, new ThirdwebTransactionInput(chainId: chainWith7702, to: eoaWalletAddress, data: executeCallData));
171+
// var hash2 = (await ThirdwebTransaction.SendAndWaitForTransactionReceipt(tx2)).TransactionHash;
172+
// Console.WriteLine($"Token claim transaction hash: {hash2}");
173+
174+
// // Log erc20 balance of executor after the claim
175+
// var executorBalanceAfter = await erc20Contract.ERC20_BalanceOf(executorWalletAddress);
176+
// Console.WriteLine($"Executor balance after: {executorBalanceAfter}");
177+
178+
#endregion
179+
154180
#region Smart Ecosystem Wallet
155181

156182
// var eco = await EcosystemWallet.Create(client: client, ecosystemId: "ecosystem.the-bonfire", authProvider: AuthProvider.Twitch);

Thirdweb/Thirdweb.Contracts/ThirdwebContract.cs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -182,19 +182,6 @@ internal static (string callData, Function function) EncodeFunctionCall(Thirdweb
182182
{
183183
var contractRaw = new Contract(null, contract.Abi, contract.Address);
184184
var function = GetFunctionMatchSignature(contractRaw, method, parameters);
185-
if (function == null)
186-
{
187-
if (method.Contains('('))
188-
{
189-
var canonicalSignature = ExtractCanonicalSignature(method);
190-
var selector = Nethereum.Util.Sha3Keccack.Current.CalculateHash(canonicalSignature)[..8];
191-
function = contractRaw.GetFunctionBySignature(selector);
192-
}
193-
else
194-
{
195-
throw new ArgumentException("Method signature not found in contract ABI.");
196-
}
197-
}
198185
return (function.GetData(parameters), function);
199186
}
200187

@@ -207,6 +194,11 @@ internal static (string callData, Function function) EncodeFunctionCall(Thirdweb
207194
/// <returns>The matching function, or null if no match is found.</returns>
208195
private static Function GetFunctionMatchSignature(Contract contract, string functionName, params object[] args)
209196
{
197+
if (functionName.StartsWith("0x"))
198+
{
199+
return contract.GetFunctionBySignature(functionName);
200+
}
201+
210202
var abi = contract.ContractBuilder.ContractABI;
211203
var functions = abi.Functions;
212204
var paramsCount = args?.Length ?? 0;
@@ -218,7 +210,17 @@ private static Function GetFunctionMatchSignature(Contract contract, string func
218210
return contract.GetFunctionBySignature(sha);
219211
}
220212
}
221-
return null;
213+
214+
if (functionName.Contains('('))
215+
{
216+
var canonicalSignature = ExtractCanonicalSignature(functionName);
217+
var selector = Utils.HashMessage(canonicalSignature)[..8];
218+
return contract.GetFunctionBySignature(selector);
219+
}
220+
else
221+
{
222+
throw new ArgumentException("Method signature not found in contract ABI.");
223+
}
222224
}
223225

224226
/// <summary>

Thirdweb/Thirdweb.Transactions/ThirdwebTransactionInput.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,12 @@ public struct EIP7702Authorization
207207
[JsonProperty(PropertyName = "s")]
208208
public string S { get; set; }
209209

210-
public EIP7702Authorization(BigInteger chainId, string address, BigInteger nonce, byte[] v, byte[] r, byte[] s)
210+
public EIP7702Authorization(BigInteger chainId, string address, BigInteger nonce, byte[] yParity, byte[] r, byte[] s)
211211
{
212212
this.ChainId = new HexBigInteger(chainId).HexValue;
213213
this.Address = address;
214214
this.Nonce = new HexBigInteger(nonce).HexValue;
215-
this.YParity = v.BytesToHex();
215+
this.YParity = yParity.BytesToHex();
216216
this.R = r.BytesToHex();
217217
this.S = s.BytesToHex();
218218
}

Thirdweb/Thirdweb.Utils/Utils.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,8 +1076,8 @@ public static (ThirdwebTransactionInput transactionInput, string signature) Deco
10761076
/// <returns>The decoded transaction input and signature.</returns>
10771077
public static (ThirdwebTransactionInput transactionInput, string signature) DecodeTransaction(byte[] signedRlpData)
10781078
{
1079-
var maybeType = signedRlpData[0];
1080-
if (maybeType is 0x04 or 0x02)
1079+
var txType = signedRlpData[0];
1080+
if (txType is 0x04 or 0x02)
10811081
{
10821082
signedRlpData = signedRlpData.Skip(1).ToArray();
10831083
}
@@ -1093,9 +1093,9 @@ public static (ThirdwebTransactionInput transactionInput, string signature) Deco
10931093
var amount = decodedElements[6].RLPData.ToBigIntegerFromRLPDecoded();
10941094
var data = decodedElements[7].RLPData?.BytesToHex();
10951095
// 8th decoded element is access list
1096-
var authorizations = DecodeAutorizationList(decodedElements[9]?.RLPData);
1096+
var authorizations = txType == 0x04 ? DecodeAutorizationList(decodedElements[9]?.RLPData) : null;
10971097

1098-
var signature = RLPSignedDataDecoder.DecodeSignature(decodedElements, 10);
1098+
var signature = RLPSignedDataDecoder.DecodeSignature(decodedElements, txType == 0x04 ? 10 : 9);
10991099
return (
11001100
new ThirdwebTransactionInput(
11011101
chainId: chainId,
@@ -1146,4 +1146,14 @@ public static List<EIP7702Authorization> DecodeAutorizationList(byte[] authoriza
11461146

11471147
return authorizationLists;
11481148
}
1149+
1150+
internal static byte[] ToByteArrayForRLPEncoding(this BigInteger value)
1151+
{
1152+
if (value == 0)
1153+
{
1154+
return Array.Empty<byte>();
1155+
}
1156+
1157+
return value.ToBytesForRLPEncoding();
1158+
}
11491159
}

0 commit comments

Comments
 (0)