-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
modulate base fee with Anvil over a period of time (#931)
- Loading branch information
Showing
2 changed files
with
127 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package client | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
"time" | ||
|
||
"github.com/rs/zerolog" | ||
) | ||
|
||
// ModulateBaseFeeOverDuration will cause the gas price to rise or drop to a certain percentage of the starting gas price | ||
// over the duration specified. | ||
// Minimum duration is 1 s | ||
// if spike is true, the gas price will rise to the target price | ||
// if spike is false, the gas price will drop to the target price | ||
func (m *RPCClient) ModulateBaseFeeOverDuration(lggr zerolog.Logger, startingBaseFee int64, percentage float64, duration time.Duration, spike bool) error { | ||
if duration < time.Second { | ||
return fmt.Errorf("duration must be at least 1s") | ||
} | ||
// Calculate the target gas price | ||
targetBaseFee := float64(startingBaseFee) * (1 + percentage) | ||
if !spike { | ||
targetBaseFee = float64(startingBaseFee) * (1 - percentage) | ||
} | ||
lggr.Info(). | ||
Int64("Starting Base Fee", startingBaseFee). | ||
Float64("Percentage", percentage). | ||
Dur("Duration", duration). | ||
Int64("Target Base Fee", int64(targetBaseFee)). | ||
Msg("Modulating base fee per gas over duration") | ||
|
||
// Divide the duration into 10 parts and update the gas price every part | ||
intTargetBaseFee := int64(targetBaseFee) | ||
partUpdate := (intTargetBaseFee - startingBaseFee) / 10 | ||
partDuration := duration / 10 | ||
ticker := time.NewTicker(partDuration) | ||
defer ticker.Stop() | ||
baseFeeToUpdate := startingBaseFee | ||
for range ticker.C { | ||
lggr.Info(). | ||
Int64("Base Fee", baseFeeToUpdate). | ||
Int64("Updating By", partUpdate). | ||
Msg("Updating base fee per gas") | ||
baseFeeToUpdate = baseFeeToUpdate + partUpdate | ||
if spike { | ||
if baseFeeToUpdate > intTargetBaseFee { | ||
baseFeeToUpdate = intTargetBaseFee | ||
} | ||
} else { | ||
if baseFeeToUpdate < intTargetBaseFee { | ||
baseFeeToUpdate = intTargetBaseFee | ||
} | ||
} | ||
err := m.AnvilSetNextBlockBaseFeePerGas([]interface{}{strconv.FormatInt(baseFeeToUpdate, 10)}) | ||
if err != nil { | ||
return fmt.Errorf("failed to set base fee %d: %w", baseFeeToUpdate, err) | ||
} | ||
lggr.Info().Int64("NextBlockBaseFeePerGas", baseFeeToUpdate).Msg("Updated base fee per gas") | ||
if baseFeeToUpdate == intTargetBaseFee { | ||
lggr.Info(). | ||
Int64("Base Fee", baseFeeToUpdate). | ||
Msg("Reached target base fee") | ||
return nil | ||
} | ||
} | ||
return fmt.Errorf("failed to reach target base fee %d", intTargetBaseFee) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package client | ||
|
||
import ( | ||
"context" | ||
"math/big" | ||
"testing" | ||
"time" | ||
|
||
"github.com/ethereum/go-ethereum/ethclient" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/smartcontractkit/chainlink-testing-framework/logging" | ||
) | ||
|
||
func TestRPCSuite(t *testing.T) { | ||
t.Run("(anvil) test we can modulate next block base fee per gas over a duration", func(t *testing.T) { | ||
ac, err := StartAnvil([]string{"--balance", "1", "--block-time", "1"}) | ||
require.NoError(t, err) | ||
client, err := ethclient.Dial(ac.URL) | ||
require.NoError(t, err) | ||
printGasPrices(t, client) | ||
// set a base fee | ||
anvilClient := NewRPCClient(ac.URL) | ||
// set fee for the next block | ||
err = anvilClient.AnvilSetNextBlockBaseFeePerGas([]interface{}{"2000000000"}) | ||
require.NoError(t, err) | ||
// mine a block | ||
err = anvilClient.AnvilMine(nil) | ||
require.NoError(t, err) | ||
blockNumber, err := client.BlockNumber(context.Background()) | ||
require.NoError(t, err) | ||
block, err := client.BlockByNumber(context.Background(), big.NewInt(int64(blockNumber))) | ||
require.NoError(t, err) | ||
// check the base fee of the block | ||
require.Equal(t, "2000000000", block.BaseFee().String(), "expected base fee to be 20 gwei") | ||
logger := logging.GetTestLogger(t) | ||
err = anvilClient.ModulateBaseFeeOverDuration(logger, 2000000000, 0.5, 20*time.Second, true) | ||
require.NoError(t, err) | ||
// mine a block | ||
err = anvilClient.AnvilMine(nil) | ||
require.NoError(t, err) | ||
blockNumber, err = client.BlockNumber(context.Background()) | ||
require.NoError(t, err) | ||
block, err = client.BlockByNumber(context.Background(), big.NewInt(int64(blockNumber))) | ||
require.NoError(t, err) | ||
// check the base fee of the block | ||
require.Equal(t, "3000000000", block.BaseFee().String(), "expected base fee to be 30 gwei") | ||
err = anvilClient.ModulateBaseFeeOverDuration(logger, 3000000000, 0.25, 15*time.Second, false) | ||
require.NoError(t, err) | ||
// mine a block | ||
err = anvilClient.AnvilMine(nil) | ||
require.NoError(t, err) | ||
blockNumber, err = client.BlockNumber(context.Background()) | ||
require.NoError(t, err) | ||
block, err = client.BlockByNumber(context.Background(), big.NewInt(int64(blockNumber))) | ||
require.NoError(t, err) | ||
// check the base fee of the block | ||
require.Equal(t, "2250000000", block.BaseFee().String(), "expected base fee to be 30 gwei") | ||
}) | ||
} |