Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LendingPair: Cache token decimals #38

Open
code423n4 opened this issue Jul 11, 2021 · 0 comments
Open

LendingPair: Cache token decimals #38

code423n4 opened this issue Jul 11, 2021 · 0 comments

Comments

@code423n4
Copy link
Contributor

Handle

greiart

Vulnerability details

Impact

It will save quite a bit of gas to cache tokenA and tokenB decimals value as uint128 so that both values can be stored in a single storage slot and retrieved, to avoid making the decimals() calls.

Referenced Codelines

https://github.com/code-423n4/2021-07-wildcredit/blob/main/contracts/LendingPair.sol

Proof Of Concept

struct TokenDecimals {
	uint128 decimalsA;
	uint128 decimalsB;
}

TokenDecimals tokenDecimals;

function initialize(
    address _lpTokenMaster,
    address _controller,
    IERC20 _tokenA,
    IERC20 _tokenB
  ) external {
    require(address(tokenA) == address(0), "LendingPair: already initialized");
    require(address(_tokenA) != address(0) && address(_tokenB) != address(0), "LendingPair: cannot be ZERO address");
		
    controller = IController(_controller);
    tokenA = address(_tokenA);
    tokenB = address(_tokenB);
		uint256 _decimalsA = _tokenA.decimals();
		uint256 _decimalsB = _tokenB.decimals();
		require(_decimalsA > 0 && _decimalsA < type(uint128).max, 'invalid token decimals');
		require(_decimalsB > 0 && _decimalsB < type(uint128).max, 'invalid token decimals');
		tokenDecimals = TokenDecimals({
			decimalsA: uint128(_decimalsA),
			decimalsB: uint128(_decimalsB)
		});
    lastBlockAccrued = block.number;

    lpToken[tokenA] = _createLpToken(_lpTokenMaster);
    lpToken[tokenB] = _createLpToken(_lpTokenMaster);
  }

// note that both _fromToken and _toToken have to be tokenA and tokenB
// though each can be either token (eg. _fromToken = tokenA or tokenB)
// currently, all functions of this contract that call this function
// either perform the required validation checks
// or explicitly use tokenA and tokenB as the input arguments
function _convertTokenValues(
    address _fromToken,
    address _toToken,
    uint    _inputAmount
  ) internal view returns(uint) {
		// 1 sload for gas optimization
		TokenDecimals memory _tokenDecimals = tokenDecimals;
    uint priceFrom = controller.tokenPrice(_fromToken) * 1e18;
    uint priceTo   = controller.tokenPrice(_toToken)   * 1e18;
		if (_fromToken == tokenA) {
			priceFrom = priceFrom / 10 ** _tokenDecimals.decimalsA;
			priceTo = priceTo / 10 ** _tokenDecimals.decimalsB;
		} else {
			priceFrom = priceFrom / 10 ** _tokenDecimals.decimalsB;
			priceTo = priceTo / 10 ** _tokenDecimals.decimalsA;
		}
		return _inputAmount * priceFrom / priceTo;
}
@code423n4 code423n4 added bug Something isn't working G (Gas Optimization) labels Jul 11, 2021
code423n4 added a commit that referenced this issue Jul 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants