From 8d8f8c786e598bf59b0d90edda59f8df16be84af Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Mon, 4 Dec 2023 23:17:11 +0530 Subject: [PATCH] fix: pr comments --- contracts/ProtocolReserve/RiskFundV2.sol | 4 +- contracts/Test/Mocks/MockACM.sol | 1 + .../TokenConverter/AbstractTokenConverter.sol | 99 ++++++++++++++----- .../IAbstractTokenConverter.sol | 63 ++++++++++++ .../TokenConverter/RiskFundConverter.sol | 14 +-- .../TokenConverter/SingleTokenConverter.sol | 2 +- contracts/Utils/Constants.sol | 3 - .../TokenConverter/AbstractTokenConverter.ts | 46 ++++++++- tests/TokenConverter/RiskFundConverter.ts | 80 ++++++++++++++- 9 files changed, 268 insertions(+), 44 deletions(-) diff --git a/contracts/ProtocolReserve/RiskFundV2.sol b/contracts/ProtocolReserve/RiskFundV2.sol index 68c488f1..f9302e88 100644 --- a/contracts/ProtocolReserve/RiskFundV2.sol +++ b/contracts/ProtocolReserve/RiskFundV2.sol @@ -9,7 +9,6 @@ import { IRiskFund } from "../Interfaces/IRiskFund.sol"; import { IRiskFundConverter } from "../Interfaces/IRiskFundConverter.sol"; import { ensureNonzeroAddress, ensureNonzeroValue } from "../Utils/Validators.sol"; -import { EXP_SCALE } from "../Utils/Constants.sol"; import { RiskFundV2Storage } from "./RiskFundStorage.sol"; @@ -126,6 +125,7 @@ contract RiskFundV2 is AccessControlledV8, RiskFundV2Storage, IRiskFund { /// @param amount Amount need to sweep for the pool /// @custom:event Emits SweepToken event on success /// @custom:error ZeroAddressNotAllowed is thrown when tokenAddress/to address is zero + /// @custom:error ZeroValueNotAllowed is thrown when amount is zero /// @custom:access Only Governance function sweepToken( address tokenAddress, @@ -172,7 +172,7 @@ contract RiskFundV2 is AccessControlledV8, RiskFundV2Storage, IRiskFund { emit PoolAssetsIncreased(comptroller, asset, amount); } - /// @notice Operations to perform before sweeping tokens + /// @dev Operations to perform before sweeping tokens /// @param tokenAddress Address of the token /// @param amount Amount transferred to address(to) /// @custom:error InsufficientBalance is thrown when amount entered is greater than balance diff --git a/contracts/Test/Mocks/MockACM.sol b/contracts/Test/Mocks/MockACM.sol index dfcdb3a4..3b86bf1f 100644 --- a/contracts/Test/Mocks/MockACM.sol +++ b/contracts/Test/Mocks/MockACM.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.13; import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; diff --git a/contracts/TokenConverter/AbstractTokenConverter.sol b/contracts/TokenConverter/AbstractTokenConverter.sol index 31f7e485..0e76c8d0 100644 --- a/contracts/TokenConverter/AbstractTokenConverter.sol +++ b/contracts/TokenConverter/AbstractTokenConverter.sol @@ -106,16 +106,44 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo event DestinationAddressUpdated(address oldDestinationAddress, address indexed destinationAddress); /// @notice Emitted when exact amount of tokens are converted for tokens - event ConvertedExactTokens(uint256 amountIn, uint256 amountOut); + event ConvertedExactTokens( + address indexed sender, + address indexed receiver, + address tokenAddressIn, + address indexed tokenAddressOut, + uint256 amountIn, + uint256 amountOut + ); /// @notice Emitted when tokens are converted for exact amount of tokens - event ConvertedForExactTokens(uint256 amountIn, uint256 amountOut); + event ConvertedForExactTokens( + address indexed sender, + address indexed receiver, + address tokenAddressIn, + address indexed tokenAddressOut, + uint256 amountIn, + uint256 amountOut + ); /// @notice Emitted when exact amount of tokens are converted for tokens, for deflationary tokens - event ConvertedExactTokensSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOut); + event ConvertedExactTokensSupportingFeeOnTransferTokens( + address indexed sender, + address indexed receiver, + address tokenAddressIn, + address indexed tokenAddressOut, + uint256 amountIn, + uint256 amountOut + ); /// @notice Emitted when tokens are converted for exact amount of tokens, for deflationary tokens - event ConvertedForExactTokensSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOut); + event ConvertedForExactTokensSupportingFeeOnTransferTokens( + address indexed sender, + address indexed receiver, + address tokenAddressIn, + address indexed tokenAddressOut, + uint256 amountIn, + uint256 amountOut + ); /// @notice Emitted when conversion is paused event ConversionPaused(address indexed sender); @@ -255,6 +283,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo /// @param tokenAddressOut Address of the token to get after conversion /// @param to Address of the tokenAddressOut receiver /// @custom:event Emits ConvertedExactTokens event on success + /// @custom:error ZeroAddressNotAllowed is thrown when to address is zero /// @custom:error InvalidToAddress error is thrown when address(to) is same as tokenAddressIn or tokenAddressOut /// @custom:error AmountOutLowerThanMinRequired error is thrown when amount of output tokenAddressOut is less than amountOutMinMantissa /// @custom:error AmountInMismatched error is thrown when amount of output tokenAddressOut is less than amountOutMinMantissa @@ -266,6 +295,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo address to ) external nonReentrant { _checkConversionPaused(); + ensureNonzeroAddress(to); if (to == tokenAddressIn || to == tokenAddressOut) { revert InvalidToAddress(); } @@ -284,7 +314,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo postConversionHook(tokenAddressIn, tokenAddressOut, actualAmountIn, actualAmountOut); - emit ConvertedExactTokens(actualAmountIn, actualAmountOut); + emit ConvertedExactTokens(msg.sender, to, tokenAddressIn, tokenAddressOut, actualAmountIn, actualAmountOut); } /// @notice Converts tokens for tokenAddressIn for exact amount of tokenAddressOut if there is enough tokens held by the contract, @@ -296,6 +326,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo /// @param tokenAddressOut Address of the token to get after conversion /// @param to Address of the tokenAddressOut receiver /// @custom:event Emits ConvertedForExactTokens event on success + /// @custom:error ZeroAddressNotAllowed is thrown when to address is zero /// @custom:error InvalidToAddress error is thrown when address(to) is same as tokenAddressIn or tokenAddressOut /// @custom:error AmountInHigherThanMax error is thrown when amount of tokenAddressIn is higher than amountInMaxMantissa /// @custom:error AmountOutMismatched error is thrown when actualAmountOut is does not match amountOutMantissa @@ -307,6 +338,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo address to ) external nonReentrant { _checkConversionPaused(); + ensureNonzeroAddress(to); if (to == tokenAddressIn || to == tokenAddressOut) { revert InvalidToAddress(); } @@ -324,7 +356,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo } postConversionHook(tokenAddressIn, tokenAddressOut, actualAmountIn, actualAmountOut); - emit ConvertedForExactTokens(actualAmountIn, actualAmountOut); + emit ConvertedForExactTokens(msg.sender, to, tokenAddressIn, tokenAddressOut, actualAmountIn, actualAmountOut); } /// @notice Converts exact amount of tokenAddressIn for tokenAddressOut if there is enough tokens held by the contract @@ -333,6 +365,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo /// @param tokenAddressIn Address of the token to convert /// @param tokenAddressOut Address of the token to get after conversion /// @param to Address of the tokenAddressOut receiver + /// @custom:error ZeroAddressNotAllowed is thrown when to address is zero /// @custom:event Emits ConvertedExactTokensSupportingFeeOnTransferTokens event on success /// @custom:error InvalidToAddress error is thrown when address(to) is same as tokenAddressIn or tokenAddressOut /// @custom:error AmountOutLowerThanMinRequired error is thrown when amount of output tokenAddressOut is less than amountOutMinMantissa @@ -344,6 +377,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo address to ) external nonReentrant { _checkConversionPaused(); + ensureNonzeroAddress(to); if (to == tokenAddressIn || to == tokenAddressOut) { revert InvalidToAddress(); } @@ -358,7 +392,14 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo postConversionHook(tokenAddressIn, tokenAddressOut, actualAmountIn, actualAmountOut); - emit ConvertedExactTokensSupportingFeeOnTransferTokens(actualAmountIn, actualAmountOut); + emit ConvertedExactTokensSupportingFeeOnTransferTokens( + msg.sender, + to, + tokenAddressIn, + tokenAddressOut, + actualAmountIn, + actualAmountOut + ); } /// @notice Converts tokens for tokenAddressIn for exact amount of tokenAddressOut if there is enough tokens held by the contract, @@ -369,6 +410,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo /// @param tokenAddressOut Address of the token to get after conversion /// @param to Address of the tokenAddressOut receiver /// @custom:event Emits ConvertedForExactTokensSupportingFeeOnTransferTokens event on success + /// @custom:error ZeroAddressNotAllowed is thrown when to address is zero /// @custom:error InvalidToAddress error is thrown when address(to) is same as tokenAddressIn or tokenAddressOut /// @custom:error AmountInHigherThanMax error is thrown when amount of tokenAddressIn is higher than amountInMaxMantissa function convertForExactTokensSupportingFeeOnTransferTokens( @@ -379,6 +421,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo address to ) external nonReentrant { _checkConversionPaused(); + ensureNonzeroAddress(to); if (to == tokenAddressIn || to == tokenAddressOut) { revert InvalidToAddress(); } @@ -393,7 +436,14 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo postConversionHook(tokenAddressIn, tokenAddressOut, actualAmountIn, actualAmountOut); - emit ConvertedForExactTokensSupportingFeeOnTransferTokens(actualAmountIn, actualAmountOut); + emit ConvertedForExactTokensSupportingFeeOnTransferTokens( + msg.sender, + to, + tokenAddressIn, + tokenAddressOut, + actualAmountIn, + actualAmountOut + ); } /// @notice To sweep ERC20 tokens and transfer them to user(to address) @@ -531,14 +581,14 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo /// @notice Get the balance for specific token /// @param token Address of the token /// @return tokenBalance Balance of the token the contract has - function balanceOf(address token) public view virtual returns (uint256 tokenBalance) {} + function balanceOf(address token) public view virtual returns (uint256 tokenBalance); - /// @notice Operations to perform before sweeping tokens + /// @dev Operations to perform before sweeping tokens /// @param token Address of the token /// @param amount Amount transferred to address(to) function preSweepToken(address token, uint256 amount) internal virtual {} - /// @notice Converts exact amount of tokenAddressIn for tokenAddressOut + /// @dev Converts exact amount of tokenAddressIn for tokenAddressOut /// @param amountInMantissa Amount of tokenAddressIn /// @param amountOutMinMantissa Min amount of tokenAddressOut required as output /// @param tokenAddressIn Address of the token to convert @@ -565,7 +615,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo } } - /// @notice Converts tokens for tokenAddressIn for exact amount of tokenAddressOut + /// @dev Converts tokens for tokenAddressIn for exact amount of tokenAddressOut /// @param amountInMaxMantissa Max amount of tokenAddressIn /// @param amountOutMantissa Amount of tokenAddressOut required as output /// @param tokenAddressIn Address of the token to convert @@ -594,7 +644,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo actualAmountOut = _doTransferOut(tokenAddressOut, to, amountOutMantissa); } - /// @notice return actualAmountOut from reserves for tokenAddressOut + /// @dev return actualAmountOut from reserves for tokenAddressOut /// @param tokenAddressOut Address of the token to get after conversion /// @param to Address of the tokenAddressOut receiver /// @param amountConvertedMantissa Amount of tokenAddressOut supposed to get transferred @@ -627,7 +677,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo actualAmountIn = balanceAfterDestination - balanceBeforeDestination; } - /// @notice Sets a new price oracle + /// @dev Sets a new price oracle /// @param priceOracle_ Address of the new price oracle to set /// @custom:event Emits PriceOracleUpdated event on success /// @custom:error ZeroAddressNotAllowed is thrown when price oracle address is zero @@ -637,7 +687,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo priceOracle = priceOracle_; } - /// @notice Sets a new destination address + /// @dev Sets a new destination address /// @param destinationAddress_ The new destination address to be set /// @custom:event Emits DestinationAddressUpdated event on success /// @custom:error ZeroAddressNotAllowed is thrown when destination address is zero @@ -647,7 +697,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo destinationAddress = destinationAddress_; } - /// @notice Hook to perform after converting tokens + /// @dev Hook to perform after converting tokens /// @param tokenAddressIn Address of the token to convert /// @param tokenAddressOut Address of the token to get after conversion /// @param amountIn Amount of tokenIn converted @@ -683,7 +733,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo conversionPaused = false; } - /// @notice To get the amount of tokenAddressOut tokens sender could receive on providing amountInMantissa tokens of tokenAddressIn + /// @dev To get the amount of tokenAddressOut tokens sender could receive on providing amountInMantissa tokens of tokenAddressIn /// @dev This function retrieves values without altering token prices. /// @param amountInMantissa Amount of tokenAddressIn /// @param tokenAddressIn Address of the token to convert @@ -718,10 +768,11 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo uint256 tokenInUnderlyingPrice = priceOracle.getPrice(tokenAddressIn); uint256 tokenOutUnderlyingPrice = priceOracle.getPrice(tokenAddressOut); - /// amount of tokenAddressOut after including incentive - uint256 conversionWithIncentive = MANTISSA_ONE + configuration.incentive; /// conversion rate after considering incentive(conversionWithIncentive) + uint256 conversionWithIncentive = MANTISSA_ONE + configuration.incentive; + /// amount of tokenAddressOut after including incentive as amountOutMantissa will be greater than actual as it gets + /// multiplied by conversionWithIncentive which will be >= 1 amountOutMantissa = (amountInMantissa * tokenInUnderlyingPrice * conversionWithIncentive) / (tokenOutUnderlyingPrice * EXP_SCALE); @@ -730,7 +781,7 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo tokenInToOutConversion = (tokenInUnderlyingPrice * conversionWithIncentive) / tokenOutUnderlyingPrice; } - /// @notice To get the amount of tokenAddressIn tokens sender would send on receiving amountOutMantissa tokens of tokenAddressOut + /// @dev To get the amount of tokenAddressIn tokens sender would send on receiving amountOutMantissa tokens of tokenAddressOut /// @dev This function retrieves values without altering token prices. /// @param amountOutMantissa Amount of tokenAddressOut user wants to receive /// @param tokenAddressIn Address of the token to convert @@ -765,16 +816,16 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo uint256 tokenInUnderlyingPrice = priceOracle.getPrice(tokenAddressIn); uint256 tokenOutUnderlyingPrice = priceOracle.getPrice(tokenAddressOut); - /// amount of tokenAddressOut after including incentive - uint256 conversionWithIncentive = MANTISSA_ONE + configuration.incentive; /// conversion rate after considering incentive(conversionWithIncentive) + uint256 conversionWithIncentive = MANTISSA_ONE + configuration.incentive; tokenInToOutConversion = (tokenInUnderlyingPrice * conversionWithIncentive) / tokenOutUnderlyingPrice; + /// amount of tokenAddressIn after considering incentive(i.e. amountInMantissa will be less than actual amountInMantissa if incentive > 0) amountInMantissa = ((amountOutMantissa * EXP_SCALE) + tokenInToOutConversion - 1) / tokenInToOutConversion; //round-up amountConvertedMantissa = amountOutMantissa; } - /// @notice To check, is conversion paused + /// @dev To check, is conversion paused /// @custom:error ConversionTokensPaused is thrown when token conversion is paused function _checkConversionPaused() internal view { if (conversionPaused) { @@ -782,6 +833,6 @@ abstract contract AbstractTokenConverter is AccessControlledV8, IAbstractTokenCo } } - /// @notice Get base asset address of the destination contract + /// @dev Get base asset address of the destination contract function _getDestinationBaseAsset() internal view virtual returns (address) {} } diff --git a/contracts/TokenConverter/IAbstractTokenConverter.sol b/contracts/TokenConverter/IAbstractTokenConverter.sol index 8a0f99bd..d587dcbf 100644 --- a/contracts/TokenConverter/IAbstractTokenConverter.sol +++ b/contracts/TokenConverter/IAbstractTokenConverter.sol @@ -91,4 +91,67 @@ interface IAbstractTokenConverter { address tokenAddressOut, address to ) external; + + /// @notice To get the amount of tokenAddressOut tokens sender could receive on providing amountInMantissa tokens of tokenAddressIn + /// @param amountInMantissa Amount of tokenAddressIn + /// @param tokenAddressIn Address of the token to convert + /// @param tokenAddressOut Address of the token to get after conversion + /// @return amountConvertedMantissa Amount of tokenAddressIn should be transferred after conversion + /// @return amountOutMantissa Amount of the tokenAddressOut sender should receive after conversion + /// @custom:error InsufficientInputAmount error is thrown when given input amount is zero + /// @custom:error ConversionConfigNotEnabled is thrown when conversion is disabled or config does not exist for given pair + function getUpdatedAmountOut( + uint256 amountInMantissa, + address tokenAddressIn, + address tokenAddressOut + ) external returns (uint256 amountConvertedMantissa, uint256 amountOutMantissa); + + /// @notice To get the amount of tokenAddressIn tokens sender would send on receiving amountOutMantissa tokens of tokenAddressOut + /// @param amountOutMantissa Amount of tokenAddressOut user wants to receive + /// @param tokenAddressIn Address of the token to convert + /// @param tokenAddressOut Address of the token to get after conversion + /// @return amountConvertedMantissa Amount of tokenAddressOut should be transferred after conversion + /// @return amountInMantissa Amount of the tokenAddressIn sender would send to contract before conversion + /// @custom:error InsufficientInputAmount error is thrown when given input amount is zero + /// @custom:error ConversionConfigNotEnabled is thrown when conversion is disabled or config does not exist for given pair + function getUpdatedAmountIn( + uint256 amountOutMantissa, + address tokenAddressIn, + address tokenAddressOut + ) external returns (uint256 amountConvertedMantissa, uint256 amountInMantissa); + + /// @notice To get the amount of tokenAddressIn tokens sender would send on receiving amountOutMantissa tokens of tokenAddressOut + /// @dev This function retrieves values without altering token prices. + /// @param amountOutMantissa Amount of tokenAddressOut user wants to receive + /// @param tokenAddressIn Address of the token to convert + /// @param tokenAddressOut Address of the token to get after conversion + /// @return amountConvertedMantissa Amount of tokenAddressOut should be transferred after conversion + /// @return amountInMantissa Amount of the tokenAddressIn sender would send to contract before conversion + /// @custom:error InsufficientInputAmount error is thrown when given input amount is zero + /// @custom:error ConversionConfigNotEnabled is thrown when conversion is disabled or config does not exist for given pair + function getAmountIn( + uint256 amountOutMantissa, + address tokenAddressIn, + address tokenAddressOut + ) external view returns (uint256 amountConvertedMantissa, uint256 amountInMantissa); + + /// @notice To get the amount of tokenAddressOut tokens sender could receive on providing amountInMantissa tokens of tokenAddressIn + /// @dev This function retrieves values without altering token prices. + /// @param amountInMantissa Amount of tokenAddressIn + /// @param tokenAddressIn Address of the token to convert + /// @param tokenAddressOut Address of the token to get after conversion + /// @return amountConvertedMantissa Amount of tokenAddressIn should be transferred after conversion + /// @return amountOutMantissa Amount of the tokenAddressOut sender should receive after conversion + /// @custom:error InsufficientInputAmount error is thrown when given input amount is zero + /// @custom:error ConversionConfigNotEnabled is thrown when conversion is disabled or config does not exist for given pair + function getAmountOut( + uint256 amountInMantissa, + address tokenAddressIn, + address tokenAddressOut + ) external view returns (uint256 amountConvertedMantissa, uint256 amountOutMantissa); + + /// @notice Get the balance for specific token + /// @param token Address of the token + /// @return tokenBalance Balance of the token the contract has + function balanceOf(address token) external view returns (uint256 tokenBalance); } diff --git a/contracts/TokenConverter/RiskFundConverter.sol b/contracts/TokenConverter/RiskFundConverter.sol index 7498f2df..944ecd8c 100644 --- a/contracts/TokenConverter/RiskFundConverter.sol +++ b/contracts/TokenConverter/RiskFundConverter.sol @@ -216,7 +216,7 @@ contract RiskFundConverter is AbstractTokenConverter { return pools; } - /// @notice Hook to perform after converting tokens + /// @dev Hook to perform after converting tokens /// @dev After transformation poolsAssetsReserves are settled by pool's reserves fraction /// @param tokenInAddress Address of the tokenIn /// @param tokenOutAddress Address of the tokenOut @@ -262,7 +262,7 @@ contract RiskFundConverter is AbstractTokenConverter { assetsReserves[tokenOutAddress] -= amountOut; } - /// @notice Operations to perform before sweeping tokens + /// @dev Operations to perform before sweeping tokens /// @param tokenAddress Address of the token /// @param amount Amount transferred to address(to) /// @custom:error InsufficientBalance is thrown when amount entered is greater than balance of token @@ -300,7 +300,7 @@ contract RiskFundConverter is AbstractTokenConverter { } } - /// @notice Update the poolAssetsReserves upon transferring the tokens + /// @dev Update the poolAssetsReserves upon transferring the tokens /// @param pool Address of the pool /// @param tokenAddress Address of the token /// @param amount Amount transferred to address(to) @@ -317,7 +317,7 @@ contract RiskFundConverter is AbstractTokenConverter { emit AssetsReservesUpdated(pool, tokenAddress, poolAmountShare); } - /// @notice Update the poolsAssetsDirectTransfer mapping + /// @dev Update the poolsAssetsDirectTransfer mapping /// @param comptrollers Addresses of the pools /// @param assets Addresses of the assets need to be added for direct transfer /// @param values Boolean value to indicate whether direct transfer is allowed for each asset. @@ -357,7 +357,7 @@ contract RiskFundConverter is AbstractTokenConverter { } } - /// @notice This function checks for the given asset is listed in core pool or not + /// @dev This function checks for the given asset is listed in core pool or not /// @param tokenAddress Address of the asset /// @return isAssetListed true if the asset is listed function isAssetListedInCore(address tokenAddress) internal view returns (bool isAssetListed) { @@ -379,7 +379,7 @@ contract RiskFundConverter is AbstractTokenConverter { } } - /// @notice This function checks for the given asset is listed or not + /// @dev This function checks for the given asset is listed or not /// @param comptroller Address of the comptroller /// @param asset Address of the asset /// @return true if the asset is listed @@ -391,7 +391,7 @@ contract RiskFundConverter is AbstractTokenConverter { return IPoolRegistry(poolRegistry).getVTokenForAsset(comptroller, asset) != address(0); } - /// @notice Get base asset address of the RiskFund + /// @dev Get base asset address of the RiskFund function _getDestinationBaseAsset() internal view override returns (address) { return IRiskFundGetters(destinationAddress).convertibleBaseAsset(); } diff --git a/contracts/TokenConverter/SingleTokenConverter.sol b/contracts/TokenConverter/SingleTokenConverter.sol index ad507983..52666a7e 100644 --- a/contracts/TokenConverter/SingleTokenConverter.sol +++ b/contracts/TokenConverter/SingleTokenConverter.sol @@ -69,7 +69,7 @@ contract SingleTokenConverter is AbstractTokenConverter { tokenBalance = token.balanceOf(address(this)); } - /// @notice Get base asset address + /// @dev Get base asset address function _getDestinationBaseAsset() internal view override returns (address) { return BASE_ASSET; } diff --git a/contracts/Utils/Constants.sol b/contracts/Utils/Constants.sol index 6e1225f2..9e6d9a5d 100644 --- a/contracts/Utils/Constants.sol +++ b/contracts/Utils/Constants.sol @@ -1,9 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.13; -/// @dev The approximate number of blocks per year that is assumed by the interest rate model -uint256 constant BLOCKS_PER_YEAR = 10_512_000; - /// @dev Base unit for computations, usually used in scaling (multiplications, divisions) uint256 constant EXP_SCALE = 1e18; diff --git a/tests/TokenConverter/AbstractTokenConverter.ts b/tests/TokenConverter/AbstractTokenConverter.ts index d24e8f3b..7431583b 100644 --- a/tests/TokenConverter/AbstractTokenConverter.ts +++ b/tests/TokenConverter/AbstractTokenConverter.ts @@ -109,7 +109,16 @@ describe("MockConverter: tests", () => { await tx.wait(); - await expect(tx).to.emit(converter, "ConvertedForExactTokens").withArgs(expectedResults[1], expectedResults[0]); + await expect(tx) + .to.emit(converter, "ConvertedForExactTokens") + .withArgs( + await owner.getAddress(), + await to.getAddress(), + tokenIn.address, + tokenOut.address, + expectedResults[1], + expectedResults[0], + ); }); it("Revert on lower amount out than expected", async () => { @@ -173,7 +182,29 @@ describe("MockConverter: tests", () => { await tx.wait(); - await expect(tx).to.emit(converter, "ConvertedExactTokens").withArgs(expectedResults[0], expectedResults[1]); + await expect(tx) + .to.emit(converter, "ConvertedExactTokens") + .withArgs( + await owner.getAddress(), + await to.getAddress(), + tokenIn.address, + tokenOut.address, + expectedResults[0], + expectedResults[1], + ); + }); + + it("Revert when to address is invalid", async () => { + await converter.setConversionConfig(tokenIn.address, tokenOut.address, ConversionConfig); + await expect( + converter.convertExactTokens( + convertToUnit(".5", 18), + convertToUnit("1.5", 18), + tokenIn.address, + tokenOut.address, + await ethers.constants.AddressZero, + ), + ).to.be.revertedWithCustomError(converter, "ZeroAddressNotAllowed"); }); it("Revert on lower amount out than expected", async () => { @@ -274,7 +305,14 @@ describe("MockConverter: tests", () => { ), ) .to.emit(converter, "ConvertedExactTokensSupportingFeeOnTransferTokens") - .withArgs(amountTransferredAfterFees, expectedResults[1]); + .withArgs( + await owner.getAddress(), + await to.getAddress(), + tokenInDeflationary.address, + tokenOut.address, + amountTransferredAfterFees, + expectedResults[1], + ); }); it("Revert on deflationary token transfer", async () => { @@ -555,7 +593,7 @@ describe("MockConverter: tests", () => { ).to.be.revertedWithCustomError(converter, "ConversionConfigNotEnabled"); }); - it("Success on conversing tokenIn to tokenOut for under tokenOut liquidity", async () => { + it("Success on converting tokenIn to tokenOut for under tokenOut liquidity", async () => { await setConversionConfig(); await oracle.getPrice.whenCalledWith(tokenIn.address).returns(TOKEN_IN_PRICE); await oracle.getPrice.whenCalledWith(tokenOut.address).returns(TOKEN_OUT_PRICE); diff --git a/tests/TokenConverter/RiskFundConverter.ts b/tests/TokenConverter/RiskFundConverter.ts index b8bf1260..4d2e1548 100644 --- a/tests/TokenConverter/RiskFundConverter.ts +++ b/tests/TokenConverter/RiskFundConverter.ts @@ -17,7 +17,7 @@ import { MockRiskFundConverter__factory, MockToken, MockToken__factory, - ResilientOracleInterface, + ResilientOracle, RiskFundV2, } from "../../typechain"; import { convertToUnit } from "../utils"; @@ -29,7 +29,7 @@ let accessControl: FakeContract; let converter: MockContract; let tokenIn: MockContract; let tokenOut: MockContract; -let oracle: FakeContract; +let oracle: FakeContract; let poolRegistry: FakeContract; let newPoolRegistry: FakeContract; let riskFund: FakeContract; @@ -59,7 +59,7 @@ async function fixture(): Promise { WBNB = await smock.fake("MockToken"); accessControl = await smock.fake("IAccessControlManagerV8"); - oracle = await smock.fake("ResilientOracleInterface"); + oracle = await smock.fake("ResilientOracle"); const MockToken = await smock.mock("MockToken"); const MockTokenDeflationary = await smock.mock("MockDeflatingToken"); @@ -244,6 +244,7 @@ describe("Risk fund Converter: tests", () => { expect(await tokenIn.balanceOf(converter.address)).to.equal(0); expect(await tokenIn.balanceOf(riskFund.address)).to.equal(POOL_A_AMOUNT); + expect(await converter.getAssetsReserves(tokenIn.address)).to.equal(0); }); it("Revert on invalid parameters", async () => { @@ -270,4 +271,77 @@ describe("Risk fund Converter: tests", () => { ).to.be.revertedWithCustomError(converter, "InvalidArguments"); }); }); + + describe("Converting function", () => { + const amount = convertToUnit("200", 18); + const amountTransferred = convertToUnit("50", 18); + const TOKEN_IN_PRICE = convertToUnit("1", 18); + const TOKEN_OUT_PRICE = convertToUnit("0.5", 18); + const INCENTIVE = convertToUnit("1", 17); + + beforeEach(async () => { + newPoolRegistry.getPoolsSupportedByAsset.returns([poolC.address]); + + await converter.setPoolsAssetsReserves(poolC.address, tokenIn.address, 0); + await converter.setPoolsAssetsReserves(poolC.address, tokenOut.address, 0); + await converter.setPoolsAssetsReserves(corePool.address, tokenIn.address, 0); + await converter.setPoolsAssetsReserves(corePool.address, tokenOut.address, 0); + await converter.setAssetsReserves(tokenIn.address, 0); + await converter.setAssetsReserves(tokenOut.address, 0); + + await tokenIn.faucet(amount); + await tokenOut.faucet(amount); + await tokenIn.transfer(converter.address, amount); + await tokenOut.transfer(converter.address, amount); + + await converter.updateAssetsState(poolC.address, tokenIn.address); + await converter.updateAssetsState(poolC.address, tokenOut.address); + }); + + it("Should update states correctly on conversion of tokens", async () => { + const [admin] = await ethers.getSigners(); + + expect(await converter.getAssetsReserves(tokenIn.address)).to.equal(amount); + expect(await converter.getAssetsReserves(tokenOut.address)).to.equal(amount); + await expect(await converter.getPoolAssetReserve(poolC.address, tokenOut.address)).to.equal(amount); + await expect(await converter.getPoolAssetReserve(poolC.address, tokenIn.address)).to.equal(amount); + + await riskFund.convertibleBaseAsset.returns(tokenIn.address); + + const ConversionConfig = { + tokenAddressIn: tokenIn.address, + tokenAddressOut: tokenOut.address, + incentive: INCENTIVE, + enabled: true, + }; + + await converter.connect(admin).setConversionConfig(tokenIn.address, tokenOut.address, ConversionConfig); + + await oracle.getPrice.whenCalledWith(tokenIn.address).returns(TOKEN_IN_PRICE); + await oracle.getPrice.whenCalledWith(tokenOut.address).returns(TOKEN_OUT_PRICE); + + const expectedResults = await converter.callStatic.getUpdatedAmountOut( + amountTransferred, + tokenIn.address, + tokenOut.address, + ); + + await tokenIn.approve(converter.address, amountTransferred); + await converter.convertExactTokens( + amountTransferred, + convertToUnit(".5", 18), + tokenIn.address, + tokenOut.address, + await unKnown.getAddress(), + ); + await expect(await converter.getAssetsReserves(tokenOut.address)).to.equal( + BigNumber(amount).minus(Number(expectedResults[1])), + ); + await expect(await converter.getAssetsReserves(tokenIn.address)).to.equal(amount); + await expect(await converter.getPoolAssetReserve(poolC.address, tokenOut.address)).to.equal( + BigNumber(amount).minus(Number(expectedResults[1])), + ); + await expect(await converter.getPoolAssetReserve(poolC.address, tokenIn.address)).to.equal(amount); + }); + }); });