Repository navigation
feat(core,core/vm,core/types,params,internal/ethapi): implement EIP-7708 ETH transfer logs, close #2767 - #2842
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
d34363a to
b9ead49
Compare
66d914e to
181c723
Compare
1ba586e to
0fa4e14
Compare
0fa4e14 to
f58a9ec
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
Consensus-visible receipt changes require protocol-maintainer validation of log ordering, fork activation, and indexer compatibility.
No open issues 🎉
What changed in this PR
Implements Amsterdam-gated EIP-7708 native-value transfer logs in EVM execution and receipts, addressing #2767. It follows the transfer-only design compatible with EIP-8246’s removal of SELFDESTRUCT burns.
Changes:
- Adds the system-log event and constructor, emitting logs for non-zero transfers between distinct addresses.
- Suppresses synthetic transfer logs in
eth_simulateV1after Amsterdam to avoid duplicates. - Adds coverage for fork gating, transfer attribution, reverted execution, creation, SELFDESTRUCT, and simulation.
| File | Description |
|---|---|
tests/vm_test_util.go |
Updates the VM test transfer callback signature. |
params/protocol_params.go |
Defines the transfer event topic. |
internal/ethapi/simulate.go |
Disables synthetic transfer logs after Amsterdam. |
internal/ethapi/logtracer.go |
Documents synthetic and protocol log emitters. |
internal/ethapi/api_test.go |
Tests simulation logs before and after Amsterdam. |
core/vm/interpreter_test.go |
Updates the interpreter test transfer callback. |
core/vm/instructions.go |
Emits Amsterdam SELFDESTRUCT transfer logs. |
core/vm/gas_table_test.go |
Updates gas-test transfer callbacks. |
core/vm/evm.go |
Passes fork rules into CALL and CREATE transfers. |
core/vm/eip8246_test.go |
Updates the SELFDESTRUCT test transfer callback. |
core/vm/contracts_kzg_stub_test.go |
Updates the precompile test transfer callback. |
core/types/log.go |
Adds the EIP-7708 log constructor. |
core/state/statedb_log_test.go |
Tests log rollback and index restoration. |
core/evm.go |
Emits fork-gated logs for value transfers. |
core/eip7708_transfer_logs_test.go |
Adds execution and receipt-log regression coverage. |
Review effort: Balanced
… logs ethereum#33645 EIP-7708 emits a system log for every value transfer from Amsterdam onwards: the transaction value, the value moved by a CALL or CREATE, and the value moved by SELFDESTRUCT. The log uses params.SystemAddress as emitter and the ERC-20 Transfer ABI (Transfer(address,address,uint256) plus a 32-byte amount), so indexers can consume it like a token transfer. The gate sits where the fork rules are available: core.Transfer takes the vm's params.Rules and emits after the balance move, while opSelfdestruct6780 emits when the beneficiary differs. Zero-value transfers and transfers to self emit nothing. No burn log is emitted: EIP-8246 (go-ethereum ethereum#35219) removes the SELFDESTRUCT burn entirely and landed before this change as XinFinOrg#2834, so a self-destruct that names a different beneficiary is the only value move the instruction makes. A self-destruct to self moves nothing and therefore logs nothing, in every fork. Amsterdam is not scheduled on any network (amsterdamBlock is nil), so the window stays dormant until it is. The log constructor takes *big.Int rather than upstream's *uint256.Int because this tree's StateDB moves balances as big.Int. Tests cover the upstream CALL/SELFDESTRUCT scenario through block processing (three transfer logs interleaved with the contract's events), the zero-value and pre-Amsterdam cases, a transfer to self, an inner revert that keeps the caller's own log, an outer revert that discards every log, the delegated-call attribution, and the upstream boundary matrix: DELEGATECALL and CALLCODE (no log), a CREATE2 endowment, a failed CREATE rolling its log back, precompile and system-address recipients, the priority-fee exclusion, and a direct core.Transfer fork-gate case pinning the 32-byte amount. core/state additionally pins that a log is reverted with its snapshot. Ref: ethereum#33645 Ref: ethereum#35617 Closes XinFinOrg#2767
…ethereum#35617 eth_simulateV1 synthesizes an ERC-7528 transfer log for every value move when traceTransfers is set. From Amsterdam the protocol emits the EIP-7708 log for the same transfer, so every transfer was reported twice. Disable the synthetic log for Amsterdam blocks, mirroring upstream ethereum#35617. The tracer still captures the protocol log through the OnLog hook, so traceTransfers keeps reporting exactly one log per transfer. The new test pins both sides of the fork: the synthetic ERC-7528 log before Amsterdam and the single protocol log at params.SystemAddress after it. The tracer comment now says that from Amsterdam onwards the protocol emits these logs itself (EIP-7708) and the synthesis is off, so the documented format only describes the synthesized pre-fork log while protocol logs carry params.SystemAddress as their emitter. Upstream still carries the older comment (zero address, no EIP-7708 note), so this is a local correction with no geth PR to mirror. Ref: ethereum#35617
f58a9ec to
6909a09
Compare
Summary
Implements EIP-7708 system logs from Amsterdam onwards: every value move made by EVM execution emits a
Transfer(address,address,uint256)log fromparams.SystemAddress(0xfffffffffffffffffffffffffffffffffffffffe), covering the transaction value, the value moved by CALL/CREATE, and the value moved by SELFDESTRUCT. Ported from go-ethereum ethereum#33645, including the self-transfer guard from upstream commit012efa11ab.eth_simulateV1synthesizes its own transfer logs; post-Amsterdam that would report every transfer twice. The synthetic log is suppressed for Amsterdam blocks in the same PR (geth ethereum#35617).Changes
params:EthTransferLogEvent.core/types:EthTransferLog.core.Transfer: emits for Amsterdam when the amount is non-zero andsender != recipient. The fork rules are passed from the VM, the only caller.core/vm:TransferFunccarries*params.Rules;opSelfdestruct6780emits for a distinct beneficiary.internal/ethapi:traceTransfersis disabled for Amsterdam blocks inprocessBlock.No burn log is emitted: EIP-8246 (go-ethereum ethereum#35219) removes the SELFDESTRUCT burn entirely and is already on the base branch as #2834, so a SELFDESTRUCT that names a distinct beneficiary is the only value move the instruction makes, and a self-destruct to self logs nothing in every fork. Amsterdam is not scheduled on any network (
amsterdamBlockis nil on mainnet and testnet), so the window stays dormant until it is. Value moved outside EVM execution is out of scope too: XDCx and XDCxlending order matching settles native XDC at block level (miner/worker.go) withstatedb.AddBalance/SubBalance(XDCx/tradingstate/relayer_state.go), never throughcore.Transfer, so those moves emit no transfer log - the same treatment the specification gives withdrawals and fees.Log ABI
params.SystemAddressTransfer(address,address,uint256),from,toThese logs become part of Amsterdam block receipts, so indexers that consume receipts will see them next to user logs.
Verification
make allclean.gofmtclean.golangci-lint --new-from-rev=f5492a96a7: 0 issues.go test ./core/... ./internal/ethapi/... ./params/... ./tests/... ./log/...passes.TestEIP7708PreAmsterdamandTestEIP7708TransferForkGate, removing the self-transfer guard failsTestEIP7708SelfTransfer, removing the SELFDESTRUCTbeneficiaryguard failsTestEIP7708SelfdestructToSelf, breaking the transfer attribution failsTestEIP7708DelegateCallAttribution, and removing the simulate suppression failsTestSimulateV1TransferLogs/post-amsterdam.core/eip7708_test.go: DELEGATECALL and CALLCODE (no log), the delegated-call attribution (a transfer made by delegated code belongs to the executing context), a SELFDESTRUCT to self vs. a distinct beneficiary, an inner revert that keeps the caller's own log, an outer revert that discards every log, every transaction type (legacy, access list, dynamic fee, set code) plus a delegated EOA, CREATE and CREATE2 endowments (including a failed CREATE rolling its log back), precompile, system-address and coinbase recipients, the priority-fee exclusion, and a directcore.Transferfork-gate case pinning the 32-byte amount (core/eip7708_transfer_logs_test.go).Closes #2767