package gas import ( "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/core/types" "github.com/synapsecns/sanguine/core" ) // CompareGas allows for gas comparisons between txes. In the case of an eip-1559 txOpts and a // non-eip 1559 txOpts the eip-1559 txOpts we follow the mempool logic of setting the gasTipCap and the GasFeeCap to the same value // legacy txes do not use effectivegastipcap since base fee is not present in non-eip 1559 chains // This is done because the current mempool logic (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) // // for non bumps (checking confirmations, etc) a nil gas block can be added which will order by tip (according to geth) // // gas block here. // // -1 if x < y // 0 if x == y // +1 if x > y func CompareGas(x *types.Transaction, y *types.Transaction, baseFee *big.Int) int { if x.Type() == types.LegacyTxType && y.Type() == types.LegacyTxType { return x.GasPrice().Cmp(y.GasPrice()) } if baseFee != nil { return x.EffectiveGasTipCmp(y, baseFee) } return x.EffectiveGasTipCmp(y, nil) } // FeeGreaterThan determines if gas for a transaction is greater than threshold. This is used to enforce limits. // DynamicFeeTxes set gas price to FeeTipCap so this will work for both. func FeeGreaterThan(tx *types.Transaction, threshold *big.Int) bool { return tx.GasPrice().Cmp(threshold) > 0 } // IsDynamicTx determines if a transaction is dynamic based on the options passed in. // this follows the same logic as the abi binder in determining what txOpts type to use. // see: https://github.com/ethereum/go-ethereum/blob/b20bc5c0cae6901209610c5f53f01401b6f7974e/accounts/abi/bind/base.go#L310 func IsDynamicTx(opts *bind.TransactOpts) bool { return opts.GasFeeCap != nil } // OptsToComparableTx converts transaction options to a transaction. This should be used for comparisons only. func OptsToComparableTx(opts *bind.TransactOpts) (comparableTx *types.Transaction) { if IsDynamicTx(opts) { comparableTx = types.NewTx(&types.DynamicFeeTx{ GasTipCap: core.CopyBigInt(opts.GasTipCap), GasFeeCap: core.CopyBigInt(opts.GasFeeCap), }) } else { comparableTx = types.NewTx(&types.LegacyTx{ GasPrice: core.CopyBigInt(opts.GasPrice), }) } return comparableTx } // BumpGasFees bumps the gas price by percent increase. In the case of legacy txes this bumps the gas price // in the case of fees, this bumps both the tip and fee cap by percent. If the fee cap exceeds the percent bump // but the fee cap doesn't and the fee cap is still below the tip cap the new fee cap is used without bumping the tip cap. func BumpGasFees(opts *bind.TransactOpts, percentIncrease int, baseFee *big.Int, maxPrice *big.Int) { if IsDynamicTx(opts) { bumpDynamicTxFees(opts, percentIncrease, baseFee, maxPrice) } else { bumpLegacyTxFees(opts, percentIncrease, maxPrice) } } // bumpLegacyTxFees bumps a legacy txes fee. func bumpLegacyTxFees(opts *bind.TransactOpts, percentIncrease int, maxPrice *big.Int) { // bump the gas price by percent increase newPrice := BumpByPercent(opts.GasPrice, percentIncrease) // if the current price exceeds max price skip if newPrice.Cmp(maxPrice) > 0 { logger.Warnf("gas price %s exceeds max price %s, not bumping", newPrice, maxPrice) return } // otherwise set it opts.GasPrice = newPrice } // bumpDynamicTxFees bumps a dynamicFeeTx fee. func bumpDynamicTxFees(opts *bind.TransactOpts, percentIncrease int, baseFee, maxPrice *big.Int) { newTipCap := BumpByPercent(opts.GasTipCap, percentIncrease) newFeeCap := BumpByPercent(opts.GasFeeCap, percentIncrease) if maxPrice.Cmp(newFeeCap) > 0 { opts.GasFeeCap = newFeeCap } else { opts.GasFeeCap = maxPrice logger.Warnf("new fee cap %s exceeds max price %s, using max price", newFeeCap, maxPrice) } // if new fee cap less than tip cap AND base (fee + fee cap) > tip cap if newFeeCap.Cmp(newTipCap) > 0 && big.NewInt(0).Sub(newFeeCap, baseFee).Cmp(newTipCap) > 0 { opts.GasTipCap = newTipCap } else { logger.Warnf("new tip cap %s still less than fee cap %s + base fee %s, bumping tip not and not fee", newTipCap, newFeeCap, baseFee) } } // BumpByPercent bumps a gas price by a percentage. func BumpByPercent(gasPrice *big.Int, percentIncrease int) *big.Int { if gasPrice == nil { return nil } price := core.CopyBigInt(gasPrice) calculatedGasPrice := big.NewFloat(0).Mul(big.NewFloat(1+0.01*float64(percentIncrease)), big.NewFloat(0).SetInt(price)) price, _ = calculatedGasPrice.Int(price) return price } // min gets the minimum of two big ints. func min(a, b *big.Int) *big.Int { if a.Cmp(b) > 0 { return b } return a }