# `EEVM.Precompiles.ModExp`
[🔗](https://github.com/mw2000/eevm/blob/v0.1.0/lib/eevm/precompiles/modexp.ex#L1)

Modular exponentiation precompile — address `0x05` (EIP-198).

Computes `base^exp mod modulus` over arbitrarily large integers.

### Gas schedule (EIP-198, Byzantium through Istanbul)

`gas = floor(multiplication_complexity * iteration_count / 20)`

where `multiplication_complexity` of `x = max(base_length, mod_length)` is
`x^2` for `x <= 64`, `x^2 / 4 + 96x - 3072` for `x <= 1024`, and
`x^2 / 16 + 480x - 199680` above, and `iteration_count` is computed as for
EIP-2565 below.

### Gas schedule (EIP-2565, Berlin through Prague)

`gas = max(200, floor(multiplication_complexity * iteration_count / 3))`

where:

- `multiplication_complexity = ceil(max(base_length, mod_length) / 8)^2`
  (equivalently `((max(base_length, mod_length) + 7) div 8)^2` in integer
  arithmetic).
- `iteration_count` is derived from the exponent length and the top bits of
  the exponent:
  - if `exp_length <= 32`: `0` when exponent is zero, otherwise the
    0-indexed highest set bit of the exponent.
  - if `exp_length > 32`: `8 * (exp_length - 32) + highest_bit_index(exp[0..31])`
    where the highest bit index of an all-zero 32-byte prefix is treated as 0.
  - the result is floored at `1`.

### Gas schedule (EIP-7883, Osaka onward)

`gas = max(500, multiplication_complexity * iteration_count)`

where:

- `multiplication_complexity` is `16` when `max(base_length, mod_length) <= 32`,
  otherwise `2 * ceil(max(base_length, mod_length) / 8)^2`.
- `iteration_count` is computed as above, except the per-byte multiplier for
  exponents longer than 32 bytes is `16` instead of `8`.

### Input bounds (EIP-7823, Osaka onward)

If `base_length`, `exp_length`, or `mod_length` exceeds 1024 bytes, the call
fails with `{:error, :invalid_input}`, consuming all gas it was given.

`execute/3` picks the gas schedule and bounds from the hardfork it is given.

Gas is charged from the 96-byte header and at most the first 32 exponent
bytes before any operand is read, so oversized declared lengths fail with
out-of-gas instead of allocating.

### Input format

- bytes `0..31`: `base_length` (big-endian uint256)
- bytes `32..63`: `exp_length` (big-endian uint256)
- bytes `64..95`: `mod_length` (big-endian uint256)
- bytes `96..`: concatenated `base || exponent || modulus`

If the input is shorter than required by those lengths, missing bytes are
treated as zero (right-padding behavior).

Output is the big-endian value of `base^exp mod modulus`, left-padded with
zeros to exactly `mod_length` bytes. If `modulus == 0` or `mod_length == 0`,
the precompile returns `mod_length` zero bytes.

# `execute`

```elixir
@spec execute(binary(), non_neg_integer(), EEVM.HardforkConfig.t()) ::
  {:ok, binary(), non_neg_integer()} | {:error, :out_of_gas | :invalid_input}
```

Executes the ModExp precompile with the gas schedule and input bounds of
`hardfork` (default: Cancun).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
