ソースを参照

Improve handling and docs for ERC4626.decimals (#3733)

(cherry picked from commit 14f98dbb581a5365ce3f0c50bd850e499c554f72)
Francisco 3 年 前
コミット
f0de978a9e
1 ファイル変更24 行追加9 行削除
  1. 24 9
      contracts/token/ERC20/extensions/ERC4626.sol

+ 24 - 9
contracts/token/ERC20/extensions/ERC4626.sol

@@ -33,18 +33,33 @@ abstract contract ERC4626 is ERC20, IERC4626 {
      * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777).
      */
     constructor(IERC20 asset_) {
-        uint8 decimals_;
-        try IERC20Metadata(address(asset_)).decimals() returns (uint8 value) {
-            decimals_ = value;
-        } catch {
-            decimals_ = super.decimals();
-        }
-
+        (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
+        _decimals = success ? assetDecimals : super.decimals();
         _asset = asset_;
-        _decimals = decimals_;
     }
 
-    /** @dev See {IERC20Metadata-decimals}. */
+    /**
+     * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
+     */
+    function _tryGetAssetDecimals(IERC20 asset_) private returns (bool, uint8) {
+        (bool success, bytes memory encodedDecimals) = address(asset_).call(
+            abi.encodeWithSelector(IERC20Metadata.decimals.selector)
+        );
+        if (success && encodedDecimals.length >= 32) {
+            uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
+            if (returnedDecimals <= type(uint8).max) {
+                return (true, uint8(returnedDecimals));
+            }
+        }
+        return (false, 0);
+    }
+
+    /**
+     * @dev Decimals are read from the underlying asset in the constructor and cached. If this fails (e.g., the asset
+     * has not been created yet), the cached value is set to a default obtained by `super.decimals()` (which depends on
+     * inheritance but is most likely 18). Override this function in order to set a guaranteed hardcoded value.
+     * See {IERC20Metadata-decimals}.
+     */
     function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {
         return _decimals;
     }