Skip to content

Commit

Permalink
ThirdwebTransaction.Sign + Modify Estimation/Simulation Flows
Browse files Browse the repository at this point in the history
  • Loading branch information
0xFirekeeper committed Apr 15, 2024
1 parent dfd8bf3 commit 2badbc4
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 39 deletions.
30 changes: 27 additions & 3 deletions Thirdweb.Tests/Thirdweb.Transactions.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ public async Task EstimateTotalCosts_CalculatesCostsCorrectly()
public async Task EstimateTotalCosts_WithoutSetting_CalculatesCostsCorrectly()
{
var transaction = await CreateSampleTransaction();
transaction.Input.From = Constants.ADDRESS_ZERO;
_ = transaction.SetValue(new BigInteger(1000));

var costs = await ThirdwebTransaction.EstimateTotalCosts(transaction);
Expand Down Expand Up @@ -162,13 +163,36 @@ public async Task EstimateGasCosts_CalculatesCostsCorrectly()
public async Task EstimateGasCosts_WithoutSetting_CalculatesCostsCorrectly()
{
var transaction = await CreateSampleTransaction();
transaction.Input.From = Constants.ADDRESS_ZERO;
_ = transaction.SetValue(new BigInteger(1000));

var costs = await ThirdwebTransaction.EstimateGasCosts(transaction);

Assert.NotEqual(BigInteger.Zero, costs.wei);
}

[Fact]
public async Task EstimateGasCosts_SmartWalletHigherThanPrivateKeyWallet()
{
var client = ThirdwebClient.Create(secretKey: _secretKey);
var privateKeyAccount = await PrivateKeyWallet.Create(client, _testPrivateKey);
var smartAccount = await SmartWallet.Create(client, personalWallet: privateKeyAccount, factoryAddress: "0xbf1C9aA4B1A085f7DA890a44E82B0A1289A40052", gasless: true, chainId: 421614);

var transaction = await ThirdwebTransaction.Create(client, smartAccount, new TransactionInput(), 421614);
_ = transaction.SetTo(Constants.ADDRESS_ZERO);
_ = transaction.SetValue(new BigInteger(1000));

var smartCosts = await ThirdwebTransaction.EstimateGasCosts(transaction);

transaction = await ThirdwebTransaction.Create(client, privateKeyAccount, new TransactionInput(), 421614);
_ = transaction.SetTo(Constants.ADDRESS_ZERO);
_ = transaction.SetValue(new BigInteger(1000));

var privateCosts = await ThirdwebTransaction.EstimateGasCosts(transaction);

Assert.True(smartCosts.wei > privateCosts.wei);
}

[Fact]
public async Task EstimateTotalCosts_HigherThanGasCostsByValue()
{
Expand Down Expand Up @@ -205,7 +229,7 @@ public async Task Simulate_ThrowsInsufficientFunds()
}

[Fact]
public async Task Simulate_ReturnsGasEstimate()
public async Task Simulate_ReturnsData()
{
var client = ThirdwebClient.Create(secretKey: _secretKey);
var privateKeyAccount = await PrivateKeyWallet.Create(client, _testPrivateKey);
Expand All @@ -214,8 +238,8 @@ public async Task Simulate_ReturnsGasEstimate()
_ = transaction.SetValue(new BigInteger(0));
_ = transaction.SetGasLimit(250000);

var gas = await ThirdwebTransaction.Simulate(transaction);
Assert.NotEqual(BigInteger.Zero, gas);
var data = await ThirdwebTransaction.Simulate(transaction);
Assert.NotNull(data);
}

[Fact]
Expand Down
64 changes: 30 additions & 34 deletions Thirdweb/Thirdweb.Transactions/ThirdwebTransaction.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Numerics;
using Nethereum.Hex.HexTypes;
using Nethereum.RPC.Eth.DTOs;
using Nethereum.RPC.Eth.Transactions;
using Newtonsoft.Json;
using Nethereum.Contracts;
using Nethereum.ABI.FunctionEncoding;
Expand Down Expand Up @@ -34,6 +33,7 @@ public static async Task<ThirdwebTransaction> Create(ThirdwebClient client, IThi
{
var address = await wallet.GetAddress();
txInput.From ??= address;
txInput.Data ??= "0x";
return address != txInput.From
? throw new ArgumentException("Transaction sender (from) must match wallet address")
: client == null
Expand Down Expand Up @@ -89,54 +89,50 @@ public ThirdwebTransaction SetNonce(BigInteger nonce)
public static async Task<TotalCosts> EstimateGasCosts(ThirdwebTransaction transaction)
{
var gasPrice = transaction.Input.GasPrice?.Value ?? await EstimateGasPrice(transaction);
var gasLimit = transaction.Input.Gas?.Value ?? await EstimateGasLimit(transaction, true);
var gasLimit = transaction.Input.Gas?.Value ?? await EstimateGasLimit(transaction);
var gasCost = BigInteger.Multiply(gasLimit, gasPrice);
return new TotalCosts { ether = gasCost.ToString().ToEth(18, false), wei = gasCost };
}

public static async Task<TotalCosts> EstimateTotalCosts(ThirdwebTransaction transaction)
{
var gasPrice = transaction.Input.GasPrice?.Value ?? await EstimateGasPrice(transaction);
var gasLimit = transaction.Input.Gas?.Value ?? await EstimateGasLimit(transaction, true);
var gasCost = BigInteger.Multiply(gasLimit, gasPrice);
var gasCostWithValue = BigInteger.Add(gasCost, transaction.Input.Value?.Value ?? 0);
return new TotalCosts { ether = gasCostWithValue.ToString().ToEth(18, false), wei = gasCostWithValue };
var gasCosts = await EstimateGasCosts(transaction);
var value = transaction.Input.Value?.Value ?? 0;
return new TotalCosts { ether = (value + gasCosts.wei).ToString().ToEth(18, false), wei = value + gasCosts.wei };
}

public static async Task<BigInteger> EstimateGasPrice(ThirdwebTransaction transaction, bool withBump = true)
{
{
var rpc = ThirdwebRPC.GetRpcInstance(transaction._client, transaction.Input.ChainId.Value);
var hex = new HexBigInteger(await rpc.SendRequestAsync<string>("eth_gasPrice"));
return withBump ? hex.Value * 10 / 9 : hex.Value;
}
var rpc = ThirdwebRPC.GetRpcInstance(transaction._client, transaction.Input.ChainId.Value);
var hex = new HexBigInteger(await rpc.SendRequestAsync<string>("eth_gasPrice"));
return withBump ? hex.Value * 10 / 9 : hex.Value;
}

public static async Task<BigInteger> Simulate(ThirdwebTransaction transaction)
public static async Task<string> Simulate(ThirdwebTransaction transaction)
{
return await EstimateGasLimit(transaction, false);
var rpc = ThirdwebRPC.GetRpcInstance(transaction._client, transaction.Input.ChainId.Value);
var data = await rpc.SendRequestAsync<string>("eth_call", transaction.Input, "latest");
return data;
}

public static async Task<BigInteger> EstimateGasLimit(ThirdwebTransaction transaction, bool overrideBalance = true)
public static async Task<BigInteger> EstimateGasLimit(ThirdwebTransaction transaction)
{
var rpc = ThirdwebRPC.GetRpcInstance(transaction._client, transaction.Input.ChainId.Value);
var from = transaction.Input.From;
var hex = overrideBalance
? await rpc.SendRequestAsync<string>(
"eth_estimateGas",
transaction.Input,
"latest",
new Dictionary<string, Dictionary<string, string>>()
{
{
from,
new() { { "balance", "0xFFFFFFFFFFFFFFFFFFFF" } }
}
}
)
: await rpc.SendRequestAsync<string>("eth_estimateGas", transaction.Input, "latest");

return new HexBigInteger(hex).Value;
if (transaction._wallet.AccountType == ThirdwebAccountType.SmartAccount)
{
var smartAccount = transaction._wallet as SmartWallet;
return await smartAccount.EstimateUserOperationGas(transaction.Input, transaction.Input.ChainId.Value);
}
else
{
var rpc = ThirdwebRPC.GetRpcInstance(transaction._client, transaction.Input.ChainId.Value);
var hex = await rpc.SendRequestAsync<string>("eth_estimateGas", transaction.Input, "latest");
return new HexBigInteger(hex).Value;
}
}

public static async Task<string> Sign(ThirdwebTransaction transaction)
{
return await transaction._wallet.SignTransaction(transaction.Input, transaction.Input.ChainId.Value);
}

public static async Task<string> Send(ThirdwebTransaction transaction)
Expand All @@ -149,10 +145,10 @@ public static async Task<string> Send(ThirdwebTransaction transaction)
transaction.Input.From ??= await transaction._wallet.GetAddress();
transaction.Input.Value ??= new HexBigInteger(0);
transaction.Input.Data ??= "0x";
transaction.Input.GasPrice ??= new HexBigInteger(await EstimateGasPrice(transaction));
transaction.Input.MaxFeePerGas = null;
transaction.Input.MaxPriorityFeePerGas = null;
transaction.Input.Gas ??= new HexBigInteger(await EstimateGasLimit(transaction));
transaction.Input.GasPrice ??= new HexBigInteger(await EstimateGasPrice(transaction));

var rpc = ThirdwebRPC.GetRpcInstance(transaction._client, transaction.Input.ChainId.Value);
string hash;
Expand Down
12 changes: 10 additions & 2 deletions Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Nethereum.Hex.HexConvertors.Extensions;
using Nethereum.Hex.HexTypes;
using Nethereum.RPC.Eth.DTOs;
using Newtonsoft.Json;
using Thirdweb.AccountAbstraction;

namespace Thirdweb
Expand Down Expand Up @@ -420,9 +421,16 @@ public Task<string> SignTypedDataV4<T, TDomain>(T data, TypedData<TDomain> typed
return _personalAccount.SignTypedDataV4(data, typedData);
}

public Task<string> SignTransaction(TransactionInput transaction, BigInteger chainId)
public async Task<BigInteger> EstimateUserOperationGas(TransactionInput transaction, BigInteger chainId)
{
return _personalAccount.SignTransaction(transaction, chainId);
var signedOp = await SignUserOp(transaction);
var cost = signedOp.CallGasLimit + signedOp.VerificationGasLimit + signedOp.PreVerificationGas;
return cost;
}

public async Task<string> SignTransaction(TransactionInput transaction, BigInteger chainId)
{
return JsonConvert.SerializeObject(EncodeUserOperation(await SignUserOp(transaction)));
}

public Task<bool> IsConnected()
Expand Down

0 comments on commit 2badbc4

Please sign in to comment.