diff --git a/internal/pipeline.go b/internal/pipeline.go index 0569198..4252b53 100644 --- a/internal/pipeline.go +++ b/internal/pipeline.go @@ -97,6 +97,8 @@ func (p *Pipeline) Execute(ctx context.Context) error { return err } + simulatedGas := estimatedGas.Amount / int64(p.cfg.Transactions) + // Extract the addresses addresses := make([]crypto.Address, 0, len(accounts[1:])) for _, account := range accounts[1:] { @@ -131,7 +133,7 @@ func (p *Pipeline) Execute(ctx context.Context) error { runKeys, runAccounts, p.cfg.Transactions, - maxGas, + simulatedGas, gasPrice, p.cfg.ChainID, p.cli.EstimateGas, diff --git a/internal/runtime/helper.go b/internal/runtime/helper.go index 8f85e90..9dd9d06 100644 --- a/internal/runtime/helper.go +++ b/internal/runtime/helper.go @@ -16,6 +16,21 @@ const gasBuffer = 10_000 // 10k gas // msgFn defines the transaction message constructor type msgFn func(creator std.Account, index int) std.Msg +// cappedSimulationGas returns the gas value to use for simulation fees. +// It caps the gas to what the account can actually afford, so the +// simulate endpoint's balance check doesn't reject the transaction. +func cappedSimulationGas(account std.Account, maxGas int64, gasPrice std.GasPrice) int64 { + balance := account.GetCoins().AmountOf(common.Denomination) + // maxAffordableGas = balance * gasPrice.Gas / gasPrice.Price.Amount + affordableGas := balance * gasPrice.Gas / gasPrice.Price.Amount + + if affordableGas < maxGas { + return affordableGas + } + + return maxGas +} + // constructTransactions constructs and signs the transactions // using the passed in message generator and signer func constructTransactions( @@ -23,7 +38,7 @@ func constructTransactions( keys []crypto.PrivKey, accounts []std.Account, transactions uint64, - maxGas int64, + simulatedGas int64, gasPrice std.GasPrice, chainID string, getMsg msgFn, @@ -40,10 +55,11 @@ func constructTransactions( fmt.Printf("\n⏳ Estimating Gas ⏳\n") - // Estimate the fee for the transaction batch - // passing in the maximum block gas, this is just a simulation + // Estimate the fee for the transaction batch using the + // simulated gas from the initial estimation, + // so sub-accounts can afford the simulation fee txFee := common.CalculateFeeInRatio( - maxGas, + simulatedGas, gasPrice, ) @@ -85,7 +101,7 @@ func constructTransactions( return nil, fmt.Errorf("unable to sign transaction, %w", err) } - fmt.Printf("\nEstimated Gas for 1 run tx: %d \n", tx.Fee.GasWanted) + fmt.Printf("\nEstimated gas (with buffer) for 1 run tx: %d \n", tx.Fee.GasWanted) fmt.Printf("\n🔨 Constructing Transactions 🔨\n\n") bar := progressbar.Default(int64(transactions), "constructing txs") @@ -146,10 +162,13 @@ func calculateRuntimeCosts( ) (std.Coin, error) { fmt.Printf("\n⏳ Estimating Gas ⏳\n") + // Cap the simulation gas to what the account can afford, + // so the simulate endpoint's balance check doesn't reject the tx + simGas := cappedSimulationGas(account, maxBlockMaxGas, gasPrice) + // Estimate the fee for the transaction batch - // passing in the maximum block gas, this is just a simulation txFee := common.CalculateFeeInRatio( - maxBlockMaxGas, + simGas, gasPrice, ) @@ -170,7 +189,7 @@ func calculateRuntimeCosts( return std.Coin{ Denom: common.Denomination, - Amount: int64(transactions) * estimatedGas, + Amount: int64(transactions) * (estimatedGas + gasBuffer), }, nil } diff --git a/internal/runtime/package_deployment.go b/internal/runtime/package_deployment.go index 76dfd43..2e6c8d9 100644 --- a/internal/runtime/package_deployment.go +++ b/internal/runtime/package_deployment.go @@ -55,7 +55,7 @@ func (c *packageDeployment) ConstructTransactions( keys []crypto.PrivKey, accounts []std.Account, transactions uint64, - maxGas int64, + simulatedGas int64, gasPrice std.GasPrice, chainID string, estimateFn EstimateGasFn, @@ -65,7 +65,7 @@ func (c *packageDeployment) ConstructTransactions( keys, accounts, transactions, - maxGas, + simulatedGas, gasPrice, chainID, c.getMsgFn, diff --git a/internal/runtime/realm_call.go b/internal/runtime/realm_call.go index 0ba005a..5c6feca 100644 --- a/internal/runtime/realm_call.go +++ b/internal/runtime/realm_call.go @@ -61,8 +61,8 @@ func (r *realmCall) Initialize( tx := &std.Tx{ Msgs: []std.Msg{msg}, - // passing in the maximum block gas, this is just a simulation - Fee: common.CalculateFeeInRatio(currentMaxGas, gasPrice), + // Cap the simulation gas to what the account can afford + Fee: common.CalculateFeeInRatio(cappedSimulationGas(account, currentMaxGas, gasPrice), gasPrice), } err := signFn(tx) @@ -113,7 +113,7 @@ func (r *realmCall) ConstructTransactions( keys []crypto.PrivKey, accounts []std.Account, transactions uint64, - maxGas int64, + simulatedGas int64, gasPrice std.GasPrice, chainID string, estimateFn EstimateGasFn, @@ -123,7 +123,7 @@ func (r *realmCall) ConstructTransactions( keys, accounts, transactions, - maxGas, + simulatedGas, gasPrice, chainID, r.getMsgFn, diff --git a/internal/runtime/realm_deployment.go b/internal/runtime/realm_deployment.go index c0f28c0..3058c9c 100644 --- a/internal/runtime/realm_deployment.go +++ b/internal/runtime/realm_deployment.go @@ -84,7 +84,7 @@ func (c *realmDeployment) ConstructTransactions( keys []crypto.PrivKey, accounts []std.Account, transactions uint64, - maxGas int64, + simulatedGas int64, gasPrice std.GasPrice, chainID string, estimateFn EstimateGasFn, @@ -94,7 +94,7 @@ func (c *realmDeployment) ConstructTransactions( keys, accounts, transactions, - maxGas, + simulatedGas, gasPrice, chainID, c.getMsgFn, diff --git a/internal/runtime/runtime.go b/internal/runtime/runtime.go index 8edc01e..92e999e 100644 --- a/internal/runtime/runtime.go +++ b/internal/runtime/runtime.go @@ -51,7 +51,7 @@ type Runtime interface { keys []crypto.PrivKey, accounts []std.Account, transactions uint64, - maxGas int64, + simulatedGas int64, gasPrice std.GasPrice, chainID string, estimateFn EstimateGasFn,