question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Reduce reliance on underlying decimals in ERC4626

See original GitHub issue

4626 uses _asset.decimals() to calculate initial share/asset conversions

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC4626.sol#L167

EIP4626 states:

All ERC-4626 tokenized Vaults MUST implement ERC-20’s optional metadata extensions.

But EIP20 states for decimals:

OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present.

So even if the vault exposes decimals for shares, the 20 spec says not to expect assets to expose decimals.

Even if both tokens expose decimals the method is for usability, not for onchain calculations.

Maybe a virtual _initialShareRatio() function could be exposed that implementing contracts can override without needing to override the entire conversion process

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:9 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
Amxxcommented, Jul 15, 2022

So we could do

    function decimals() public view override(IERC20Metadata, ERC20) returns (uint8) {
        try IERC20Metadata(address(_asset)).decimals() returns (uint8 value) {
            return value;
        } catch {
            return super.decimals();
        }
    }
    
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256 shares) {
        uint256 supply = totalSupply();
        return
            (assets == 0 || supply == 0)
                ? assets
                : assets.mulDiv(supply, totalAssets(), rounding);
    }

    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256 assets) {
        uint256 supply = totalSupply();
        return
            (supply == 0)
                ? shares
                : shares.mulDiv(totalAssets(), supply, rounding);
    }

and no possibility to override decimal … or

    function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {
        try IERC20Metadata(address(_asset)).decimals() returns (uint8 value) {
            return value;
        } catch {
            return super.decimals();
        }
    }
    
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256 shares) {
        uint256 supply = totalSupply();
        return
            (assets == 0 || supply == 0)
                ? assets.mulDiv(10**decimals(), 10**ERC4626.decimals(), rounding)
                : assets.mulDiv(supply, totalAssets(), rounding);
    }

    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256 assets) {
        uint256 supply = totalSupply();
        return
            (supply == 0)
                ? shares.mulDiv(10**ERC4626.decimals(), 10**decimals(), rounding)
                : shares.mulDiv(totalAssets(), supply, rounding);
    }

That way devs can still override

0reactions
pedrommaiaacommented, Jul 25, 2022

So we could do

    function decimals() public view override(IERC20Metadata, ERC20) returns (uint8) {
        try IERC20Metadata(address(_asset)).decimals() returns (uint8 value) {
            return value;
        } catch {
            return super.decimals();
        }
    }
    
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256 shares) {
        uint256 supply = totalSupply();
        return
            (assets == 0 || supply == 0)
                ? assets
                : assets.mulDiv(supply, totalAssets(), rounding);
    }

    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256 assets) {
        uint256 supply = totalSupply();
        return
            (supply == 0)
                ? shares
                : shares.mulDiv(totalAssets(), supply, rounding);
    }

and no possibility to override decimal … or

    function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {
        try IERC20Metadata(address(_asset)).decimals() returns (uint8 value) {
            return value;
        } catch {
            return super.decimals();
        }
    }
    
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256 shares) {
        uint256 supply = totalSupply();
        return
            (assets == 0 || supply == 0)
                ? assets.mulDiv(10**decimals(), 10**ERC4626.decimals(), rounding)
                : assets.mulDiv(supply, totalAssets(), rounding);
    }

    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256 assets) {
        uint256 supply = totalSupply();
        return
            (supply == 0)
                ? shares.mulDiv(10**ERC4626.decimals(), 10**decimals(), rounding)
                : shares.mulDiv(totalAssets(), supply, rounding);
    }

That way devs can still override

Really like the first option @Amxx

Read more comments on GitHub >

github_iconTop Results From Across the Web

ERC-4626: Extending ERC-20 for Interest Management
The new standard ERC-4626 extends ERC-20 for interest bearing accounts. We'll explore how the standard works and how it can help you with...
Read more >
Vaults and the ERC-4626 token contract - Crypto Market Pool
Surely the ERC-4626 token standard reduces the amount of time and work ... external requiresAuth { // Ensure the underlying token's decimals match...
Read more >
Decimals Operations: Multiplying - Extranet
Multiplying decimals relies on adapting multiplication of whole numbers. When one of the numbers being multiplied is reduced to a tenth of its...
Read more >
What is Defactor (FACTR) | What is Defactor token | What is FACT ...
Defactor reduces the barriers to entry for Real World Asset Originators by providing tools to manage and scale their processes and giving investors ......
Read more >
Cellar | Address 0x6b7f87279982d919bbf85182ddeab179b366d8f2 ...
@author Brian Le */ contract Cellar is ERC4626, Owned, ... contract * @param _asset address of underlying token used for the for accounting,...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found