Skip to content

Commit

Permalink
modulate base fee with Anvil over a period of time (#931)
Browse files Browse the repository at this point in the history
  • Loading branch information
AnieeG authored Apr 27, 2024
1 parent 6d06777 commit acc490a
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 0 deletions.
67 changes: 67 additions & 0 deletions client/rpc_suite.go
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)
}
60 changes: 60 additions & 0 deletions client/rpc_suite_test.go
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")
})
}

0 comments on commit acc490a

Please sign in to comment.