Environment
This information is requested a System Contract called SystemContext.
On how the contract is called, see the relevant section.
ADDRESS
Original EVM instruction.
This value is fetched with a native EraVM instruction: context.this
.
BALANCE
Original EVM instruction.
ORIGIN
Original EVM instruction.
CALLER
Original EVM instruction.
This value is fetched with a native EraVM instruction: context.caller
.
CALLVALUE
Original EVM instruction.
This value is fetched with a native EraVM instruction: context.get_context_u128
.
CALLDATALOAD
Original EVM instruction.
Calldata is accessed using a generic memory access instruction, but the memory chunk itself references the caller’s heap. A “fat pointer” to the parent contract is passed via ABI through registers.
LLVM IR
@ptr_calldata = private unnamed_addr global ptr addrspace(3) null ; global variable declaration
...
store ptr addrspace(3) %0, ptr @ptr_calldata, align 32 ; saving the pointer from `r1` to the global variable
...
%calldata_pointer = load ptr addrspace(3), ptr @ptr_calldata, align 32 ; loading the pointer from the global variable to `calldata_pointer`
%calldata_value = load i256, ptr addrspace(3) %calldata_pointer, align 32 ; loading the value from the calldata pointer
EraVM Assembly
ptr.add r1, r0, stack[@ptr_calldata] ; saving the pointer from `r1` to the global variable
...
ptr.add stack[@ptr_calldata], r0, r1 ; loading the pointer from the global variable to `r1`
ld r1, r1 ; loading the value to `r1`
CALLDATASIZE
Original EVM instruction.
The calldata size is stored in the fat pointer from the parent contract (see CALLDATALOAD), and it can be extracted using bitwise operations, as demonstrated below.
LLVM IR
@calldatasize = private unnamed_addr global i256 0 ; global variable declaration
...
%abi_pointer_value = ptrtoint ptr addrspace(3) %0 to i256 ; converting the pointer to an integer
%abi_pointer_value_shifted = lshr i256 %abi_pointer_value, 96 ; shifting the integer right 96 bits
%abi_length_value = and i256 %abi_pointer_value_shifted, 4294967295 ; keeping the lowest 32 bits of the integer
store i256 %abi_length_value, ptr @calldatasize, align 32 ; saving the value to the global variable
EraVM Assembly
ptr.add r1, r0, stack[@ptr_calldata] ; saving the pointer from `r1` to the global variable
shr.s 96, r1, r1 ; shifting the integer right 96 bits
and @CPI0_0[0], r1, stack[@calldatasize] ; keeping the lowest 32 bits of the integer, saving the value to the global variable
...
CPI0_0:
.cell 4294967295
CALLDATACOPY
Original EVM instruction.
Unlike on EVM, EraVM employs a simple loop over memory operations on 256-bit values.
LLVM IR
; loading the pointer from the global variable to `calldata_pointer`
%calldata_pointer = load ptr addrspace(3), ptr @ptr_calldata, align 32
; shifting the pointer by 122 bytes
%calldata_source_pointer = getelementptr i8, ptr addrspace(3) %calldata_pointer, i256 122
; copying 64 bytes from calldata at offset 122 to the heap at offset 128
call void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) align 1 inttoptr (i256 128 to ptr addrspace(1)), ptr addrspace(3) align 1 %calldata_source_pointer, i256 64, i1 false)
EraVM Assembly
.BB0_3:
shl.s 5, r2, r3 ; shifting the offset by 32
ptr.add r1, r3, r4 ; adding the offset to the calldata pointer
ld r4, r4 ; reading the calldata value
add 128, r3, r3 ; adding the offset to the heap pointer
st.1 r3, r4 ; writing the calldata value to the heap
add 1, r2, r2 ; incrementing the offset
sub.s! 2, r2, r3 ; checking the bounds
jump.lt @.BB0_3 ; loop continuation branching
CODECOPY
Original EVM instruction.
See the EraVM docs.
CODESIZE
Original EVM instruction.
See the EraVM docs.
GASPRICE
Original EVM instruction.
EXTCODESIZE
Original EVM instruction.
EXTCODECOPY
Original EVM instruction.
Not supported. Triggers a compile-time error.
RETURNDATASIZE
Original EVM instruction.
Similarly to CALLDATASIZE, return data size is read from the fat pointer that the child contract returns. It can also be extracted with bitwise operations.
LLVM IR
%contract_call_external = tail call { ptr addrspace(3), i1 } @__farcall(i256 0, i256 0, i256 undef, i256 undef, i256 undef, i256 undef, i256 undef, i256 undef, i256 undef, i256 undef, i256 undef, i256 undef)
%contract_call_external_result_abi_data = extractvalue { ptr addrspace(3), i1 } %contract_call_external, 0
%contract_call_memcpy_from_child_pointer_casted = ptrtoint ptr addrspace(3) %contract_call_external_result_abi_data to i256
%contract_call_memcpy_from_child_return_data_size_shifted = lshr i256 %contract_call_memcpy_from_child_pointer_casted, 96
%contract_call_memcpy_from_child_return_data_size_truncated = and i256 %contract_call_memcpy_from_child_return_data_size_shifted, 4294967295
EraVM Assembly
near_call r0, @__farcall, @DEFAULT_UNWIND ; calling a child contract
shr.s 96, r1, r1 ; shifting the pointer value right 96 bits
and @CPI0_1[0], r1, r1 ; keeping the lowest 32 bits of the pointer value
...
CPI0_1:
.cell 4294967295
RETURNDATACOPY
Original EVM instruction.
Unlike on EVM, EraVM employs a simple loop over memory operations on 256-bit values.
LLVM IR
; loading the pointer from the global variable to `return_data_pointer`
%return_data_pointer = load ptr addrspace(3), ptr @ptr_return_data, align 32
; shifting the pointer by 122 bytes
%return_data_source_pointer = getelementptr i8, ptr addrspace(3) %return_data_pointer, i256 122
; copying 64 bytes from return data at offset 122 to the heap at offset 128
call void @llvm.memcpy.p1.p3.i256(ptr addrspace(1) align 1 inttoptr (i256 128 to ptr addrspace(1)), ptr addrspace(3) align 1 %return_data_source_pointer, i256 64, i1 false)
EraVM Assembly
.BB0_3:
shl.s 5, r2, r3 ; shifting the offset by 32
ptr.add r1, r3, r4 ; adding the offset to the return data pointer
ld r4, r4 ; reading the return data value
add 128, r3, r3 ; adding the offset to the heap pointer
st.1 r3, r4 ; writing the return data value to the heap
add 1, r2, r2 ; incrementing the offset
sub.s! 2, r2, r3 ; checking the bounds
jump.lt @.BB0_3 ; loop continuation branching
EXTCODEHASH
Original EVM instruction.