From b9800459aca7bae0360865947b7e12172b7474c1 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 2 Oct 2024 22:20:06 +0400 Subject: [PATCH] add x fee section, staking intro + post-etna, token dist init alloc (#174) --- .../04-staking/01-staking-tokens.mdx | 20 ++- .../03-staking-contract-post-etna.mdx | 155 +++++++++++++++++- .../05-transaction-fees/01-introduction.mdx | 24 ++- .../02-transaction-fee-configuration.mdx | 69 +++++++- .../03-dynamic-fee-configuration.mdx | 118 ++++++++++++- .../04-fee-distribution.mdx | 30 +++- .../06-distribution/01-initial-allocation.mdx | 28 +++- 7 files changed, 430 insertions(+), 14 deletions(-) diff --git a/content/course/l1-tokenomics/04-staking/01-staking-tokens.mdx b/content/course/l1-tokenomics/04-staking/01-staking-tokens.mdx index 643ba48..613d7cb 100644 --- a/content/course/l1-tokenomics/04-staking/01-staking-tokens.mdx +++ b/content/course/l1-tokenomics/04-staking/01-staking-tokens.mdx @@ -1,9 +1,25 @@ --- title: Introduction -description: Enter description +description: Learn about staking on Avalanche L1s updated: 2024-09-03 authors: [0xstt] icon: Book --- -TBD +In many networks, such as Ethereum, the same token is used for both staking and paying for gas. However, in the Avalanche network, staking tokens and gas tokens can be separated, as they fulfill different purposes within the blockchain ecosystem. + +### Staking Tokens + +Staking tokens are used for securing public, permissionless Avalanche L1s through a proof-of-stake (PoS) consensus mechanism. Holders of staking tokens can: + +- Run validators +- Participate in the consensus process by staking a certain amount of tokens as collateral + +Validators propose and validate new blocks on your L1 blockchain. Staking tokens play a crucial role in: + +- Maintaining network security +- Incentivizing validator participation + +### Private Avalanche L1s + +Not all Avalanche L1s are public and permissionless. Many enterprises choose a **Proof-of-Authority** setup, where the validators are selected by the enterprise. These blockchains do not require a staking token. diff --git a/content/course/l1-tokenomics/04-staking/03-staking-contract-post-etna.mdx b/content/course/l1-tokenomics/04-staking/03-staking-contract-post-etna.mdx index d2de04c..f83a3e7 100644 --- a/content/course/l1-tokenomics/04-staking/03-staking-contract-post-etna.mdx +++ b/content/course/l1-tokenomics/04-staking/03-staking-contract-post-etna.mdx @@ -1,9 +1,160 @@ --- title: Staking Contract (post Etna Upgrade) -description: Enter description +description: How staking will work post Etna upgrade updated: 2024-09-03 authors: [0xstt] icon: Book --- -TBD +## Validator Manager Contract + +[`ValidatorManager.sol`](https://github.com/ava-labs/teleporter/blob/staking-contract/contracts/validator-manager/ValidatorManager.sol) defines the abstract staking contract used to manage subnet-only validators, as defined in [ACP-77](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/77-reinventing-subnets). + +## Usage + +The common case for adding or removing a validator from a Subnet's validator set follows these general steps: + +1. Initiate the change through the Validator Manager contract. The Validator Manager contract will construct a Warp message attesting to the change in the validator set. +2. Deliver the Warp message containing the validator set change to the P-Chain. The P-Chain will construct a Warp message acknowledging the change to the validator set. +3. Deliver the Warp message containing the P-Chain acknowledgement back to the Validator Manager contract on the Subnet. The Validator Manager contract finalizes the validator set change. + +### Initializing a Validator Set + +If the Subnet has no validators registered on the P-Chain, then it will not be able to sign a Warp message to add a validator (step 1 above). In this case, Subnet recovery must be performed on the P-Chain, which allows an initial validator set to be specified, along with each validator's expiry. These validators are *not* registered with the Subnet's Validator Manager, and are only used to bootstrap subnet consensus and Warp message signing. The following steps describe how to register validators with the Subnet's Validator Manager, after which the initial validator set specified during Subnet recovery may safely exit the validator set or expire. + +### Registering a Validator + +Validator registration is initiated with a call to `initializeValidatorRegistration` on the concrete Validator Manager contract. The Validator Manager contract constructs a [`RegisterSubnetValidatorMessage`](#registersubnetvalidatormessage) Warp message to be sent to the P-Chain. Each validator registration request includes all of the information needed to identify the validator and its stake weight, as well as an `expiry` timestamp before which the `RegisterSubnetValidatorMessage` must be delivered to the P-Chain. + +The `RegisterSubnetValidatorMessage` is delivered to the P-Chain as the Warp message payload of a [`RegisterSubnetValidatorTx`](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/77-reinventing-subnets#registersubnetvalidatortx). Please see the transaction [specification](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/77-reinventing-subnets#step-2-issue-a-registersubnetvalidatortx-on-the-p-chain) for validity requirements. The successful `RegisterSubnetValidatorTx` results in a [`SubnetValidatorRegistrationMessage`](#subnetvalidatorregistrationmessage) Warp message indicating that the specified validator was successfully registered on the P-Chain. + +The `SubnetValidatorRegistrationMessage` is delivered to the Validator Manager contract via a call to `completeValidatorRegistration`. For PoS Validator Managers, staking rewards begin accruing at this time. + +#### Handling a Missed Expiry + +In the case of a missed expiry, the `RegisterSubnetValidatorTx` will result in a `SubnetValidatorRegistrationMessage` Warp message with `valid=0`. This serves as proof that the corresponding validation has not started and may never start. The `SubnetValidatorRegistrationMessage` can be provided to the Validator Manager contract by calling `completeEndValidation` with `setWeightMessageType=false`. + +### Exiting the Validator Set + +Validator exit is initiated with a call to `initializeEndValidation` on the Validator Manager contract. For PoS Validator Managers, a [`ValidationUptimeMessage`](#validationuptimemessage) Warp message may optionally be provided in order to calculate the staking rewards; otherwise the validator's uptime will be set to `0`. This proof may be requested directly from the Subnet validators, which will provide it in a `ValidationUptimeMessage` Warp message. Once `initializeEndValidation` is called, staking rewards cease accruing for PoS Validator Managers: the Validator Manager contract constructs a [`SetSubnetValidatorWeightMessage`](#setsubnetvalidatorweightmessage) Warp message, setting the weight to `0`. + +The `SetSubnetValidatorWeightMessage` is delivered to the P-Chain as the payload of a [`SetSubnetValidatorWeightTx`](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/77-reinventing-subnets#setsubnetvalidatorweighttx). The P-Chain acknowledges validator exit by signing a `SubnetValidatorRegistrationMessage` with `valid=0`, which is delivered to the Validator Manager by calling `completeEndValidation`. The validation is removed from the contract's state, and for PoS Validator Managers, staking rewards are disbursed and stake is returned. + +#### Disable a Validator Directly on the P-Chain + +ACP-77 also provides a method to disable a validator without interacting with the Subnet directly. The P-Chain transaction [`DisableValidatorTx`](https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/77-reinventing-subnets#disablevalidatortx) disables the validator on the P-Chain. The disabled validator's weight will still count towards the Subnet's total weight. + +Disabled Subnet validators can re-activate at any time by increasing their balance with an `IncreaseBalanceTx`. Anyone can call `IncreaseBalanceTx` for any validator on the P-Chain. A disabled validator can only be totally removed from the validator set by a call to `initializeEndValidation`. + +## Warp Message Format Reference + +### `RegisterSubnetValidatorMessage` + +Description: Issued by the Validator Manager contract in order to register a Subnet validator + +Signed by: Subnet +Consumed by: P-Chain + +Specification: + +``` ++--------------+----------+-----------+ +| codecID : uint16 | 2 bytes | ++--------------+----------+-----------+ +| typeID : uint32 | 4 bytes | ++--------------+----------+-----------+ +| subnetID : [32]byte | 32 bytes | ++--------------+----------+-----------+ +| nodeID : [32]byte | 32 bytes | ++--------------+----------+-----------+ +| weight : uint64 | 8 bytes | ++--------------+----------+-----------+ +| blsPublicKey : [48]byte | 48 bytes | ++--------------+----------+-----------+ +| expiry : uint64 | 8 bytes | ++--------------+----------+-----------+ + | 134 bytes | + +-----------+ + +``` + +### `SubnetValidatorRegistrationMessage` + +Description: Issued by the P-Chain in order to confirm Subnet validator registration + +Signed by: P-Chain +Consumed by: Validator Manager contract + +Specification: + +``` ++--------------+----------+----------+ +| codecID : uint16 | 2 bytes | ++--------------+----------+----------+ +| typeID : uint32 | 4 bytes | ++--------------+----------+----------+ +| validationID : [32]byte | 32 bytes | ++--------------+----------+----------+ +| valid : bool | 1 byte | ++--------------+----------+----------+ + | 39 bytes | + +----------+ +``` + +### `ValidationUptimeMessage` + +Description: Issued by the Subnet in order to provide validator uptime to the Subnet to calculate staking rewards + +Signed by: Subnet +Consumed by: Validator Manager contract + +Specification: + +``` ++--------------+----------+----------+ +| codecID : uint16 | 2 bytes | ++--------------+----------+----------+ +| typeID : uint32 | 4 bytes | ++--------------+----------+----------+ +| validationID : [32]byte | 32 bytes | ++--------------+----------+----------+ +| uptime : uint64 | 8 bytes | ++--------------+----------+----------+ + | 46 bytes | + +----------+ +``` + +### `SetSubnetValidatorWeightMessage` + +Description: Used to set a validator's stake weight on another chain + +Signed by: Subnet +Consumed by: P-Chain + +Specification: + +``` ++--------------+----------+----------+ +| codecID : uint16 | 2 bytes | ++--------------+----------+----------+ +| typeID : uint32 | 4 bytes | ++--------------+----------+----------+ +| validationID : [32]byte | 32 bytes | ++--------------+----------+----------+ +| nonce : uint64 | 8 bytes | ++--------------+----------+----------+ +| weight : uint64 | 8 bytes | ++--------------+----------+----------+ + | 54 bytes | + +----------+ +``` + +## Types of Validator Managers + +### Proof of Authority + +The Proof of Authority (PoA) validator manager is ownable, and only allows the owner to modify changes to the validator set of the Subnet. Validators are given a weight by the owner, but do not accrue staking rewards. + +### Proof of Stake + +The Proof of Stake (PoS) validator manager allows any validator to register and exit the Subnet validator set. Validators are given a weight based on the amount of stake they provide, and accrue staking rewards based on their uptime. The staking rewards can be either native or erc20 tokens. diff --git a/content/course/l1-tokenomics/05-transaction-fees/01-introduction.mdx b/content/course/l1-tokenomics/05-transaction-fees/01-introduction.mdx index 643ba48..2426490 100644 --- a/content/course/l1-tokenomics/05-transaction-fees/01-introduction.mdx +++ b/content/course/l1-tokenomics/05-transaction-fees/01-introduction.mdx @@ -1,9 +1,29 @@ --- title: Introduction -description: Enter description +description: Learn about blockchain transaction fees. updated: 2024-09-03 authors: [0xstt] icon: Book --- -TBD +### Background +When creating an Avalanche L1 we cannot only configure our custom native token, but also how the transaction fees (also known as gas fees) are determined. This allows Avalanche L1s to define the desired or maximal throughput of the blockchain differently. + +![](/course-images/multi-chain-architecture/subnet-fee-comparison.png) + +In the context of the EVM, gas is a unit that measures the computational effort required to execute specific operations. Each operation performed by a contract or transaction on an EVM chain consumes a certain number of gas units based on its complexity. Operations that require more computational resources cost more gas. The EVM calculates the required gas units automatically, and developers are encouraged to optimize their contract code to reduce gas consumption. +```bash +Transaction Cost = Gas Units * Gas Price +``` +For EVM based Avalanche L1s, gas payment can be configured to better suit the use case of the L1. This means the L1 can design and decide whether the gas fees are burned, paid to incentivize validators, or used for any other custom behavior. + +### Gas Price and Gas Limit +Each transaction specifies the gas price and gas limit: + +**Gas Price**: The gas price is the amount of the Avalanche L1's native token that the sender is **willing to spend per unit of gas**, typically denoted in gwei (1 native token = 1,000,000,000 gwei). + +**Gas Limit**: The gas limit is the **maximum amount** of gas the sender is willing to use for the transaction. It was introduced to prevent infinite loops in contract execution. + +In a Turing-complete language like Solidity (the main programming language in the EVM), it is possible to write a contract with an infinite loop, either accidentally or intentionally. While an infinite loop might be a nuisance in traditional computing, it could cause significant issues in a decentralized blockchain by causing the network to hang as it attempts to process a never-ending transaction. The gas limit prevents this by halting execution once the gas consumed reaches the limit. + +If a transaction exceeds the gas limit, it fails, but the fee amounting to the gas limit is still paid by the sender. \ No newline at end of file diff --git a/content/course/l1-tokenomics/05-transaction-fees/02-transaction-fee-configuration.mdx b/content/course/l1-tokenomics/05-transaction-fees/02-transaction-fee-configuration.mdx index c87e220..704ca72 100644 --- a/content/course/l1-tokenomics/05-transaction-fees/02-transaction-fee-configuration.mdx +++ b/content/course/l1-tokenomics/05-transaction-fees/02-transaction-fee-configuration.mdx @@ -1,9 +1,74 @@ --- title: Transaction Fee Configuration -description: Enter description +description: Configure the transaction fee for an Avalanche L1 updated: 2024-09-03 authors: [0xstt] icon: Book --- -TBD +## Configuration Format + +The fees are configured in the `chainConfig` in the `feeConfig` field: + +```json +{ + "config": { + // ... + "feeConfig": { // [!code highlight] + "gasLimit": 15000000, + "minBaseFee": 25000000000, + "targetGas": 15000000, + "baseFeeChangeDenominator": 36, + "minBlockGasCost": 0, + "maxBlockGasCost": 1000000, + "targetBlockRate": 2, + "blockGasCostStep": 200000 + }, + "allowFeeRecipients": false + }, + "alloc": { + // ... + }, + // ... + + "gasLimit": 0xe4e1c0, + // ... +} +``` + +## Gas Configuration Parameters + +### `gasLimit` + +Sets the maximum amount of gas consumed per block. This restriction caps the computational capacity of a single block and thereby limits the maximum gas usage allowed for any single transaction. For reference, the C-Chain value is set to 15,000,000. + +You might notice that the `gasLimit` field appears twice. This is because Avalanche introduced its own fee configuration under the `feeConfig` key while maintaining compatibility with the standard EVM configuration. Ensure that both fields have the same decimal and hexadecimal equivalent values. + +### `targetBlockRate` + +Specifies the target rate of block production in seconds. For instance, a target of 2 aims to produce a block every 2 seconds. If blocks are produced faster than this rate, it signals that more blocks are being issued to the network than anticipated, leading to an increase in base fees. For C-Chain, this value is set to 2. + +### `minBaseFee` + +Establishes a lower bound on the EIP-1559 base fee for a block. This minimum base fee effectively sets the minimum gas price for any transaction included in that block. + +### `targetGas` + +Indicates the targeted amount of gas (including block gas cost) to be consumed within a rolling 10-second window. The dynamic fee algorithm adjusts the base fee proportionally based on how actual network activity compares to this target. If network activity exceeds the `targetGas`, the base fee is increased accordingly. + +### `baseFeeChangeDenominator` + +Determines how much to adjust the base fee based on the difference between actual and target utilization. A larger denominator results in a slower-changing base fee, while a smaller denominator allows for quicker adjustments. For C-Chain, this value is set to 36, meaning the base fee changes by a factor of 1/36 of the parent block's base fee. + +### `minBlockGasCost` + +Sets the minimum amount of gas charged for the production of a block. In the C-Chain, this value is set to 0. + +### `maxBlockGasCost` + +Specifies the maximum amount of gas charged for the production of a block. + +### `blockGasCostStep` + +Defines how much to increase or decrease the block gas cost based on the time elapsed since the previous block. If a block is produced at the target rate, the block gas cost remains the same as the parent block. If the production rate deviates from the target, the block gas cost is adjusted by the `blockGasCostStep` value for each second faster or slower than the target block rate. + diff --git a/content/course/l1-tokenomics/05-transaction-fees/03-dynamic-fee-configuration.mdx b/content/course/l1-tokenomics/05-transaction-fees/03-dynamic-fee-configuration.mdx index 712f12d..871bf0e 100644 --- a/content/course/l1-tokenomics/05-transaction-fees/03-dynamic-fee-configuration.mdx +++ b/content/course/l1-tokenomics/05-transaction-fees/03-dynamic-fee-configuration.mdx @@ -1,9 +1,123 @@ --- title: Dynamic Fee Configuration -description: Enter description +description: Dynamically configure transaction fees on the Avalanche network. updated: 2024-09-03 authors: [0xstt] icon: Book --- -TBD +### Configuring Dynamic Fees​ +You can configure the parameters of the dynamic fee algorithm on chain using the `FeeConfigManager`. In order to activate this feature, you will need to provide the `FeeConfigManager` in the genesis: + +```json +{ + "config": { + "feeManagerConfig": { + "blockTimestamp": 0, + "adminAddresses": ["0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC"] + } + } +} +``` + +The precompile implements the `FeeManager` interface which includes the same `AllowList` interface used by `ContractNativeMinter`, `TxAllowList`, etc. + +The Stateful Precompile contract powering the `FeeConfigManager` adheres to the following Solidity interface at `0x0200000000000000000000000000000000000003` (you can load this interface and interact directly in Remix). + + +```solidity +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +import "./IAllowList.sol"; + +interface IFeeManager is IAllowList { + struct FeeConfig { + uint256 gasLimit; + uint256 targetBlockRate; + uint256 minBaseFee; + uint256 targetGas; + uint256 baseFeeChangeDenominator; + uint256 minBlockGasCost; + uint256 maxBlockGasCost; + uint256 blockGasCostStep; + } + event FeeConfigChanged( + address indexed sender, + FeeConfig oldFeeConfig, + FeeConfig newFeeConfig + ); + + // Set fee config fields to contract storage + function setFeeConfig( + uint256 gasLimit, + uint256 targetBlockRate, + uint256 minBaseFee, + uint256 targetGas, + uint256 baseFeeChangeDenominator, + uint256 minBlockGasCost, + uint256 maxBlockGasCost, + uint256 blockGasCostStep + ) external; + + // Get fee config from the contract storage + function getFeeConfig() + external + view + returns ( + uint256 gasLimit, + uint256 targetBlockRate, + uint256 minBaseFee, + uint256 targetGas, + uint256 baseFeeChangeDenominator, + uint256 minBlockGasCost, + uint256 maxBlockGasCost, + uint256 blockGasCostStep + ); + + // Get the last block number changed the fee config from the contract storage + function getFeeConfigLastChangedAt() + external + view + returns (uint256 blockNumber); +} +``` + +`FeeConfigManager` precompile uses `IAllowList` interface directly, meaning that it uses the same AllowList interface functions like `readAllowList` and `setAdmin`, `setManager`, `setEnabled`, `setNone`. + +In addition to the AllowList interface, the `FeeConfigManager` adds the following capabilities: + +- `getFeeConfig`: retrieves the current dynamic fee config +- `getFeeConfigLastChangedAt`: retrieves the timestamp of the last block where the fee config was updated +- `setFeeConfig`: sets the dynamic fee config on chain (see here for details on the fee config parameters). This function can only be called by an Admin, Manager or Enabled address. +- `FeeConfigChanged`: an event that is emitted when the fee config is updated. Topics include the sender, the old fee config, and the new fee config. + +You can also get the fee configuration at a block with the `eth_feeConfig` RPC method. + +### Initial Fee Config Configuration​ +It's possible to enable this precompile with an initial configuration to activate its effect on activation timestamp. This provides a way to define your fee structure to take effect at the activation. + +To use the initial configuration, you need to specify the fee config in `initialFeeConfig` field in your genesis or upgrade file: + +```json +{ + "config": { + "feeManagerConfig": { + "blockTimestamp": 0, + "adminAddresses": ["0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC"], + "initialFeeConfig": { + "gasLimit": 15000000, + "minBaseFee": 25000000000, + "targetGas": 15000000, + "baseFeeChangeDenominator": 36, + "minBlockGasCost": 0, + "maxBlockGasCost": 1000000, + "targetBlockRate": 2, + "blockGasCostStep": 200000 + } + } + } +} +``` + +This will set the fee config to the values specified in the `initialFeeConfig` field. + diff --git a/content/course/l1-tokenomics/05-transaction-fees/04-fee-distribution.mdx b/content/course/l1-tokenomics/05-transaction-fees/04-fee-distribution.mdx index 627911b..0755bab 100644 --- a/content/course/l1-tokenomics/05-transaction-fees/04-fee-distribution.mdx +++ b/content/course/l1-tokenomics/05-transaction-fees/04-fee-distribution.mdx @@ -1,9 +1,35 @@ --- title: Fee Distribution -description: Enter description +description: Customize fee distribution on an Avalanche L1 updated: 2024-09-03 authors: [0xstt] icon: Book --- +import { Callout } from 'fumadocs-ui/components/callout'; -TBD +### Setting a Custom Fee Recipient​ + +By default, all fees are burned (sent to the black hole address with `"allowFeeRecipients": false`). However, it is possible to enable block producers to set a fee recipient (who will get compensated for blocks they produce). + +To enable this feature, you'll need to add the following to your genesis file (under the "config" key): + +```json +{ + "config": { + "allowFeeRecipients": true + } +} +``` + +### Fee Recipient Address​ +With `allowFeeRecipients` enabled, your validators can specify their addresses to collect fees. They need to update their EVM chain config with the following to specify where the fee should be sent to. + +```json +{ + "feeRecipient": "" +} +``` + +If `allowFeeRecipients` feature is enabled on the Avalanche L1, but a validator doesn't specify a `feeRecipient`, the fees will be burned in blocks it produces. + +This mechanism can be also activated as a precompile. See [Changing Fee Reward Mechanisms](https://docs.avax.network/avalanche-l1s/upgrade/customize-avalanche-l1#changing-fee-reward-mechanisms) in Avalanche docs for more details diff --git a/content/course/l1-tokenomics/06-distribution/01-initial-allocation.mdx b/content/course/l1-tokenomics/06-distribution/01-initial-allocation.mdx index 3ec012e..cc2b6de 100644 --- a/content/course/l1-tokenomics/06-distribution/01-initial-allocation.mdx +++ b/content/course/l1-tokenomics/06-distribution/01-initial-allocation.mdx @@ -1,9 +1,33 @@ --- title: Initial Allocation -description: Enter description +description: Considerations for the initial token allocation on an Avalanche L1 updated: 2024-09-03 authors: [0xstt] icon: Book --- -TBD +The creator of an Avalanche L1 can pre-allocate native tokens during the chain genesis event. This **initial token allocation** refers to the distribution of tokens or digital assets when a new blockchain network is launched, which is a crucial aspect of the network's design. + +> **Note:** Initial token allocation determines how tokens are distributed among stakeholders and participants. + +### Key Considerations for Initial Token Allocation + +There are several factors to consider when allocating tokens: + +#### Founders and Development Team + +A portion of the initial token supply is often allocated to the founders and the development team as a reward for their efforts in creating the blockchain protocol. This serves as an incentive for continued development and network maintenance. + +#### Early Investors and Backers + +Another portion of the token supply may be allocated to early investors and backers who provided funding or support during the project's early stages. These entities take on early risk and are rewarded with tokens that may increase in value as the network grows. + +#### Community + +A significant portion of tokens may be allocated to the community through mechanisms such as token sales, airdrops, or other distribution methods. This ensures widespread ownership and participation in the network, fostering decentralization and security. + +#### Reserve + +Some tokens may be allocated to a reserve or treasury to fund ongoing development, marketing, and ecosystem growth initiatives. This reserve can be managed by a DAO or another entity tasked with allocating funds for the network's benefit. + +> **Tip:** Transparent and equitable token allocation mechanisms are essential for fostering trust and confidence in the network among its stakeholders.