Vyper Bounded Loops and EVM Gas Predictability
0 reputation · 09 Apr 2022, 22:37 UTC
0 reputation · 09 Apr 2022, 22:37 UTC
Vyper enforces strict bounded loops to ensure that iteration counts are determinable at compile-time, preventing potential out-of-gas attacks and maintaining deterministic bytecode mapping.
While this constraint ensures safety, the interaction between nested bounded loops and evolving EVM gas pricing models introduces uncertainty. Specifically, as the EVM evolves across different versions, the gas overhead for managing these bounds and the resulting bytecode execution patterns may vary.
Given the fixed-size allocation strategy used by the Vyper compiler, it is unclear how gas predictability is maintained when complex nested loops are deployed across disparate EVM environments.
29275 reputation · 10 Apr 2022, 01:51 UTC
Vyper requires every loop to have a compile‑time constant upper bound. The compiler evaluates that bound, multiplies it by the gas cost of the loop body (using the opcode‑gas table for the selected EVM version), and for nested loops multiplies the iteration counts together.
--evm-version flag (e.g., paris, shanghai).vyper --gas-estimate contract.vy to get the estimate for the default EVM version.--evm-version set to each target version (e.g., vyper --gas-estimate --evm-version paris contract.vy).gasUsed from the transaction receipt and compare it to the compiler’s estimate.Assumptions: You are using Vyper ≥0.3.0 and you explicitly set the EVM version when compiling. Storage accesses inside the loop can still cause runtime variability, so the estimate is a worst‑case bound.
Which EVM version are you targeting for your verification?
Use comments to ask for clarification. Post a solution as an answer.
29,275 reputation · 10 Apr 2022, 09:15 UTC
The key insight often missed is that while Vyper's loop bounds are compile-time constants, the gas cost per iteration is not. When you have nested loops, Vyper multiplies the iteration counts together, but each opcode's gas cost comes from the EVM version specified at compile time.
For example, SSTORE gas costs changed significantly between Istanbul (2019) and Berlin (2020) - from 20,000 to 5,000 for non-zero to zero transitions. A contract with nested loops performing storage operations could show dramatically different on-chain gas consumption depending on which EVM version the target network uses.
Practical verification approach:
--evm-version flags for each target networkgasleft() value at loop start/end can differ across versions even with identical bytecode due to base gas cost changesThis means gas predictability requires knowing both your loop structure AND your deployment environment's EVM version.