diff --git a/framework/rpc/rpc.go b/framework/rpc/rpc.go index 6a7ed52f2..96f58c9e3 100644 --- a/framework/rpc/rpc.go +++ b/framework/rpc/rpc.go @@ -6,7 +6,7 @@ import ( "encoding/json" "fmt" "math/big" - "math/rand" + "math/rand/v2" "net/http" "os" "strconv" @@ -51,6 +51,21 @@ func New(url string, headers http.Header) *RPCClient { } } +// EVMIncreaseTime jumps forward in time by `seconds`. +// The parameter is a JSON number (in seconds) +func (m *RPCClient) EVMIncreaseTime(seconds uint64) error { + payload := map[string]interface{}{ + "jsonrpc": "2.0", + "method": "evm_increaseTime", + "params": []interface{}{seconds}, + "id": rand.Int(), //nolint:gosec + } + if _, err := m.client.R().SetBody(payload).Post(m.URL); err != nil { + return errors.Wrap(err, "evm_increaseTime") + } + return nil +} + // AnvilAutoImpersonate sets auto impersonification to true or false func (m *RPCClient) AnvilAutoImpersonate(b bool) error { //nolint:gosec diff --git a/framework/rpc/rpc_test.go b/framework/rpc/rpc_test.go index 0ee83b873..f10932630 100644 --- a/framework/rpc/rpc_test.go +++ b/framework/rpc/rpc_test.go @@ -243,4 +243,38 @@ func TestRPCAPI(t *testing.T) { require.GreaterOrEqual(t, int64(block.Transactions().Len()), txnInBlock) } }) + + t.Run("(anvil) evm_increaseTime advances timestamp", func(t *testing.T) { + ac, err := StartAnvil([]string{"--no-mine"}) + require.NoError(t, err) + client, err := ethclient.Dial(ac.URL) + require.NoError(t, err) + + anvil := New(ac.URL, nil) + + // Mine an initial block to establish a baseline timestamp + require.NoError(t, anvil.AnvilMine([]interface{}{1})) + + // Read latest header/time + h1, err := client.HeaderByNumber(context.Background(), nil) + require.NoError(t, err) + t1 := h1.Time // uint64 (seconds) + + // Jump forward in time, then mine exactly one block so the new timestamp is materialized + advance := uint64(60) // 1 minute + require.NoError(t, anvil.EVMIncreaseTime(advance)) + require.NoError(t, anvil.AnvilMine([]interface{}{1})) + + // Read new header/time + h2, err := client.HeaderByNumber(context.Background(), nil) + require.NoError(t, err) + t2 := h2.Time + + // Assert the delta is at least the requested advance + // (Anvil should add exactly `advance`, but we allow >= to be safe.) + require.GreaterOrEqual(t, t2-t1, advance, "timestamp did not advance by expected seconds") + + t.Logf("advanced time by %d seconds: %d -> %d (Δ=%d)", advance, t1, t2, t2-t1) + }) + }