Skip to content

Commit

Permalink
EstimateGasLimit overrides balance, Simulate doesn't
Browse files Browse the repository at this point in the history
Additional tests
  • Loading branch information
0xFirekeeper committed Apr 12, 2024
1 parent 3cae929 commit 07f4eaa
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 8 deletions.
105 changes: 105 additions & 0 deletions Thirdweb.Tests/Thirdweb.Transactions.Tests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Numerics;
using Nethereum.Hex.HexTypes;
using Nethereum.RPC.Eth.DTOs;

namespace Thirdweb.Tests;

Expand All @@ -7,6 +9,109 @@ public class TransactionTests : BaseTests
public TransactionTests(ITestOutputHelper output)
: base(output) { }

private async Task<ThirdwebTransaction> CreateSampleTransaction()
{
var client = ThirdwebClient.Create(secretKey: _secretKey);
var wallet = await PrivateKeyWallet.Create(client, _testPrivateKey);
var chainId = new BigInteger(1);

var transaction = await ThirdwebTransaction.Create(client, wallet, new TransactionInput(), chainId);
return transaction;
}

[Fact]
public async Task Create_ValidatesInputParameters()
{
var client = ThirdwebClient.Create(secretKey: _secretKey);
var wallet = await PrivateKeyWallet.Create(client, _testPrivateKey);
var txInput = new TransactionInput() { From = await wallet.GetAddress() };
var chainId = new BigInteger(1);
var transaction = await ThirdwebTransaction.Create(client, wallet, txInput, chainId);
Assert.NotNull(transaction);
}

[Fact]
public async Task Create_ThrowsOnInvalidChainId()
{
var client = ThirdwebClient.Create(secretKey: _secretKey);
var wallet = await PrivateKeyWallet.Create(client, _testPrivateKey);
var txInput = new TransactionInput() { From = "0x123" };
_ = await Assert.ThrowsAsync<ArgumentException>(() => ThirdwebTransaction.Create(client, wallet, txInput, BigInteger.Zero));
}

[Fact]
public async Task SetTo_UpdatesToAddress()
{
var transaction = await CreateSampleTransaction();
transaction.SetTo("0x456");
Assert.Equal("0x456", transaction.Input.To);
}

[Fact]
public async Task SetValue_SetsValue()
{
var transaction = await CreateSampleTransaction();
var value = new BigInteger(1000);
transaction.SetValue(value);
Assert.Equal(value.ToHexBigInteger(), transaction.Input.Value);
}

[Fact]
public async Task Send_ThrowsIfToAddressNotProvided()
{
var transaction = await CreateSampleTransaction();
transaction.SetTo(null);

_ = await Assert.ThrowsAsync<ArgumentException>(() => ThirdwebTransaction.Send(transaction));
}

[Fact]
public async Task Send_CorrectlyHandlesNonce()
{
var transaction = await CreateSampleTransaction();
_ = transaction.SetNonce(123);

Assert.Equal("0x7b", transaction.Input.Nonce.HexValue);
Assert.Equal("123", transaction.Input.Nonce.Value.ToString());
}

[Fact]
public async Task EstimateTotalCosts_CalculatesCostsCorrectly()
{
var transaction = await CreateSampleTransaction();
_ = transaction.SetValue(new BigInteger(1000));
_ = transaction.SetGasLimit(21000);

var costs = await ThirdwebTransaction.EstimateTotalCosts(transaction);

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

[Fact]
public async Task Simulate_ThrowsInsufficientFunds()
{
var transaction = await CreateSampleTransaction();
_ = transaction.SetValue(new BigInteger(1000000000000000000));
_ = transaction.SetGasLimit(21000);

var exception = await Assert.ThrowsAsync<Exception>(() => ThirdwebTransaction.Simulate(transaction));
Assert.Contains("insufficient funds", exception.Message);
}

[Fact]
public async Task Simulate_ReturnsGasEstimate()
{
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.SetValue(new BigInteger(0));
_ = transaction.SetGasLimit(250000);

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

[Fact]
public async Task WaitForTransactionReceipt()
{
Expand Down
37 changes: 29 additions & 8 deletions Thirdweb/Thirdweb.Transactions/ThirdwebTransaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ private ThirdwebTransaction(ThirdwebClient client, IThirdwebWallet wallet, Trans

public static async Task<ThirdwebTransaction> Create(ThirdwebClient client, IThirdwebWallet wallet, TransactionInput txInput, BigInteger chainId)
{
txInput.From ??= await wallet.GetAddress();
return await wallet.GetAddress() != txInput.From
? throw new ArgumentException("Transaction sender (from) must match wallet address")
: client == null
Expand Down Expand Up @@ -86,26 +87,46 @@ public ThirdwebTransaction SetNonce(BigInteger nonce)

public static async Task<GasCosts> EstimateTotalCosts(ThirdwebTransaction transaction)
{
var gasLimit = await EstimateGasLimit(transaction);
var gasPrice = await EstimateGasPrice(transaction._client, transaction.Input.ChainId.Value);
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 GasCosts { ether = gasCostWithValue.ToString().ToEth(18, false), wei = gasCostWithValue };
}

public static async Task<BigInteger> EstimateGasPrice(ThirdwebClient client, BigInteger chainId, bool withBump = true)
public static async Task<BigInteger> EstimateGasPrice(ThirdwebTransaction transaction, bool withBump = true)
{
{
var rpc = ThirdwebRPC.GetRpcInstance(client, chainId);
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> EstimateGasLimit(ThirdwebTransaction transaction)
public static async Task<BigInteger> Simulate(ThirdwebTransaction transaction)
{
return await EstimateGasLimit(transaction, false);
}

public static async Task<BigInteger> EstimateGasLimit(ThirdwebTransaction transaction, bool overrideBalance = true)
{
var rpc = ThirdwebRPC.GetRpcInstance(transaction._client, transaction.Input.ChainId.Value);
var hex = await rpc.SendRequestAsync<string>("eth_estimateGas", transaction.Input);
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;
}

Expand All @@ -117,12 +138,12 @@ public static async Task<string> Send(ThirdwebTransaction transaction)
}

transaction.Input.From ??= await transaction._wallet.GetAddress();
transaction.Input.Gas ??= new HexBigInteger(await EstimateGasLimit(transaction));
transaction.Input.Value ??= new HexBigInteger(0);
transaction.Input.Data ??= "0x";
transaction.Input.GasPrice ??= new HexBigInteger(await EstimateGasPrice(transaction._client, transaction.Input.ChainId.Value));
transaction.Input.GasPrice ??= new HexBigInteger(await EstimateGasPrice(transaction));
transaction.Input.MaxFeePerGas = null;
transaction.Input.MaxPriorityFeePerGas = null;
transaction.Input.Gas ??= new HexBigInteger(await EstimateGasLimit(transaction));

var rpc = ThirdwebRPC.GetRpcInstance(transaction._client, transaction.Input.ChainId.Value);
string hash;
Expand Down

0 comments on commit 07f4eaa

Please sign in to comment.