diff --git a/.gitignore b/.gitignore index c00a9f0f..482036d1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ # vim editor *.swp + +# macOS +.DS_Store diff --git a/ACPs/224-dynamic-gas-limit-in-subnet-evm/README.md b/ACPs/224-dynamic-gas-limit-in-subnet-evm/README.md index 7caf2faa..fcfbf007 100644 --- a/ACPs/224-dynamic-gas-limit-in-subnet-evm/README.md +++ b/ACPs/224-dynamic-gas-limit-in-subnet-evm/README.md @@ -2,7 +2,7 @@ | :--- | :--- | | **Title** | Introduce ACP-176-Based Dynamic Gas Limits and Fee Manager Precompile in Subnet-EVM | | **Author(s)** | Ceyhun Onur ([@ceyonur](https://github.com/ceyonur)), Michael Kaplan ([@michaelkaplan13](https://github.com/michaelkaplan13)) | -| **Status** | Proposed ([Discussion](https://github.com/avalanche-foundation/ACPs/discussions/230)) | +| **Status** | Implementable ([Discussion](https://github.com/avalanche-foundation/ACPs/discussions/230)) | | **Track** | Standards | ## Abstract @@ -31,8 +31,8 @@ This ACP uses the same parameters as in the [ACP-176 specification](https://gith | $P$ | minimum target gas consumption per second | 1,000,000 | | $D$ | target gas consumption rate update constant | 2^25 | | $Q$ | target gas consumption rate update factor change limit | 2^15 | -| $M$ | minimum gas price | 1×10^-18 AVAX | -| $K$ | initial gas price update factor | 87*T | +| $M$ | minimum gas price | 1 Wei (10^-18 AVAX) | +| $K$ | gas price update constant ($KMult * T$) | 87*T | ### Prior Subnet-EVM Fee Configuration Parameters @@ -70,13 +70,11 @@ ACP-176 will make `GasLimit` and `BaseFeeChangeDenominator` configurations obsol `TargetBlockRate`, `MinBlockGasCost`, `MaxBlockGasCost`, and `BlockGasCostStep` will be also removed by [ACP-226](https://github.com/avalanche-foundation/ACPs/tree/ce51dfab/ACPs/226-dynamic-minimum-block-times). -`MinGasPrice` is equivalent to `M` in ACP-176 and will be used to set the minimum gas price for ACP-176. This is similar to `MinBaseFee` in old Subnet-EVM fee configuration, and roughly gives the same effect. Currently the default value is `25 * 10^-18` (25 nAVAX/Gwei). This default will be changed to the minimum possible denomination of the native EVM asset (1 Wei), which is aligned with the C-Chain. +`MinGasPrice` is equivalent to `M` in ACP-176 and will be used to set the minimum gas price for ACP-176. This is similar to `MinBaseFee` in old Subnet-EVM fee configuration, and roughly gives the same effect. Currently the default value is `25 * 10^-9 AVAX` (25 nAVAX / 25 Gwei). This default will be changed to the minimum possible denomination of the native EVM asset (1 Wei), which is aligned with the C-Chain. `TargetGas` is equivalent to `T` (target gas consumed per second) in ACP-176 and will be used to set the target gas consumed per second for ACP-176. -`MaxCapacityFactor` is equivalent to the factor in `C` in ACP-176 and controls the maximum gas capacity (i.e. block gas limit). This determines the `C` as `C = MaxCapacityFactor * T`. The default value will be 10, which is aligned with the C-Chain. - -`TimeToDouble` will be used to control the speed of the fee adjustment (`K`). This determines the `K` as `K = (RMult * TimeToDouble) / ln(2)`, where `RMult` is the factor in `R` which is defined as 2. The default value for `TimeToDouble` will be 60 (seconds), making `K=~87*T`, which is aligned with the C-Chain. +`TimeToDouble` will be used to control the speed of the fee adjustment. In ACP-176, the gas price update constant $K$ is defined as $K = KMult \cdot T$, where $T$ is the target gas per second and $KMult$ is a multiplier. The `TimeToDouble` parameter configures $KMult$ directly via the relationship $KMult = \frac{TimeToDouble}{ln(2)}$. The default value for `TimeToDouble` is 60 seconds, yielding $KMult = \frac{60}{ln(2)} \approx 87$, which is aligned with the C-Chain (where $K = 87 \cdot T$). At sustained maximum capacity ($2T$ gas/second), this results in the gas price doubling approximately every 60 seconds. As a result parameters will be set as follows: @@ -84,39 +82,46 @@ As a result parameters will be set as follows: | :--- | :--- | :--- | :--- | | $T$ | target gas consumed per second | 1,000,000 | :white_check_mark: | | $R$ | gas capacity added per second | 2*T | :x: -| $C$ | maximum gas capacity | 10*T | :white_check_mark: Through `MaxCapacityFactor` (default 10) +| $C$ | maximum gas capacity | 10*T | :x: | $P$ | minimum target gas consumption per second | 1,000,000 | :x: | $D$ | target gas consumption rate update constant | 2^25 | :x: | $Q$ | target gas consumption rate update factor change limit | 2^15 | :x: | $M$ | minimum gas price | 1 Wei | :white_check_mark: -| $K$ | gas price update constant | ~87*T | :white_check_mark: Through `TimeToDouble` (default 60s) +| $K$ | gas price update constant ($KMult \cdot T$) | ~87*T | :white_check_mark: Through `TimeToDouble` (default 60s equivalent to $KMult \approx 87$)$ -The gas capacity added per second (`R`) always being equal to `2*T` keeps it such that the gas price is capable of increases and decrease at the same rate. The values of `Q` and `D` affect the magnitude of change to `T` that each block can have, and the granularity at which the target gas consumption rate can be updated. The proposed values match the C-Chain, allowing each block to modify the current gas target by roughly $\frac{1}{1024}$ of its current value. This has provided sufficient responsiveness and granularity as is, removing the need to make `D` and `Q` dynamic or configurable. Similarly, 1,000,000 gas/second should be a low enough minimum target gas consumption for any EVM L1. The target gas for a given L1 will be able to be increased from this value dynamically and has no maximum. +The gas capacity added per second (`R`) always being equal to `2*T` keeps it such that the gas price is capable of increase and decrease at the same rate. The values of `Q` and `D` affect the magnitude of change to `T` that each block can have, and the granularity at which the target gas consumption rate can be updated. The proposed values match the C-Chain, allowing each block to modify the current gas target by roughly $\frac{1}{1024}$ of its current value. This has provided sufficient responsiveness and granularity as is, removing the need to make `D` and `Q` dynamic or configurable. Similarly, 1,000,000 gas/second should be a low enough minimum target gas consumption for any EVM L1. The target gas for a given L1 will be able to be increased from this value dynamically and has no maximum. -### Genesis Configuration +#### Max Capacity Factor (C) Design Rationale -There will be a new genesis chain configuration to set the parameters for the chain without requiring the ACP224FeeManager precompile to be activated. This will be similar to the existing fee configuration parameters in chain configuration. If there is no genesis configuration for the new fee parameters the default values for the C-Chain will be used. This will look like the following: +The maximum gas capacity (`C`) is intentionally not configurable for L1s. [ACP-194 (SAE)](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/194-streaming-asynchronous-execution#block-size) defines the max gas capacity (i.e., max block size/block gas limit) as $2 \cdot T \cdot \tau \cdot \lambda$, where $\tau$ is the constant delay and $\lambda$ is the inverse of the minimum percentage of the gas limit charged. This definition ensures that the transaction queue can always be fully saturated. This means that the max capacity of the C-Chain will actually double upon ACP-194 activation, since it is currently $2 \cdot T \cdot 5$ and it will become $2 \cdot T \cdot 5 \cdot 2$. -```json -{ - ... - "acp224Timestamp": uint64 - "acp224FeeConfig": { - "minGasPrice": uint64 - "maxCapacityFactor": uint64 - "timeToDouble": uint64 - } -} +The original motivation to make this configurable was to allow for very high maximum gas usage by a single block, primarily to support large contract deployments. Given that SAE will be activated at the same time as ACP-224, the max capacity of the C-Chain will double upon activation, further reducing the need for configurability. Additionally Ethereum's Fusaka upgrade introduces a maximum transaction gas limit of $2^{24}$ (~16.7M), which makes this concern largely moot. -``` +Given these considerations, `C` was changed to not be parametrizable for L1s because: + +1. SAE provides clear rationale for the max capacity value (ensuring the transaction queue can always be fully saturated). +2. The future maximum transaction gas limit of 16.7M makes large contract deployments less of a concern. +3. There are very limited benefits to allowing `C` to be higher than the SAE-defined value in the future. +4. It's still a function of `T`, so it can be adjusted dynamically via the `ACP224FeeManagerPrecompile` if needed. ### Dynamic Gas Target Via Validator Preference For L1s that want their gas target to be dynamically adjusted based on the preferences of their validator sets, the same mechanism introduced on the C-Chain in ACP-176 will be employed. Validators will be able to set their `gas-target` preference in their node's configuration, and block builders can then adjust the target excess in blocks that they propose based on their preference. -### Dynamic Gas Target & Fee Configuration Via `ACP224FeeManagerPrecompile` +Validator target gas preferences are active in the following cases: + +1. **Precompile not activated**: When the `ACP224FeeManagerPrecompile` is not activated at all, validators can control `targetGas` by using the `gas-target` preference in their node's configuration. If a validator does not set a `gas-target` preference, the parent block's gas target is maintained. +2. **`validatorTargetGas` enabled**: When the precompile is activated and `validatorTargetGas` is set to `true` (either via `initialFeeConfig` or by an admin calling `setFeeConfig` with `validatorTargetGas: true`), the precompile's stored `targetGas` is **not used for block building**. Validators adjust `targetExcess` from the parent block's current value within ACP-176's bounded update limits. If a validator does not set a `gas-target` preference, the parent block's gas target is maintained. + +When `validatorTargetGas` is `false` (the default), the precompile's stored `targetGas` value is authoritative and validator preferences for gas target are ignored. + +### ACP224FeeManagerPrecompile + +#### Solidity Interface + +The `ACP224FeeManagerPrecompile` provides an on-chain interface for managing fee parameters dynamically. The `FeeConfig` struct contains all configuration parameters, including both numeric values and mode flags (`staticPricing`, `validatorTargetGas`). All changes are made through a single `setFeeConfig` call, keeping the interface minimal and straightforward. This design ensures configurations are applied atomically and consistently. -For L1s that want an "admin" account to be able to dynamically configuration their gas target and other fee parameters, a new optional `ACP224FeeManagerPrecompile` will be introduced and can be activated. The precompile will offer similar controls as the existing `FeeManagerPrecompile` implemented in Subnet-EVM [here](https://github.com/ava-labs/subnet-evm/tree/53f5305/precompile/contracts/feemanager). The solidity interface will be as follows: +The precompile offers similar controls as the existing `FeeManagerPrecompile` implemented in Subnet-EVM [here](https://github.com/ava-labs/subnet-evm/tree/53f5305/precompile/contracts/feemanager). The solidity interface is as follows: ```solidity //SPDX-License-Identifier: MIT @@ -128,11 +133,14 @@ import "./IAllowList.sol"; /// @dev Inherits from IAllowList for access control interface IACP224FeeManager is IAllowList { /// @notice Configuration parameters for the dynamic fee mechanism + /// @dev Fields are ordered so each mode flag precedes the parameter(s) it governs, + /// reducing the risk of mis-ordering arguments. struct FeeConfig { - uint256 targetGas; // Target gas consumption per second - uint256 minGasPrice; // Minimum gas price in wei - uint256 maxCapacityFactor; // Maximum capacity factor (C = factor * T) - uint256 timeToDouble; // Time in seconds for gas price to double at max capacity + bool validatorTargetGas; // When true, validators control targetGas via node preferences + uint64 targetGas; // Target gas consumption per second (T) + bool staticPricing; // When true, gas price is always minGasPrice + uint64 minGasPrice; // Minimum gas price in wei (M) + uint64 timeToDouble; // Seconds for gas price to double at max capacity } /// @notice Emitted when fee configuration is updated @@ -154,36 +162,192 @@ interface IACP224FeeManager is IAllowList { function getFeeConfigLastChangedAt() external view returns (uint256 blockNumber); } ``` -For chains with the precompile activated, `setFeeConfig` can be used to dynamically change each of the values in the fee configurations. Importantly, any updates made via calls to `setFeeConfig` in a transaction will take effect only as of _settlement_ of the transaction, not as of _acceptance_ or _execution_ (for transaction life cycles/status, refer to ACP-194 [here](https://github.com/avalanche-foundation/ACPs/tree/61d2a2a/ACPs/194-streaming-asynchronous-execution#description)). This ensures that all nodes apply the same worst-case bounds validation on transactions being accepted into the queue, since the worst-case bounds are affected by changes to the fee configuration. -In addition to storing the latest fee configuration to be returned by `getFeeConfig`, the precompile will also maintain state storing the latest values of $q$ and $K$. These values can be derived from the `targetGas` and `timeToDouble` values given to the precompile, respectively. The value of $q$ can be deterministically calculated using the same method as Coreth currently employs to calculate a node's desired target excess [here](https://github.com/ava-labs/coreth/blob/b4c8300490afb7f234df704fdcc446f227e4ec2f/plugin/evm/upgrade/acp176/acp176.go#L170). Similarly, the value of $K$ could be computed directly according to: +For chains with the precompile activated, `setFeeConfig` can be used to dynamically change all fee configuration parameters, including both numeric values and mode flags. Importantly, any updates made via calls to `setFeeConfig` in a transaction will take effect only as of _settlement_ of the transaction, not as of _acceptance_ or _execution_ (for transaction life cycles/status, refer to ACP-194 [here](https://github.com/avalanche-foundation/ACPs/tree/61d2a2a/ACPs/194-streaming-asynchronous-execution#description)). This ensures that all nodes apply the same worst-case bounds validation on transactions being accepted into the queue, since the worst-case bounds are affected by changes to the fee configuration. -$$ K = \frac{targetGas \cdot timeToDouble}{ln(2)} $$ +#### FeeConfig Fields -However, floating point math may introduce inaccuracies. Instead, a similar approach will be employed using binary search to determine the closest integer solution for $K$. +The `FeeConfig` struct fields are shared by both `setFeeConfig` and `initialFeeConfig`: + +- `validatorTargetGas` (bool): When `true`, validators control `targetGas` dynamically via their node preferences, using the same mechanism as the C-Chain (see [Dynamic Gas Target Via Validator Preference](#dynamic-gas-target-via-validator-preference)). The stored `targetGas` value is not used for block building; validators always adjust from the parent block's `targetExcess`. When `true`, `targetGas` **must** be `0`. +- `targetGas` (uint64): Target gas consumption per second ($T$). When `validatorTargetGas` is `false`, must be at least 1,000,000 (the minimum target gas consumption $P$). When `validatorTargetGas` is `true`, must be `0` meaning validators determine the gas target from the parent block's state. +- `staticPricing` (bool): When `true`, the gas price is always `minGasPrice`, bypassing the ACP-176 dynamic fee mechanism entirely. When `staticPricing` is `true`, `timeToDouble` **must** be `0`. +- `minGasPrice` (uint64): Minimum gas price in wei ($M$). Must be greater than `0`. When `staticPricing` is `true`, this is the fixed gas price. +- `timeToDouble` (uint64): Seconds for the gas price to double when the chain is at maximum capacity. Determines $K$ as described in [ACP-176 Parameters in Subnet-EVM](#acp-176-parameters-in-subnet-evm). Must be greater than `0` when `staticPricing` is `false`. When `staticPricing` is `true`, must be `0`. + +**Toggling behavior:** + +- **Enabling** (setting `validatorTargetGas` to `true` via `setFeeConfig`): `targetGas` must be `0`. Validators begin adjusting `targetExcess` from the parent block's current value. Since ACP-176's bounded update mechanism limits changes to roughly $\frac{1}{1024}$ of the current value per block, the transition is always gradual. The parent block's `targetExcess` (which was set by the precompile) provides an unambiguous starting point. +- **Disabling** (setting `validatorTargetGas` to `false` via `setFeeConfig`): The `targetGas` value provided in the same `setFeeConfig` call (must be >= 1,000,000) is immediately authoritative for block building upon settlement. The caller should check the current effective gas target (from block headers or RPC) and provide an appropriate `targetGas` value, since validators may have drifted the effective gas target far from the previously stored value and using a stale value could cause a dangerous sudden jump in gas capacity. + +#### Initial Fee Configuration + +All fee configuration for ACP-224 is done through the `ACP224FeeManagerPrecompile`. There is no separate genesis chain configuration for the new fee parameters. If the precompile is not activated at all, or no `initialFeeConfig` is provided, default values aligned with the C-Chain are used. + +The precompile can be activated with an `initialFeeConfig` to set the initial fee parameters at the activation timestamp. This follows the established Subnet-EVM pattern for [initial precompile configurations](https://build.avax.network/docs/avalanche-l1s/evm-configuration/customize-avalanche-l1#initial-precompile-configurations). If no admin, manager, or enabled addresses are provided, the precompile becomes **read-only**: `getFeeConfig` and `getFeeConfigLastChangedAt` remain callable, but `setFeeConfig` reverts. In this case the precompile has to be disabled and re-enabled with the desired admin, manager, or enabled addresses to change the configuration. + +When `initialFeeConfig` is present, all three uint fields (`targetGas`, `minGasPrice`, `timeToDouble`) are **required**. This prevents silent misconfiguration from typos (e.g., a misspelled `"minGasprice"` would otherwise silently fall back to a default, potentially making the chain near-free to spam). Boolean mode flags (`staticPricing`, `validatorTargetGas`) default to `false` when omitted. + +The precompile configuration will look like the following: + +```json +{ + "acp224FeeManagerConfig": { + "blockTimestamp": , + "adminAddresses": ["0x..."], + "managerAddresses": ["0x..."], + "enabledAddresses": ["0x..."], + "initialFeeConfig": { + "validatorTargetGas": true, + "targetGas": 0, + "staticPricing": false, + "minGasPrice": 25000000000, + "timeToDouble": 60 + } + } +} +``` + +#### Example Configurations + +**Custom fees, fully locked (read-only precompile, dynamic pricing):** + +```json +{ + "acp224FeeManagerConfig": { + "blockTimestamp": , + "initialFeeConfig": { + "targetGas": 5000000, + "minGasPrice": 25000000000, + "timeToDouble": 60 + } + } +} +``` + +No admin addresses means the precompile is read-only. All boolean flags default to `false`: dynamic pricing is active, and the precompile controls `targetGas`. + +**Validators control target gas, fee parameters locked:** + +```json +{ + "acp224FeeManagerConfig": { + "blockTimestamp": , + "initialFeeConfig": { + "validatorTargetGas": true, + "targetGas": 0, + "minGasPrice": 25000000000, + "timeToDouble": 60 + } + } +} +``` + +Read-only precompile. Validators adjust gas target dynamically via their node preferences. `targetGas` is `0` because `validatorTargetGas` is `true`. `minGasPrice` and `timeToDouble` are locked. + +**Static pricing, fully locked:** + +```json +{ + "acp224FeeManagerConfig": { + "blockTimestamp": , + "initialFeeConfig": { + "targetGas": 15000000, + "staticPricing": true, + "minGasPrice": 25000000000, + "timeToDouble": 0 + } + } +} +``` + +Gas price is always 25 gwei regardless of demand. `timeToDouble` is `0` because `staticPricing` is `true`. `targetGas` still governs block gas capacity (15,000,000 gas/second). + +**Static pricing with validator-controlled target gas:** + +```json +{ + "acp224FeeManagerConfig": { + "blockTimestamp": , + "initialFeeConfig": { + "validatorTargetGas": true, + "targetGas": 0, + "staticPricing": true, + "minGasPrice": 1000000000, + "timeToDouble": 0 + } + } +} +``` + +Flat 1 gwei gas price. Validators adjust throughput dynamically. `targetGas` and `timeToDouble` are both `0` because `validatorTargetGas` and `staticPricing` are `true`, respectively. Read-only precompile. + +**Admin controls pricing, validators control target gas:** + +```json +{ + "acp224FeeManagerConfig": { + "blockTimestamp": , + "adminAddresses": ["0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC"], + "initialFeeConfig": { + "validatorTargetGas": true, + "targetGas": 0, + "minGasPrice": 25000000000, + "timeToDouble": 60 + } + } +} +``` + +Admin controls all fee parameters via `setFeeConfig`. Validators control target gas (`targetGas` is `0` because `validatorTargetGas` is `true`). Admin can set `validatorTargetGas` to `false` via `setFeeConfig` to take back control of `targetGas` (must then provide a `targetGas` >= 1,000,000). + +#### Internal State + +In addition to storing the latest fee configuration to be returned by `getFeeConfig`, the precompile will also maintain state storing the latest values of $q$ (the target excess) and $KMult$. These values can be derived from the `targetGas` and `timeToDouble` values given to the precompile, respectively. The value of $q$ can be deterministically calculated using the same method as Coreth currently employs to calculate a node's desired target excess [here](https://github.com/ava-labs/coreth/blob/b4c8300490afb7f234df704fdcc446f227e4ec2f/plugin/evm/upgrade/acp176/acp176.go#L170). Similarly, $KMult$ is approximated directly from `timeToDouble` according to: + +$$KMult = \frac{TimeToDouble}{ln(2)}$$ +where $ln(2) \approx 0.69$ + +The resulting ACP-176 gas price update constant is then $K = KMult \cdot T$. Note that `timeToDouble` is the user-facing configuration parameter, while $KMult$ is the internally derived multiplier. When $T$ changes (via `setFeeConfig` or validator preferences), $K$ adjusts proportionally since $KMult$ remains fixed. Similar to the [desired target excess calculation in Coreth](https://github.com/ava-labs/coreth/blob/0255516f25964cf4a15668946f28b12935a50e0c/plugin/evm/upgrade/acp176/acp176.go#L170), which takes a node's desired gas target and calculates its desired target excess value, the `ACP224FeeManagerPrecompile` will use binary search to determine the resulting dynamic target excess value given the `targetGas` value passed to `setFeeConfig`. All blocks accepted after the settlement of such a call must have the correct target excess value as derived from the binary search result. -Block building logic can follow the below diagram for determining the target excess of blocks. +#### Configuration Precedence + +Configuration precedence is as follows: + ```mermaid flowchart TD A[ACP-224 activated] --> B{Is ACP224FeeManager precompile active?} - B -- Yes --> C[Use targetExcess from precompile storage at latest settled root] + B -- Yes --> C{Is validatorTargetGas enabled?} + C -- Yes --> VP[Delegate targetGas to validator preferences] + C -- No --> PC[Use targetExcess from precompile storage] + + B -- No --> VP2[Use default targetGas with validator preferences] + + VP --> F{Is gas-target set in node config?} + F -- Yes --> G[Calculate targetExcess from preference and allowed bounds] + F -- No --> I[Use parent block ACP176 gas target] - B -- No --> D{Is gas-target set in node chain config file?} - D -- Yes --> E[Calculate targetExcess from configured preference and allowed update bounds] + VP2 --> F - D -- No --> F{Does parent block have ACP176 fields?} - F -- Yes --> G[Use parent block ACP176 gas target] - F -- No --> H[Use MinTargetPerSecond] + subgraph pricing [Gas Price Determination] + K{Is staticPricing enabled?} + K -- Yes --> L[Gas price = minGasPrice] + K -- No --> M["Gas price = minGasPrice * e^(excess/K)"] + end + + PC --> pricing + G --> pricing + I --> pricing ``` #### Adjustment to ACP-176 calculations for price discovery ACP-176 defines the gas price for a block as: -$$gas\_price = M \cdot e^{\frac{x}{K}}$$ +$$p = M \cdot e^{\frac{x}{K}}$$ Now, whenever $M$ (`minGasPrice`) or $K$ (derived from `timeToDouble`) are changed via the `ACP224FeeManagerPrecompile`, $x$ must also be updated. @@ -201,11 +365,23 @@ This makes it such that the current gas price stays the same when $K$ is changed ## Backwards Compatibility -ACP-224 will require a network update in order to activate the new fee mechanism. Another activation will also be required to activate the new fee manager precompile. The activation of precompile should never occur before the activation of ACP-224 (the fee mechanism) since the precompile depends on ACP-224’s fee update logic to function correctly. +ACP-224 will require a network update in order to activate the new fee mechanism. The `ACP224FeeManagerPrecompile` requires a separate activation and can be activated before or after the ACP-224 fee mechanism. If activated before, the precompile operates in a pending state where configuration can be set but does not take effect until ACP-224 activates (see [Early Activation of `ACP224FeeManagerPrecompile`](#early-activation-of-acp224feemanagerprecompile)). + +Activation of the ACP-224 mechanism will deactivate the prior fee mechanism and the prior `FeeManagerPrecompile`. This ensures that there is no ambiguity or overlap between legacy and new pricing logic. The `ACP224FeeManagerPrecompile` with `initialFeeConfig` replaces the prior genesis chain configuration for fee parameters. For existing networks, a network upgrade that activates the `ACP224FeeManagerPrecompile` with `initialFeeConfig` should be used to configure the new fee parameters. + +ACP-224 will be activated at the same time as ACP-194 (SAE) in the same network upgrade (Helicon). This coordinated activation is required because ACP-194 depends on the gas target and capacity mechanism defined by ACP-176, which this ACP implements for Subnet-EVM. Networks that do not activate ACP-224 will not be able to use ACP-194. -Activation of ACP-224 mechanism will deactivate the prior fee mechanism and the prior fee manager precompile. This ensures that there is no ambiguity or overlap between legacy and new pricing logic. In order to provide a configuration for existing networks, a network upgrade override for both activation time and ACP-176 configuration parameters will be introduced. +### Early Activation of `ACP224FeeManagerPrecompile` -These upgrades will be optional at the moment. However, with introduction of ACP-194 (SAE), it will be required to activate this ACP; otherwise the network will not be able to use ACP-194. +For continuity purposes, the `ACP224FeeManagerPrecompile` can be activated before the Helicon network upgrade (which activates ACP-224 and ACP-194). This allows L1 admins to prepare their fee configuration ahead of time. + +When the precompile is activated before Helicon: + +1. **Configuration calls are accepted**: The precompile's `setFeeConfig` can be called to set desired fee parameters and modes. The values are stored in the precompile's state. If `initialFeeConfig` is provided at activation, its values are also stored. +2. **Values are pending**: The stored fee configuration does not affect the current fee mechanism. The existing `FeeManagerPrecompile` and legacy fee mechanism remain active and in control. +3. **Activation applies stored values**: When Helicon activates, the stored fee configuration immediately takes effect. The legacy fee mechanism and `FeeManagerPrecompile` are deactivated at this point. + +This approach ensures a smooth migration path where admins can test and verify their configuration before it becomes active, avoiding any race conditions at the moment of activation. ## Reference Implementation @@ -215,17 +391,11 @@ A reference implementation is not yet available and must be provided for this AC Generally, this has the same security considerations as ACP-176. However, due to the dynamic nature of parameters exposed in the `ACP224FeeManagerPrecompile` there is an additional risk of misconfiguration. Misconfiguration of parameters could leave the network vulnerable to a DoS attack or result in higher transaction fees than necessary. -## Open Questions - -* Should activation of the `ACP224FeeManager` precompile disable the old precompile itself or should we require it to be manually disabled as a separate upgrade? -* Should we use `targetGas` in genesis/chain config as an optional field signaling whether the chain config should have a precedence over the validator preferences? -* Similarly above, should we have a toggle in `ACP224FeeManager` precompile to give control to validators for `targetGas`? - ## Acknowledgements -* [Stephen Buttolph](https://github.com/StephenButtolph) -* [Arran Schlosberg](https://github.com/ARR4N) -* [Austin Larson](https://github.com/alarso16) +- [Stephen Buttolph](https://github.com/StephenButtolph) +- [Arran Schlosberg](https://github.com/ARR4N) +- [Austin Larson](https://github.com/alarso16) ## Copyright diff --git a/ACPs/236-auto-renewed-staking/README.md b/ACPs/236-auto-renewed-staking/README.md new file mode 100644 index 00000000..3585678c --- /dev/null +++ b/ACPs/236-auto-renewed-staking/README.md @@ -0,0 +1,194 @@ +| ACP | 236 | +|:--------------|:------------------------------------------------------------| +| **Title** | Auto-Renewed Staking | +| **Author(s)** | Razvan Angheluta ([@rrazvan1](https://github.com/rrazvan1)) | +| **Status** | Proposed ([Discussion](https://github.com/avalanche-foundation/ACPs/discussions/244)) | +| **Track** | Standards | + +## Abstract + +This proposal introduces auto-renewed staking for validators on the Avalanche P-Chain. Validators can renew their staking position automatically, allowing their stake to compound over time, accruing rewards once per specified cycle. +Note that this mechanism applies only to primary network validation. It does not apply to L1 validators or to legacy subnet validators. + +## Motivation + +The current staking system on the Avalanche P-Chain restricts flexibility for stakers by requiring them to specify an explicit end time for their stake and by enforcing minimum and maximum staking durations, limiting their ability to respond to changing market conditions or liquidity needs. Managing a large number of nodes is also challenging, as re-staking at the end of each period is labor-intensive, time-consuming, and poses security risks due to the required transaction signing. Additionally, tokens can remain idle at the end of a staking period until stakers initiate the necessary transactions to stake them again. + +## Specification + +Auto-renewed staking introduces a mechanism that allows validators to remain staked indefinitely, without having to manually renew staking transactions at the end of each period. + +Instead of committing to a fixed endtime upfront, validators specify a cycle duration (period) and an `AutoCompoundRewardShares` value when they submit an `AddAutoRenewedValidatorTx`. At the end of each cycle, the validator is automatically restaked for a new cycle. The validator (via `Owner`) may update the auto-renew config at any time during a cycle; such updates take effect only at the end of the current cycle. To stop validating, the validator signals intent to stop validating by updating the next cycle’s period to `0`; this causes the validator to gracefully exit at the end of the current cycle and unlock their staked funds. The minimum and maximum cycle lengths follow the same protocol parameters as before (`MinStakeDuration` and `MaxStakeDuration`). + +Note: On mainnet, the current configuration is: `MinStakeDuration = 14 days` and `MaxStakeDuration = 365 days`. + +Clarification: In the rewards formula, `StakingPeriod` is the cycle’s duration, not the total accumulated time across cycles. Each cycle is treated separately when computing rewards. + +Delegator interaction remains unchanged, and the same constraints apply: a delegation period must fit entirely within the validator’s cycle. Delegators cannot delegate across multiple cycles, since there is no guarantee that a validator will continue validating after the current cycle. Essentially, it is not possible to delegate with auto-renewal. + +Rewards are accrued once per cycle and are managed according to the `AutoCompoundRewardShares` value: the specified portion is restaked and the remainder withdrawn. Auto-renewal only occurs if the validator is eligible for rewards for that cycle. If the validator is not reward-eligible for the cycle, the validator is forced to exit at the end of the cycle and staked funds are unlocked, and accrued rewards are withdrawn. + +If the updated stake weight (previous stake + staking rewards + delegation commission rewards) exceeds `MaxStakeLimit`, only the excess above `MaxStakeLimit` is withdrawn and distributed to `ValidatorRewardsOwner` and `DelegatorRewardsOwner`. + +Because of the way `RewardValidatorTx` is structured, multiple instances cannot be issued without resulting in identical transaction IDs. To resolve this, a new transaction type has been introduced for both rewarding and stopping auto-renewed validators: `RewardAutoRenewedValidatorTx`. Along with the validator’s creation transaction ID, it also includes a timestamp. + +Auto-renewed validators follow the existing uptime requirements. The main difference is that uptime is measured separately for each cycle. At the end of every cycle, the validator’s uptime during that specific period is evaluated to determine eligibility for rewards. Auto-renewed staking is conditioned on reward eligibility. When a new cycle begins, uptime tracking resets and starts again for the next period. + +Note: Submitting an `AddAutoRenewedValidatorTx` immediately followed by a `SetAutoRenewedValidatorConfigTx` that sets the next period to `0` replicates the behavior of the current fixed-period staking system (stake for a single cycle, then gracefully exit). + +### Auto-Renew Config + +The `Owner` field defines who is authorized to modify the validator's auto-renew config. + +The auto-renew config defines the validator's end-of-cycle behavior: whether it continues into the next cycle and how rewards are split between restaking and withdrawal. + +At creation, validators set the auto-renew config: `AutoCompoundRewardShares` and `Period`. + +`AutoCompoundRewardShares` specifies, in millionths (percentage * 10_000), what portion of earned rewards should be automatically restaked at the end of each cycle. The remaining portion of the rewards will be withdrawn. +For example, a value of 300,000, restakes 30% of the rewards and withdraws 70%. + +`Period` defines the duration of the next validation cycle and can be updated during a cycle with changes taking effect at cycle end. + +Stopping is requested by setting the next cycle’s `Period` to `0` via `SetAutoRenewedValidatorConfigTx`. + +### New P-Chain Transaction Types + +The following new transaction types will be introduced to the P-Chain to support this functionality: + +#### AddAutoRenewedValidatorTx + +```golang +type AddAutoRenewedValidatorTx struct { + // Metadata, inputs and outputs + BaseTx `serialize:"true"` + + // Node ID of the validator + ValidatorNodeID ids.NodeID `serialize:"true" json:"nodeID"` + + // [Signer] is the BLS key for this validator. + Signer signer.Signer `serialize:"true" json:"signer"` + + // Where to send staked tokens when done validating + StakeOuts []*avax.TransferableOutput `serialize:"true" json:"stake"` + + // Where to send validation rewards when done validating + ValidatorRewardsOwner fx.Owner `serialize:"true" json:"validationRewardsOwner"` + + // Where to send delegation rewards when done validating + DelegatorRewardsOwner fx.Owner `serialize:"true" json:"delegationRewardsOwner"` + + // Who is authorized to modify the auto-renew config + Owner fx.Owner `serialize:"true" json:"owner"` + + // Fee this validator charges delegators as a percentage, times 10,000 + // For example, if this validator has DelegationShares=300,000 then they + // take 30% of rewards from delegators + DelegationShares uint32 `serialize:"true" json:"shares"` + + // Weight of this validator used when sampling + Wght uint64 `serialize:"true" json:"weight"` + + // Percentage of rewards to restake at the end of each cycle, expressed in millionths (percentage * 10,000). + // Range [0..1_000_000]: + // 0 = restake principal only; withdraw 100% of rewards + // 300_000 = restake 30% of rewards; withdraw 70% + // 1_000_000 = restake 100% of rewards; withdraw 0% + AutoCompoundRewardShares uint32 `serialize:"true" json:"autoCompoundRewardShares"` + + // Period is the validation cycle duration, in seconds. + Period uint64 `serialize:"true" json:"period"` +} + +``` + +#### SetAutoRenewedValidatorConfigTx + +```golang +type SetAutoRenewedValidatorConfigTx struct { + // Metadata, inputs and outputs + BaseTx `serialize:"true"` + + // ID of the tx that created the auto-renewed validator. + TxID ids.ID `serialize:"true" json:"txID"` + + // Authorizes this validator to be updated. + Auth verify.Verifiable `serialize:"true" json:"auth"` + + // Percentage of rewards to restake at the end of each cycle, expressed in millionths (percentage * 10,000). + // Range [0..1_000_000]: + // 0 = restake principal only; withdraw 100% of rewards + // 300_000 = restake 30% of rewards; withdraw 70% + // 1_000_000 = restake 100% of rewards; withdraw 0% + AutoCompoundRewardShares uint32 `serialize:"true" json:"autoCompoundRewardShares"` + + // Period for the next cycle (in seconds). Takes effect at cycle end. + // If 0, stop at the end of the current cycle and unlock funds. + Period uint64 `serialize:"true" json:"period"` +} +``` + +#### RewardAutoRenewedValidatorTx + +```golang +type RewardAutoRenewedValidatorTx struct { + // ID of the tx that created the validator being removed/rewarded + TxID ids.ID `serialize:"true" json:"txID"` + + // End time of the validation cycle. + Timestamp uint64 `serialize:"true" json:"timestamp"` +} + +``` + +### UTXO Creation + +Auto-renewed staking creates UTXOs across different transactions depending on the withdrawal reason: + +Attached to `AddAutoRenewedValidatorTx`: +- Initial stake (returned when validator stops) + +Attached to `RewardAutoRenewedValidatorTx`: +- Validation/delegatee rewards withdrawn based on `AutoCompoundRewardShares` +- Excess rewards withdrawn when restaking would exceed `MaxValidatorStake` +- Accrued validation/delegatee rewards when validator stops (gracefully or forced) + +## Backwards Compatibility + +This change requires a network upgrade to make sure that all validators are able to verify and execute the new introduced transactions. + +## Considerations + +Auto-renewed staking makes it easier for users to keep their funds staked longer than with fixed-period staking, since it involves fewer transactions, lower friction, and reduced risks. Greater staking participation leads to stronger overall network security. + +Validators benefit by not having to manually restart at the end of each cycle, which reduces transaction volume and the risk of network congestion. + +However, the uptime risk per cycle slightly increases depending on cycle length and validator performance. For example, missing five days in a one-year cycle will still yield validation rewards, whereas missing five days in a two-week cycle may affect rewards. + +## Flow of a Validator with Auto-Renewing +```mermaid +flowchart TD + A[Issue AddAutoRenewedValidatorTx] --> B[Validator active] + + B -->|Optional during cycle| C[Issue SetAutoRenewedValidatorConfigTx to update auto-renew config or request stop] + + B --> D[Cycle end reached] + D --> E[Block builder issues RewardAutoRenewedValidatorTx] + E --> F[Evaluate uptime and compute cycle rewards] + F --> G{Stop requested?} + + G -->|Yes| H[Withdraw rewards and unlock principal] + H --> I[Validator exits] + + G -->|No| J{Eligible for rewards?} + J -->|No| H + J -->|Yes| K[Apply auto-renew config and split rewards into restake/withdrawal] + K --> L{New stake exceeds MaxStakeLimit?} + L -->|Yes| M[Withdraw excess above MaxStakeLimit] + L -->|No| N[Start new cycle] + M --> N[Start new cycle] + N --> B +``` + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/ACPs/236-continuous-staking/README.md b/ACPs/236-continuous-staking/README.md deleted file mode 100644 index 6c3d95c6..00000000 --- a/ACPs/236-continuous-staking/README.md +++ /dev/null @@ -1,171 +0,0 @@ -| ACP | 236 | -|:--------------|:------------------------------------------------------------| -| **Title** | Continuous Staking | -| **Author(s)** | Razvan Angheluta ([@rrazvan1](https://github.com/rrazvan1)) | -| **Status** | Proposed ([Discussion](https://github.com/avalanche-foundation/ACPs/discussions/244)) | -| **Track** | Standards | - -## Abstract - -This proposal introduces continuous staking for validators on the Avalanche P-Chain. Validators can stake their tokens continuously, allowing their stake to compound over time, accruing rewards once per specified cycle. -Note that this mechanism applies only to primary network validation. It does not apply to L1 validators or to legacy subnet validators. - -## Motivation - -The current staking system on the Avalanche P-Chain restricts flexibility for stakers by requiring them to specify an explicit end time for their stake and by enforcing minimum and maximum staking durations, limiting their ability to respond to changing market conditions or liquidity needs. Managing a large number of nodes is also challenging, as re-staking at the end of each period is labor-intensive, time-consuming, and poses security risks due to the required transaction signing. Additionally, tokens can remain idle at the end of a staking period until stakers initiate the necessary transactions to stake them again. - -## Specification - -Continuous staking introduces a mechanism that allows validators to remain staked indefinitely, without having to manually submit new staking transactions at the end of each period. - -Instead of committing to a fixed endtime upfront, validators specify a cycle duration (period) and an `AutoRenewRewardsShares` value when they submit an `AddContinuousValidatorTx`. At the end of each cycle, the validator is automatically restaked for a new cycle of the same duration, unless the validator submits a `SetAutoRenewPolicyTx` with `AutoRenewRewardsShares` set to the sentinel value `MaxUint64`. If a validator submits such a `SetAutoRenewPolicyTx` during a cycle, the validator will continue validating until the end of the current cycle, at which point the validator exits and the funds are unlocked. Both `AddContinuousValidatorTx` and `SetAutoRenewPolicyTx` include the `AutoRenewRewardsShares` field, which controls the automatic restaking or withdrawal behavior of the rewards at the end of each cycle. The minimum and maximum cycle lengths follow the same protocol parameters as before (`MinStakeDuration` and `MaxStakeDuration`). - -Note: On mainnet, the current configuration is: `MinStakeDuration = 14 days` and `MaxStakeDuration = 365 days`. - -Clarification: In the rewards formula, `StakingPeriod` is the cycle’s duration, not the total accumulated time across cycles. Each cycle is treated separately when computing rewards. - -Delegator interaction remains unchanged, and the same constraints apply: a delegation period must fit entirely within the validator’s cycle. Delegators cannot delegate across multiple cycles, since there is no guarantee that a validator will continue validating after the current cycle. Essentially, it is not possible to delegate continuously. - -Rewards are accrued once per cycle and are managed according to the `AutoRenewRewardsShares` value: the specified portion is restaked and the remainder withdrawn. If the updated stake weight (previous stake + staking rewards + delegatee rewards) exceeds `MaxStakeLimit`, only the excess above `MaxStakeLimit` is withdrawn and distributed to `ValidatorRewardsOwner` and `DelegatorRewardsOwner`. - -Because of the way `RewardValidatorTx` is structured, multiple instances cannot be issued without resulting in identical transaction IDs. To resolve this, a new transaction type has been introduced for both rewarding and stopping continuous validators: `RewardContinuousValidatorTx`. Along with the validator’s creation transaction ID, it also includes a timestamp. - -Continuous validators follow the existing uptime requirements. The main difference is that uptime is measured separately for each cycle. At the end of every cycle, the validator’s uptime during that specific period is evaluated to determine eligibility for rewards. When a new cycle begins, uptime tracking resets and starts again for the next period. - -Note: Submitting an `AddContinuousValidatorTx` immediately followed by a `SetAutoRenewPolicyTx` (with `AutoRenewRewardsShares = MaxUint32`) replicates the behavior of the current staking system. - -### Auto-Renew Policy - -The `PolicyOwner` field defines who is authorized to modify the validator's auto-renew policy. Only those specified as the `PolicyOwner` can update the auto-renew policy or signal the validator to exit at the end of a cycle. - -To support flexible reward withdrawal while keeping UX simple, continuous validators include an auto-renew policy field at creation called `AutoRenewRewardsShares`. This value specifies, in millionths (percentage * 10_000), what portion of earned rewards should be automatically restaked at the end of each cycle. The remaining portion of the rewards will be withdrawn. -Using a sentinel value of `MaxUint64` signals that the validator should exit at the end of the current cycle. - -For example, a value of 300,000, restakes 30% of the rewards and withdraws 70%. - -### New P-Chain Transaction Types - -The following new transaction types will be introduced to the P-Chain to support this functionality: - -#### AddContinuousValidatorTx - -```golang -type AddContinuousValidatorTx struct { - // Metadata, inputs and outputs - BaseTx `serialize:"true"` - - // Node ID of the validator - ValidatorNodeID ids.NodeID `serialize:"true" json:"nodeID"` - - // Period (in seconds). - Period uint64 `serialize:"true" json:"period"` - - // [Signer] is the BLS key for this validator. - Signer signer.Signer `serialize:"true" json:"signer"` - - // Where to send staked tokens when done validating - StakeOuts []*avax.TransferableOutput `serialize:"true" json:"stake"` - - // Where to send validation rewards when done validating - ValidatorRewardsOwner fx.Owner `serialize:"true" json:"validationRewardsOwner"` - - // Where to send delegation rewards when done validating - DelegatorRewardsOwner fx.Owner `serialize:"true" json:"delegationRewardsOwner"` - - // Who is authorized to modify the auto renew rewards shares - PolicyOwner fx.Owner `serialize:"true" json:"policyOwner"` - - // Fee this validator charges delegators as a percentage, times 10,000 - // For example, if this validator has DelegationShares=300,000 then they - // take 30% of rewards from delegators - DelegationShares uint32 `serialize:"true" json:"shares"` - - // Weight of this validator used when sampling - Wght uint64 `serialize:"true" json:"weight"` - - // Auto-renew policy for rewards, expressed in percentage, times 10,000. - // Range [0..1_000_000] means the percentage of cycle rewards to auto-restake: - // 0 = restake principal only; withdraw 100% of rewards - // 300_000 = restake 30% of rewards; withdraw 70% - // 1_000_000 = restake 100% of rewards; withdraw 0% - // Sentinel value MaxUint64 indicates "stop at the end of current cycle". - AutoRenewRewardsShares uint32 `serialize:"true" json:"autoRenewRewardsShares"` -} - -``` - -#### SetAutoRenewPolicyTx - -```golang -type SetAutoRenewPolicyTx struct { - // Metadata, inputs and outputs - BaseTx `serialize:"true"` - - // ID of the tx that created the continuous validator. - TxID ids.ID `serialize:"true" json:"txID"` - - // Authorizes this validator to be updated. - Auth verify.Verifiable `serialize:"true" json:"auth"` - - // Auto-renew policy for rewards, expressed in percentage, times 10,000. - // Range [0..1_000_000] means the percentage of cycle rewards to auto-restake: - // 0 = restake principal only; withdraw 100% of rewards - // 300_000 = restake 30% of rewards; withdraw 70% - // 1_000_000 = restake 100% of rewards; withdraw 0% - // Sentinel value MaxUint64 indicates "stop at the end of current cycle". - AutoRenewRewardsShares uint32 `serialize:"true" json:"autoRenewRewardsShares"` -} -``` - -#### RewardContinuousValidatorTx - -```golang -type RewardContinuousValidatorTx struct { - // ID of the tx that created the validator being removed/rewarded - TxID ids.ID `serialize:"true" json:"txID"` - - // End time of the validation cycle. - Timestamp uint64 `serialize:"true" json:"timestamp"` - - unsignedBytes []byte // Unsigned byte representation of this data -} - -``` - -## Backwards Compatibility - -This change requires a network upgrade to make sure that all validators are able to verify and execute the new introduced transactions. - -## Considerations - -Continuous staking makes it easier for users to keep their funds staked longer than with fixed-period staking, since it involves fewer transactions, lower friction, and reduced risks. Greater staking participation leads to stronger overall network security. - -Validators benefit by not having to manually restart at the end of each cycle, which reduces transaction volume and the risk of network congestion. - -However, the uptime risk per cycle slightly increases depending on cycle length and validator performance. For example, missing five days in a one-year cycle will still yield validation rewards, whereas missing five days in a two-week cycle may affect rewards. - -## Flow of a Continuous Validator -```mermaid -flowchart TD - A[Issue AddContinuousValidatorTx] --> B[Validator active] - - B -->|Optional during cycle| C[Issue SetAutoRenewPolicyTx to update AutoRenewRewardsShares or request stop] - - B --> D[Cycle endtime reached] - D --> E[Block builder issues RewardContinuousValidatorTx] - E --> F[Compute cycle rewards] - F --> G{Stop requested?} - - G -->|Yes| H[Withdraw rewards and unlock principal] - H --> I[Validator exits] - - G -->|No| J[Apply auto-renew policy and split rewards into restake and withdrawal] - J --> K{New stake exceeds MaxStakeLimit?} - K -->|Yes| L[Withdraw excess above MaxStakeLimit] - K -->|No| N[Start new cycle] - L --> N[Start new cycle] - N --> B -``` -## Copyright - -Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/ACPs/247-delegation-multiplier-increase-maximum-validator-weight-reduction/README.md b/ACPs/247-delegation-multiplier-increase/README.md similarity index 75% rename from ACPs/247-delegation-multiplier-increase-maximum-validator-weight-reduction/README.md rename to ACPs/247-delegation-multiplier-increase/README.md index 35d7ae1f..34f13eb9 100644 --- a/ACPs/247-delegation-multiplier-increase-maximum-validator-weight-reduction/README.md +++ b/ACPs/247-delegation-multiplier-increase/README.md @@ -1,13 +1,13 @@ | ACP | 247 | | :--- | :--- | -| **Title** | Delegation Multiplier Increase & Maximum Validator Weight Reduction | +| **Title** | Delegation Multiplier Increase | | **Authors** | Giacomo Barbieri ([@ijaack94](https://x.com/ijaack94)), BENQI ([@benqifinance](https://x.com/benqifinance)) | -| **Status** | Proposed ([Discussion](https://github.com/avalanche-foundation/ACPs/discussions/248)) | +| **Status** | Implementable ([Discussion](https://github.com/avalanche-foundation/ACPs/discussions/248)) | | **Track** | Standards | ## Abstract -This Avalanche Community Proposal advocates for two targeted adjustments to Primary Network validator staking parameters: (1) reducing the maximum validator weight from 3,000,000 AVAX to **1,000,000 AVAX** to prevent excessive stake concentration; and (2) increasing the delegation multiplier from 5x to **24x** to enable validators to efficiently serve larger delegated bases within the new weight constraint. These changes maintain the 2,000 AVAX minimum validator stake and focus on improving capital efficiency for existing, well-capitalized validators rather than broadening participation. +This Avalanche Community Proposal advocates for one targeted adjustment to Primary Network validator staking parameters: increasing the delegation multiplier from 5x to **24x** to enable validators to efficiently serve larger delegated bases within the new weight constraint. These changes maintain the 2,000 AVAX minimum validator stake and focus on improving capital efficiency for existing, well-capitalized validators rather than broadening participation. ## Motivation @@ -61,17 +61,10 @@ Real validator distribution data (as of October 28, 2025) provides empirical evi - They cannot attract delegations at current economics - Our profitability calculations ($180/month) match their behavior -### Maximum Validator Weight Centralization Risk - -The current 3,000,000 AVAX maximum weight limit allows individual validators to accumulate excessive stake (0.42% of 720M AVAX total supply). - -**Proposed 1,000,000 AVAX cap would reduce this to 0.14% per validator**, promoting more distribution in both self- and delegated stake. - ### Proposed Solution Implement two complementary changes: -1. **Reduce maximum validator weight to 1,000,000 AVAX** - Prevents dangerous concentration 2. **Increase delegation multiplier to 24x** - Increases the maximum _potential_ rewards that validators could receive from delegations, not changing any of the underlying incentives of delegation if not flipping the perception of maximum rewards for validators on Avalanche. **Resulting Economics at 24x Multiplier** (at $20 AVAX, 2,000 AVAX self-stake, 8.25% APY, 5% delegation fee): @@ -95,29 +88,6 @@ This transformation: ### Technical Changes -#### Change 1: Maximum Validator Weight Reduction - -**Current Parameter**: -``` -MaxValidatorStake = 3,000,000 AVAX -``` - -**Proposed Parameter**: -``` -MaxValidatorStake = 1,000,000 AVAX -``` - -**Implementation Details**: -- Affects calculation of maximum validator weight (stake + delegations) -- Existing validators exceeding 1,000,000 AVAX weight are **not immediately affected** -- New delegations to validators at or above 1,000,000 AVAX total weight are **rejected by consensus** -- Validators already exceeding 1,000,000 AVAX continue earning rewards until staking period ends -- Upon period end, rewards paid and stake returned; new staking cannot exceed 1,000,000 AVAX - -**Rationale**: Prevents single validators from accumulating excessive network influence while allowing existing large positions to mature naturally. - -#### Change 2: Delegation Multiplier Increase - **Current Parameter**: ``` DelegationMultiplier = 4 @@ -133,14 +103,14 @@ DelegationMultiplier = 24 MaxDelegatorStake(validator) = Min(ValidatorStake * 24, MaxValidatorWeight - ValidatorStake) Example for 2,000 AVAX validator: -MaxDelegatorStake = Min(2,000 * 24, 1,000,000 - 2,000) +MaxDelegatorStake = Min(2,000 * 24, 3,000,000 - 2,000) MaxDelegatorStake = Min(48,000, 998,000) MaxDelegatorStake = 48,000 AVAX ``` **Implementation Details**: - Applies uniformly to all validators -- Only validators with 40,000+ AVAX self-stake cannot utilize full 24x due to 1M weight cap +- Only validators with 120,000+ AVAX self-stake cannot utilize full 24x due to 3M weight cap - All 2,000 AVAX validators can deploy full 24x multiplier - Non-breaking for existing delegation relationships @@ -267,11 +237,6 @@ The 24x multiplier change has **minimal impact** on P-Chain resources: - 2,000 AVAX minimum stake maintained - Economic cost to create malicious validator unchanged -**Centralization Risk**: **Improved** -- 1M AVAX maximum weight reduces concentration -- Individual validators limited to 0.14% of maximum supply (vs 0.42% current) -- Prevents dangerous mega-validators - ## Consequences Analysis ### Positive Consequences @@ -299,13 +264,7 @@ The 24x multiplier change has **minimal impact** on P-Chain resources: - Delegators benefit from stable, profitable validators - Competitive fee discovery enabled -**5. Prevents Dangerous Centralization** (Very High probability, High severity) -- 1M AVAX cap prevents mega-validators -- Individual validator influence limited to 0.14% (vs 0.42% current) -- Maintains security through diversification -- Real data confirms no validators exceed 2.5% currently - -**6. Minimal Network Disruption** (Very High probability, Medium severity) +**5. Minimal Network Disruption** (Very High probability, Medium severity) - No new validator cohort entering (no min stake change) - Validator count unchanged (~854) - P-Chain load impact minimal @@ -313,14 +272,7 @@ The 24x multiplier change has **minimal impact** on P-Chain resources: ### Negative Consequences -**1. Delegation Concentration Risk Increases** (High probability, Medium severity) -- Larger multiplier enables validators to accumulate more delegations -- Top performers may attract disproportionate share -- Network vulnerable to large validator failures - -**Mitigation**: 1M AVAX weight cap prevents validators from becoming too large. Current 2.5% max means risk is contained. - -**2. Geographic Diversity Unchanged** (High probability, Low severity) +**1. Geographic Diversity Unchanged** (High probability, Low severity) - Entry barrier remains $40,000 (unchanged) - Validator count unlikely to increase (~854 baseline) - Cloud provider concentration persists @@ -353,23 +305,6 @@ This proposal maintains full backwards compatibility: 4. **Staking Durations**: All parameters unchanged 5. **Uptime Requirements**: 80% threshold unchanged -### Transition Mechanism - -**Phase 1 - Activation**: -- New parameters take effect at specified block height -- Existing validators with weight > 1M AVAX continue operating -- New delegations to validators > 1M AVAX weight are rejected - -**Phase 2 - Maturation**: -- Existing large validators' delegations mature naturally -- Upon maturation, new stakes must comply with 1M AVAX cap -- Validators rebalance to optimize under new parameters - -**Phase 3 - Equilibrium**: -- Network reaches new steady state -- Validators optimize delegation capacity within constraints -- Fee market establishes new equilibrium - ## Open Questions **Core Question**: "Should Avalanche prioritize validator capital efficiency through multiplier increase and weight cap reduction?" @@ -377,8 +312,7 @@ This proposal maintains full backwards compatibility: **Supporting Questions**: 1. Is 24x multiplier appropriate? -2. Is 1M AVAX the right weight cap? -3. Should minimum stake remain at 2,000 AVAX? +2. Should minimum stake remain at 2,000 AVAX? - YES: Maintain current barrier (approved) - NO: Lower it (pursue separate ACP) diff --git a/ACPs/256-hardware-recommendations/README.md b/ACPs/256-hardware-recommendations/README.md new file mode 100644 index 00000000..0cce4d96 --- /dev/null +++ b/ACPs/256-hardware-recommendations/README.md @@ -0,0 +1,84 @@ +| ACP | 256 | +| :- | :- | +| **Title** | Update Hardware Requirements for Primary Network Nodes | +| **Author(s)** | Aaron Buchwald ([@aaronbuchwald](https://github.com/aaronbuchwald)), Martin Eckardt ([@martineckardt](https://github.com/martineckardt)), Meaghan FitzGerald ([@meaghanfitzgerald](https://github.com/meaghanfitzgerald)) | +| **Status** | Proposed ([Discussion](https://github.com/avalanche-foundation/ACPs/discussions/257))| +| **Track** | Best Practices | + +## Abstract + +This ACP updates the recommended minimum hardware for Avalanche Primary Network nodes. + +[Call to Action]: Move Primary Network node storage to a physically-mounted (local) NVMe SSD. + +_The majority of node operators currently fulfill these requirements. All other operators are encouraged to update by January 17th, 2026._ + +Avalanche L1 nodes: No change required. + +## Motivation + +EVM throughput on Avalanche is primarily limited by state access latency. Lower-latency storage enables higher C-Chain throughput (gas/sec). This ACP standardizes Primary Network node storage guidance on local NVMe, consistent with common practice across major L1 blockchains. + +## Specification + +### Updated Minimum Recommended Hardware for Primary Network nodes + +- CPU: Equivalent of 8 AWS vCPU +- RAM: 16 GB +- Storage Size: 1 TB + - Nodes storing large historal/archival state, or running custom configs may require up to ~15 TB +- Storage Type: Physically-mounted (local) NVMe SSD (new) +- Network: Reliable IPv4 or IPv6 connectivity, with an open public port + +### Migration (Recommended Path) + +If a node does not meet the storage requirement, the operator should migrate to a freshly state-synced node (zero downtime, can be done during a validation period): +[https://build.avax.network/docs/nodes/node-storage/periodic-state-sync](https://build.avax.network/docs/nodes/node-storage/periodic-state-sync). + +Validators that cannot update to low-latency storage can periodically [manually delete state-sync snapshots](https://build.avax.network/docs/nodes/node-storage/state-sync-snapshot-deletion). + +## Validator Storage Exception and Required State Management + +Validators may use higher-latency storage (e.g., network-attached NVMe / SSD) only if stored state is kept to < 500 GB. + +### How to meet the < 500 GB requirement: + +- Periodically replace the node with a freshly state-synced node: +[https://build.avax.network/docs/nodes/node-storage/periodic-state-sync](https://build.avax.network/docs/nodes/node-storage/periodic-state-sync) + +- Or manually delete state sync snapshots: +[https://build.avax.network/docs/nodes/node-storage/state-sync-snapshot-deletion](https://build.avax.network/docs/nodes/node-storage/state-sync-snapshot-deletion) + +Non-validator nodes (RPC, archival, history-heavy): Use local NVMe. If you need historical access, state management alone is not a substitute for low-latency disks. + +### Cloud Service Provider Guidance + +- AWS: Instance Store NVMe (i3 / i4i / i4g) recommended; EBS gp3 acceptable only for validators with state management +- Azure: Lsv3-series (local NVMe) recommended; Premium SSD Managed Disks (P30+) acceptable for validators only with state management +- Google Cloud: Local SSD (NVMe) recommended; Persistent SSD acceptable for validators only with state management + +Note: These offerings are subject to change at the discretion of the CSP. It is imperative that all operators independently confirm that their cloud instance offering is, in fact, locally-mounted NVMe. + +## Background + +State access latency is driven by: + +1. Disk latency: Network-attached disks add latency from network/protocol overhead. +2. Stored state size: Larger state increases storage pressure; needs vary by node type. + +Reducing either one enables the node to process higher throughput. The second option is not available to nodes that require historical state. + +## Cost Considerations + +- Validators: Network-attached storage can be a cost compromise only with disciplined state management. +- Archival / history-heavy: Treat local NVMe as mandatory for functional performance. + +## Backwards Compatibility + +This ACP introduces no protocol changes. All recommendations are compatible with current and historical versions of AvalancheGo. Existing configurations will continue to function, but higher-latency storage increases the risk of falling behind during load spikes. + +## Security Considerations + +1. Under-resourced validators may become unresponsive during stress, reducing effective participation. +2. State management procedures require operational care (and sometimes downtime). +3. Cloud storage failure modes exist for both ephemeral local disks and network-attached volumes—use monitoring, protections, and recovery plans. diff --git a/ACPs/267-uptime-requirement-increase/README.md b/ACPs/267-uptime-requirement-increase/README.md new file mode 100644 index 00000000..844caa4c --- /dev/null +++ b/ACPs/267-uptime-requirement-increase/README.md @@ -0,0 +1,59 @@ +| ACP | 267 | +| :--- | :--- | +| **Title** | Increase Validator Uptime Requirement from 80% to 90% | +| **Author(s)** | Martin Eckardt ([@martineckardt](https://github.com/martineckardt)) | +| **Status** | Proposed [Discussion](https://github.com/avalanche-foundation/ACPs/discussions/268) | +| **Track** | Best Practices | + +## Abstract + +This proposal increases the minimum uptime requirement for Avalanche Primary Network validators from 80% to 90%. Validators must maintain at least 90% uptime during their staking period to receive their validation rewards. This change aims to enhance overall network health, reliability, and performance by ensuring validators meet a higher standard of availability. + +## Motivation + +### Current State + +The Avalanche Primary Network currently requires validators to maintain a minimum of 80% uptime during their staking period to be eligible for staking rewards. While this threshold has served the network adequately, there are compelling reasons to raise the bar. + +### Need for Enhanced Network Reliability + +Higher validator uptime is essential for achieving further performance gains across the Avalanche Primary Network. Even a small number of validators operating at 80% uptime can cause substantial network degradation, including increased latency in API endpoints and delayed block finalization. + +When the Snowman consensus protocol queries validators about a block, encountering too many non-responsive nodes among the sampled set causes the query to fail. This forces the protocol to issue additional queries, delaying block agreement and reducing overall network throughput. As a result, sustained validator availability is critical to ensure the network consistently processes transactions at optimal speed. + +## Specification + +### Technical Changes + +Update the validator uptime requirement from 80% to 90% by changing the following in `MainnetParams` (defined in [`genesis/genesis_mainnet.go`](https://github.com/ava-labs/avalanchego/blob/master/genesis/genesis_mainnet.go)): + +```go +StakingConfig: StakingConfig{ + UptimeRequirement: .9, // 90% +} +``` + +The same change would be applied to `genesis/genesis_fuji.go` and `genesis/genesis_local.go` for consistency across all networks. + +### Implementation Details + +The proposed change only raises the validator uptime requirement to 90%, with the uptime calculation method remaining unchanged. Validators are measured by their observed responsiveness during the staking period. Validators and their delegators will only receive rewards if the validator achieves at least 90% uptime; otherwise, they receive no rewards, maintaining the current all-or-nothing reward model with no partial payouts. + +### Effective Date + +The 90% uptime requirement only applies to validations that meet **both** of the following conditions: + +1. The validation **started on or after April 1, 2026**. +2. The validation **has not ended before the activation time of the AvalancheGo release implementing this ACP**. + +Validations that started before April 1, 2026, or that ended before the activation of this change, continue to be evaluated against the existing 80% uptime threshold. + +## Backwards Compatibility + +Each node continuously tracks its perceived uptime of its peers throughout the peer's validator staking period. At the end of the peer's validator staking period, each node sets its preference of whether or not to reward the peer based on its perceived uptime. + +The effective date ensures that validators who began their staking period without knowledge of the increased requirement are not retroactively penalized. This ACP was announced on February 10, 2026, and notifications were added in relevant places including the Core staking UI, block explorers, and validator uptime statistics dashboards. The April 1, 2026 effective date provides sufficient lead time for validators to adjust their infrastructure. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/ACPs/273-reduce-minimum-staking-duration/README.md b/ACPs/273-reduce-minimum-staking-duration/README.md new file mode 100644 index 00000000..449f679f --- /dev/null +++ b/ACPs/273-reduce-minimum-staking-duration/README.md @@ -0,0 +1,82 @@ +| ACP | 273 | +| :--- | :--- | +| **Title** | Reduce Minimum Validator Staking Duration | +| **Author(s)** | Eric Lu ([ericlu-avax](https://github.com/ericlu-avax)), Martin Eckardt ([@martineckardt](https://github.com/martineckardt)), Meaghan FitzGerald ([@meaghanfitzgerald](https://github.com/meaghanfitzgerald)), Stephen Buttolph ([@StephenButtolph](https://github.com/StephenButtolph)) | +| **Status** | Proposed ([Discussion](https://github.com/avalanche-foundation/ACPs/discussions/274)) | +| **Track** | Standards | + +## Abstract + +This proposal reduces the minimum validator staking period on Avalanche's Primary Network from 2 weeks (336 hours) to 2 day (48 hours). The change lowers barriers to validator participation while maintaining network security, enabling more flexible staking strategies and improving capital efficiency across the ecosystem. + +## Motivation + +### Current State + +The Avalanche Primary Network currently requires validators and delegators to stake for a minimum of 2 weeks (336 hours) to be eligible for staking rewards. While this ensures validator commitment to network security, it creates friction for participants who require more flexible liquidity access. + +### Benefits of a Shorter Minimum Period + +1. Increased Validator Participation: A 48-hour minimum removes a significant barrier to entry. Validators who cannot commit capital for 2 weeks can now participate, increasing overall network stake and decentralization. + +2. Improved Capital Efficiency: Stakers gain reliable access to liquidity after 48 hours rather than 14 days. This enables more dynamic capital allocation strategies while maintaining commitment to network security during the staking period. + +### Reward Impact Analysis + +Using Avalanche's reward formula at the time of writing, the APY difference between 48-hour and 2-week staking periods is approximately 0.04 percentage points (6.08% vs 6.12%). This minimal difference preserves reward incentives while enabling liquidity flexibility. + +## Specification + +### Technical Changes + +Update the minimum staking duration by modifying `MinStakeDuration` in the genesis configuration: + +Current: +```go +MinStakeDuration = 336 * time.Hour +``` + +Proposed: +```go +MinStakeDuration = 48 * time.Hour +``` + +### Implementation Details + +The staking mechanism remains unchanged. Validators must still meet all existing requirements: + +- Minimum stake: 2,000 AVAX for Primary Network validators +- Uptime requirement: 90% (per ACP-267) +- Hardware requirements: As specified in ACP-256 + +Only the minimum duration parameter changes, with all other validation rules and reward calculations preserved. + +## Backwards Compatibility + +This is a non-backwards compatible change to the P-Chain validation and execution requirements. Therefore, it requires a network upgrade in order to be implemented. + +This change affects only new staking periods initiated after activation. Existing validators with active staking periods remain unaffected and will complete their original durations under the previous rules. + +The change is non-breaking: all existing validation infrastructure, reward mechanisms, and consensus operations continue functioning without modification. + +## Security Considerations + +Shorter staking periods increase the likelihood of significant validator churn over a short period of time. This may impact Primary Network consensus safety and the reliability of Interchain Message delivery, which is required for L1 validator operations. This risk may impact the eventual implementation details of this ACP. + +Additionally, if the incentive to stake for longer than 48 hours is insufficient, the vast majority of stake may opt for the minimum 48-hour duration (particularly likely with the addition of [ACP-236](https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/236-auto-renewed-staking/README.md), which introduces auto-renewed staking). In this scenario, essentially all entities securing the network could change completely within 48 hours. The entire validator set could disappear, or a low-stake-weight validator could become a high-stake-weight validator within a very short time frame (i.e. a validator could go from 1% network stake to 20% network stake in just a few hours). This instability poses a real cost in terms of network security and should be weighed against the benefits of shorter staking periods. + +Validators remain subject to the same accountability standards during the 48-hour period. Network consensus sampling assumes the same validator availability model. Historical data demonstrates that validator uptime patterns remain consistent regardless of staking duration length, as infrastructure quality and operational commitment drive uptime, not duration requirements alone. + +## Open Questions + +1. If the minimum validation period is shortened, should the minimum delegation period also be reduced? + +At the time of writing, the ratio of `AddPermissionlessDelegatorTx`s to `AddPermissionlessValidatorTx`s over the past 365 days was 100:1, with delegations accounting for 46% of all P-Chain transactions. Assuming all delegations are currently set to the minimum duration (14 days), reducing the minimum to 48 hours would increase the rate of state growth from delegation operations on the P-Chain by approximately 14x. + +Given this potential impact, if the minimum delegation period is reduced, should there be an additional requirement that short-term delegations (e.g., 48 hours) must stake at least 1,000 AVAX? + +2. Based on the scenario posed in Security Considerations, should the rewards rate differ for shorter vs longer validation periods to incentivize network stability? + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/ACPs/283-dynamic-minimum-gas-price/README.md b/ACPs/283-dynamic-minimum-gas-price/README.md new file mode 100644 index 00000000..8bdb3e72 --- /dev/null +++ b/ACPs/283-dynamic-minimum-gas-price/README.md @@ -0,0 +1,192 @@ +| ACP | 283 | +| :- | :- | +| **Title** | Dynamic Minimum Gas Price | +| **Author(s)** | Stephen Buttolph ([@StephenButtolph](https://github.com/StephenButtolph)), Martin Eckardt ([@martineckardt](https://github.com/martineckardt)) | +| **Status** | Proposed [Discussion](https://github.com/avalanche-foundation/ACPs/discussions/284) | +| **Track** | Standards | + +## Abstract + +Proposes making the minimum gas price parameter on the C-Chain dynamically adjustable via validator voting, using the same mechanism established by [ACP-176](../176-dynamic-evm-gas-limit-and-price-discovery-updates/README.md) (gas target) and [ACP-226](../226-dynamic-minimum-block-times/README.md) (minimum block delay). + +Currently, ACP-176 sets the minimum gas price to 1 wei, allowing spam transactions to flood the chain at near-zero cost during low-activity periods. This has been exploited by protocols such as XEN, which performs proof-of-work mining on-chain when gas prices are negligible, causing unnecessary state growth and validator resource consumption. The current defense relies on wallets artificially inflating gas prices via failing transactions — a stopgap measure costing the community several AVAX per day. + +This ACP applies the proven validator voting pattern from ACP-176 and ACP-226 to the minimum gas price. Validators express their preferred minimum gas price via node configuration, and the network converges on the median preference weighted by stake. The dynamic minimum gas price acts as a true floor: during low-activity periods the floor binds, while during congestion the existing ACP-176 fee mechanism dominates unchanged. This requires no future upgrades for parameter adjustments and allows the network to organically respond to changing spam conditions. + +## Motivation + +[ACP-176](../176-dynamic-evm-gas-limit-and-price-discovery-updates/README.md) introduced a dynamic fee mechanism for the C-Chain, setting the minimum gas price $M$ to 1 wei — the smallest possible denomination of the native EVM asset. The rationale was that the dynamic fee mechanism would raise prices under load, making a near-zero floor acceptable for price discovery. + +In practice, the lowest observed C-Chain gas prices have been on the order of $10^5$ wei (~0.0001 nAVAX), cheap enough that spam protocols like XEN remain profitable, with their own demand keeping ACP-176's price discovery from pushing prices even lower. + +The validator voting mechanism for adjusting network parameters has already been proven twice on the Avalanche network: + +1. **ACP-176**: Validators dynamically adjust the target gas consumption rate $T$ +2. **ACP-226**: Validators dynamically adjust the minimum block delay time + +This ACP applies the same proven pattern to the minimum gas price. The benefits are: + +- **No future upgrades required**: Future adjustments to the minimum gas price do not require coordinated network upgrades +- **Stake-weighted convergence**: The effective value converges on the median preference weighted by validator stake +- **Decentralized governance**: Each validator independently sets their preference based on local assessment of network conditions + +Pre-ACP-176, the C-Chain gas price was approximately 25 nAVAX. Even a minimum gas price floor of 0.01 nAVAX — 2,500x below this baseline — would be entirely invisible to normal users while making spam economically infeasible. + +## Specification + +### Block Header Changes + +A single new field is added to block headers: `minimumGasPriceExponent`, represented as a `uint64`. + +The value of `minimumGasPriceExponent` MAY be updated in each block, similar to the gas target exponent introduced in ACP-176. The mechanism is specified below. + +### Dynamic minimum gas price mechanism + +The effective minimum gas price $M$, in wei, is defined as: + +$$M = e^{\dfrac{q}{D}}$$ + +Where: +- $q$ is a non-negative integer carried in the `minimumGasPriceExponent` header field, initialized to $q_{initial}$ at activation. +- $D$ is a fixed conversion constant. + +Both $q_{initial}$ and $D$ are specified in [Activation Parameters](#activation-parameters). + +After the execution of transactions in block $b$, the value of $q$ can be increased or decreased by up to $Q$. It MUST be the case that $\left|\Delta q\right| \leq Q$, or block $b$ is considered invalid. The amount by which $q$ changes after executing block $b$ is specified by the block builder. + +Block builders (i.e., validators) MAY set their desired value for $M$ in their configuration via the `min-price-target` setting (specified in wei). Their desired value for $q$ is then calculated locally as: + +$$q_{desired} = \left\lceil D \cdot \ln(M_{desired}) \right\rceil$$ + +Since $q_{desired}$ is only used locally, it is safe for implementations to approximate the value of $\ln(M_{desired})$ and round the resulting value to the nearest integer. Alternatively, client implementations MAY choose to use binary search to find the closest integer solution. + +If a validator does not set `min-price-target`, the validator SHOULD default to the previous block's value of $q$. This matches the abstention default of ACP-176 and ACP-226. + +When building a block, builders calculate their next preferred value for $q$ based on the network's current value (`q_current`) according to the same procedure used in ACP-176 and ACP-226: + +```python +# Calculates a node's new desired value for q for a given block +def calc_next_q(q_current: int, q_desired: int, max_change: int) -> int: + if q_desired > q_current: + return q_current + min(q_desired - q_current, max_change) + else: + return q_current - min(q_current - q_desired, max_change) +``` + +As $q$ is updated after the execution of transactions within the block, $M$ is also updated such that $M = e^{q/D}$ at all times. The change to $q$ in block $b$ takes effect for the floor used when validating and building block $b+1$; it does not affect the gas price of transactions included in block $b$ itself. + +Allowing block builders to adjust the minimum gas price in blocks that they produce makes it such that the effective value should converge over time to the point where 50% of the voting stake weight wants it increased and 50% of the voting stake weight wants it decreased, because the number of blocks each validator produces is proportional to their stake weight. + +### Compatibility with ACP-176 + +ACP-176 defines the per-block gas price as $P = M \cdot e^{x/K}$, where $M$ is a static minimum, $x$ is the gas price excess, and $K$ is the gas price update factor. Making $M$ dynamic without changing the structure of this formula would require rebalancing $x$ on every floor change to keep $P$ continuous. This is workable when $M$ changes rarely but breaks down once $M$ may oscillate every block. + +To support oscillating $M$ values, the price formula is changed to: + +$$P = \max\left(M, e^{\dfrac{x}{K}}\right)$$ + +The $\max$ enforces the floor directly: $P$ is never below $M$, even when integer rounding of $e^{x/K}$ produces a value just under $M$ (see the bias-low case below). When $M = 1$ wei (the value at activation), $e^{x/K} \geq 1$ always, so the $\max$ is a no-op and the formula reduces to ACP-176's $P = M \cdot e^{x/K}$. + +The state variable $x$ is additionally subject to a ratchet constraint: + +$$x \geq x_{floor}$$ + +where $x_{floor}$ is derived from $M$ via binary search over $\text{price}(\cdot)$, the integer-rounded forward price function used elsewhere in block validation. + +Let $x^* = \min\{x : \text{price}(x) \geq M\}$: + +- If $\text{price}(x^*) = M$, $x_{floor} = x^*$. +- If $\text{price}(x^*) > M$ (the integer-rounded price function cannot represent $M$ exactly), $x_{floor} = x^* - 1$, the largest integer where $\text{price}(x_{floor}) < M$. + +The second case biases toward the lower price when $M$ is not exactly representable by $\text{price}(\cdot)$, keeping the floor enforcement consistent with prices the implementation actually produces. Implementations MUST use this binary-search procedure rather than evaluating the closed-form $\left\lceil q \cdot K / D \right\rceil$, which may diverge from $\text{price}(\cdot)$. + +After updating $q$ for the new block, the block-execution rule is: + +1. If the current $x < x_{floor}$, set $x \leftarrow x_{floor}$. +2. Otherwise, leave $x$ unchanged. + +In particular, $x$ is never reduced as a consequence of a floor change. A decrease in $q$ relaxes the constraint but does not modify $x$. + +### Activation Parameters + +The only value this ACP specifies is `BlocksToDouble`; the remaining values are either derived from it or set implicitly. + +**Parameters** + +
+ +| Parameter | Description | Value | +| - | - | - | +| `BlocksToDouble` | max-change blocks required to halve or double $M$ | $3{,}600$ | + +
+ +`BlocksToDouble` expresses the number of consecutive maximum-change blocks required to halve or double $M$ under sustained voting pressure, from which $Q$ is mechanically derived. A value of $3{,}600$ corresponds to roughly one hour at one-second block times, the current C-Chain block rate. + +**Constants** + +
+ +| Constant | Description | Value | +| - | - | - | +| $D$ | exponential-update conversion constant | $415{,}828{,}534{,}307{,}635{,}077$ | +| $Q$ | per-block max $\left\|\Delta q\right\|$ | $80{,}063{,}993{,}375{,}475$ | +| $q_{initial}$ | initial value of `minimumGasPriceExponent` | $0$ | + +
+ +$D$ is fully determined by the type constraint. The allowed range of $M$ is $[1, 2^{64})$ wei (the full `uint64` range), realized when $q$ ranges over $[0, 2^{64})$. + +Setting $M_{max} = q_{max} = 2^{64}-1$ and solving for $D$: + +$$ +\begin{align} +M_{max} &= e^{q_{max}/D} \\ +\ln(M_{max}) &= \dfrac{q_{max}}{D} \\ +D &= \left\lfloor \dfrac{q_{max}}{\ln(M_{max})} \right\rfloor \\ +&= \left\lfloor \dfrac{2^{64}-1}{\ln(2^{64}-1)} \right\rfloor +\end{align} +$$ + +$Q$ is derived from $D$ and `BlocksToDouble`: + +$$Q = \left\lfloor \dfrac{D \cdot \ln(2)}{\text{BlocksToDouble}} \right\rfloor$$ + +$q_{initial} = 0$ means the effective minimum gas price at activation is $M = e^0 = 1$ wei, identical to the pre-activation value. Activation introduces the mechanism but does not change any user-visible parameter; subsequent movement of the floor requires explicit validator votes. + +### Choosing `min-price-target` + +This new mechanism allows for validators to specify their desired minimum gas price ($M_{desired}$) in their configuration via the `min-price-target` setting (specified in wei), and the value that they set impacts the effective minimum gas price of the network over time. When choosing what value makes sense for them, validators should consider: + +- The cost of spam during low-activity periods (the floor primarily binds when there is limited organic demand to drive the ACP-176 base fee). +- The risk of pricing out legitimate users. +- The trajectory of organic gas prices. + +While Avalanche Network Clients MAY suggest reference values, each validator chooses `min-price-target` independently. The default behavior SHOULD be to abstain. + +## Backwards Compatibility + +The changes proposed in this ACP require a network upgrade in order to take effect. Prior to its activation, the current minimum gas price of 1 wei continues to apply. Its activation should have minimal compatibility effects: + +- **Transaction formats**: Unchanged. Wallets and transaction signing are not impacted. +- **User fees**: Activation does not change the effective minimum gas price — the initial value remains 1 wei, identical to the pre-activation behavior. The mechanism enables future increases, which validators must explicitly vote for. +- **Tooling**: Any tooling parsing the RLP block bytes will need to update. +- **Gas price estimation APIs**: `eth_gasPrice` and related APIs will need to respect the new dynamic minimum gas price floor. + +## Reference Implementation + +This section will be updated with a tagged release once a complete reference implementation has been merged. + +## Security Considerations + +This ACP changes the minimum gas price from a static constant to a dynamically-adjusted parameter governed by validator voting. Several security aspects should be considered: + +**Validators setting the minimum gas price too high**: The exponential mechanism bounds how quickly the minimum gas price can change per block. With `BlocksToDouble = 3,600` (approximately one hour at one-second block times), the floor cannot halve or double in less than that many consecutive maximum-change blocks, giving the community time to detect and respond to concerning changes. There is no policy ceiling on the floor; the implicit ceiling at $M = 2^{64}-1$ wei is an artifact of the `uint64` representation of $q$, not a design choice. Validators are also economically incentivized not to price out users, as doing so reduces network utility and the value of their staked AVAX. + +**Validators setting the minimum gas price too low**: The same bounded adjustment speed applies in the downward direction, providing significant time for detection and response. + +**Comparison to existing approaches**: The minimum gas price adjustment mechanism has the same structure as the proven gas target adjustment (ACP-176) and minimum block delay adjustment (ACP-226), providing confidence in its security properties. Compared to Base's approach of administrator-set static floors, the validator voting mechanism achieves the same spam deterrence in a decentralized manner. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/ACPs/285-reduce-minimum-consumption-rate/ARR Spread.png b/ACPs/285-reduce-minimum-consumption-rate/ARR Spread.png new file mode 100644 index 00000000..4782d811 Binary files /dev/null and b/ACPs/285-reduce-minimum-consumption-rate/ARR Spread.png differ diff --git a/ACPs/285-reduce-minimum-consumption-rate/AVAX Staking ARR vs Staking Duration over time.jpeg b/ACPs/285-reduce-minimum-consumption-rate/AVAX Staking ARR vs Staking Duration over time.jpeg new file mode 100644 index 00000000..20c1ec7c Binary files /dev/null and b/ACPs/285-reduce-minimum-consumption-rate/AVAX Staking ARR vs Staking Duration over time.jpeg differ diff --git a/ACPs/285-reduce-minimum-consumption-rate/Projected AVAX supply.png b/ACPs/285-reduce-minimum-consumption-rate/Projected AVAX supply.png new file mode 100644 index 00000000..0b04ee40 Binary files /dev/null and b/ACPs/285-reduce-minimum-consumption-rate/Projected AVAX supply.png differ diff --git a/ACPs/285-reduce-minimum-consumption-rate/Projected net inflation.png b/ACPs/285-reduce-minimum-consumption-rate/Projected net inflation.png new file mode 100644 index 00000000..d69547ef Binary files /dev/null and b/ACPs/285-reduce-minimum-consumption-rate/Projected net inflation.png differ diff --git a/ACPs/285-reduce-minimum-consumption-rate/README.md b/ACPs/285-reduce-minimum-consumption-rate/README.md new file mode 100644 index 00000000..4dc604ab --- /dev/null +++ b/ACPs/285-reduce-minimum-consumption-rate/README.md @@ -0,0 +1,188 @@ +| ACP | 285 | +| :--- | :--- | +| **Title** | Reduce Minimum Consumption Rate | +| **Author(s)** | Matias Antonio ([@crypto-virgil](https://github.com/crypto-virgil)), Eric Lu ([@ericlu-avax](https://github.com/ericlu-avax)), Martin Eckardt ([@martineckardt](https://github.com/martineckardt)), Meaghan FitzGerald ([@meaghanfitzgerald](https://github.com/meaghanfitzgerald)) | +| **Status** | Proposed ([Discussion](https://github.com/avalanche-foundation/ACPs/discussions/288)) | +| **Track** | Standards | + +## Abstract + +This proposal reduces the `MinConsumptionRate` parameter governing Primary Network staking rewards from 10% to 7.5%. `MinConsumptionRate` sets the annualized reward rate (ARR) for a validator whose stake duration approaches the minimum staking duration. `MaxConsumptionRate` is not modified, leaving the maximum reward available to long-term validators unchanged. The change is intended to improve the long-term alignment between staking duration and network security and to redistribute rewards toward longer-duration participants. It is one of a series of ACPs intended to improve network security and validator economics. + +## Motivation + +The proposed change has three goals: reducing the AVAX inflation rate, extending the network's security budget, and incentivizing longer staking durations. + +First, lowering the reward-rate floor cuts the rewards given to short-duration stakes. This is projected to reduce the AVAX inflation rate by 0.5 to 1 percentage point per year, reflecting a lower reward rate partially offset by longer average staking durations. A reduction in inflation would benefit stakers and holders alike by structurally reducing the rate at which the protocol token is diluted. + +Second, lowering the floor slows the rate at which the network spends its finite security budget, leaving more issuance available to sustain rewards in the future. + +Third, lowering the floor steepens the ARR gradient between minimum- and maximum-duration staking, widening the gap from approximately 1.02 percentage points to 2.30 percentage points. This directly encourages new and existing validators to commit their stake for longer. Economic modeling estimates that this change would extend the stake-weighted average staking duration by approximately two months. + +This proposal is designed to work alongside two related ACPs: [ACP-236](https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/236-auto-renewed-staking/README.md) enables auto-renewal of staking positions, and [ACP-273](https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/273-reduce-minimum-staking-duration/README.md) reduces the minimum staking duration to 48 hours. Together, those two proposals make a compounding 48-hour stake operationally identical to a long-tenor stake while still getting near-maximum rewards. By widening the staking duration difference to 2.30 percentage points, this ACP restores and improves the incentive for longer-term staking. + +### Background + +Primary Network staking rewards follow a linear interpolation between `MinConsumptionRate` and `MaxConsumptionRate` based on a validator's stake duration relative to the `MintingPeriod` (365 days). Annualized reward rate (ARR) denotes the rate produced by this formula: + +``` +ARR(d) = (RemainingSupply / CurrentSupply) × (MinRate + (MaxRate − MinRate) × d / MintingPeriod) +``` + +Where: +- `d` = validator stake duration in days +- `MinRate` = `MinConsumptionRate / PercentDenominator` = 10% (current) +- `MaxRate` = `MaxConsumptionRate / PercentDenominator` = 12% (unchanged) +- `MintingPeriod` = 365 days +- `RemainingSupply` = `SupplyCap − CurrentSupply` + +This formula produces an ARR that increases linearly with duration. `MinConsumptionRate` sets the floor: the reward a validator receives if it stakes for the shortest possible period. `MaxConsumptionRate` sets the ceiling: the reward for a full 365-day commitment. + +### Goal 1: Reduce AVAX Inflation + +As noted in a [recent public discussion](https://x.com/frostLedger/status/2039683238285713557) by the Avalanche Foundation: *"Inflation should be used surgically. To reward specific behaviors: uptime, performance, ecosystem contribution. Not as a blanket payment for passively existing on the network."* + +Once a validator selects a staking duration, `MinConsumptionRate` is a key determinant of the staking rewards received, and therefore of the AVAX created under the protocol as rewards. In aggregate, it is a key protocol parameter determining the AVAX inflation rate, alongside the aggregate stake durations chosen by validators. Given the current network stake, staking behavior, and protocol parameter configurations, the trailing one-year inflation rate is approximately 5.5%. That figure uses circulating supply (AVAX issued minus burned minus staked) as the monetary base, which is the closest analogue to M1 (cash and cash equivalents) in monetary economics. + +The current 10% `MinConsumptionRate` functions as untargeted issuance at the floor of the reward formula. It is a code-level parameter, not the realized ARR. The formula multiplies it by the remaining-supply ratio, so the effective minimum-duration ARR today sits near 5.4% and keeps declining as the supply budget is drawn down. Because of that multiplier, lowering the parameter from 10% to 7.5% reduces the realized minimum-duration ARR by roughly 1.3 percentage points, not the full 2.5 that the raw numbers suggest. Separately, because `MaxConsumptionRate` is unchanged, the same cut more than doubles the gap between the minimum-duration and maximum-duration rates, widening the duration premium across the curve. + +The mechanical impact of the change can be isolated by holding staking behavior constant, with the aggregate staked amount, staking durations, and transaction fee levels (which affect net emissions) all held at recently observed values. Under those assumptions, inflation is projected to be approximately 0.5 percentage point lower than under the current `MinConsumptionRate`. + +![Projected net inflation]() + +The level effect reduces total annual AVAX issuance for as long as the supply budget remains, and the gradient effect roughly doubles the gap between minimum-duration and 365-day reward rates. Today a validator committing for 14 days receives roughly 84% of the rate given for a 365-day commitment, so duration accounts for only about 16% of that variation. Lowering the floor to 7.5% reduces that flat subsidy without touching the ceiling long-duration stakers already receive. + +As discussed in more detail in Goal 3, staking behavior, and the choice of staking duration in particular, is also likely to respond to the change. A structural model of staking preferences was estimated from historical staking data under the current protocol parameter configurations, then used to run counterfactual simulations at the proposed `MinConsumptionRate`, accounting for the staking duration changes. The simulations suggest that the annual inflation rate would fall by 0.5 to 1 percentage point from its current level. The full model specification and estimation are available [here](https://x.com/eric_lu_sc/status/2049126828527251907?s=20). + +### Goal 2: Extend the Network's Security Budget + +Lower emissions for short-duration stakers slow the rate at which new AVAX enters circulation, even with no change in staking behavior. + +If `MinConsumptionRate` were left at 10%, the protocol would continue spending its security budget faster than necessary. Primary Network rewards are funded from the finite 720 million AVAX cap, and every reward issued is a permanent draw on that budget. The slower it is spent, the more issuance remains available as a policy tool for future needs, and the longer the network sustains rewards before inflation-funded emissions taper toward zero. + +A faster issuance schedule also brings more newly minted AVAX into circulating supply over any given period. To the extent that some portion of new issuance is sold rather than restaked, a higher emission rate adds more AVAX to circulating supply per unit time than a lower one. Reducing the floor to 7.5% slows that pace. + +Holding staking behavior constant, the mechanical impact on the token supply path can be estimated in the same way. Both total supply and circulating supply grow more slowly under the lower `MinConsumptionRate` than at the current level, leaving more of the security budget available for the future. + +![Projected AVAX supply]() + +### Goal 3: Encourage Validators to Stake Longer + +Under the proposed change, the gap between the 14-day and 365-day ARR widens from 1.02 to 2.30 percentage points, more than doubling the duration premium. A wider premium should encourage validators to choose longer staking periods. + +This matters because the relative incentive for longer staking has been shrinking as AVAX supply grows. ACP-236 will erode it further by largely eliminating the extra operational cost of renewing short-duration stakes. The proposed change compensates for that erosion and strengthens the incentive for long-term staking. + +![AVAX Staking ARR vs Staking Duration over time]() + +![ARR Spread Between Long (365-day) and Short (14-day) Staking Duration]() + +This is an expected secondary effect, not a guaranteed outcome. The same structural model behind Goal 1's inflation estimate also projects that the stake-weighted average staking duration would rise by roughly two months. Empirical evidence is mixed on whether the wider premium will flip large validators' duration choices. Conversations with node operators and large-position stakers suggest that liquidity preference may dominate even a 2.30 percentage point premium for some cohorts. + +If validators do shift to longer durations, the network benefits further. Longer restaking cycles mean validators rotate less often, which strengthens stability and security. If validators do not change their behavior, or if they opt for shorter durations, the goals in the previous sections can still be realized. + +### Proposed Fix: Lower the Floor, Keep the Ceiling + +Reducing `MinConsumptionRate` from 10% to 7.5% produces the following reward schedule: + +| Duration | Current ARR (MinRate = 10%) | ARR (MinRate = 7.5%) | Compounding ARR (MinRate = 7.5%) | +| -------- | --------------------------- | -------------------- | -------------------------------- | +| 2 days | - | 4.00% | 4.08% | +| 14 days | 5.36% | 4.08% | 4.16% | +| 90 days | 5.58% | 4.58% | 4.65% | +| 182 days | 5.85% | 5.18% | 5.25% | +| 365 days | 6.38% | 6.38% | 6.38% | + +> All values calculated using the live reward formula with current circulating supply of 470,134,316 AVAX and supply cap of 720,000,000 AVAX. Compounding ARR assumes the user utilizes ACP-236 auto-renewal for 365 days, and all rewards received are added to the principal and restaked. + +The maximum reward for a 365-day validator is unchanged. The incentive gradient more than doubles, going from a spread of 1.02 percentage points to 2.30 percentage points, an increase of over 125%. + +## Specification + +The Primary Network reward rate increases linearly with stake duration, between a floor $r$ (`MinConsumptionRate`) and a ceiling (`MaxConsumptionRate`). This ACP lowers the floor from $10\%$ to $7.5\%$, applied as a linear ramp over 90 days rather than a single step. The ceiling, minting period, and supply cap are unchanged. + +### Mechanism + +Let $r_0$ be the initial floor rate, $\Delta$ the total reduction, $P$ the ramp duration, and $t_0$ the activation timestamp. + +Prior to activation, the floor rate is unchanged. For a stake with start time $t < t_0$: + +$$r(t) = r_0$$ + +At and after activation, for a stake with start time $t \ge t_0$: + +$$r(t) = r_0 - \Delta \cdot \frac{\min(t - t_0,\ P)}{P}$$ + +This decreases linearly from $r_0$ at $t_0$ to $r_0 - \Delta$ at $t_0 + P$, and holds at $r_0 - \Delta$ thereafter. + +$r(t)$ is evaluated once per stake, using the stake's start time, and is fixed for that stake's full duration. A stake beginning during the ramp locks in the rate at its start, so a stake that starts on day 45 uses $8.75\%$ for its entire term. Stakes already active at $t_0$ are unaffected, since their reward is determined at their own start time. + +Rates are stored in units of `PercentDenominator` $= 10^6$, so $r_0 = 100{,}000$ and $\Delta = 25{,}000$. Because $r(t)$ is an integer, it decreases by one unit, $0.0001$ percentage points, approximately every $5.184$ minutes, which is the $129{,}600$-minute window divided by $25{,}000$ units. + +The parameters at activation are: + +| Parameter | P-Chain Configuration | +| --------- | --------------------- | +| $r_0$ - initial `MinConsumptionRate` | 10% (100,000) | +| $\Delta$ - total reduction | 2.5% (25,000) | +| $P$ - reduction period | 90 days | +| $t_0$ - activation time | Helicon | + +### A Note on the Linear Ramp + +The reduction is phased so that the floor rate is continuous in a stake's start time. No single moment should carry a disproportionate incentive to enter just before or just after it. + +A single-step reduction breaks that property. The floor would drop discontinuously at $t_0$. A stake beginning just before $t_0$ locks in $10\%$ for its full term, and a stake beginning just after locks in $7.5\%$. An arbitrarily small difference in start time produces the entire $2.5$-point change, so the marginal value of staking one moment earlier becomes unbounded at the boundary. That concentrates a one-time incentive to front-run activation, producing a spike of staking immediately before $t_0$ followed by a lull. + +The linear ramp removes the discontinuity. Just after $t_0$ the floor is still approximately $10\%$, and it declines by only $0.0001$ percentage points about every $5.184$ minutes. Staking one day earlier during the window changes the locked-in rate by roughly $0.028$ percentage points rather than the full $2.5$. The incentive to enter at a higher rate is spread smoothly across the 90 days instead of concentrated at a single block, and integrators such as liquid staking protocols have the full window to adjust rather than repricing instantaneously. + +### Unchanged Parameters + +The following staking parameters are explicitly **not modified** by this proposal: + +| Parameter | Value | +|-----------|-------| +| `MaxConsumptionRate` | 12% | +| `MinStakeDuration` | Governed separately by [ACP-273](https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/273-reduce-minimum-staking-duration/README.md) | +| `MaxStakeDuration` | 365 days | +| Minimum validator stake | 2,000 AVAX | +| Minimum delegator stake | 25 AVAX | +| Uptime requirement | 90% (per [ACP-267](https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/267-uptime-requirement-increase/README.md)) | +| Reward formula structure | Unchanged | + +## Backwards Compatibility + +This is a non-backwards-compatible change to P-Chain reward calculation. It requires a network upgrade to activate. + +The change affects only new staking periods initiated after activation. Validators and delegators with active staking positions at the time of activation continue to receive rewards under the original parameters for the remainder of their existing stake periods. No migration or re-registration is required. + +## Reference Implementation + +The genesis `MinConsumptionRate` stays at 10%. Two new values drive the transition, a total reduction of 2.5% and a reduction period of 90 days. They are applied in the `GetRewardsCalculator` function (`vms/platformvm/txs/executor/state_changes.go`), which reads the current chain timestamp and decides the rate. Before activation it returns the static 10% calculator. For 90 days after the Helicon upgrade, the P-Chain builds the calculator from a copy of the reward config with `MinConsumptionRate` reduced by the interpolated amount. After the 90-day window, it builds the calculator with the full 2.5% reduction applied, landing exactly on 7.5%. + +The reward formula in `vms/platformvm/reward/calculator.go` is unchanged and simply receives the reduced rate. Activation is gated on the Helicon upgrade timestamp in the upgrade configuration. The implementation does not introduce new state transitions or P-Chain transaction types. + +## Security Considerations + +### Interaction With ACP-273 and ACP-236 + +This proposal is designed to work in conjunction with [ACP-273](https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/273-reduce-minimum-staking-duration/README.md) and [ACP-236](https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/236-auto-renewed-staking/README.md). ACP-273 reduces the minimum staking duration to 48 hours. ACP-236 enables zero-friction auto-renewal. Without a change to `MinConsumptionRate`, these two proposals together make minimum-duration compounded stakes economically near-equivalent to long-duration commitments (cost of choosing 2-day rolling vs. 90-day: ~0.50 pp). This proposal provides the complementary reward gradient that ACP-273's own Security Considerations section identified as potentially necessary. + +If ACP-273 activates without this proposal, the combination of 48-hour minimum duration and auto-renewal represents a meaningful security regression. This proposal should be considered a companion to ACP-273. + +## Open Questions + +1. Is 7.5% the right value, or should `MinConsumptionRate` be reduced further? + + The 7.5% floor was selected to produce a meaningful but not extreme shift in the ARR gradient. The expected impact from a wider range of `MinConsumptionRate` changes was analyzed, and 7.5% was found to be a good balance between the goals and potential risks. A lower floor (e.g., 5%) might reduce minimum-duration validator participation to levels that negatively affect decentralization, or lead to staking duration increases that are too large, resulting in higher emissions. It is worth noting that these analyses extrapolate beyond what has been observed historically, so a more conservative change is also a precaution against such model misspecification risks. In addition, the risk to certain ecosystem applications (e.g., LSTs and relevant strategies) that are beyond the scope of the structural model for staking preferences was considered and investigated, and the impact on the key variables was assessed. A moderate change to 7.5% and the phased implementation of the change were both chosen to mitigate the potential unmodeled risk. + +2. Should `MaxConsumptionRate` change simultaneously? + + Reducing `MinConsumptionRate` without touching `MaxConsumptionRate` preserves the full reward for 365-day stakers. An alternative framing would reduce `MaxConsumptionRate` as well to slow total issuance, accepting lower returns for long-term stakers in exchange for a slower approach to the supply cap. This proposal does not pursue that direction, but it is worth discussing. + +3. Should this proposal activate concurrently with ACP-273? + + Given the Security Considerations above, simultaneous activation with ACP-273 is strongly preferred. The two proposals address the same underlying tension of short staking duration versus network stability, from complementary angles. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/ACPs/292-configurable-evm-code-size-limit/README.md b/ACPs/292-configurable-evm-code-size-limit/README.md new file mode 100644 index 00000000..09931a8c --- /dev/null +++ b/ACPs/292-configurable-evm-code-size-limit/README.md @@ -0,0 +1,191 @@ +# ACP-292: C-Chain-First EVM Contract Code Size Increase with Avalanche L1 Customizability + +| ACP | 292 | +| :--- | :--- | +| **Title** | C-Chain-First EVM Contract Code Size Increase with Avalanche L1 Customizability | +| **Author(s)** | Giacomo Barbieri [(@ijaack94)](https://x.com/ijaack94) | +| **Status** | Proposed | +| **Track** | Standards | + +## Abstract + +This ACP proposes a C-Chain-first increase to the maximum deployed EVM contract code size, while also making the same limit configurable for Avalanche L1s that run Subnet-EVM-compatible execution environments. Today, both environments inherit Ethereum's fixed runtime bytecode limit introduced by EIP-170 (24,576 bytes), as well as the corresponding initcode ceiling introduced by EIP-3860. Those defaults preserve Ethereum compatibility, but they can unnecessarily constrain Avalanche builders who prefer larger contract artifacts in exchange for simpler application architecture, fewer proxy splits, and less deployment fragmentation. + +Under this ACP, the Avalanche C-Chain default `maxCodeSize` is increased to `49,152` bytes, with `maxInitCodeSize` increased proportionally to `98,304` bytes. Avalanche L1s MUST support configurable `maxCodeSize` and `maxInitCodeSize` consensus parameters, allowing each network to preserve Ethereum defaults, match the C-Chain, or choose another supported value. This ACP does not change opcode semantics or core gas accounting. Instead, it makes code-size policy an explicit Avalanche design surface: C-Chain moves first, and Avalanche L1s retain customizability. + +## Motivation + +Avalanche should not treat Ethereum's bytecode-size limit as immutable when the constraint is materially affecting real builder behavior. The EIP-170 contract size limit is a policy choice optimized for Ethereum's historical tradeoffs, not a universal optimum for all EVM environments. + +This proposal takes the position that Avalanche should lead on the C-Chain first. + +Several application teams want to deploy larger contracts for reasons that are operationally rational: + +- complex applications may prefer fewer contract boundaries and fewer proxy patterns; +- security reviews are sometimes easier on a single coherent artifact than on a heavily fragmented deployment; +- bytecode fragmentation can add engineering and auditing overhead without improving application logic; +- Avalanche is well positioned to compete for builders who want a more permissive, but still well-guarded, EVM environment. + +Recent market examples, including Robinhood's reported support for materially larger contracts than Ethereum, show that code-size policy is increasingly a competitive chain feature rather than just a low-level implementation detail. + +Fresh on-chain sampling also suggests the current EIP-170 ceiling is a binding constraint in active EVM environments rather than a merely theoretical one. In a 2026-07-04 sample of the most recent 2,000 Base blocks, there were 217 successful contract creations. Of those deployments, 12 produced runtime bytecode of at least 22,000 bytes, 11 were at least 23,000 bytes, and 4 were at least 24,000 bytes. The largest observed runtime artifacts were 24,561 bytes, only 15 bytes below Ethereum's 24,576-byte cap. This is strong evidence that builders already ship directly against the existing ceiling when chain activity is high. A contemporaneous recent C-Chain sample showed much lower contract-creation volume, so the cleaner empirical signal comes from broader EVM deployment behavior rather than recent Avalanche-only flow. + +Making the C-Chain the first mover does two things: + +1. it gives Avalanche's flagship EVM environment a stronger builder proposition immediately; and +2. it establishes a clean precedent for Avalanche L1s to customize the same parameter according to their own validator and application requirements. + +Avalanche L1 customizability still matters. Some L1s may want to retain Ethereum defaults for strict compatibility, while others may want to match or exceed the C-Chain limit. This ACP therefore pairs a C-Chain-first default increase with an explicit L1 configuration surface rather than forcing a single network-wide policy everywhere. + +## Specification + +### 1. Scope + +This ACP applies to: + +- the Avalanche C-Chain; and +- Avalanche L1s that run Subnet-EVM-compatible execution environments. + +On the C-Chain, this ACP increases the default contract size and initcode limits. + +On Avalanche L1s, this ACP requires support for configurable contract size and initcode limits through consensus parameters. + +### 2. New Consensus Parameters + +Subnet-EVM-compatible Avalanche L1s MUST support the following consensus parameters: + +- `maxCodeSize`: maximum number of bytes permitted for deployed runtime bytecode. +- `maxInitCodeSize`: maximum number of bytes permitted for initcode during contract creation. + +For the Avalanche C-Chain, the post-activation defaults are: + +- `maxCodeSize = 49,152` bytes +- `maxInitCodeSize = 98,304` bytes + +For Avalanche L1s, if these parameters are not explicitly configured, implementations MUST preserve Ethereum-compatible defaults: + +- `maxCodeSize = 24,576` bytes +- `maxInitCodeSize = 49,152` bytes + +These values correspond to the existing EIP-170 and EIP-3860 defaults. + +### 3. Configuration Rules + +`maxCodeSize` and `maxInitCodeSize` are consensus parameters and therefore MUST be identical across all validating nodes for a given Avalanche L1. + +The Avalanche C-Chain MUST activate the following values at the upgrade timestamp defined by the implementation: + +- `maxCodeSize = 49,152` +- `maxInitCodeSize = 98,304` + +An Avalanche L1 MAY set its own parameters: + +- in genesis at chain launch; or +- in a coordinated network upgrade activated at a specific timestamp. + +If an Avalanche L1 explicitly sets `maxCodeSize` and omits `maxInitCodeSize`, `maxInitCodeSize` MUST default to `2 * maxCodeSize`. + +Implementations MAY reject Avalanche L1 configurations where: + +- `maxCodeSize < 24,576` bytes; +- `maxInitCodeSize < maxCodeSize`; or +- `maxInitCodeSize != 2 * maxCodeSize`, if the implementation chooses to enforce a fixed ratio for simplicity. + +To maximize interoperability across Avalanche L1 tooling, reference implementations should initially enforce: + +- `24,576 <= maxCodeSize <= 98,304` +- `maxInitCodeSize = 2 * maxCodeSize` + +This lets Avalanche L1s preserve Ethereum compatibility, match the C-Chain, or increase further up to 4x Ethereum's deployed-code limit while keeping the parameter surface narrow and predictable. + +### 4. Contract Creation Semantics + +For `CREATE` and `CREATE2`: + +- if the resulting runtime bytecode exceeds `maxCodeSize`, contract creation MUST fail; +- if the supplied initcode exceeds `maxInitCodeSize`, contract creation MUST fail. + +No other EVM execution semantics are changed by this ACP. + +In particular: + +- opcode behavior is unchanged; +- code deposit gas remains unchanged; +- existing gas metering for initcode analysis remains unchanged unless separately modified by a future ACP. + +### 5. RPC and Tooling Exposure + +Implementations SHOULD expose the active `maxCodeSize` and `maxInitCodeSize` values through chain configuration inspection endpoints, client configuration output, or both, so that explorers, SDKs, deployment tooling, and auditors can determine the active deployment constraints of a given Avalanche L1. + +### 6. Activation Requirements + +Any network upgrade that changes `maxCodeSize` or `maxInitCodeSize` MUST clearly specify: + +- activation timestamp; +- old and new values; +- whether previously failing deployments are expected to become valid after activation. + +Previously deployed contracts are unaffected. Only contract creation validity after activation changes. + +## Backwards Compatibility + +This ACP is not backwards compatible with the pre-upgrade C-Chain deployment limit, because the C-Chain default contract size and initcode limits increase at activation. + +On the C-Chain, the compatibility impact is limited to contract creation validity after activation: contracts that would previously fail due to EIP-170/EIP-3860-sized limits may deploy successfully after the upgrade. Ordinary execution semantics for already-deployed contracts remain unchanged. + +For Avalanche L1s, this ACP is backwards compatible by default because chains that do not opt in retain Ethereum-compatible defaults. + +Potential compatibility considerations across both environments include: + +- deployment tools may assume Ethereum's 24,576-byte runtime limit; +- explorers, static analyzers, and indexers may have hardcoded assumptions around EIP-170-sized artifacts; +- bridges, wallets, and SDKs that market themselves as “Ethereum-compatible” may need to clarify that compatibility does not imply Ethereum-identical code-size policy. + +## Reference Implementation + +The reference implementation for this ACP is split across two codebases: + +- **Coreth (C-Chain):** a network upgrade that activates `maxCodeSize = 49,152` and `maxInitCodeSize = 98,304` on the C-Chain, and threads those activated values through the existing EIP-170/EIP-3860 enforcement points in `core/state_transition.go`, `core/txpool/validation.go`, and `sync/client/client.go`, with matching coverage in `core/state_processor_test.go`, `sync/client/client_test.go`, and `params/protocol_params_test.go`. +- **Subnet-EVM (Avalanche L1s):** explicit `maxCodeSize` and `maxInitCodeSize` consensus parameters in chain configuration and upgrade parsing, validation at genesis and upgrade time, `maxInitCodeSize = 2 * maxCodeSize` defaulting when omitted, and enforcement through the same contract creation, txpool, and sync code paths. The implementation naturally touches `params/extras/config.go`, `params/config_extra.go`, `core/genesis.go`, `core/state_transition.go`, `core/txpool/validation.go`, and `sync/client/client.go`, with corresponding tests. + +Together, these changes provide the full reference implementation: Coreth demonstrates the C-Chain-first activation path, while Subnet-EVM demonstrates the Avalanche L1 customizability surface. + +A compliant implementation should: + +1. increase the C-Chain `maxCodeSize` to `49,152` and `maxInitCodeSize` to `98,304` at a defined upgrade point; +2. add `maxCodeSize` and `maxInitCodeSize` as explicit consensus parameters in Subnet-EVM-compatible Avalanche L1 configuration; +3. validate those parameters during genesis parsing and network-upgrade activation; +4. apply the configured values in contract creation checks for `CREATE` and `CREATE2`; +5. expose the configured values through operator-facing or tooling-facing interfaces; and +6. include local-network telemetry comparing contract deployment latency, block-building behavior, and state growth at multiple size thresholds. + +## Security Considerations + +Larger deployable contracts increase worst-case resource usage during contract creation, code dissemination, and downstream tooling analysis. This does not automatically make larger limits unsafe, but it does make the tradeoff explicit. + +Relevant risks include: + +- **Block construction and verification cost:** larger contract artifacts may increase block processing and propagation time. +- **State growth:** larger bytecode blobs increase long-term state footprint. +- **Tooling fragility:** off-chain systems may silently assume Ethereum-sized artifacts. +- **DoS surface:** chains that raise limits too aggressively may increase the cost asymmetry between deployers and validators if other guardrails are not sufficient. + +These risks are why this ACP proposes: + +- a bounded C-Chain increase to 2x Ethereum's default rather than an unbounded jump; +- explicit Avalanche L1 configurability rather than forcing the C-Chain value everywhere; +- a narrow initial recommended Avalanche L1 range capped at 4x Ethereum's default; and +- explicit visibility of active limits for operators and tooling. + +The C-Chain upgrade should be benchmarked before activation, and any Avalanche L1 adopting a higher limit should benchmark deployment latency, block propagation, and archival/storage implications before activation. + +## Open Questions + +- Is 2x Ethereum's default the right C-Chain starting point, or should the first C-Chain increase be smaller or larger? +- Should Avalanche L1 implementations be permitted to exceed 4x Ethereum's default from day one, or should the interoperable range remain tighter initially? +- Should tooling-facing RPC standardization be part of this ACP, or left to implementation-specific documentation? +- Should future work pair larger code-size limits with additional deployment gas or other anti-DoS guardrails? + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/ACPs/99-validatorsetmanager-contract/README.md b/ACPs/99-validatorsetmanager-contract/README.md index 0ac6be57..6744256b 100644 --- a/ACPs/99-validatorsetmanager-contract/README.md +++ b/ACPs/99-validatorsetmanager-contract/README.md @@ -2,7 +2,7 @@ | :----------- | :-------------------------------------------------------------------------------------------------------------------------- | | Title | Validator Manager Solidity Standard | | Author(s) | Gauthier Leonard ([@Nuttymoon](https://github.com/Nuttymoon)), Cam Schultz ([@cam-schultz](https://github.com/cam-schultz)) | -| Status | Proposed ([Discussion](https://github.com/avalanche-foundation/ACPs/discussions/165)) | +| Status | Activated | | Track | Best Practices | | Dependencies | [ACP-77](../77-reinventing-subnets/README.md) | @@ -226,7 +226,7 @@ function completeValidatorWeightUpdate(uint32 messageIndex) returns (bytes32 validationID, uint64 nonce); ``` ->Note: While `getValidator` provides a way to fetch a `Validator` based on its `validationID`, no method that returns all active validators is specified. This is because a `mapping` is a reasonable way to store active validators internally, and Solidity `mapping`s are not iterable. This can be worked around by storing additional indexing metadata in the contract, but not all applications may wish to incur that added complexity. +> Note: While `getValidator` provides a way to fetch a `Validator` based on its `validationID`, no method that returns all active validators is specified. This is because a `mapping` is a reasonable way to store active validators internally, and Solidity `mapping`s are not iterable. This can be worked around by storing additional indexing metadata in the contract, but not all applications may wish to incur that added complexity. #### Private Methods diff --git a/README.md b/README.md index c40a2256..342f6995 100644 --- a/README.md +++ b/README.md @@ -131,10 +131,14 @@ _You can view the status of each ACP on the [ACP Tracker](https://github.com/org |[194](./ACPs/194-streaming-asynchronous-execution/README.md)|Streaming Asynchronous Execution|Arran Schlosberg ([@ARR4N](https://github.com/ARR4N)), Stephen Buttolph ([@StephenButtolph](https://github.com/StephenButtolph))|Standards| |[204](ACPs/204-precompile-secp256r1/README.md)|Precompile for secp256r1 Curve Support|Santiago Cammi ([@scammi](https://github.com/scammi)), Arran Schlosberg ([@ARR4N](https://github.com/ARR4N))|Standards| |[209](ACPs/209-eip7702-style-account-abstraction/README.md)|EIP-7702-style Set Code for EOAs|Stephen Buttolph ([@StephenButtolph](https://github.com/StephenButtolph)), Arran Schlosberg ([@ARR4N](https://github.com/ARR4N)), Aaron Buchwald ([aaronbuchwald](https://github.com/aaronbuchwald)), Michael Kaplan ([@michaelkaplan13](https://github.com/michaelkaplan13))|Standards| -|[224](ACPs/224-dynamic-gas-limit-in-subnet-evm/README.md)|Introduce ACP-176-Based Dynamic Gas Limits and Fee Manager Precompile in Subnet-EVM|Ceyhun Onur ([@ceyonur](https://github.com/ceyonur), Michael Kaplan ([@michaelkaplan13](https://github.com/michaelkaplan13))|Standards| +|[224](ACPs/224-dynamic-gas-limit-in-subnet-evm/README.md)|Introduce ACP-176-Based Dynamic Gas Limits and Fee Manager Precompile in Subnet-EVM|Ceyhun Onur ([@ceyonur](https://github.com/ceyonur)), Michael Kaplan ([@michaelkaplan13](https://github.com/michaelkaplan13))|Standards| |[226](ACPs/226-dynamic-minimum-block-times/README.md)|Dynamic Minimum Block Times|Stephen Buttolph ([@StephenButtolph](https://github.com/StephenButtolph)), Michael Kaplan ([@michaelkaplan13](https://github.com/michaelkaplan13))|Standards| -|[236](ACPs/236-continuous-staking/README.md)|Continuous Staking |Razvan Angheluta ([@rrazvan1](https://github.com/rrazvan1))|Standards| +|[236](ACPs/236-auto-renewed-staking/README.md)|Auto-Renewed Staking|Razvan Angheluta ([@rrazvan1](https://github.com/rrazvan1))|Standards| |[247](ACPs/247-delegation-multiplier-increase-maximum-validator-weight-reduction/README.md)|Delegation Multiplier Increase & Maximum Validator Weight Reduction|Giacomo Barbieri ([@ijaack94](https://x.com/ijaack94)), BENQI ([@benqifinance](https://x.com/benqifinance))|Standards| +|[256](ACPs/256-hardware-recommendations/README.md)|Update Hardware Requirements for Primary Network Nodes|Aaron Buchwald ([@aaronbuchwald](https://github.com/aaronbuchwald)), Martin Eckardt ([@martineckardt](https://github.com/martineckardt)), Meaghan FitzGerald ([@meaghanfitzgerald](https://github.com/meaghanfitzgerald))|Best Practices| +|[267](ACPs/267-uptime-requirement-increase/README.md)|Increase Validator Uptime Requirement from 80% to 90%|Martin Eckardt ([@martineckardt](https://github.com/martineckardt))|Best Practices| +|[273](ACPs/273-reduce-minimum-staking-duration/README.md)| Reduce Minimum Validator Staking Duration|Eric Lu ([ericlu-avax](https://github.com/ericlu-avax)), Martin Eckardt ([@martineckardt](https://github.com/martineckardt)), Meaghan FitzGerald ([@meaghanfitzgerald](https://github.com/meaghanfitzgerald)), Stephen Buttolph ([@StephenButtolph](https://github.com/StephenButtolph))|Standards| +|[283](ACPs/283-dynamic-minimum-gas-price/README.md)|Dynamic Minimum Gas Price|Stephen Buttolph ([@StephenButtolph](https://github.com/StephenButtolph)), Martin Eckardt ([@martineckardt](https://github.com/martineckardt))|Standards| ## Contributing