Skip to content

Commit 0b4fe1a

Browse files
joshklopclaude
andcommitted
feat(op-acceptance-tests): add EIP-7883 modexp gas cost increase test
Add TestEIP7883ModExpGasCostIncrease to verify the modexp precompile gas floor increases from 200 to 500 after the Karst fork. Uses empty calldata (precompile zero-pads missing input) to avoid EIP-7623 calldata cost inflation, allowing precise control of execution gas via the eth_call gas limit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b90ea10 commit 0b4fe1a

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

op-acceptance-tests/tests/osaka_on_l2_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,49 @@ func TestEIP7823UpperBoundModExp(gt *testing.T) {
7373
t.Require().Equal([]byte{3}, result, "2^3 mod 5 should equal 3")
7474
}
7575

76+
func TestEIP7883ModExpGasCostIncrease(gt *testing.T) {
77+
t := devtest.ParallelT(gt)
78+
79+
karstOffset := uint64(3)
80+
sys := presets.NewMinimal(t, presets.WithDeployerOptions(sysgo.WithKarstAtOffset(&karstOffset)))
81+
82+
activationBlock := sys.L2Chain.AwaitActivation(t, forks.Karst)
83+
t.Require().Greater(activationBlock.Number, uint64(0), "karst must not activate at genesis")
84+
preForkBlockNum := activationBlock.Number - 1
85+
postForkBlockNum := activationBlock.Number + 1
86+
sys.L2EL.WaitForBlockNumber(postForkBlockNum)
87+
88+
l2Client := sys.L2EL.EthClient()
89+
90+
// Call modexp with empty calldata. The precompile pads missing bytes with
91+
// zeros, giving Bsize=0, Esize=0, Msize=0. This hits exactly the gas floor:
92+
// EIP-2565 (pre-Karst): max(200, floor(0*0/3)) = 200 gas
93+
// EIP-7883 (post-Karst): max(500, floor(0*0)) = 500 gas
94+
// Empty calldata also avoids EIP-7623 calldata cost inflation, so intrinsic
95+
// gas is just 21,000 and we can precisely control execution gas via Gas limit.
96+
97+
// Pre-fork: 21,000 + 300 execution gas is enough for 200-gas floor.
98+
_, err := l2Client.Call(t.Ctx(), ethereum.CallMsg{
99+
To: &modexpPrecompile,
100+
Gas: 21_300,
101+
}, rpc.BlockNumber(preForkBlockNum))
102+
t.Require().NoError(err, "pre-fork: modexp should succeed with 300 execution gas (floor is 200)")
103+
104+
// Post-fork: 21,000 + 300 execution gas is NOT enough for 500-gas floor.
105+
_, err = l2Client.Call(t.Ctx(), ethereum.CallMsg{
106+
To: &modexpPrecompile,
107+
Gas: 21_300,
108+
}, rpc.BlockNumber(postForkBlockNum))
109+
t.Require().Error(err, "post-fork: modexp should fail with 300 execution gas (floor is 500)")
110+
111+
// Post-fork: 21,000 + 600 execution gas is enough for 500-gas floor.
112+
_, err = l2Client.Call(t.Ctx(), ethereum.CallMsg{
113+
To: &modexpPrecompile,
114+
Gas: 21_600,
115+
}, rpc.BlockNumber(postForkBlockNum))
116+
t.Require().NoError(err, "post-fork: modexp should succeed with 600 execution gas (floor is 500)")
117+
}
118+
76119
func TestEIP7939CLZ(gt *testing.T) {
77120
t := devtest.ParallelT(gt)
78121

0 commit comments

Comments
 (0)