<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      solidity學習之ERC4626

      什么是ERC4626

      ERC4626是對ERC20代幣標準的擴展,用于實現收益金庫的標準化,用戶可以將資產質押到合約中,持有相應的shares憑證,通過憑證來享有合約后續的收益。

      實現邏輯

      ERC4626繼承了ERC20,合約本身會發行一種shares代幣,當用戶存入指定的代幣后,就會根據金庫內的資產情況鑄造響應的shares份額分發給用戶,當用戶想要贖回資產時,調用合約就會銷毀shares份額,并將存入的代幣和附加的收益返還給用戶。

      ERC4626提供了兩種計算方式,用戶可以根據存入的資產算出shares份額,也可以通過想要得到的份額計算出需要存入多少資產。

      接口合約如下:

      interface IERC4626 is IERC20, IERC20Metadata {
          /*//////////////////////////////////////////////////////////////
                                       事件
          //////////////////////////////////////////////////////////////*/
          // 存款時觸發
          event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
      
          // 取款時觸發
          event Withdraw(
              address indexed sender,
              address indexed receiver,
              address indexed owner,
              uint256 assets,
              uint256 shares
          );
      
          /*//////////////////////////////////////////////////////////////
                                  元數據
          //////////////////////////////////////////////////////////////*/
          /**
           * @dev 返回金庫的基礎資產代幣地址 (用于存款,取款)
           * - 必須是 ERC20 代幣合約地址.
           * - 不能revert
           */
          function asset() external view returns (address assetTokenAddress);
      
          /*//////////////////////////////////////////////////////////////
                              存款/提款邏輯
          //////////////////////////////////////////////////////////////*/
          /**
           * @dev 存款函數: 用戶向金庫存入 assets 單位的基礎資產,然后合約鑄造 shares 單位的金庫額度給 receiver 地址
           *
           * - 必須釋放 Deposit 事件.
           * - 如果資產不能存入,必須revert,比如存款數額大大于上限等。
           */
          function deposit(uint256 assets, address receiver) external returns (uint256 shares);
      
          /**
           * @dev 鑄造函數: 用戶需要存入 assets 單位的基礎資產,然后合約給 receiver 地址鑄造 share 數量的金庫額度
           * - 必須釋放 Deposit 事件.
           * - 如果全部金庫額度不能鑄造,必須revert,比如鑄造數額大大于上限等。
           */
          function mint(uint256 shares, address receiver) external returns (uint256 assets);
      
          /**
           * @dev 提款函數: owner 地址銷毀 share 單位的金庫額度,然后合約將 assets 單位的基礎資產發送給 receiver 地址
           * - 釋放 Withdraw 事件
           * - 如果全部基礎資產不能提取,將revert
           */
          function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
      
          /**
           * @dev 贖回函數: owner 地址銷毀 shares 數量的金庫額度,然后合約將 assets 單位的基礎資產發給 receiver 地址
           * - 釋放 Withdraw 事件
           * - 如果金庫額度不能全部銷毀,則revert
           */
          function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
      
          /*//////////////////////////////////////////////////////////////
                                  會計邏輯
          //////////////////////////////////////////////////////////////*/
      
          /**
           * @dev 返回金庫中管理的基礎資產代幣總額
           * - 要包含利息
           * - 要包含費用
           * - 不能revert
           */
          function totalAssets() external view returns (uint256 totalManagedAssets);
      
          /**
           * @dev 返回利用一定數額基礎資產可以換取的金庫額度
           * - 不要包含費用
           * - 不包含滑點
           * - 不能revert
           */
          function convertToShares(uint256 assets) external view returns (uint256 shares);
      
          /**
           * @dev 返回利用一定數額金庫額度可以換取的基礎資產
           * - 不要包含費用
           * - 不包含滑點
           * - 不能revert
           */
          function convertToAssets(uint256 shares) external view returns (uint256 assets);
      
          /**
           * @dev 用于鏈上和鏈下用戶在當前鏈上環境模擬存款一定數額的基礎資產能夠獲得的金庫額度
           * - 返回值要接近且不大于在同一交易進行存款得到的金庫額度
           * - 不要考慮 maxDeposit 等限制,假設用戶的存款交易會成功
           * - 要考慮費用
           * - 不能revert
           * NOTE: 可以利用 convertToAssets 和 previewDeposit 返回值的差值來計算滑點
           */
          function previewDeposit(uint256 assets) external view returns (uint256 shares);
      
          /**
           * @dev 用于鏈上和鏈下用戶在當前鏈上環境模擬鑄造 shares 數額的金庫額度需要存款的基礎資產數量
           * - 返回值要接近且不小于在同一交易進行鑄造一定數額金庫額度所需的存款數量
           * - 不要考慮 maxMint 等限制,假設用戶的存款交易會成功
           * - 要考慮費用
           * - 不能revert
           */
          function previewMint(uint256 shares) external view returns (uint256 assets);
      
          /**
           * @dev 用于鏈上和鏈下用戶在當前鏈上環境模擬提款 assets 數額的基礎資產需要贖回的金庫份額
           * - 返回值要接近且不大于在同一交易進行提款一定數額基礎資產所需贖回的金庫份額
           * - 不要考慮 maxWithdraw 等限制,假設用戶的提款交易會成功
           * - 要考慮費用
           * - 不能revert
           */
          function previewWithdraw(uint256 assets) external view returns (uint256 shares);
      
          /**
           * @dev 用于鏈上和鏈下用戶在當前鏈上環境模擬銷毀 shares 數額的金庫額度能夠贖回的基礎資產數量
           * - 返回值要接近且不小于在同一交易進行銷毀一定數額的金庫額度所能贖回的基礎資產數量
           * - 不要考慮 maxRedeem 等限制,假設用戶的贖回交易會成功
           * - 要考慮費用
           * - 不能revert.
           */
          function previewRedeem(uint256 shares) external view returns (uint256 assets);
      
          /*//////////////////////////////////////////////////////////////
                           存款/提款限額邏輯
          //////////////////////////////////////////////////////////////*/
          /**
           * @dev 返回某個用戶地址單次存款可存的最大基礎資產數額。
           * - 如果有存款上限,那么返回值應該是個有限值
           * - 返回值不能超過 2 ** 256 - 1 
           * - 不能revert
           */
          function maxDeposit(address receiver) external view returns (uint256 maxAssets);
      
          /**
           * @dev 返回某個用戶地址單次鑄造可以鑄造的最大金庫額度
           * - 如果有鑄造上限,那么返回值應該是個有限值
           * - 返回值不能超過 2 ** 256 - 1 
           * - 不能revert
           */
          function maxMint(address receiver) external view returns (uint256 maxShares);
      
          /**
           * @dev 返回某個用戶地址單次取款可以提取的最大基礎資產額度
           * - 返回值應該是個有限值
           * - 不能revert
           */
          function maxWithdraw(address owner) external view returns (uint256 maxAssets);
      
          /**
           * @dev 返回某個用戶地址單次贖回可以銷毀的最大金庫額度
           * - 返回值應該是個有限值
           * - 如果沒有其他限制,返回值應該是 balanceOf(owner)
           * - 不能revert
           */
          function maxRedeem(address owner) external view returns (uint256 maxShares);
      }
      

      邏輯分為四部分:

      1. 元數據,asset()方法,返回的是基礎資產,也就是存入資產的token地址。
      2. 存款提款邏輯,存款和提款各有兩個方法,分別以asset和share為基準進行計算。
      3. 會計邏輯,用來計算asset和share互相轉換的方法,既是存款提款的基礎方法,也可以對外提供方便用戶進行模擬計算。此處可以增加額外的邏輯,用來實現更復雜的算法。
      4. 限額邏輯,計算用戶可存取的限額,與合約的設置以及用戶的余額有關。

      具體實現

        function totalAssets() public view override returns (uint256 totalManagedAssets){
            return _asset.balanceOf(address(this));
        }
      

      totalAssets就是簡單地將當前合約下的asset返回。

        function convertToShares(uint256 assets) public view override returns (uint256 shares){
            uint256 supply = totalSupply();
      
            return supply == 0 ? assets: assets * supply / totalAssets();
        }
      
        function convertToAssets(uint256 shares) public view override returns (uint256 assets){
            uint256 supply = totalSupply();
      
            return supply == 0? shares: shares * totalAssets() / supply;
        }
      

      convertToSharesconvertToAssets是share和asset互相轉換的方法,其中當supply為0時,也就是此時合約中還未有任何資產,也未有任何share,那么assetshare按照1:1的比例進行兌換。如果此時已經有資產,那么supply/totalAssets()就是每份asset可以兌換的share數量,反之亦然。

      這里有兩個思考點:

      1. 按這種計算方式,那么assetshare始終都是1:1的,那么會出現不相等的情況是因為合約中是會有收益產生的(依賴于其他邏輯),此時asset增加而share不變,那么比例就發生了變化。
      2. 按理來說supply=0時,share也應該為0,應該沒有任何可兌換的asset,但此處兼容了這一場景,是為了防止revert的出現。
        function previewDeposit(uint256 assets) public view override returns (uint256 shares){
            return convertToShares(assets);
        }
        
        function previewMint(uint256 shares) public view returns (uint256 assets) {
            return convertToAssets(shares);
        }
      
        function previewWithdraw(uint256 assets) public view returns (uint256 shares){
            return  convertToShares(assets);
        }
      
        function previewRedeem(uint256 shares) public view returns (uint256 assets) {
            return convertToAssets(shares);
        }
      

      四個會計方法都簡單地用了convert的調用,因為這里實現的是簡版的合約,沒有其他邏輯。

        function maxDeposit(address receiver) external view returns (uint256 maxAssets){
            return type(uint256).max;
        }
      
        function maxMint(address receiver) external view returns (uint256 maxShares){
            return type(uint256).max;
        }
      
        function maxWithdraw(address owner) external view returns (uint256 maxAssets){
            return convertToAssets(balanceOf(owner));
        }
      
        function maxRedeem(address owner) external view returns (uint256 maxShares){
            return balanceOf(owner);
        }
      

      同樣,限額邏輯也是簡單地實現,并沒有做過多地限制。

      function deposit(uint256 assets, address receiver) external override returns (uint256 shares){
          shares = previewDeposit(assets);
      
          _asset.transferFrom(msg.sender, address(this), assets);
          _mint(receiver, shares);
          emit Deposit(msg.sender, receiver, assets, shares);
      
      }
      
      function mint(uint256 shares, address receiver) external returns (uint256 assets){
          // 利用 previewMint() 計算需要存款的基礎資產數額
          assets = previewMint(shares);
      
          // 先 transfer 后 mint,防止重入
          _asset.transferFrom(msg.sender, address(this), assets);
          _mint(receiver, shares);
      
          // 釋放 Deposit 事件
          emit Deposit(msg.sender, receiver, assets, shares);
      }
      
      function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares){
          shares = previewWithdraw(assets);
      
          if(msg.sender != owner){
              _spendAllowance(owner, msg.sender, shares);
          }
          _burn(owner, shares);
          _asset.transfer(receiver, assets);
      
          emit Withdraw(msg.sender, receiver, owner, assets, shares);
      }
      
      function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets){
          assets = previewRedeem(shares);
      
          if(msg.sender != owner){
              _spendAllowance(owner, msg.sender, shares);
          }
          _burn(owner, shares);
          _asset.transfer(receiver, assets);
      
          emit Withdraw(msg.sender, receiver, owner, assets, shares);
      }
      

      可以看到depositmintwithdrawredeem的實現都是基本一致的,只在于數量計算的區別。

      有一個值得注意的地方就是depositmint只支持存入msg.sender自己的資產,但是withdrawredeem是支持通過授權的形式由他人來調用的,所以當msg.sender不等于owner的時候,需要使用授權額度。

      posted @ 2025-08-11 17:03  Felix07  閱讀(31)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲青青草视频在线播放| 国产精品亚洲精品日韩已满十八小| 99久久国产综合精品成人影院| 久久久天堂国产精品女人| 性欧美乱熟妇xxxx白浆| 日韩精品av一区二区三区| 亚洲成av人片乱码色午夜| 国产老妇伦国产熟女老妇高清| 精品人妻午夜一区二区三区四区| 亚洲熟女乱一区二区三区| 久久无码中文字幕免费影院蜜桃| 四虎影视www在线播放| 波多野结衣av无码| 成人免费在线播放av| 日本久久99成人网站| 色吊a中文字幕一二三区| 国产精品一区二区av片| 国产精品大全中文字幕| 久久久久人妻精品一区三寸 | 日韩精品专区在线影观看| 成人av午夜在线观看| 亚洲黄色一级片在线观看| 亚洲国产精品一区二区第一页| 久久婷婷五月综合色国产免费观看 | 一区二区三区日本久久九| 在线视频中文字幕二区| 乱人伦中文字幕成人网站在线| 日本精品网| 亚洲国产中文字幕在线视频综合| 久久亚洲精品中文字幕| 久久96热人妻偷产精品| 久久一区二区中文字幕| 军人粗大的内捧猛烈进出视频| 国产一区二区三区九精品| 国产亚洲色视频在线| 平江县| 婷婷六月天在线| 久热伊人精品国产中文| 欧美精品一区二区三区中文字幕| 国产激情一区二区三区成人| 婷婷丁香五月亚洲中文字幕|