Skip to content

Commit aa0bb3b

Browse files
committed
Implement EIP-5920
1 parent 68f3a1f commit aa0bb3b

File tree

3 files changed

+79
-0
lines changed

3 files changed

+79
-0
lines changed

src/ethereum/osaka/utils/address.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
from ethereum.utils.byte import left_pad_zero_bytes
2323

2424
from ..fork_types import Address
25+
from ..vm.exceptions import InvalidParameter
26+
27+
MAX_ADDRESS = Address(b"\xff" * 20)
2528

2629

2730
def to_address(data: Union[Uint, U256]) -> Address:
@@ -41,6 +44,30 @@ def to_address(data: Union[Uint, U256]) -> Address:
4144
return Address(data.to_be_bytes32()[-20:])
4245

4346

47+
def to_address_without_mask(data: U256) -> Address:
48+
"""
49+
Convert a U256 value to a valid address (20 bytes).
50+
51+
Parameters
52+
----------
53+
data :
54+
The string to be converted to bytes.
55+
56+
Raises
57+
------
58+
InvalidParameter
59+
If `data` is larger than `MAX_ADDRESS`.
60+
61+
Returns
62+
-------
63+
address : `Address`
64+
The obtained address.
65+
"""
66+
if data > U256.from_be_bytes(MAX_ADDRESS):
67+
raise InvalidParameter("Address is too large")
68+
return Address(data.to_be_bytes32()[-20:])
69+
70+
4471
def compute_contract_address(address: Address, nonce: Uint) -> Address:
4572
"""
4673
Computes address of the new account that needs to be created.

src/ethereum/osaka/vm/instructions/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ class Ops(enum.Enum):
210210
DELEGATECALL = 0xF4
211211
CREATE2 = 0xF5
212212
STATICCALL = 0xFA
213+
PAY = 0xFC
213214
REVERT = 0xFD
214215
SELFDESTRUCT = 0xFF
215216

@@ -361,6 +362,7 @@ class Ops(enum.Enum):
361362
Ops.DELEGATECALL: system_instructions.delegatecall,
362363
Ops.SELFDESTRUCT: system_instructions.selfdestruct,
363364
Ops.STATICCALL: system_instructions.staticcall,
365+
Ops.PAY: system_instructions.pay,
364366
Ops.REVERT: system_instructions.revert,
365367
Ops.CREATE2: system_instructions.create2,
366368
}

src/ethereum/osaka/vm/instructions/system.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
compute_contract_address,
3232
compute_create2_contract_address,
3333
to_address,
34+
to_address_without_mask,
3435
)
3536
from ...vm.eoa_delegation import access_delegation
3637
from .. import (
@@ -742,3 +743,52 @@ def revert(evm: Evm) -> None:
742743

743744
# PROGRAM COUNTER
744745
pass
746+
747+
748+
def pay(evm: Evm) -> None:
749+
"""
750+
Transfer ether to an account.
751+
752+
Parameters
753+
----------
754+
evm :
755+
The current EVM frame.
756+
"""
757+
# STACK
758+
to = to_address_without_mask(pop(evm.stack))
759+
value = pop(evm.stack)
760+
761+
# GAS
762+
if to in evm.accessed_addresses:
763+
access_gas_cost = GAS_WARM_ACCESS
764+
else:
765+
evm.accessed_addresses.add(to)
766+
access_gas_cost = GAS_COLD_ACCOUNT_ACCESS
767+
768+
create_gas_cost = (
769+
Uint(0)
770+
if is_account_alive(evm.message.block_env.state, to) or value == 0
771+
else GAS_NEW_ACCOUNT
772+
)
773+
774+
transfer_gas_cost = Uint(0) if value == U256(0) else GAS_CALL_VALUE
775+
776+
charge_gas(evm, access_gas_cost + create_gas_cost + transfer_gas_cost)
777+
778+
# OPERATION
779+
if evm.message.is_static:
780+
raise WriteInStaticContext("Cannot PAY in static context")
781+
782+
try:
783+
move_ether(
784+
evm.message.block_env.state,
785+
evm.message.current_target,
786+
to,
787+
value,
788+
)
789+
push(evm.stack, U256(1))
790+
except AssertionError:
791+
push(evm.stack, U256(0))
792+
793+
# PROGRAM COUNTER
794+
evm.pc += Uint(1)

0 commit comments

Comments
 (0)