diff --git a/.github/workflows/addressChecks.yml b/.github/workflows/addressChecks.yml index 2186a5b4e..06879af7e 100644 --- a/.github/workflows/addressChecks.yml +++ b/.github/workflows/addressChecks.yml @@ -13,6 +13,7 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: token: ${{ secrets.RG_ISSUES_TOKEN }} @@ -59,7 +60,6 @@ jobs: cat scripts/fetchedAddressData.json >> issue_body.md fi - - name: Create an issue if checks failed if: env.all_checks_passed != 'true' uses: peter-evans/create-issue-from-file@v4 diff --git a/docs/developers/addresses/v3-contracts.md b/docs/developers/addresses/v3-contracts.md index e04fbf116..b964fcd2f 100644 --- a/docs/developers/addresses/v3-contracts.md +++ b/docs/developers/addresses/v3-contracts.md @@ -1,7 +1,9 @@ # yVaults v3 Contract Addresses + + :::info Deployments are done using create2 factories and should be stable across all EVM chains the protocol has been deployed on. diff --git a/docs/developers/addresses/veyfi-contracts.md b/docs/developers/addresses/veyfi-contracts.md index 027e0d193..f77941766 100644 --- a/docs/developers/addresses/veyfi-contracts.md +++ b/docs/developers/addresses/veyfi-contracts.md @@ -34,4 +34,5 @@ | 13 | `yG-yvcrvUSD-2` | yGauge crvUSD-2 yVault | [`0x71c3223D6f836f84cAA7ab5a68AAb6ECe21A9f3b`](https://etherscan.io/address/0x71c3223D6f836f84cAA7ab5a68AAb6ECe21A9f3b) | | 14| `yG-yvUSDS-1` | yGauge USDS-1 yVault | [`0xd57aEa3686d623dA2dCEbc87010a4F2F38Ac7B15`](https://etherscan.io/address/0xd57aEa3686d623dA2dCEbc87010a4F2F38Ac7B15) | + The above Gauges can be verified using the [YFI Gauge Registry](https://etherscan.io/address/0x1D0fdCb628b2f8c0e22354d45B3B2D4cE9936F8B#readContract) contract and querying the `gauges()` function with the index listed above. diff --git a/docs/developers/addresses/ypools-contracts.md b/docs/developers/addresses/ypools-contracts.md new file mode 100644 index 000000000..f49dc2e0f --- /dev/null +++ b/docs/developers/addresses/ypools-contracts.md @@ -0,0 +1,27 @@ +# yPools Contract Addresses + +A list of all the current yPool contract addresses. + +## yETH contract Addresses + +| Name | Address | +|--------------------------------------------------|-------------------------------------| +| yETH | [`0x1BED97CBC3c24A4fb5C069C6E311a967386131f7`](https://etherscan.io/address/0x1BED97CBC3c24A4fb5C069C6E311a967386131f7) | +| st-yETH | [`0x583019fF0f430721aDa9cfb4fac8F06cA104d0B4`](https://etherscan.io/address/0x583019fF0f430721aDa9cfb4fac8F06cA104d0B4) | +| Pool | [`0x2cced4ffA804ADbe1269cDFc22D7904471aBdE63`](https://etherscan.io/address/0x2cced4ffA804ADbe1269cDFc22D7904471aBdE63) | +| | | +| Management | [`0xbBBBBbbB6B942883EAd4976882C99201108c784d`](https://etherscan.io/address/0xbBBBBbbB6B942883EAd4976882C99201108c784d) | +| Guardian | [`0xDC775e813cDB38a4f02c4BAd3942319088018eFA`](https://etherscan.io/address/0xDC775e813cDB38a4f02c4BAd3942319088018eFA) | +| | | +| Protocol Owned Liquidity | [`0x929401e30Aab6bd648dEf2d30FF44952BaB04478`](https://etherscan.io/address/0x929401e30Aab6bd648dEf2d30FF44952BaB04478) | +| Bootstrap: Deposit, Vote, Claim Incentives | [`0x41B994C192183793bB9cc35bAAb8bD9C6885c6bf`](https://etherscan.io/address/0x41B994C192183793bB9cc35bAAb8bD9C6885c6bf) | +| Bootstrap: Claim st-yETH | [`0x7cf484D9d16BA26aB3bCdc8EC4a73aC50136d491`](https://etherscan.io/address/0x7cf484D9d16BA26aB3bCdc8EC4a73aC50136d491) | +| Rate Providers | [`0x4e322aeAf355dFf8fb9Fd5D18F3D87667E8f8316`](https://etherscan.io/address/0x4e322aeAf355dFf8fb9Fd5D18F3D87667E8f8316) | +| | | +| Inclusion Vote | ['0x6bc0878939669339e82dbFa13d260c89230f2c31'](https://etherscan.io/address/0x6bc0878939669339e82dbFa13d260c89230f2c31) | +| Executor | ['0x71258Ee726644f1D52d6A9F5E11C21d1E38c2bF1'](https://etherscan.io/address/0x71258Ee726644f1D52d6A9F5E11C21d1E38c2bF1) | +| GenericGovernor | ['0xB7a528CF6D36F736Fa678A629b98A427d43E5ba5'](https://etherscan.io/address/0xB7a528CF6D36F736Fa678A629b98A427d43E5ba5) | + +> Due to a redeploy of st-yETH during the bootstrap process the first st-yETH contract has been deprecated, use the `Bootstrap: Claim st-yETH` contract to claim the new version if you participated in the bootstrap phase. + +Find more information about the yPools Roles, modes, and privileges [here](/developers/ypools/ypools-roles) diff --git a/docs/developers/ypools/yeth/adding-assets.md b/docs/developers/ypools/yeth/adding-assets.md new file mode 100644 index 000000000..0517166ab --- /dev/null +++ b/docs/developers/ypools/yeth/adding-assets.md @@ -0,0 +1,29 @@ +# Adding Assets to yPools + +Follow these steps to enable the inclusion of new assets into yETH via governance proposal and voting. + +:::info[Pre-requisites] + +You will need access to the necessary governance contracts and a sufficient voting weight of at least 100 to create proposals. You can check your voting weight [here](https://etherscan.io/address/0x583019fF0f430721aDa9cfb4fac8F06cA104d0B4#readContract#F13). + +::: + +## Steps + +### Step 1: Check Inclusion Vote Status + +Since the adoption of [this](https://snapshot.org/#/ylsd.eth/proposal/0x139698bed7752b80a16bb6d2fc0d9e8c82b622916ded2f064022be3c46ec9bb4) proposal, inclusion voting is off by default and needs to be enabled for the next epoch + +### Step 2: Enable One-Off Inclusion Vote + +To enable a one-off inclusion vote, a governance call must be made to the set_enable_epoch(next_epoch) function on the [InclusionVote contract](https://etherscan.io/address/0x6bc0878939669339e82dbFa13d260c89230f2c31#code). + +This step requires crafting and approving a governance proposal. Follow the instructions [here](create-gov-proposal.md) to do that successfully. + +### Step 3: Eligible Tokens for Voting + +Any token that has applied for inclusion and has a rate provider set by Yearn will be eligible for this inclusion vote. + +### Step 4: Outcome + +Once the vote is complete, the winner (if any) will be included into yETH. diff --git a/docs/developers/ypools/yeth/create-gov-proposal.md b/docs/developers/ypools/yeth/create-gov-proposal.md new file mode 100644 index 000000000..501e87607 --- /dev/null +++ b/docs/developers/ypools/yeth/create-gov-proposal.md @@ -0,0 +1,40 @@ +--- +rpcCalls: + - name: 'yPools Inclusion' + chain: '1' + address: '0x6bc0878939669339e82dbFa13d260c89230f2c31' + abiName: 'yPoolsInclusionVoteABI' + methods: + - 'epoch' +--- + + +# Create a Governance Proposal + +:::info[Pre-requisites] + +You will need access to the necessary governance contracts and a sufficient voting weight of at least 100 to create proposals. You can check your voting weight [here](https://etherscan.io/address/0x583019fF0f430721aDa9cfb4fac8F06cA104d0B4#readContract#F13). + +::: + +## Step 1: Craft the Proposal Script + +1. Generate a script by calling the `script(_to, _data)` view function on the [Executor contract](https://etherscan.io/address/0x71258Ee726644f1D52d6A9F5E11C21d1E38c2bF1#readContract#F1). + + In this case, set the `_to(address)` field to the [InclusionVote contract](../../addresses/ypools-contracts.md#yeth-contract-addresses) `0x6bc0878939669339e82dbFa13d260c89230f2c31` and use ABI-encoded data for the function set_enable_epoch(next_epoch) as the calldata (_data). + + + +2. To enable multiple calls in a single proposal, concatenate the scripts together. + +## Step 2: Submit a Governance Proposal + +1. You can create the proposal via the [proposal page](https://yeth.yearn.fi/propose) or directly by calling the propose(ipfs_hash, script) function on the [GenericGovernor contract](https://etherscan.io/address/0xB7a528CF6D36F736Fa678A629b98A427d43E5ba5). +2. The ipfs_hash should point to a document hosted on IPFS with details explaining the proposal and its rationale. You can host your own IPFS document or use a provider like [Piñata](https://pinata.cloud/). + +## Step 3: Proposal Approval and Execution + +Once the proposal is created, the governance process will need to: + +1. Approve the proposal. +2. Execute the set_enable_epoch(next_epoch) function to enable the inclusion vote for the next epoch. diff --git a/docs/developers/ypools/yeth/periphery-spec.md b/docs/developers/ypools/yeth/periphery-spec.md new file mode 100644 index 000000000..bb13c46b5 --- /dev/null +++ b/docs/developers/ypools/yeth/periphery-spec.md @@ -0,0 +1,49 @@ +# Governance Specification + +Contracts for fully on-chain governance, consisting of multiple cooperating components. Each component can be swapped out in the future if our requirements change. The different concepts and contracts are as follows: + +```mermaid +graph TD + classDef entity fill:#f9f,stroke:#red,stroke-width:5px; + classDef relationship fill:#bbf,stroke:#green,stroke-width:5px; + +yPoolsMultiSig -->|Manages| OwnershipProxy +OwnershipProxy -->|Manages| Executor +Executor -->|Manages| GenericGovernor +Executor -->|Manages| PoolGovernor + +GenericGovernor -->|Uses| Measure +PoolGovernor -->|Uses| Measure + +Measure -->|Types| LaunchMeasure +Measure -->|Types| DelegateMeasure + +DelegateMeasure -->|Delegates Voting Power via| DelegatedStaking + +PoolGovernor -->|Uses| InclusionVote +PoolGovernor -->|Uses| WeightVote + +InclusionVote -->|Interacts with| InclusionIncentives +WeightVote -->|Interacts with| WeightIncentives +``` + +- The entire protocol defines a set of management roles, which have powers within the protocol to set variables, rates, add assets etc. With the transition to on-chain governance, the **OwnershipProxy** will become the new owner of all these management roles, including the ones descriped below (excluding the proxy's own). This contract is very simple and is able to execute arbitary contract calls. Note that this is not a delegatecall proxy! +- The proxy has its own management, which will be the **Executor** and has permission to execute calls through the proxy. The executor maintains a list of governors, which are allowed to execute function calls through executor onto the proxy. In addition, the executor has the ability to enable a whitelist or blacklist for combination of address+selector. This allows us to define governors with only limited power. +The executor is self-governing, meaning the proxy is supposed to fulfill its management role. +The two-layer design of proxy and executor allows us to easily replace the executor if desired without having to move all the management permissions over one by one. +- There are two governors: **GenericGovernor** and **PoolGovernor**. They both take governance actions based on on-chain voting procedures. +- On-chain voting is based on 4 week long _epochs_. These coincide with Curve epochs, starting on a Thursday 00:00 GMT. +All voting takes place on the last week of the epoch. +- The voting power of governance participant is measured by a **Measure**. At launch, we used the **LaunchMeasure** on Snapshot (with **SnapshotToken**, which simply provides an ERC20 for the launch measure). The launch measure simply defines the vote weight as the user's st-yETH weight plus the user's share of the bootstrap st-yETH weight. +- With the transition to on-chain governance, a new measure is introduced, the **DelegateMeasure**. By default this returns the same value as the launch measure. But management has the ability to delegate the voting power of an account that deposited into a **DelegatedStaking** contract to another account. This gives some of the st-yETH balance as voting power to the delegator. +- In the first 3 weeks of the epoch, the **GenericGovernor** accepts proposals from anyone with sufficient voting weight. Proposals come in the form of a script, which will be executed on the executor if passed. In the final week of the epoch, users vote in favor or against each proposal. At the end of the epoch, all proposals that pass a threshold of relative votes in favor, will have passed. They become executable by anyone after a delay. +The author of the proposal can retract their proposal, and management can cancel any before it is executed. +- A second voting contract is **InclusionVote**. In the first three weeks, anyone can apply (upon paying a fee) for a token to be whitelisted for the vote. +A special role, the operator, has the task of setting rate providers for each of the applicants. An application with a rate provider automatically becomes whitelisted, meaning they can be voted on. +The voting takes place in the last week of the epoch. The asset with the largest number of votes will be added to the pool. +One of the vote options is a 'blank' vote. If that option has the most votes, no new asset will be added to the pool this epoch. +- Anyone can post incentives for any asset to be added to the pool in **InclusionIncentives**. If the asset wins the vote and is added, the incentives are paid out to everyone that voted. If the asset does not win, the incentives are refunded. +- The third voting contract is **WeightVote**. This contract is used to vote on the existing assets in the pool, and a 'blank' option. At the end of the epoch, a certain percentage of all weight is redistributed according to the result of this vote. +- The **WeightIncentives** contract is used to post incentives for voting on the assets. The incentives are distributed according to the voting weight dedicated by each user to that specific asset. +- The inclusion voting and weight voting contracts are not governors, instead they are used by the **PoolGovernor**. After the end of the epoch, the pool governor first adds the winning asset (if any) to the pool with a very low weight, through the executor. Immediately after, it starts a weight ramp, redistributing weights according to the vote results, as well as increasing the weight of the new asset. +The pool governor is operated by an operator, which is a role tasked with adding the asset to the pool while minimizing the arb opportunities. This is a trusted role, but has very limited powers as it cannot change weights at will or add arbitrary assets. diff --git a/docs/developers/ypools/yeth/wp.md b/docs/developers/ypools/yeth/wp.md new file mode 100644 index 000000000..701ba19df --- /dev/null +++ b/docs/developers/ypools/yeth/wp.md @@ -0,0 +1,162 @@ +# yETH Weighted Stableswap + +## Author + +0xkorin - Yearn Finance (0xkorin@proton.me) + +--- + +## Invariant Derivation + +### Constant Sum + +$$ +\sum_i x_i = c_1 +$$ + +### Constant Weighted Product + +$$ +\prod_i x_i^{w_i} = c_2 \quad \text{with} \quad \sum_i w_i = 1 +$$ + +Define: +$$ +\frac{1}{f} \coloneqq \prod_i w_i^{w_i}, \quad v_i \coloneqq w_i^n +$$ + +### Balanced Pool + +$$ +x_i = w_i D \implies c_1 = D \sum_i w_i = D, \quad c_2 = D \prod_i w_i^{w_i} = \frac{D}{f} +$$ + +$$ +x_i = \frac{D}{f^{v_i}} +$$ + +--- + +## Leveraged Invariant + +$$ +\chi D^{n-1} \sum_i x_i + \prod_i x_i^{v_i} = \chi D^n + \frac{D^n}{f^n} +$$ + +--- + +## Dynamic Leverage + +$$ +\chi = A \prod_i x_i^{v_i} \frac{D^n}{f^n} +$$ + +--- + +## Weighted Stableswap Invariant + +$$ +A f^n \sum_i x_i + D = A D f^n + \frac{D^{n+1}}{f^n \prod_i x_i^{v_i}} +$$ + +This reduces to the original stableswap invariant if we set equal weights $w_i = \frac{1}{n}$. + +Define: +$$ +\sigma \coloneqq \sum_i x_i, \quad \pi \coloneqq \frac{D^n}{\prod_i w_i x_i^{v_i}} +$$ + +$$ +A f^n \sigma + D = A D f^n + D \pi \tag{1} +$$ + +--- + +## Supply Calculation + +Given a pool with weights $\{w_i\}$ and virtual balances $\{x_i\}$, the equilibrium supply $D$ can be found by solving Equation (1) iteratively: + +$$ +D_{m+1} = \frac{A f^n \sigma - D_m \pi_m}{A f^n - 1} +$$ + +Where: +$$ +\pi_m = \frac{D_m^n}{D_{m-1}^n} \pi_{m-1} +$$ + +Starting with: +$$ +\pi_0 = \prod_i \frac{D_0 w_i x_i^{v_i}}{D_0} +$$ + +The iterative process begins with a good initial guess for $D_0$ (e.g., $\sigma$) and continues until the desired precision is achieved. + +--- + +## Rate Update + +$$ +x_i = b_i r_i \rightarrow x_i' = b_i r_i' +$$ + +$$ +\sigma \rightarrow \sigma' = \sigma + b_i (r_i' - r_i) +$$ + +$$ +D \rightarrow D', \quad \pi \rightarrow \pi' = \frac{D'^n}{D^n} \frac{r_i}{r_i'} v_i \pi +$$ + +Equation (2) is used iteratively to find both $D'$ and $\pi'$, starting with $D'_0 = D$ and $\pi'_0 = \pi \frac{r_i}{r_i'} v_i$. + +--- + +## Balance Calculation + +Given weights $\{w_i\}$, virtual balances $\{x_i\}_{i \neq j}$, and supply $D$, the balance of a specific asset $j$ can be found by solving Equation (1) for $y \coloneqq x_j$. + +Define intermediary variables: +$$ +\tilde{\sigma} \coloneqq \sum_{i \neq j} x_i, \quad \tilde{\pi} \coloneqq \frac{D^n w_j^{v_j}}{\prod_{i \neq j} w_i x_i^{v_i}} +$$ + +Rewriting Equation (1): +$$ +A f^n (\tilde{\sigma} + y) + D = A D f^n + D \tilde{\pi} y^{v_j} +$$ + +Rearranging: +$$ +y^{v_j+1} + \tilde{\sigma} y^{v_j} - \frac{D}{A f^n} y - \frac{D}{A f^n} \tilde{\pi} = 0 +$$ + +This root can be found using Newton's method. + +--- + +## Swaps + +### Exact Input + +Find $\Delta b_l$ given $\Delta b_k$: +$$ +\tilde{\sigma} = \sigma + \Delta b_k r_k - x_l, \quad \tilde{\pi} = \frac{x_k}{x_k'} v_k \frac{x_l^{v_l}}{\pi} +$$ + +$$ +\Delta b_l = \frac{x_l - x_l'}{r_l} +$$ + +--- + +### Exact Output + +Find $\Delta b_k$ given $\Delta b_l$: +$$ +\tilde{\sigma} = \sigma - x_k - \Delta b_l r_l, \quad \tilde{\pi} = \frac{x_k^{v_k}}{x_l x_l'} v_l \pi +$$ + +$$ +\Delta b_k = \frac{x_k' - x_k}{r_k} +$$ diff --git a/docs/developers/ypools/yeth/yETH-spec.md b/docs/developers/ypools/yeth/yETH-spec.md new file mode 100644 index 000000000..27fe94836 --- /dev/null +++ b/docs/developers/ypools/yeth/yETH-spec.md @@ -0,0 +1,104 @@ +# yETH protocol specification + +### Definitions + +- yETH: token that represents one to one beacon chain ETH +- staked yETH (st-yETH): yETH that has been deposited into the staking contract. Stakers will effectively receive all yield and slashings from beacon chain +- Management: trusted with privileged access for limited operations. Should eventually be replaced by a smart contract +- Guardian: trusted with emergency privileges +- Treasury: benefactor of performance fees + +## Pool specification + +### Normal operation + +- Contract contains a set of whitelisted tokens. The assets are non-rebasing and represent different type of LSDs +- Each asset has a corresponding rate provider: a contract per asset that calculates the amount of beacon chain ETH per token unit +- Contract keeps track of asset balances as well as the asset's rate +- The asset balance in the pool multiplied by the rate is called the "virtual balance" +- Each asset has a weight associated with it, representing the desired share of the total beacon chain ETH in the pool +- The actual composition of assets in the pool is allowed to fluctuate within a fixed range around the weight +- Contract keeps track of a variable `D`, representing the pools total virtual balance if it is perfectly balanced +- The relation between `D`, the virtual balances and the weights is governed by the weighted stableswap invariant +- If any operation increases or decreases `D`, an equal amount of LP tokens (yETH) will always be minted or burned, respectively. This mechanism ensures the 1:1 peg, because the net yETH the pool has minted (the supply) is always equal to the beacon chain ETH the pool owns in a balanced state. For this reason we call `D` the LP supply +- Users can deposit any combination of whitelisted assets into the pool. The invariant will be evaluated to determine the new LP supply and an amount of LP tokens equal to the increase in supply is minted to the user +- Users are able to burn LP tokens to either receive assets in a balanced manner or receive a single asset. The LP supply is reduced by the amount of tokens burned +- In case of a balanced withdrawal, the user receives a share of every asset in the pool equal to the share of LP tokens burned, i.e. `tokens_received = pool_token_balance * lp_burned / lp_supply` for each whitelisted asset +- In case of a single sided withdrawal, the invariant is solved (after applying the supply change) to calculate the new virtual balance of the asset. The difference between the new and old virtual balance is divided by the asset's rate to calculate the amount of tokens the user receives +- Users are able to perform swaps using the pool assets. Like a traditional stableswap pool, this is done by first updating the virtual balance of the input asset and solving the invariant for the new virtual balance of the output asset. The decrease divided by the rate is the amount of output tokens the user receives +- The pool charges a fee on swaps, in the form of LP tokens minted to the staking contract +- For safety reasons, any change in asset balances due to a deposit/withdrawal/swap is only accepted if the resulting composition is within a specific tolerance range of the desired composition, or if the change brings it closer to that desired composition +- The rate of each asset can be synchronised by an internal or external (non-privileged) call, which will in turn call the corresponding rate provider and store the new rate in the contract +- If a synchronisation changes any rate, the new virtual balances are calculated and the invariant is used to calculate the new LP supply. The change in supply is minted to or burned from the staking contract +- Any deposit or withdrawal of an asset will be preceded by a sychronisation of its rate +- A rate is only allowed to increase at most 10% at once +- Management can start a gradual amplification and weight change, as long as no change is active yet +- Management can whitelist a new asset, which sets an initial weight, sets the rate provider and requires an initial deposit +- New assets can only be whitelisted if no amplification or weight change is active +- Management can update the rate provider for every whitelisted asset +- Management can approve rate increases above 10% +- Management can update the staking contract +- Management can set the pool swap fee +- Management can set the tolerance range of all assets +- Management can set the new management address +- Management can set the new guardian address +- Management can trigger pause mode +- Management can trigger killed mode +- Guardian can set the new guardian address +- Guardian can trigger pause mode + +### Pause mode + +_Note_: this mode is to be enabled in the event of extreme market conditions or suspicious LSD minting behaviour or oracle activity. + +- During pause mode, no user may swap assets with the contract +- During pause mode, no user may deposit assets into the contract +- During pause mode, users may only withdraw assets in a balanced manner, single sided withdrawals are not allowed +- During pause mode, weights, rates and rate providers cannot be updated +- During pause mode, a weight and/or amplification ramp cannot be started +- During pause mode, management can trigger killed mode +- During pause mode, management or guardian can undo pause mode to resume normal operation + +### Killed mode + +_Note:_ this mode is to be activated in the event of a LSD depeg, such as a mint bug or a compromised oracle or a critical bug in the protocol. + +- Killed mode may only be activated during pause mode +- During killed mode, pause mode may not be undone +- There is no way to undo killed mode + +## LP token contract specification (yETH) + +- ERC20 contract representing beacon chain ETH +- Management can set and unset addresses that are allowed to mint and burn tokens + +## Staking contract specification (st-yETH) + +- Users can deposit yETH to mint shares representing a proportional amount of the underlying asset contained in the staking contract +- Users can burn shares to receive the underlying asset from the staking contract in proportion to the total number of shares +- The contract caches its own yETH balance, which is separated in buckets: pending, streaming and unlocked. +- Before minting or burning shares, the stored yETH balance is updated + - If the balance has increased, it is added to the pending bucket. If one or more week has been missed, the increase is distributed instead over the three buckets fairly. + - If the balance has decreased, it is subtracted from the pending bucket until it is empty. If the bucket is empty, the remainder is subtracted from the streaming bucket. If that bucket is also empty, the remainder is subtracted from the unlocked bucket +- At the end of the week, the pending bucket becomes the streaming bucket and a new pending bucket is created +- If the first update of the week is in the first day, it is added to the streaming bucket directly instead +- The streaming bucket is unlocked linearly during the week +- User deposits and withdrawals only affect the unlocked bucket +- Each user has an internal vote weight that increases asymptotically to the user's share count. After `t` seconds, their vote weight is `s * t / (t + t_half)` where `s` is the number of shares and `t_half` is the voting half time +- The voting half time determines the time it takes until half the voting weight is reached +- The user's external vote weight is equal to the internal vote weight at the end of the previous week +- Management can set the voting half time +- Users can freely transfer their tokens to other users +- The contract implements ERC20 +- The contract implements ERC4626 + +### Fees + +- The treasury collects a performance fee on the yield generated by the protocol +- The performance fees are credited to the treasury in the form of st-yETH shares +- Management can set the performance fees, within a certain range + +## Rate provider specification + +- Contract has a function that returns the asset rate: the amount of beacon chain ETH backing the asset, per unit token +- Should always return the latest rate and not cache values diff --git a/docs/developers/ypools/yeth/yeth-overview.md b/docs/developers/ypools/yeth/yeth-overview.md new file mode 100644 index 000000000..dcd641eaa --- /dev/null +++ b/docs/developers/ypools/yeth/yeth-overview.md @@ -0,0 +1,24 @@ +# yETH + +## Overview + +yETH is a user-governed yPool consisting of various Ethereum Liquid Staking Derivatives (LSTs). + +The yETH protocol is an Automated Market Maker (AMM) for LSTs. Each LST in the yETH pool is priced according to the amount of [beacon chain](https://ethereum.org/en/upgrades/beacon-chain/) ETH it represents. This lets users deposit their LSTs into the pool and receive yETH tokens pegged 1:1 with beacon chain ETH. Users can also stake their yETH tokens to mint st-yETH, accrue yield, and participate in yETH governance. + +This AMM model, combined with the governance and incentive mechanisms of the yETH protocol, aims to provide an optimal risk-adjusted yield for ETH staking by dynamically adjusting the weights of the LSTs in the pool. It also offers users flexibility with single-sided deposits and withdrawals, and maintains the pool's balance and diversification through a weight management system. + +The yETH protocol is governed by its users, who can vote to adjust the weights of the LSTs in the pool, helping to maximize yield and mitigate risks associated with individual LSTs. + +All yields generated by yETH go to Staked yETH (st-yETH) holders, making yETH an ideal token for Liquidity Providing in stableswap pools like those on Curve. To acquire yETH, users can mint yETH by depositing LSTs or swap against the yETH/ETH Curve pool. + +yETH follows the standard [yPools](../ypools-overview) specification for + +- [Staked Tokens](../ypools-overview.md#staked-ytokens) +- [Staked Token User Vote Weight](../ypools-overview.md#st-ytoken-user-vote-weight) +- [Pool Weights for each LST](../ypools-overview.md#pool-weights-for-each-lst) +- [Single Sided Deposits and Withdrawals](../ypools-overview.md#single-sided-deposits-and-withdrawals) + +[Source Code Repo](https://github.com/yearn/yETH) +[yETH Specification](https://github.com/yearn/yETH/blob/main/SPECIFICATION.md) +[yETH dApp](https://yeth.yearn.fi) diff --git a/docs/developers/ypools/ypools-overview.md b/docs/developers/ypools/ypools-overview.md new file mode 100644 index 000000000..ed76cdbe8 --- /dev/null +++ b/docs/developers/ypools/ypools-overview.md @@ -0,0 +1,95 @@ +# yPools + +## Definitions + +- **yToken**: A token received when depositing an LST into its corresponding yPool. (e.g. yETH) +- **st-yToken**: A token received when staking a yToken. (e.g. st-yETH) + +## Overview + +yPools are user-governed liquidity pool tokens consisting of various like-kind Liquid Staking Derivatives (LSTs). + +The yPools protocol is an Automated Market Maker (AMM) for LSTs. Each LST in the pool is priced according to the amount of underlying assets it represents. This lets users deposit their LSTs into the pool and receive yPool tokens pegged 1:1 with the underlying. Users can also stake their yPool tokens to mint st-yTokens, accrue yield, and participate in their respective yToken governance. + +This AMM model, combined with the governance and incentive mechanisms of the yPools protocol, aims to provide an optimal risk-adjusted yield for LST staking by dynamically adjusting the weights of the LSTs in the pool. It also offers users flexibility with single-sided deposits and withdrawals, and maintains the pool's balance and diversification through a weight management system. + +Each yPool is governed by its users, who can vote to adjust the weights of the LSTs in the pool, helping to maximize yield and mitigate risks associated with individual LSTs. + +All yields generated by a yPool go to the Staked yPool token holders, making yTokens an ideal token for Liquidity Providing in stableswap pools like those on Curve. To acquire a yToken, users can mint it by depositing LSTs or swap against the Curve pool. + +## Staked yTokens + +Users stake their yTokens to mint staked versions (st-yToken), accrue yield, and can later unstake to receive the yToken back according to their earnings. Stakers receive all yield and slashings from the underlying yield sources and earn incentives if they participate and vote in their respective yPool governance. + +By bundling LSTs, yPools aims to generate the best risk-adjusted yield from LSTs. Through protocol governance, st-yToken users can readjust pool weights to maximize yield while mitigating catastrophic scenarios where one or several LSTs in the composition suffer adverse events like de-pegging or security incidents. + +### st-yToken User Vote Weight + +Each user has an internal vote weight that increases asymptotically to the user's share count. After `t` seconds, their vote weight is `s * t / (t + t_half)` where `s` is the number of shares and `t_half` is the voting half-time. The voting half-time variable determines the time it takes until half the voting weight is reached for a staker. + +The user's external vote weight equals the internal vote weight at the end of the previous week. + +For yETH, you can find the current voting half-time on [Etherscan](https://etherscan.io/address/0x583019fF0f430721aDa9cfb4fac8F06cA104d0B4#readContract#F20) in seconds. Thus, the wait to get to half of your st-yETH voting power is 60 days. + +## Pool Weights For Each LST + +Each Liquid Staking Derivative (LST) has an assigned weight representing its proportion in the pool. The weight management system ensures that the pool remains diversified and balanced. As an LST performs well or gains popularity, its weight in the pool may increase, attracting more liquidity and providing better returns. Conversely, if an LST underperforms or faces issues, its weight may decrease, reducing its impact on the overall pool performance. This dynamic adjustment helps maintain an optimal risk-adjusted yield for yPool users. + +For each epoch, users can vote to adjust the weights of the LSTs in the pool. The voting process also involves a "do nothing" option, allowing the current weight distribution to remain unchanged. If a new LST is added during the voting process, it starts at 0% weight and gradually increases to 1% in the first epoch. In the subsequent epoch, they participate like all other LSTs. + +:::note[Example] + +Suppose we have four LSTs: A, B, C, and D with weights 10%, 20%, 30%, and 40% respectively in epoch n. For the next epoch (n+1), C has incentives worth $100. + +The voting outcome for epoch n+1 is: + +- Do nothing: 30% +- A: 7% +- B: 10% +- C: 43% +- D: 10% + +Here's how the voting outcome affects the weights: + +1. The "do nothing" vote is distributed to the current weight, reducing the total redistribution to 7%. +2. The incentives for voting are distributed only to those who explicitly voted for a particular LST, making the incentive system more effective. +3. If a new LST, say E, is added during the voting process, they start at 0% weight and do not fight for the 7% redistribution. They are gradually increased to 1% in the first epoch. In the next epoch, they participate like all other LSTs. + +::: + +## Single-Sided Deposits and Withdrawals + +Single-sided deposits and withdrawals allow users to add or remove a specific asset from the pool. This differs from balanced operations where users deposit or withdraw a proportionate amount of all assets in the pool. Single-sided operations can be more convenient but may also incur bonuses or penalties. + +### Single-Sided Deposits + +When a user makes a single-sided deposit, they add a specific amount of one asset to the pool. The system calculates the equivalent amount of yPool tokens to mint based on the current rate of the deposited asset. + +However, single-sided deposits can distort the balance of assets in the pool. The system applies a deposit penalty if the deposited asset's weight increases above its target weight due to the deposit. This penalty reduces the amount of yPool tokens minted for the depositor, making the deposit operation more expensive. The penalty serves as an incentive for users to maintain the balance of assets in the pool. + +Conversely, the system applies a deposit bonus if the deposited asset's weight is below its target weight. This bonus increases the yPool tokens minted for the depositor, making the deposit operation cheaper. The bonus serves as an incentive for users to restore the balance of assets in the pool. + +### Single-Sided Withdrawals + +Users who make a single-sided withdrawal burn a specific amount of yPool tokens to withdraw one asset from the pool. The system calculates the amount of the asset to send based on the current rate. + +Like single-sided deposits, single-sided withdrawals can also distort the balance of assets in the pool. If the withdrawn asset's weight decreases below its target weight due to the withdrawal, the system applies a withdrawal penalty. This penalty reduces the amount of the asset sent to the withdrawer, making the withdrawal operation more expensive. + +Conversely, the system applies a withdrawal bonus if the withdrawn asset's weight exceeds its target weight. This bonus increases the amount of the asset sent to the withdrawer, effectively making the withdrawal operation cheaper. + +:::note[Example] + +Let's consider a pool with two assets, A and B, with a target weight of 50%. Due to market fluctuations, the current weights are 60% for A and 40% for B. + +- If a user deposits asset A, they will incur a deposit penalty because the deposit increases the weight of A above its target weight. The system will mint fewer yPool tokens for the depositor than the rate would suggest. +- If a user deposits asset B, they will receive a deposit bonus because the deposit brings the weight of B closer to its target weight. The system will mint more yPool tokens for the depositor than the rate would suggest. +- If a user withdraws asset A, they will receive a withdrawal bonus because the withdrawal brings the weight of A closer to its target weight. The system will send more asset A to the withdrawer than the rate would suggest. +- If a user withdraws asset B, they will incur a withdrawal penalty because the withdrawal decreases the weight of B below its target weight. The system will send less asset B to the withdrawer than the rate would suggest. + +::: + +For a deeper dive into the math behind the calculation of yPool weighted stable swap check this paper: https://github.com/yearn/yETH/blob/main/whitepaper/derivation.pdf + +## Protocol Specs + +- The yETH specification can be found in the source repo: https://github.com/yearn/yETH/blob/main/SPECIFICATION.md diff --git a/docs/developers/ypools/ypools-roles.md b/docs/developers/ypools/ypools-roles.md new file mode 100644 index 000000000..7ec35d0ae --- /dev/null +++ b/docs/developers/ypools/ypools-roles.md @@ -0,0 +1,44 @@ +# yPools Roles and Modes + +## Management Role + +Trusted addresses with privileged access for limited operations. Should eventually be replaced by a smart contract: + +- Can start a gradual weight change, as long as no weight change is active yet. +- Can whitelist a new asset, which sets an initial weight, sets the rate provider, and requires an initial deposit. New assets can only be whitelisted if no weight change has been scheduled yet. +- Can update the rate provider for every whitelisted asset. +- Can approve rate increases above 10%. +- Can update the staking contract. +- Can set the pool swap fee. +- Can set the tolerance range. +- Can set the new management address. +- Can set the new guardian addresses. +- Can trigger `pause mode`. +- Can trigger `killed mode`. + +## Guardian Role + +Trusted addresses with emergency privileges: + +- Can trigger pause mode. + +### Pause mode + +> This mode is to be activated in the event of extreme market conditions or detected suspicious behavior, either in the protocol itself or in the underlying LST tokens that back it. + +- No user may swap assets with the contract. +- No user may deposit assets into the contract. +- Users may only withdraw assets in a balanced manner, single-sided withdrawals are not allowed. +- Weights, rates, and rate providers cannot be updated during this mode. +- Management or guardian can undo pause mode to resume normal operation. + +### Killed mode + +> This mode is to be activated in the event of critical failures, whether in the protocol itself or in any of the underlying LST tokens that back it. This can also be used to migrate to a new version of the yETH protocol. + +- No user may deposit assets into the contract. +- Users may only withdraw assets in a balanced manner. +- The reward controller may not update the beacon chain amounts. +- Pause mode may not be undone. + +There is no way to undo `killed mode`. diff --git a/docs/getting-started/products/ypools/yeth/overview.md b/docs/getting-started/products/ypools/yeth/overview.md index ba7d1d98a..6daabed6c 100644 --- a/docs/getting-started/products/ypools/yeth/overview.md +++ b/docs/getting-started/products/ypools/yeth/overview.md @@ -1,5 +1,7 @@ # yETH +![image](/img/product-pages/yeth-banner3.png) + yETH is a user-governed liquidity pool token consisting of various Ethereum Liquid Staking Derivatives (LSTs). The yETH protocol is an Automated Market Maker (AMM) for LSTs. Each LST in the yETH pool is priced according to the amount of [beacon chain](https://ethereum.org/en/upgrades/beacon-chain/) ETH it represents. This lets users deposit their LSTs into the pool and receive yETH tokens pegged 1:1 with beacon chain ETH. Users can also stake their yETH tokens to mint st-yETH, accrue yield, and participate in yETH governance. @@ -18,146 +20,8 @@ Users stake their yETH to mint st-yETH, accrue yield, and later unstake st-yETH By bundling LSTs, st-yETH aims to generate the best risk-adjusted yield from ETH staking. Through protocol governance, st-yETH users can readjust pool weights to maximize yield while mitigating catastrophic scenarios where one or several LSTs in the yETH composition suffer adverse events like de-pegging or security incidents. -### st-yETH user vote weight - -Each user has an internal vote weight that increases asymptotically to the user's share count. After `t` seconds, their vote weight is `s * t / (t + t_half)` where `s` is the number of shares and `t_half` is the voting half-time. - -The voting half-time variable determines the time it takes until half the voting weight is reached for a staker. You can find the current voting half-time on [Etherscan](https://etherscan.io/address/0x583019fF0f430721aDa9cfb4fac8F06cA104d0B4#readContract#F20) in seconds. Thus the wait to get to half of your st-yETH voting power is 60 days. - -The user's external vote weight equals the internal vote weight at the end of the previous week. - -## Pool Weights for each LST - -In yETH, each Liquid Staking Derivative (LST) has an assigned weight representing its proportion in the pool. The weight management system ensures that the pool remains diversified and balanced. As an LST performs well or gains popularity, its weight in the pool may increase, attracting more liquidity and providing better returns. Conversely, if an LST underperforms or faces issues, its weight may decrease, reducing its impact on the overall pool performance. This dynamic adjustment helps maintain an optimal risk-adjusted yield for yETH users. - -For each epoch, users can vote to adjust the weights of the LSTs in the pool. The voting process also involves a "do nothing" option, allowing the current weight distribution to remain unchanged. If a new LST is added during the voting process, it starts at 0% weight and gradually increases to 1% in the first epoch. In the subsequent epoch, they participate like all other LSTs. - -### Example - -Suppose we have four LSTs: A, B, C, and D with weights 10%, 20%, 30%, and 40% respectively in epoch n. For the next epoch (n+1), C has incentives worth $100. - -The voting outcome for epoch n+1 is: - -- Do nothing: 30% -- A: 7% -- B: 10% -- C: 43% -- D: 10% - -Here's how the voting outcome affects the weights: - -1. The "do nothing" vote is distributed to the current weight, reducing the total redistribution to 7%. -2. The incentives for voting are distributed only to those who explicitly voted for a particular LST, making the incentive system more effective. -3. If a new LST, say E, is added during the voting process, they start at 0% weight and do not fight for the 7% redistribution. They are gradually increased to 1% in the first epoch. In the next epoch, they participate like all other LSTs. - -## Single-Sided Deposits and Withdrawals - -Single-sided deposits and withdrawals allow users to add or remove a specific asset from the pool. This differs from balanced operations where users deposit or withdraw a proportionate amount of all assets in the pool. Single-sided operations can be more convenient but may also incur bonuses or penalties. - -### Single-Sided Deposits - -When a user makes a single-sided deposit, they add a specific amount of one asset to the pool. The system calculates the equivalent amount of yETH tokens to mint based on the current rate of the deposited asset. - -However, single-sided deposits can distort the balance of assets in the pool. The system applies a deposit penalty if the deposited asset's weight increases above its target weight due to the deposit. This penalty reduces the amount of yETH tokens minted for the depositor, making the deposit operation more expensive. The penalty serves as an incentive for users to maintain the balance of assets in the pool. - -Conversely, the system applies a deposit bonus if the deposited asset's weight is below its target weight. This bonus increases the yETH tokens minted for the depositor, making the deposit operation cheaper. The bonus serves as an incentive for users to restore the balance of assets in the pool. - -### Single-Sided Withdrawals - -Users who make a single-sided withdrawal burn a specific amount of yETH tokens to withdraw one asset from the pool. The system calculates the amount of the asset to send based on the current rate. - -Like single-sided deposits, single-sided withdrawals can also distort the balance of assets in the pool. If the withdrawn asset's weight decreases below its target weight due to the withdrawal, the system applies a withdrawal penalty. This penalty reduces the amount of the asset sent to the withdrawer, making the withdrawal operation more expensive. - -Conversely, the system applies a withdrawal bonus if the withdrawn asset's weight exceeds its target weight. This bonus increases the amount of the asset sent to the withdrawer, effectively making the withdrawal operation cheaper. - -### Examples - -Let's consider a pool with two assets, A and B, with a target weight of 50%. Due to market fluctuations, the current weights are 60% for A and 40% for B. - -- If a user deposits asset A, they will incur a deposit penalty because the deposit increases the weight of A above its target weight. The system will mint fewer yETH tokens for the depositor than the rate would suggest. -- If a user deposits asset B, they will receive a deposit bonus because the deposit brings the weight of B closer to its target weight. The system will mint more yETH tokens for the depositor than the rate would suggest. -- If a user withdraws asset A, they will receive a withdrawal bonus because the withdrawal brings the weight of A closer to its target weight. The system will send more asset A to the withdrawer than the rate would suggest. -- If a user withdraws asset B, they will incur a withdrawal penalty because the withdrawal decreases the weight of B below its target weight. The system will send less asset B to the withdrawer than the rate would suggest. - -For a deeper dive into the math behind the calculation of yETH weighted stable swap check this paper: https://github.com/yearn/yETH/blob/main/whitepaper/derivation.pdf - -## Contracts & Roles - -| Name | Address | -|--------------------------------------------------|-------------------------------------| -| yETH | [`0x1BED97CBC3c24A4fb5C069C6E311a967386131f7`](https://etherscan.io/address/0x1BED97CBC3c24A4fb5C069C6E311a967386131f7) | -| st-yETH | [`0x583019fF0f430721aDa9cfb4fac8F06cA104d0B4`](https://etherscan.io/address/0x583019fF0f430721aDa9cfb4fac8F06cA104d0B4) | -| Management | [`0xbBBBBbbB6B942883EAd4976882C99201108c784d`](https://etherscan.io/address/0xbBBBBbbB6B942883EAd4976882C99201108c784d) | -| Protocol Owned Liquidity | [`0x929401e30Aab6bd648dEf2d30FF44952BaB04478`](https://etherscan.io/address/0x929401e30Aab6bd648dEf2d30FF44952BaB04478) | -| Bootstrap: Deposit, Vote, Claim Incentives | [`0x41B994C192183793bB9cc35bAAb8bD9C6885c6bf`](https://etherscan.io/address/0x41B994C192183793bB9cc35bAAb8bD9C6885c6bf) | -| Bootstrap: Claim st-yETH | [`0x7cf484D9d16BA26aB3bCdc8EC4a73aC50136d491`](https://etherscan.io/address/0x7cf484D9d16BA26aB3bCdc8EC4a73aC50136d491) | -| Guardian | [`0xDC775e813cDB38a4f02c4BAd3942319088018eFA`](https://etherscan.io/address/0xDC775e813cDB38a4f02c4BAd3942319088018eFA) | -| Pool | [`0x0Ca1bd1301191576Bea9b9afCFD4649dD1Ba6822`](https://etherscan.io/address/0x0Ca1bd1301191576Bea9b9afCFD4649dD1Ba6822) | -| Rate Providers | [`0x90cfBe0fCccfbd6F895E3b065Aa45C56B635903B`](https://etherscan.io/address/0x90cfBe0fCccfbd6F895E3b065Aa45C56B635903B) | - -> Due to a redeploy of st-yETH during the bootstrap process the first st-yETH contract has been deprecated, use the `Bootstrap: Claim st-yETH` contract to claim the new version if you participated in the bootstrap phase. - -
- - - -### Deprecated Contract Addresses - - ---- - -These contracts were deprecated when mevETH was removed from yETH. - -| Name | Address | -|--------------------------------------------------|-------------------------------------| -| Deprecated Pool | [`0x2cced4ffA804ADbe1269cDFc22D7904471aBdE63`](https://etherscan.io/address/0x2cced4ffA804ADbe1269cDFc22D7904471aBdE63) | -| Deprecated Rate Providers | [`0x4e322aeAf355dFf8fb9Fd5D18F3D87667E8f8316`](https://etherscan.io/address/0x4e322aeAf355dFf8fb9Fd5D18F3D87667E8f8316) | - -
- -## Management Role - -Trusted addresses with privileged access for limited operations. Should eventually be replaced by a smart contract: - -- Can start a gradual weight change, as long as no weight change is active yet. -- Can whitelist a new asset, which sets an initial weight, sets the rate provider, and requires an initial deposit. New assets can only be whitelisted if no weight change has been scheduled yet. -- Can update the rate provider for every whitelisted asset. -- Can approve rate increases above 10%. -- Can update the staking contract. -- Can set the pool swap fee. -- Can set the tolerance range. -- Can set the new management address. -- Can set the new guardian addresses. -- Can trigger `pause mode`. -- Can trigger `killed mode`. - -### Pause mode - -> This mode is to be activated in the event of extreme market conditions or detected suspicious behavior, either in the protocol itself or in the underlying LST tokens that back it. - -- No user may swap assets with the contract. -- No user may deposit assets into the contract. -- Users may only withdraw assets in a balanced manner, single-sided withdrawals are not allowed. -- Weights, rates, and rate providers cannot be updated during this mode. -- Management or guardian can undo pause mode to resume normal operation. - -### Killed mode - -> This mode is to be activated in the event of critical failures, whether in the protocol itself or in any of the underlying LST tokens that back it. This can also be used to migrate to a new version of the yETH protocol. - -- No user may deposit assets into the contract. -- Users may only withdraw assets in a balanced manner. -- The reward controller may not update the beacon chain amounts. -- Pause mode may not be undone. - -There is no way to undo `killed mode`. - -## Guardian Role - -Trusted addresses with emergency privileges: - -- Can trigger pause mode. - -## Protocol Specs +Get yETH, swap it for st-yETH, vote and more on the app: +[yETH dApp](https://yeth.yearn.fi/) -- The yETH specification can be found in the source repo: https://github.com/yearn/yETH/blob/main/SPECIFICATION.md +Want to dive into the details? Check out the Developer Docs: +[yETH Developer Docs](/developers/ypools/yeth/yeth-overview) diff --git a/docs/getting-started/products/ypools/yeth/yeth-faq.md b/docs/getting-started/products/ypools/yeth/yeth-faq.md index e6f41e965..7c51c6ab8 100644 --- a/docs/getting-started/products/ypools/yeth/yeth-faq.md +++ b/docs/getting-started/products/ypools/yeth/yeth-faq.md @@ -6,7 +6,7 @@ yETH earns APY through various sources: - Swap fee income from the Automated Market Maker (AMM) - Incentives fee income, which are incentives for staker participation in governance -- Liquid Staking Derivative (LSD) income from staked ETH yield +- Liquid Staking Token (LST) income from staked ETH yield - Buying LSTs at a discount - Whitelisting fees @@ -16,16 +16,22 @@ Please note that yield is paid out one week after it is generated. The yield gen ### What other benefits does yETH give holders? -yETH provides diversification by holding a basket of LSDs, which helps to spread the risk. +yETH provides diversification by holding a basket of LSTs, which helps to spread the risk. ### How is yield passed onto stakers? -LSD Protocols generate yield and update their on-chain rates. This results in yETH being minted and sent to st-yETH. +LST Protocols generate yield and update their on-chain rates. This results in yETH being minted and sent to st-yETH. -### Can I withdraw multiple LSDs? +### Can I withdraw multiple LSTs? -Yes, you can withdraw multiple LSDs or just one LSD. However, your withdrawal cannot cause an LSD to leave its safety bands around its target weight. For example, if a pool has a weight of 20% and a band of 5%, the actual weight is allowed to be between 15% and 25%. This caps losses to at most 25%, assuming that token permanently depegs and goes to 0. In that worst-case scenario, yETH depegs to 0.75 ETH, because you can always do a balanced withdrawal of all the assets, of which only 25% is worthless. Compare this with holding the token by yourself, you’d be -100%. +Yes, you can withdraw multiple LSTs or just one LST. However, your withdrawal cannot cause an LST to leave its safety bands around its target weight. For example, if a pool has a weight of 20% and a band of 5%, the actual weight is allowed to be between 15% and 25%. This caps losses to at most 25%, assuming that token permanently depegs and goes to 0. In that worst-case scenario, yETH depegs to 0.75 ETH, because you can always do a balanced withdrawal of all the assets, of which only 25% is worthless. Compare this with holding the token by yourself, you’d be -100%. ### Is there slippage with proportional withdrawal? No, there is no slippage with proportional withdrawal. + +### How do I vote? + +Every 4th week (known as an epoch in yPools speak), st-yETH holders can vote on the composition of yETH. You can do that here: https://yeth.yearn.fi/vote?action=weight + +You must have held st-yETH from the beginning of the epoch . Your voting power grows over time. Read more about how it is calculated [here](/developers/ypools/ypools-overview#st-ytoken-user-vote-weight) diff --git a/docs/getting-started/products/ypools/ypools-overview.md b/docs/getting-started/products/ypools/ypools-overview.md new file mode 100644 index 000000000..fdff62318 --- /dev/null +++ b/docs/getting-started/products/ypools/ypools-overview.md @@ -0,0 +1,43 @@ +--- +rpcCalls: + - name: 'yPools Governance' + chain: '1' + address: '0xB7a528CF6D36F736Fa678A629b98A427d43E5ba5' + abiName: 'yPoolsGenericGovernorABI' + methods: + - 'epoch' + - 'propose_open' + - 'vote_open' + - 'genesis' +--- + +# yPools + +![image](/img/product-pages/ypools-banner3.png) + +yPools are user-governed baskets of similar assets, typically liquid staking tokens(LSTs). They serve as both a re-balancing index and liquidity pool (AMM) of the underlying tokens. This AMM model, combined with governance and incentive mechanisms, aims to provide an optimal risk-adjusted yield by dynamically adjusting the weights of the LST tokens in the pool. + +Users can stake their yPool tokens to mint st-yTokens, accrue yield, and later unstake st-yTokens to receive yPool tokens back according to their earnings. Only stakers receive yield and slashings from the underlying yield sources and earn incentives if they participate and vote in their respective yPool's governance process. + +## Governance at a Glance + +yPools are governed by their depositors, who stake their yPool tokens for governance power. Once staked, yPool token holders can set pool parameters, weights as well as vote on whitelisted pool assets. The governance process follows a 4 week cadence of *epochs* where the first 3 weeks are reserved for proposals for new LSTs to be added to the pool and a final week where proposals and pool weights are voted on. + +:::yearnData[Live Governance Info] + + + +::: + +## yETH + +The inaugural yPool is yETH, which is a basket of LSTs of Beacon Chain ETH (stETH, rETH, etc.), earning Ethereum's Validator Staking rewards. + +[yETH Docs](./yeth/overview) + +[yETH dApp](https://yeth.yearn.fi/) + +## More Info + +Want to dive into the details? Check out the Developer Docs: +[yPools Developer Docs](/developers/ypools/ypools-overview) diff --git a/docusaurus.config.js b/docusaurus.config.js index b69c81d0b..6a798a4d6 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -11,6 +11,7 @@ const alchemyKey = process.env.ALCHEMY_API_KEY || 'unknown' const yDaemon = process.env.YDAEMON_ENDPOINT || 'unknown' const yPriceMagic = process.env.YPRICEMAGIC_ENDPOINT || 'unknown' + export default { title: 'Yearn Docs', tagline: 'DeFi made simple', @@ -30,6 +31,7 @@ export default { alchemyKey, yDaemon, yPriceMagic, + }, themes: ['@docusaurus/theme-mermaid'], themeConfig: { diff --git a/scripts/runAddressChecks.ts b/scripts/runAddressChecks.ts index 512d4cc9a..2b539bc1a 100644 --- a/scripts/runAddressChecks.ts +++ b/scripts/runAddressChecks.ts @@ -39,7 +39,7 @@ const fetchAddresses = async () => { failedChecks ) v3CheckFlag = topLevelData?.checkFlag - + if (!topLevelData) throw new Error('Failed to fetch top-level contract addresses') diff --git a/sidebars/sidebarsDeveloperDocs.js b/sidebars/sidebarsDeveloperDocs.js index f816998b4..4df0ac3a1 100644 --- a/sidebars/sidebarsDeveloperDocs.js +++ b/sidebars/sidebarsDeveloperDocs.js @@ -82,6 +82,37 @@ export default { 'v2/naming-convention', ], }, + // yPools + { + type: 'category', + label: 'yPools', + link: { + type: 'doc', + id: 'ypools/ypools-overview', + }, + items: [ + { + type: 'category', + label: 'yETH', + link: { + type: 'doc', + id: 'ypools/yeth/yeth-overview', + }, + items: [ + 'ypools/yeth/adding-assets', + 'ypools/yeth/create-gov-proposal', + 'ypools/yeth/yETH-spec', + 'ypools/yeth/periphery-spec', + ], + }, + 'ypools/ypools-roles', + { + type: 'link', + label: 'yPools Contract Addresses →', + href: '/developers/addresses/ypools-contracts', + }, + ], + }, // Yearn Data Services { type: 'category', @@ -179,6 +210,11 @@ export default { label: 'yPRISMA', id: 'addresses/yprisma-contracts', }, + { + type: 'doc', + label: 'yPools', + id: 'addresses/ypools-contracts', + }, { type: 'doc', label: 'Lens', diff --git a/sidebars/sidebarsUserDocs.js b/sidebars/sidebarsUserDocs.js index fdd4a7370..98bfea859 100644 --- a/sidebars/sidebarsUserDocs.js +++ b/sidebars/sidebarsUserDocs.js @@ -125,6 +125,10 @@ module.exports = { { type: 'category', label: 'yPools', + link: { + type: 'doc', + id: 'products/ypools/ypools-overview', + }, items: [ { type: 'category', @@ -146,6 +150,11 @@ module.exports = { }, ], }, + { + type: 'link', + label: 'yPools Contract Addresses →', + href: '/developers/addresses/ypools-contracts', + }, ], }, { diff --git a/src/components/AbiEncoder.tsx b/src/components/AbiEncoder.tsx new file mode 100644 index 000000000..0b3c6a98c --- /dev/null +++ b/src/components/AbiEncoder.tsx @@ -0,0 +1,114 @@ +import React, { useState, useMemo } from 'react' +import * as AllABIs from '../ethereum/ABIs' +import { encodeFunctionData } from 'viem' +import { + Card, + CardHeader, + CardTitle, + CardContent, + CardFooter, +} from './shadcn/card/card' +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectItem, +} from './shadcn/select/select' +import Input from './shadcn/input/input' +import { Button } from './shadcn/button/button' + +interface AbiEncodingWidgetProps { + defaultAbi?: string + defaultFunction?: string +} + +export function AbiEncodingWidget({ + defaultAbi = 'yPoolsInclusionVoteABI', + defaultFunction = 'set_enable_epoch', +}: AbiEncodingWidgetProps) { + const [selectedAbiKey, setSelectedAbiKey] = useState(defaultAbi) + const [selectedFunction, setSelectedFunction] = useState(defaultFunction) + const [argValue, setArgValue] = useState('') + const [encodedData, setEncodedData] = useState('') + + const allAbiKeys = Object.keys(AllABIs).filter((key) => + Array.isArray((AllABIs as any)[key]) + ) + const functions = useMemo(() => { + const abi = (AllABIs as any)[selectedAbiKey] || [] + return abi + .filter((entry: any) => entry.type === 'function') + .map((entry: any) => entry.name) + }, [selectedAbiKey]) + + const encodeData = () => { + try { + const abi = (AllABIs as any)[selectedAbiKey] + const data = encodeFunctionData({ + abi, + functionName: selectedFunction, + // For simplicity, assume one argument that requires a bigint + args: [BigInt(argValue || '0')], + }) + setEncodedData(data) + } catch (err) { + setEncodedData('Error encoding data') + } + } + + return ( + + + ABI Encoder + + + + + setArgValue(e.target.value)} + /> + + + + + {encodedData && ( +
+ {encodedData} +
+ )} +
+ ) +} +export default AbiEncodingWidget diff --git a/src/components/AbiEncodingWidget.tsx b/src/components/AbiEncodingWidget.tsx new file mode 100644 index 000000000..60a65131e --- /dev/null +++ b/src/components/AbiEncodingWidget.tsx @@ -0,0 +1,149 @@ +import React, { useState, useMemo } from 'react' +import * as AllABIs from '../ethereum/ABIs' +import { encodeFunctionData } from 'viem' +import { + Card, + CardHeader, + CardTitle, + CardContent, + CardFooter, +} from './shadcn/card/card' +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectItem, +} from './shadcn/select/select' +import Input from './shadcn/input/input' +import { Button } from './shadcn/button/button' +import styles from '../css/widgets.module.css' +import { CopyIcon } from 'lucide-react' + +interface AbiEncodingWidgetProps { + defaultAbi?: string + defaultFunction?: string + widgetTitle?: string | undefined + functionArg?: string +} + +export function AbiEncodingWidget({ + defaultAbi = 'yPoolsInclusionVoteABI', + defaultFunction = 'set_enable_epoch', + widgetTitle = undefined, + functionArg = '', +}: AbiEncodingWidgetProps) { + const [selectedAbiKey, setSelectedAbiKey] = useState(defaultAbi) + const [selectedFunction, setSelectedFunction] = useState(defaultFunction) + const [argValue, setArgValue] = useState(functionArg) + const [encodedData, setEncodedData] = useState('') + + const allAbiKeys = Object.keys(AllABIs).filter((key) => + Array.isArray((AllABIs as any)[key]) + ) + const functions = useMemo(() => { + const abi = (AllABIs as any)[selectedAbiKey] || [] + return abi + .filter((entry: any) => entry.type === 'function') + .map((entry: any) => entry.name) + }, [selectedAbiKey]) + + const encodeData = () => { + try { + const abi = (AllABIs as any)[selectedAbiKey] + const data = encodeFunctionData({ + abi, + functionName: selectedFunction, + // For simplicity, assume one argument that requires a bigint + args: [BigInt(argValue || '0')], + }) + setEncodedData(data) + } catch (err) { + setEncodedData('Error encoding data') + } + } + + const copyToClipboard = () => { + navigator.clipboard.writeText(encodedData) + } + + return ( + <> + + {widgetTitle && ( + + {widgetTitle} + + )} + + + + setArgValue(e.target.value)} + /> + + + + {encodedData && ( + <> + Result: + + + {encodedData} + +
+ +
+
+ + )} +
+
+ + ) +} + +export default AbiEncodingWidget diff --git a/src/components/GovDataYPools.tsx b/src/components/GovDataYPools.tsx new file mode 100644 index 000000000..145a2a9c9 --- /dev/null +++ b/src/components/GovDataYPools.tsx @@ -0,0 +1,106 @@ +import React, { useContext, useEffect, useState } from 'react' +import { ContractDataContext } from '../context/ContractDataContext' + +enum GovernanceState { + PROPOSAL = 'Proposal Period', + VOTING = 'Voting Period', + NONE = 'None', +} + +const GovDataYPools = () => { + const data = useContext(ContractDataContext) + const yPoolsGovernance = data?.['yPools Governance'] + const blockTimestamp = data?.['blockTimestamp'] + + const [governanceState, setGovernanceState] = useState(GovernanceState.NONE) + const [votingPeriodTime, setVotingPeriodTime] = useState(null) + const [epochEndTime, setEpochEndTime] = useState(null) + + useEffect(() => { + if (!yPoolsGovernance || !blockTimestamp) return + + const { genesis } = yPoolsGovernance + if (!genesis) return + + const epochDuration = 4 * 7 * 24 * 60 * 60 + const proposalDuration = 3 * 7 * 24 * 60 * 60 + const votingDuration = 1 * 7 * 24 * 60 * 60 + + const timeSinceGenesis = blockTimestamp - Number(genesis) + const currentEpochTime = timeSinceGenesis % epochDuration + + let newGovernanceState = GovernanceState.NONE + let newVotingPeriodTime: string | null = null + + // Always calculate current epoch's end time + const currentEpochStart = + Number(genesis) + + Math.floor(timeSinceGenesis / epochDuration) * epochDuration + const currentEpochEnd = currentEpochStart + epochDuration + const newEpochEndTime = new Date(currentEpochEnd * 1000).toLocaleString( + 'en-US', + { timeZone: 'UTC' } + ) + setEpochEndTime(newEpochEndTime) + + if (currentEpochTime < proposalDuration) { + newGovernanceState = GovernanceState.PROPOSAL + const startOfVotingPeriod = currentEpochStart + proposalDuration + newVotingPeriodTime = new Date(startOfVotingPeriod * 1000).toLocaleString( + 'en-US', + { timeZone: 'UTC' } + ) + } else { + newGovernanceState = GovernanceState.VOTING + const endOfVotingPeriod = currentEpochStart + epochDuration + newVotingPeriodTime = new Date(endOfVotingPeriod * 1000).toLocaleString( + 'en-US', + { timeZone: 'UTC' } + ) + } + + if (newGovernanceState !== governanceState) { + setGovernanceState(newGovernanceState) + } + if (newVotingPeriodTime !== votingPeriodTime) { + setVotingPeriodTime(newVotingPeriodTime) + } + }, [yPoolsGovernance, blockTimestamp, governanceState, votingPeriodTime]) + + if (!yPoolsGovernance) { + return 'Fetching contract data...' + } + + const { epoch } = yPoolsGovernance + + return ( +
+
+ Current Epoch: {epoch?.toString()} +
+
+ Governance State: {governanceState} +
+ {votingPeriodTime && ( +
+ {governanceState === GovernanceState.PROPOSAL ? ( +
+ Start of Voting Period: {votingPeriodTime} UTC +
+ ) : ( +
+ End of Voting Period: {votingPeriodTime} UTC +
+ )} +
+ )} + {epochEndTime && ( +
+ End of Current Epoch: {epochEndTime} UTC +
+ )} +
+ ) +} + +export default GovDataYPools diff --git a/src/components/veYFI-calculator.tsx b/src/components/veYFI-calculator.tsx index 2a3de01f6..bc0bdfa6a 100644 --- a/src/components/veYFI-calculator.tsx +++ b/src/components/veYFI-calculator.tsx @@ -31,7 +31,6 @@ import { ResponsiveContainer, } from 'recharts' import styles from '../css/veYFI-calc.module.css' -import Label from './shadcn/label/label' import { Button } from './shadcn/button/button' import VeYFILockCalculator from './VeYFILockCalculator' // Import the new component diff --git a/src/context/ContractDataContext.tsx b/src/context/ContractDataContext.tsx index 77a635e35..f4ca377d5 100644 --- a/src/context/ContractDataContext.tsx +++ b/src/context/ContractDataContext.tsx @@ -64,7 +64,12 @@ const fetchData = async ( (arg0: (prevData: any) => any): void } ) => { + console.log('contractReadParams', contractReadParams) try { + // Fetch the latest block timestamp + const block = await publicClient.getBlock({ blockTag: 'latest' }) + const blockTimestamp = Number(block.timestamp) + for (const contractReadCall of contractReadParams) { const address = contractReadCall.address const abi = ABIs[contractReadCall.abiName] @@ -105,6 +110,7 @@ const fetchData = async ( } newData[contractReadCall.name][methodName] = result }) + newData['blockTimestamp'] = blockTimestamp return newData }) } diff --git a/src/css/widgets.module.css b/src/css/widgets.module.css new file mode 100644 index 000000000..62b62f555 --- /dev/null +++ b/src/css/widgets.module.css @@ -0,0 +1,48 @@ +.CardHeader { + padding: 0; + padding-bottom: 1rem; +} + +.CardContent { + display: flex; + flex-direction: column; + position: relative; + padding: 0 1.5rem 0 1.5rem; + gap: 1rem; +} + +.inputElements { + display: flex; + flex-direction: row; + gap: 1rem; + width: 100%; +} + +.CardFooter { + display: flex; + flex-direction: column; + gap: 1rem; + align-items: left; + padding: 0; + justify-content: space-between; + padding-top: 1rem; +} + +.encodedDataCard { + margin-top: -1rem; + padding: 0.5rem; + cursor: pointer; + position: relative; +} + +.copyIcon { + position: absolute; + top: 10px; + right: 10px; + opacity: 0; + transition: opacity 0.3s; +} + +.encodedDataCard:hover .copyIcon { + opacity: 0.5; +} diff --git a/src/ethereum/ABIs/index.ts b/src/ethereum/ABIs/index.ts index be8c48ace..4ee699709 100644 --- a/src/ethereum/ABIs/index.ts +++ b/src/ethereum/ABIs/index.ts @@ -11,3 +11,5 @@ export * from './v3ReleaseRegistryABI' export * from './v3VaultFactoryABI' export * from './yearnV3RoleManagerABI' export * from './v3VaultFactoryBlueprintABI' +export * from './yPoolsGenericGovernorABI' +export * from './yPoolsInclusionVoteABI' diff --git a/src/ethereum/ABIs/yPoolsGenericGovernorABI.ts b/src/ethereum/ABIs/yPoolsGenericGovernorABI.ts new file mode 100644 index 000000000..0ec271355 --- /dev/null +++ b/src/ethereum/ABIs/yPoolsGenericGovernorABI.ts @@ -0,0 +1,389 @@ +export const yPoolsGenericGovernorABI = [ + { + name: 'Propose', + inputs: [ + { name: 'idx', type: 'uint256', indexed: true }, + { name: 'epoch', type: 'uint256', indexed: true }, + { name: 'author', type: 'address', indexed: true }, + { name: 'ipfs', type: 'bytes32', indexed: false }, + { name: 'script', type: 'bytes', indexed: false }, + ], + anonymous: false, + type: 'event', + }, + { + name: 'Retract', + inputs: [{ name: 'idx', type: 'uint256', indexed: true }], + anonymous: false, + type: 'event', + }, + { + name: 'Cancel', + inputs: [{ name: 'idx', type: 'uint256', indexed: true }], + anonymous: false, + type: 'event', + }, + { + name: 'Vote', + inputs: [ + { name: 'account', type: 'address', indexed: true }, + { name: 'idx', type: 'uint256', indexed: true }, + { name: 'yea', type: 'uint256', indexed: false }, + { name: 'nay', type: 'uint256', indexed: false }, + { name: 'abstain', type: 'uint256', indexed: false }, + ], + anonymous: false, + type: 'event', + }, + { + name: 'Enact', + inputs: [ + { name: 'idx', type: 'uint256', indexed: true }, + { name: 'by', type: 'address', indexed: true }, + ], + anonymous: false, + type: 'event', + }, + { + name: 'SetMeasure', + inputs: [{ name: 'measure', type: 'address', indexed: true }], + anonymous: false, + type: 'event', + }, + { + name: 'SetExecutor', + inputs: [{ name: 'executor', type: 'address', indexed: true }], + anonymous: false, + type: 'event', + }, + { + name: 'SetDelay', + inputs: [{ name: 'delay', type: 'uint256', indexed: false }], + anonymous: false, + type: 'event', + }, + { + name: 'SetQuorum', + inputs: [{ name: 'quorum', type: 'uint256', indexed: false }], + anonymous: false, + type: 'event', + }, + { + name: 'SetMajority', + inputs: [{ name: 'majority', type: 'uint256', indexed: false }], + anonymous: false, + type: 'event', + }, + { + name: 'SetProposeMinWeight', + inputs: [{ name: 'min_weight', type: 'uint256', indexed: false }], + anonymous: false, + type: 'event', + }, + { + name: 'PendingManagement', + inputs: [{ name: 'management', type: 'address', indexed: true }], + anonymous: false, + type: 'event', + }, + { + name: 'SetManagement', + inputs: [{ name: 'management', type: 'address', indexed: true }], + anonymous: false, + type: 'event', + }, + { + stateMutability: 'nonpayable', + type: 'constructor', + inputs: [ + { name: '_genesis', type: 'uint256' }, + { name: '_measure', type: 'address' }, + { name: '_executor', type: 'address' }, + { name: '_quorum', type: 'uint256' }, + { name: '_majority', type: 'uint256' }, + { name: '_delay', type: 'uint256' }, + ], + outputs: [], + }, + { + stateMutability: 'view', + type: 'function', + name: 'epoch', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'propose_open', + inputs: [], + outputs: [{ name: '', type: 'bool' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'vote_open', + inputs: [], + outputs: [{ name: '', type: 'bool' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'quorum', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'previous_quorum', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'majority', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'previous_majority', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'delay', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'previous_delay', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'proposal', + inputs: [{ name: '_idx', type: 'uint256' }], + outputs: [ + { + name: '', + type: 'tuple', + components: [ + { name: 'epoch', type: 'uint256' }, + { name: 'author', type: 'address' }, + { name: 'ipfs', type: 'bytes32' }, + { name: 'state', type: 'uint256' }, + { name: 'hash', type: 'bytes32' }, + { name: 'yea', type: 'uint256' }, + { name: 'nay', type: 'uint256' }, + { name: 'abstain', type: 'uint256' }, + ], + }, + ], + }, + { + stateMutability: 'view', + type: 'function', + name: 'proposal_state', + inputs: [{ name: '_idx', type: 'uint256' }], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'update_proposal_state', + inputs: [{ name: '_idx', type: 'uint256' }], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'propose', + inputs: [ + { name: '_ipfs', type: 'bytes32' }, + { name: '_script', type: 'bytes' }, + ], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'retract', + inputs: [{ name: '_idx', type: 'uint256' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'cancel', + inputs: [{ name: '_idx', type: 'uint256' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'vote_yea', + inputs: [{ name: '_idx', type: 'uint256' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'vote_nay', + inputs: [{ name: '_idx', type: 'uint256' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'vote_abstain', + inputs: [{ name: '_idx', type: 'uint256' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'vote', + inputs: [ + { name: '_idx', type: 'uint256' }, + { name: '_yea', type: 'uint256' }, + { name: '_nay', type: 'uint256' }, + { name: '_abstain', type: 'uint256' }, + ], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'enact', + inputs: [ + { name: '_idx', type: 'uint256' }, + { name: '_script', type: 'bytes' }, + ], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_measure', + inputs: [{ name: '_measure', type: 'address' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_executor', + inputs: [{ name: '_executor', type: 'address' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_quorum', + inputs: [{ name: '_quorum', type: 'uint256' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_majority', + inputs: [{ name: '_majority', type: 'uint256' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_delay', + inputs: [{ name: '_delay', type: 'uint256' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_propose_min_weight', + inputs: [{ name: '_propose_min_weight', type: 'uint256' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_management', + inputs: [{ name: '_management', type: 'address' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'accept_management', + inputs: [], + outputs: [], + }, + { + stateMutability: 'view', + type: 'function', + name: 'genesis', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'management', + inputs: [], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'pending_management', + inputs: [], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'measure', + inputs: [], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'executor', + inputs: [], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'propose_min_weight', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'num_proposals', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'voted', + inputs: [ + { name: 'arg0', type: 'address' }, + { name: 'arg1', type: 'uint256' }, + ], + outputs: [{ name: '', type: 'bool' }], + }, +] as const diff --git a/src/ethereum/ABIs/yPoolsInclusionVoteABI.ts b/src/ethereum/ABIs/yPoolsInclusionVoteABI.ts new file mode 100644 index 000000000..09789b16f --- /dev/null +++ b/src/ethereum/ABIs/yPoolsInclusionVoteABI.ts @@ -0,0 +1,427 @@ +export const yPoolsInclusionVoteABI = [ + { + name: 'Apply', + inputs: [ + { name: 'epoch', type: 'uint256', indexed: true }, + { name: 'token', type: 'address', indexed: true }, + { name: 'account', type: 'address', indexed: false }, + ], + anonymous: false, + type: 'event', + }, + { + name: 'Whitelist', + inputs: [ + { name: 'epoch', type: 'uint256', indexed: true }, + { name: 'token', type: 'address', indexed: true }, + { name: 'idx', type: 'uint256', indexed: false }, + ], + anonymous: false, + type: 'event', + }, + { + name: 'Vote', + inputs: [ + { name: 'epoch', type: 'uint256', indexed: true }, + { name: 'account', type: 'address', indexed: true }, + { name: 'weight', type: 'uint256', indexed: false }, + { name: 'votes', type: 'uint256[]', indexed: false }, + ], + anonymous: false, + type: 'event', + }, + { + name: 'Finalize', + inputs: [ + { name: 'epoch', type: 'uint256', indexed: true }, + { name: 'winner', type: 'address', indexed: true }, + ], + anonymous: false, + type: 'event', + }, + { + name: 'SetRateProvider', + inputs: [ + { name: 'token', type: 'address', indexed: true }, + { name: 'provider', type: 'address', indexed: false }, + ], + anonymous: false, + type: 'event', + }, + { + name: 'SetEnableEpoch', + inputs: [{ name: 'epoch', type: 'uint256', indexed: false }], + anonymous: false, + type: 'event', + }, + { + name: 'SetOperator', + inputs: [{ name: 'operator', type: 'address', indexed: true }], + anonymous: false, + type: 'event', + }, + { + name: 'SetTreasury', + inputs: [{ name: 'treasury', type: 'address', indexed: true }], + anonymous: false, + type: 'event', + }, + { + name: 'SetFeeToken', + inputs: [{ name: 'fee_token', type: 'address', indexed: true }], + anonymous: false, + type: 'event', + }, + { + name: 'SetFees', + inputs: [ + { name: 'initial', type: 'uint256', indexed: false }, + { name: 'subsequent', type: 'uint256', indexed: false }, + ], + anonymous: false, + type: 'event', + }, + { + name: 'SetMeasure', + inputs: [{ name: 'measure', type: 'address', indexed: true }], + anonymous: false, + type: 'event', + }, + { + name: 'PendingManagement', + inputs: [{ name: 'management', type: 'address', indexed: true }], + anonymous: false, + type: 'event', + }, + { + name: 'SetManagement', + inputs: [{ name: 'management', type: 'address', indexed: true }], + anonymous: false, + type: 'event', + }, + { + stateMutability: 'nonpayable', + type: 'constructor', + inputs: [ + { name: '_genesis', type: 'uint256' }, + { name: '_measure', type: 'address' }, + { name: '_fee_token', type: 'address' }, + ], + outputs: [], + }, + { + stateMutability: 'view', + type: 'function', + name: 'epoch', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'apply_open', + inputs: [], + outputs: [{ name: '', type: 'bool' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'vote_open', + inputs: [], + outputs: [{ name: '', type: 'bool' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'enabled', + inputs: [], + outputs: [{ name: '', type: 'bool' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'has_applied', + inputs: [{ name: '_token', type: 'address' }], + outputs: [{ name: '', type: 'bool' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'application_fee', + inputs: [{ name: '_token', type: 'address' }], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'apply', + inputs: [{ name: '_token', type: 'address' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'whitelist', + inputs: [{ name: '_tokens', type: 'address[]' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'vote', + inputs: [{ name: '_votes', type: 'uint256[]' }], + outputs: [], + }, + { + stateMutability: 'view', + type: 'function', + name: 'latest_finalized_epoch', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'finalize_epochs', + inputs: [], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_rate_provider', + inputs: [ + { name: '_token', type: 'address' }, + { name: '_provider', type: 'address' }, + ], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'sweep', + inputs: [{ name: '_token', type: 'address' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'sweep', + inputs: [ + { name: '_token', type: 'address' }, + { name: '_recipient', type: 'address' }, + ], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_operator', + inputs: [{ name: '_operator', type: 'address' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_treasury', + inputs: [{ name: '_treasury', type: 'address' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_measure', + inputs: [{ name: '_measure', type: 'address' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_enable_epoch', + inputs: [{ name: '_epoch', type: 'uint256' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_application_fee_token', + inputs: [{ name: '_token', type: 'address' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_application_fees', + inputs: [ + { name: '_initial', type: 'uint256' }, + { name: '_subsequent', type: 'uint256' }, + ], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'set_management', + inputs: [{ name: '_management', type: 'address' }], + outputs: [], + }, + { + stateMutability: 'nonpayable', + type: 'function', + name: 'accept_management', + inputs: [], + outputs: [], + }, + { + stateMutability: 'view', + type: 'function', + name: 'genesis', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'management', + inputs: [], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'pending_management', + inputs: [], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'operator', + inputs: [], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'treasury', + inputs: [], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'measure', + inputs: [], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'enable_epoch', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'num_candidates', + inputs: [{ name: 'arg0', type: 'uint256' }], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'candidates', + inputs: [ + { name: 'arg0', type: 'uint256' }, + { name: 'arg1', type: 'uint256' }, + ], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'candidates_map', + inputs: [ + { name: 'arg0', type: 'uint256' }, + { name: 'arg1', type: 'address' }, + ], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'applications', + inputs: [{ name: 'arg0', type: 'address' }], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'rate_providers', + inputs: [{ name: 'arg0', type: 'address' }], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'total_votes', + inputs: [{ name: 'arg0', type: 'uint256' }], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'votes', + inputs: [ + { name: 'arg0', type: 'uint256' }, + { name: 'arg1', type: 'uint256' }, + ], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'votes_user', + inputs: [ + { name: 'arg0', type: 'address' }, + { name: 'arg1', type: 'uint256' }, + ], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'winners', + inputs: [{ name: 'arg0', type: 'uint256' }], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'winner_rate_providers', + inputs: [{ name: 'arg0', type: 'uint256' }], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'fee_token', + inputs: [], + outputs: [{ name: '', type: 'address' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'initial_fee', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, + { + stateMutability: 'view', + type: 'function', + name: 'subsequent_fee', + inputs: [], + outputs: [{ name: '', type: 'uint256' }], + }, +] as const diff --git a/src/theme/MDXComponents.js b/src/theme/MDXComponents.js index 49f59e9cf..13992b87a 100644 --- a/src/theme/MDXComponents.js +++ b/src/theme/MDXComponents.js @@ -5,7 +5,8 @@ import ContractData from '@site/src/components/ContractData' import ContractAddress from '@site/src/components/StaticContractAddress' import AddressCheck from '@site/src/components/AddressCheck' import VeYFICalculator from '../components/veYFI-calculator' - +import GovDataYPools from '@site/src/components/GovDataYPools' +import AbiEncodingWidget from '@site/src/components/AbiEncodingWidget' /** * Manually add the custom components to the list of MDXComponents that docusaurus uses @@ -17,5 +18,6 @@ export default { ContractAddress, AddressCheck, VeYFICalculator, - + GovDataYPools, + AbiEncodingWidget, } diff --git a/static/img/icons/copy-icon.svg b/static/img/icons/copy-icon.svg new file mode 100644 index 000000000..1e4a3db61 --- /dev/null +++ b/static/img/icons/copy-icon.svg @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/static/img/icons/light-mode.svg b/static/img/icons/light-mode.svg new file mode 100644 index 000000000..1a7a96d61 --- /dev/null +++ b/static/img/icons/light-mode.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/static/img/product-pages/yeth-banner.png b/static/img/product-pages/yeth-banner.png new file mode 100644 index 000000000..3d9422b7a Binary files /dev/null and b/static/img/product-pages/yeth-banner.png differ diff --git a/static/img/product-pages/yeth-banner2.png b/static/img/product-pages/yeth-banner2.png new file mode 100644 index 000000000..f07dfd796 Binary files /dev/null and b/static/img/product-pages/yeth-banner2.png differ diff --git a/static/img/product-pages/yeth-banner3.png b/static/img/product-pages/yeth-banner3.png new file mode 100644 index 000000000..bb323efed Binary files /dev/null and b/static/img/product-pages/yeth-banner3.png differ diff --git a/static/img/product-pages/ypools-banner.png b/static/img/product-pages/ypools-banner.png new file mode 100644 index 000000000..02f42115c Binary files /dev/null and b/static/img/product-pages/ypools-banner.png differ diff --git a/static/img/product-pages/ypools-banner2.png b/static/img/product-pages/ypools-banner2.png new file mode 100644 index 000000000..e23192d3c Binary files /dev/null and b/static/img/product-pages/ypools-banner2.png differ diff --git a/static/img/product-pages/ypools-banner3.png b/static/img/product-pages/ypools-banner3.png new file mode 100644 index 000000000..d1361e885 Binary files /dev/null and b/static/img/product-pages/ypools-banner3.png differ diff --git a/tsconfig.json b/tsconfig.json index 01df9a91f..1b0da2a89 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,5 @@ "moduleResolution": "node", "esModuleInterop": true, "target": "ESNext" - } } diff --git a/yarn.lock b/yarn.lock index fcaaff444..52ac6d7b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12146,7 +12146,6 @@ vfile@^6.0.0, vfile@^6.0.1: unist-util-stringify-position "^4.0.0" vfile-message "^4.0.0" - victory-vendor@^36.6.8: version "36.9.2" resolved "https://registry.yarnpkg.com/victory-vendor/-/victory-vendor-36.9.2.tgz#668b02a448fa4ea0f788dbf4228b7e64669ff801"