argotorg / argotorg/solidity

Check memory limit before copy dynamic array from storage to memory(gas optimization)

Open
#14,278 0 comments 0 reactions 0 assignees View on GitHub
bug :bug:
Dominant language
C++
Stars
25.7k
Forks
6.2k
Avg merge
2d 19h
Merged PRs (30d)
29

Description

## Description

## Environment

- Compiler version: *
- Target EVM version (as per compiler settings): *
- Framework/IDE (e.g. Truffle or Remix): Visual studio code
- EVM execution environment / backend / blockchain client: *
- Operating system: linux

## Steps to Reproduce

When compiling the following function in Solidity, when the line `uint[] memory ma = a;` is compiled, the operation of copying the value from storage to memory is executed first, and then the memory usage length check is performed. When `uint[] a` is relatively long, it consumes more gas. **To optimize gas usage, it is recommended to perform the memory check based on the array length before the copy operation.**

```
contract TestMemory{
uint[] a;
function b() public{
uint[] memory ma = a;
}
}
```
Here is yul code:
```
function abi_encode_t_array$_t_uint256_$dyn_storage_to_t_array$_t_uint256_$dyn_memory_ptr(value, pos) -> end {
let length := array_length_t_array$_t_uint256_$dyn_storage(value)
pos := array_storeLengthForEncoding_t_array$_t_uint256_$dyn_memory_ptr(pos, length)
let baseRef := array_dataslot_t_array$_t_uint256_$dyn_storage(value)
let srcPtr := baseRef
for { let i := 0 } lt(i, length) { i := add(i, 1) }
{
let elementValue0 := read_from_storage_offset_0_t_uint256(srcPtr)
pos := abi_encodeUpdatedPos_t_uint256_to_t_uint256(elementValue0, pos)
srcPtr := array_nextElement_t_array$_t_uint256_$dyn_storage(srcPtr)
}
end := pos
}

function abi_encodeUpdatedPos_t_array$_t_uint256_$dyn_storage_to_t_array$_t_uint256_$dyn_memory_ptr(value0, pos) -> updatedPos {
updatedPos := abi_encode_t_array$_t_uint256_$dyn_storage_to_t_array$_t_uint256_$dyn_memory_ptr(value0, pos)
}

function finalize_allocation(memPtr, size) {
let newFreePtr := add(memPtr, round_up_to_mul_of_32(size))
// protect against overflow
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() }
mstore(64, newFreePtr)
}

function copy_array_from_storage_to_memory_t_array$_t_uint256_$dyn_storage(slot) -> memPtr {
memPtr := allocate_unbounded()
let end := abi_encodeUpdatedPos_t_array$_t_uint256_$dyn_storage_to_t_array$_t_uint256_$dyn_memory_ptr(slot, memPtr)
finalize_allocation(memPtr, sub(end, memPtr))
}
```

Contributor guide

Open the contributing guide

Research direction

No repository files or tests are named. Start by reproducing the Solidity example and inspecting the generated Yul path for copying a dynamic storage array into memory, especially allocation and finalization. Done means the memory limit is checked from the array length before the storage-to-memory copy, with the gas optimization covered by regression testing.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, solidity
Domain
compilers, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.