lido
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- tags: withdrawals, draft, integrations --- # Lido V2 on Goerli: a note for integrations On March 24, Lido protocol on Goerli testnet has been successfully [upgraded to V2](https://twitter.com/LidoFinance/status/1640697078283223040). This document is intended for Lido's existing partners and developers looking to understand the scope of the upgrade in terms of how their product interacts with Lido protocol. A high-level upgrade overview can be found in [the blog post](https://blog.lido.fi/introducing-lido-v2/). The code of the upgrade is open and available [on Lido's GitHub](https://github.com/lidofinance/lido-dao/tree/feature/shapella-upgrade). You can find all the changes [in this PR](https://github.com/lidofinance/lido-dao/pull/482). The addresses of all the contracts deployed on Goerli are listed on [the Lido's docs hub](https://docs.lido.fi/deployed-contracts/goerli). Lido UI has also been upgraded to V2 and is now being tested at https://stake.testnet.fi/ ### Deposit flow No changes has been done to the deposit flow. All the addresses, calls, methods and events remain the same as before the protocol upgrade. ### Subgraph and Reward History Subgraph and Reward History on Goerli are not operational at the moment. The issue is being addressed, we expect the services to resume by end of April. ### stETH shares Shares mechanics explained in [our integration guide](https://docs.lido.fi/guides/steth-integration-guide#steth-internals-share-mechanics) remain the same as before the protocol upgrade. ### Withdrawal flow The biggest highlight of the upgrade is enabling withdrawals. The withdrawal flow is quite different from the deposit flow, unlike deposit flow, it is also async. The process consists of the following steps: - user sends withdrawal request and receives in return an NFT representing the position of their request in the FIFO queue; - after some oracle reports their request becomes claimable (FIFO order holds here as well, no request can be skipped); - user claims their stETH, while the NFT gets burnt. Request size should be at least **100 wei** (in stETH) and at most **1000 stETH**. Larger amounts should be withdrawn in multiple requests, which can be batched via in-protocol API. Once requested, withdrawal cannot be canceled. The withdrawal NFT can be transferred to a different address, and the new owner will be able to claim the requested withdrawal once finalized. The amount of claimable ETH is determined once the withdrawal request is finalised. The rate stETH/ETH of the request finalisation can't get higher than it's been at the moment of request creation. the user will be able to claim: - normally – ETH amount corresponding to the stETH amount at the moment of the request's placement **OR** - lowered ETH amount corresponding to the oracle-reported share rate in case the protocol had undergone significant losses (slashings and penalties) The second option is unlikely, and we haven't ever seen the conditions for it on mainnet so far. **See the detailed description of the withdrawal flow below.** The end-user contract to deal with the withdrawals is `WithdrawalQueueERC721.sol`, which implements the ERC721 standard. NFT represents the position in the withdrawal queue and may be claimed after finalization of the request. The overview of the most frequent operations around the NFT queue. - Get the NFTs owned by the user (the not yet claimed ones) - Call to `getWithdrawalRequests(address _owner)` will return an array of NFT ids. - Get NFT asset metadata - Call `tokenURI(uint256)` as declared via ERC721 (Returns empty string currently. Subject to change in the future.) - Init withdrawal - stETH - Call `requestWithdrawalsWithPermit(uint256[] calldata amounts, address _owner, PermitInput calldata _permit)` and get the ids of created positions, where `msg.sender` will be used to transfer tokens from and the `_owner` will be the address that can claim or transfer NFT (defaults to `msg.sender` if it’s not provided. - Alternatively, spending stETH on behalf of `WithdrawalQueueERC721.sol` contract can be approved in a separate upfront transaction (`stETH.approve(withdrawalQueueERC712.address, allowance)`), and the `requestWithdrawals(uint256[] calldata amounts, address _owner)` method called afterwards. - wstETH - Call `requestWithdrawalsWstETHWithPermit(uint256[] calldata amounts, address _owner, PermitInput calldata _permit)` and get the ids of created positions, where `msg.sender` will be used to transfer tokens from, and the `_owner` will be the address that can claim or transfer NFT (defaults to `msg.sender` if it’s not provided. - Alternatively, spending wstETH on behalf of `WithdrawalQueueERC721.sol` contract can be approved in a separate upfront transaction (`wstETH.approve(withdrawalQueueERC712.address, allowance)`), and the `requestWithdrawalsWstETH(uint256[] calldata amounts, address _owner)` method called afterwards. `PermitInput` structure defined as follows: ```solidity struct PermitInput { uint256 value; uint256 deadline; uint8 v; bytes32 r; bytes32 s; } ``` - Get withdrawal request status (statuses refresh every ~4 hours on Goerli and once a day on mainnet) - Call `getWithdrawalStatus(uint256[] calldata _requestIds)` with ids of the user's positions and get the amount of stETH from the returned [struct](https://github.com/lidofinance/lido-dao/blob/feature/shapella-upgrade/contracts/0.8.9/WithdrawalQueueBase.sol#L67-L81 ). ```solidity /// @notice output format struct for `_getWithdrawalStatus()` method struct WithdrawalRequestStatus { /// @notice stETH token amount that was locked on withdrawal queue for this request uint256 amountOfStETH; /// @notice amount of stETH shares locked on withdrawal queue for this request uint256 amountOfShares; /// @notice address that can claim or transfer this request address owner; /// @notice timestamp of when the request was created, in seconds uint256 timestamp; /// @notice true, if request is finalized bool isFinalized; /// @notice true, if request is claimed. Request is claimable if (isFinalized && !isClaimed) bool isClaimed; } ``` >NOTE: Since stETH is an essential token if the user tries to request a withdrawal using wstETH directly, the amount will be nominated in stETH on request creation. - Claim user's ETH - Transact to `claimWithdrawal(uint256)` with the NFT Id on behalf of the NFT owner. - It is also possible to claim withdrawals in batches, it's being done in two steps: - Call `claimWithdrawals(uint256[] calldata _requestIDs, hints)` where - hints = `findCheckpointHints(uint256[] calldata _requestIDs, 1, lastCheckpoint)` - lastCheckpoint = `getLastCheckpointIndex()` There is an unlikely scenario of Lido protocol going into [bunker mode](https://research.lido.fi/t/withdrawals-for-lido-on-ethereum-bunker-mode-design-and-implementation/3890) in case of Lido's validators getting slashed. This will result in the withdrawals processing becoming significantly slower, but it isn't meant to interfere with any specific withdrawal flow elements. However, the amount of ETH claimed in this case might be lower than the amount requested in stETH. The full withdrawal flow is expected to take from a few hours on Goerli or a single day on mainnet to up to several weeks (in case of the bunker mode on). In other words, the request finalization can't happen earlier than on the next Accounting Oracle report, but will take longer if there's not enough ETH in EL buffers, and validator exits will be triggered. ### How to get APR Lido on Ethereum V2 employs not one, but two Oracles: Accounting Oracle & Exit Bus. Former provides rebase data & finalizes withdrawal request, the latter — determines which validators to ask for exiting. In order to retain backwards compatibility, original Lido Oracle contract is updated & renamed to Legacy Oracle. The data is updated, but the contract is deprecated and would be removed in the upgrades following V2. Although the old way of calculating the APR would still result in relevant numbers, the math might be off in case of significant withdrawals. #### How it was > NB: OUTDATED OLD FORMULA ```javascript protocolAPR = (postTotalPooledEther - preTotalPooledEther) * secondsInYear / (preTotalPooledEther * timeElapsed) lidoFeeAsFraction = lidoFee / basisPoint userAPR = protocolAPR * (1 - lidoFeeAsFraction) ``` #### How it is New proper way: from [`[1]`](https://github.com/lidofinance/lido-dao/blob/feature/shapella-upgrade/contracts/0.4.24/Lido.sol#L229-L238): ```solidity // Emits when token rebased (total supply and/or total shares were changed) event TokenRebased( uint256 indexed reportTimestamp, uint256 timeElapsed, uint256 preTotalShares, uint256 preTotalEther, /* preTotalPooledEther */ uint256 postTotalShares, uint256 postTotalEther, /* postTotalPooledEther */ uint256 sharesMintedAsFees /* fee part included in `postTotalShares` */ ); ``` To calculated APR for the last completed oracle frame: ```javascript preShareRate = preTotalEther * 1e27 / preTotalShares postShareRate = postTotalEther * 1e27 / postTotalShares userAPR = secondsInYear * ( (postShareRate - preShareRate) / preShareRate ) / timeElapsed ``` So, the new formula takes into account `preTotalShares` and `postTotalShares` values, while, in contrast, the old formula didn't use them: The new formula also doesn't require to calculate `lidoFee` at all (because fee distribution works by changing [total shares amount](https://github.com/lidofinance/lido-dao/blob/feature/shapella-upgrade/contracts/0.4.24/Lido.sol#L988-L1022) under the hood). #### Why does it matter When Lido V2 protocol finalizes withdrawal requests, the `Lido` contract sends ether to `WithdrawalQueue` (excluding these funds from `totalPooledEther`, i.e., decreasing TVL) and assigns to burn underlying locked requests' `stETH` shares in return. In other words, withdrawal finalization decreases both TVL and total shares. Old formula isn't suitable anymore because it catches TVL changes, but skips total shares changes. Illustrative example (using smallish numbers far from the real ones for simplicity): ```javascript preTotalEther = 1000 ETH preTotalShares = 1000 * 10^18 // 1 share : 1 wei postTotalEther = 990 ETH postTotalShares = 950 * 10^18 timeElapsed = 24 * 60 * 60 // 1 day, or 86400 seconds //!!! using the old (broken) method // protocolAPR = (postTotalPooledEther - preTotalPooledEther) * secondsInYear / (preTotalPooledEther * timeElapsed) protocolAPR = (990ETH - 1000ETH) * 31557600 / (1000ETH * 86400) = -3.6525 //lidoFeeAsFraction = lidoFee / basisPoint = 0.1 //userAPR = protocolAPR * (1 - lidoFeeAsFraction) = protocolAPR * (1 - 0.1) userAPR = -3.6525 * (1 - 0.1) = -3.28725 //!!! i.e, userAPR now is ~minus 329% //!!! using the new (proper) method preShareRate = 1e27 postShareRate = 1042105263157894736842105263 // ~1e27 * 1.0421052631578946 userAPR = 31557600 * (42105263157894736842105263 / 1e27) / 86400 = 15.378947368421048 //!!! i.e., userAPR now is ~plus 1539% ``` #### APR shown on the Lido's UIs Please note that the APR shown on Lido's UI is not expected to match the APR calculated on-chain. The one displayed on our websites is a 7-days rolling average APR. ### Other notable changes - Lido's stETH now supports `permit` function as introduced in the EIP-2621: https://github.com/lidofinance/lido-dao/blob/feature/shapella-upgrade/contracts/0.4.24/StETHPermit.sol#L99-L112 In other words, users can provide allowance for stETH using the off-chain generated EIP-712 compatible signatures - Lido's stETH now supports `isValidSignature(hash, signature)` function as introduced in [the EIP-1271](https://eip1271.io/): https://github.com/lidofinance/lido-dao/blob/feature/shapella-upgrade/contracts/common/lib/SignatureUtils.sol#L29-L57 - `transferSharesFrom` method has been added for stETH (had only `transferShares` before) working within the provided allowance.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully