@@ -518,6 +518,73 @@ func TestBALStaticCallTargetIncluded(t *testing.T) {
518518 assertEmpty (t , assertPresent (t , b , target ))
519519}
520520
521+ // makeValueCaller emits a single value-transferring CALL-family op (CALL 0xf1
522+ // or CALLCODE 0xf2) against `target` with value=1, then STOPs. Used together
523+ // with a zero-balance caller to make the value transfer fail CanTransfer.
524+ func makeValueCaller (op byte , target common.Address ) []byte {
525+ code := []byte {
526+ 0x60 , 0x00 , // retSize
527+ 0x60 , 0x00 , // retOff
528+ 0x60 , 0x00 , // argsSize
529+ 0x60 , 0x00 , // argsOff
530+ 0x60 , 0x01 , // value = 1
531+ 0x73 , // PUSH20 target
532+ }
533+ code = append (code , target .Bytes ()... )
534+ return append (code , 0x5a , op , 0x50 , 0x00 ) // GAS, op, POP, STOP
535+ }
536+
537+ // TestBALCallToDelegatedTargetBalanceFail asserts the EIP-7928 rule revised in
538+ // ethereum/EIPs#11838: when a CALL targets an EIP-7702 delegated account and the
539+ // delegated address passes its access_cost gas check, the delegated
540+ // (implementation) address MUST appear in the BAL even when the call then fails
541+ // its sender-balance check, because the delegation is resolved before that
542+ // check. CALL routes through the EIP-8037 gas path.
543+ func TestBALCallToDelegatedTargetBalanceFail (t * testing.T ) {
544+ delegated := common .HexToAddress ("0xde1e9a7ed" ) // EOA carrying a 7702 designator
545+ impl := common .HexToAddress ("0x111111" ) // delegation target (implementation)
546+ caller := common .HexToAddress ("0xca11" ) // zero-balance contract issuing the CALL
547+
548+ env := newBALTestEnv (types.GenesisAlloc {
549+ caller : {Code : makeValueCaller (0xf1 /* CALL */ , delegated ), Balance : common .Big0 },
550+ delegated : {Code : types .AddressToDelegation (impl ), Balance : common .Big0 },
551+ impl : {Code : []byte {0x00 }, Balance : common .Big0 }, // STOP
552+ })
553+
554+ b , _ := env .run (t , func (g * BlockGen ) {
555+ g .AddTx (env .tx (0 , & caller , big .NewInt (0 ), 1_000_000 , 0 , nil ))
556+ })
557+
558+ assertPresent (t , b , caller )
559+ assertPresent (t , b , delegated )
560+ // The call failed its sender-balance check, so the implementation never
561+ // executed: it is recorded with an empty change set, but it MUST be present.
562+ assertEmpty (t , assertPresent (t , b , impl ))
563+ }
564+
565+ // TestBALCallCodeToDelegatedTargetBalanceFail is the CALLCODE analogue of
566+ // TestBALCallToDelegatedTargetBalanceFail, exercising the EIP-7702 gas path
567+ // (CALLCODE/STATICCALL/DELEGATECALL) rather than the EIP-8037 one.
568+ func TestBALCallCodeToDelegatedTargetBalanceFail (t * testing.T ) {
569+ delegated := common .HexToAddress ("0xde1e9a7ed" )
570+ impl := common .HexToAddress ("0x111111" )
571+ caller := common .HexToAddress ("0xca11" )
572+
573+ env := newBALTestEnv (types.GenesisAlloc {
574+ caller : {Code : makeValueCaller (0xf2 /* CALLCODE */ , delegated ), Balance : common .Big0 },
575+ delegated : {Code : types .AddressToDelegation (impl ), Balance : common .Big0 },
576+ impl : {Code : []byte {0x00 }, Balance : common .Big0 }, // STOP
577+ })
578+
579+ b , _ := env .run (t , func (g * BlockGen ) {
580+ g .AddTx (env .tx (0 , & caller , big .NewInt (0 ), 1_000_000 , 0 , nil ))
581+ })
582+
583+ assertPresent (t , b , caller )
584+ assertPresent (t , b , delegated )
585+ assertEmpty (t , assertPresent (t , b , impl ))
586+ }
587+
521588// ============================== Revert behaviour ==============================
522589
523590// TestBALRevertedTxStillIncluded: a tx whose top-level call REVERTs still
0 commit comments