From 43c8ef02dbcfae1fbbbde572345ecd54637108d1 Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 15 Sep 2020 16:53:20 +0300 Subject: [PATCH 01/11] initial integration of pei, no tests --- .../interfaces/IAaveIncentivesController.sol | 11 +++++ contracts/mocks/upgradeability/MockAToken.sol | 5 ++- .../upgradeability/MockStableDebtToken.sol | 8 +++- .../upgradeability/MockVariableDebtToken.sol | 14 ++++++- contracts/tokenization/AToken.sol | 20 ++++++++- contracts/tokenization/ERC20.sol | 41 ++++++++++++++++--- contracts/tokenization/StableDebtToken.sol | 5 ++- contracts/tokenization/VariableDebtToken.sol | 5 ++- contracts/tokenization/base/DebtTokenBase.sol | 5 ++- contracts/tokenization/interfaces/IAToken.sol | 8 ++++ 10 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 contracts/interfaces/IAaveIncentivesController.sol diff --git a/contracts/interfaces/IAaveIncentivesController.sol b/contracts/interfaces/IAaveIncentivesController.sol new file mode 100644 index 00000000..83f1769c --- /dev/null +++ b/contracts/interfaces/IAaveIncentivesController.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.8; +pragma experimental ABIEncoderV2; + +interface IAaveIncentivesController { + function handleAction( + address user, + uint256 userBalance, + uint256 totalSupply + ) external; +} diff --git a/contracts/mocks/upgradeability/MockAToken.sol b/contracts/mocks/upgradeability/MockAToken.sol index 05f0db4e..f84543d9 100644 --- a/contracts/mocks/upgradeability/MockAToken.sol +++ b/contracts/mocks/upgradeability/MockAToken.sol @@ -9,8 +9,9 @@ contract MockAToken is AToken { LendingPool _pool, address _underlyingAssetAddress, string memory _tokenName, - string memory _tokenSymbol - ) public AToken(_pool, _underlyingAssetAddress, _tokenName, _tokenSymbol) {} + string memory _tokenSymbol, + address incentivesController + ) public AToken(_pool, _underlyingAssetAddress, _tokenName, _tokenSymbol, incentivesController) {} function getRevision() internal override pure returns (uint256) { return 0x2; diff --git a/contracts/mocks/upgradeability/MockStableDebtToken.sol b/contracts/mocks/upgradeability/MockStableDebtToken.sol index 52e7a99f..6f44cf07 100644 --- a/contracts/mocks/upgradeability/MockStableDebtToken.sol +++ b/contracts/mocks/upgradeability/MockStableDebtToken.sol @@ -9,8 +9,12 @@ contract MockStableDebtToken is StableDebtToken { address _pool, address _underlyingAssetAddress, string memory _tokenName, - string memory _tokenSymbol - ) public StableDebtToken(_pool, _underlyingAssetAddress, _tokenName, _tokenSymbol) {} + string memory _tokenSymbol, + address incentivesController + ) + public + StableDebtToken(_pool, _underlyingAssetAddress, _tokenName, _tokenSymbol, incentivesController) + {} function getRevision() internal override pure returns (uint256) { return 0x2; diff --git a/contracts/mocks/upgradeability/MockVariableDebtToken.sol b/contracts/mocks/upgradeability/MockVariableDebtToken.sol index cb46d4d8..4e4a7ffa 100644 --- a/contracts/mocks/upgradeability/MockVariableDebtToken.sol +++ b/contracts/mocks/upgradeability/MockVariableDebtToken.sol @@ -9,8 +9,18 @@ contract MockVariableDebtToken is VariableDebtToken { address _pool, address _underlyingAssetAddress, string memory _tokenName, - string memory _tokenSymbol - ) public VariableDebtToken(_pool, _underlyingAssetAddress, _tokenName, _tokenSymbol) {} + string memory _tokenSymbol, + address incentivesController + ) + public + VariableDebtToken( + _pool, + _underlyingAssetAddress, + _tokenName, + _tokenSymbol, + incentivesController + ) + {} function getRevision() internal override pure returns (uint256) { return 0x2; diff --git a/contracts/tokenization/AToken.sol b/contracts/tokenization/AToken.sol index c2924d3d..3c248fdb 100644 --- a/contracts/tokenization/AToken.sol +++ b/contracts/tokenization/AToken.sol @@ -45,8 +45,9 @@ contract AToken is VersionedInitializable, ERC20, IAToken { LendingPool pool, address underlyingAssetAddress, string memory tokenName, - string memory tokenSymbol - ) public ERC20(tokenName, tokenSymbol, 18) { + string memory tokenSymbol, + address incentivesController + ) public ERC20(tokenName, tokenSymbol, 18, incentivesController) { POOL = pool; UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress; } @@ -161,6 +162,21 @@ contract AToken is VersionedInitializable, ERC20, IAToken { return super.balanceOf(user); } + /** + * @dev returns the principal balance of the user and principal total supply. + * @param user the address of the user + * @return the principal balance of the user + * @return the principal total supply + **/ + function getScaledUserBalanceAndSupply(address user) + external + override + view + returns (uint256, uint256) + { + return (super.balanceOf(user), super.totalSupply()); + } + /** * @dev calculates the total supply of the specific aToken * since the balance of every single user increases over time, the total supply diff --git a/contracts/tokenization/ERC20.sol b/contracts/tokenization/ERC20.sol index f2902ce4..91194087 100644 --- a/contracts/tokenization/ERC20.sol +++ b/contracts/tokenization/ERC20.sol @@ -5,6 +5,7 @@ import {Context} from '../misc/Context.sol'; import {IERC20} from '../interfaces/IERC20.sol'; import {IERC20Detailed} from '../interfaces/IERC20Detailed.sol'; import {SafeMath} from '../libraries/math/SafeMath.sol'; +import {IAaveIncentivesController} from '../interfaces/IAaveIncentivesController.sol'; /** * @title ERC20 @@ -14,6 +15,8 @@ import {SafeMath} from '../libraries/math/SafeMath.sol'; contract ERC20 is Context, IERC20, IERC20Detailed { using SafeMath for uint256; + IAaveIncentivesController internal immutable _incentivesController; + mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; @@ -24,11 +27,13 @@ contract ERC20 is Context, IERC20, IERC20Detailed { constructor( string memory name, string memory symbol, - uint8 decimals + uint8 decimals, + address incentivesController ) public { _name = name; _symbol = symbol; _decimals = decimals; + _incentivesController = IAaveIncentivesController(incentivesController); } /** @@ -167,8 +172,16 @@ contract ERC20 is Context, IERC20, IERC20Detailed { _beforeTokenTransfer(sender, recipient, amount); - _balances[sender] = _balances[sender].sub(amount, 'ERC20: transfer amount exceeds balance'); + uint256 oldSenderBalance = _balances[sender]; + _balances[sender] = oldSenderBalance.sub(amount, 'ERC20: transfer amount exceeds balance'); + uint256 oldRecipientBalance = _balances[recipient]; _balances[recipient] = _balances[recipient].add(amount); + + if (address(_incentivesController) != address(0x0)) { + uint256 totalSupply = _totalSupply; + _incentivesController.handleAction(sender, totalSupply, oldSenderBalance); + _incentivesController.handleAction(recipient, totalSupply, oldRecipientBalance); + } emit Transfer(sender, recipient, amount); } @@ -177,8 +190,16 @@ contract ERC20 is Context, IERC20, IERC20Detailed { _beforeTokenTransfer(address(0), account, amount); - _totalSupply = _totalSupply.add(amount); - _balances[account] = _balances[account].add(amount); + uint256 oldTotalSupply = _totalSupply; + _totalSupply = oldTotalSupply.add(amount); + + uint256 oldAccountBalance = _balances[account]; + _balances[account] = oldAccountBalance.add(amount); + + if (address(_incentivesController) != address(0x0)) { + _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance); + } + emit Transfer(address(0), account, amount); } @@ -187,8 +208,16 @@ contract ERC20 is Context, IERC20, IERC20Detailed { _beforeTokenTransfer(account, address(0), amount); - _balances[account] = _balances[account].sub(amount, 'ERC20: burn amount exceeds balance'); - _totalSupply = _totalSupply.sub(amount); + uint256 oldTotalSupply = _totalSupply; + _totalSupply = oldTotalSupply.sub(amount); + + uint256 oldAccountBalance = _balances[account]; + _balances[account] = oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance'); + + if (address(_incentivesController) != address(0x0)) { + _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance); + } + emit Transfer(account, address(0), amount); } diff --git a/contracts/tokenization/StableDebtToken.sol b/contracts/tokenization/StableDebtToken.sol index 8e5da21b..fd2affca 100644 --- a/contracts/tokenization/StableDebtToken.sol +++ b/contracts/tokenization/StableDebtToken.sol @@ -26,8 +26,9 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { address pool, address underlyingAsset, string memory name, - string memory symbol - ) public DebtTokenBase(pool, underlyingAsset, name, symbol) {} + string memory symbol, + address incentivesController + ) public DebtTokenBase(pool, underlyingAsset, name, symbol, incentivesController) {} /** * @dev gets the revision of the stable debt token implementation diff --git a/contracts/tokenization/VariableDebtToken.sol b/contracts/tokenization/VariableDebtToken.sol index e52f7c61..879ffc17 100644 --- a/contracts/tokenization/VariableDebtToken.sol +++ b/contracts/tokenization/VariableDebtToken.sol @@ -22,8 +22,9 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { address pool, address underlyingAsset, string memory name, - string memory symbol - ) public DebtTokenBase(pool, underlyingAsset, name, symbol) {} + string memory symbol, + address incentivesController + ) public DebtTokenBase(pool, underlyingAsset, name, symbol, incentivesController) {} /** * @dev gets the revision of the stable debt token implementation diff --git a/contracts/tokenization/base/DebtTokenBase.sol b/contracts/tokenization/base/DebtTokenBase.sol index a6bd4005..f09e92d4 100644 --- a/contracts/tokenization/base/DebtTokenBase.sol +++ b/contracts/tokenization/base/DebtTokenBase.sol @@ -38,8 +38,9 @@ abstract contract DebtTokenBase is ERC20, VersionedInitializable { address pool, address underlyingAssetAddress, string memory name, - string memory symbol - ) public ERC20(name, symbol, 18) { + string memory symbol, + address incentivesController + ) public ERC20(name, symbol, 18, incentivesController) { POOL = ILendingPool(pool); UNDERLYING_ASSET = underlyingAssetAddress; } diff --git a/contracts/tokenization/interfaces/IAToken.sol b/contracts/tokenization/interfaces/IAToken.sol index 873860a9..e1975074 100644 --- a/contracts/tokenization/interfaces/IAToken.sol +++ b/contracts/tokenization/interfaces/IAToken.sol @@ -82,6 +82,14 @@ interface IAToken is IERC20 { **/ function scaledBalanceOf(address user) external view returns (uint256); + /** + * @dev returns the principal balance of the user and principal total supply. + * @param user the address of the user + * @return the principal balance of the user + * @return the principal total supply + **/ + function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256); + /** * @dev Used to validate transfers before actually executing them. * @param user address of the user to check From 84cf68e58ef464a7c229c1e074cbc9468512de99 Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 15 Sep 2020 17:02:21 +0300 Subject: [PATCH 02/11] fix deployment and tests --- helpers/contracts-helpers.ts | 78 ++++++++++++++++++------------------ test/__setup.spec.ts | 9 ++++- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index 2f46cf71..9179aa21 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -32,8 +32,8 @@ import {Ierc20Detailed} from '../types/Ierc20Detailed'; import {StableDebtToken} from '../types/StableDebtToken'; import {VariableDebtToken} from '../types/VariableDebtToken'; import {MockSwapAdapter} from '../types/MockSwapAdapter'; -import { signTypedData_v4, TypedData } from "eth-sig-util"; -import { fromRpcSig, ECDSASignature } from "ethereumjs-util"; +import {signTypedData_v4, TypedData} from 'eth-sig-util'; +import {fromRpcSig, ECDSASignature} from 'ethereumjs-util'; export const registerContractInJsonDb = async (contractId: string, contractInstance: Contract) => { const currentNetwork = BRE.network.name; @@ -251,49 +251,55 @@ export const deployDefaultReserveInterestRateStrategy = async ([ ] ); -export const deployStableDebtToken = async ([name, symbol, underlyingAsset, poolAddress]: [ - string, - string, - tEthereumAddress, - tEthereumAddress -]) => { +export const deployStableDebtToken = async ([ + name, + symbol, + underlyingAsset, + poolAddress, + incentivesController, +]: [string, string, tEthereumAddress, tEthereumAddress, tEthereumAddress]) => { const token = await deployContract(eContractid.StableDebtToken, [ poolAddress, underlyingAsset, name, symbol, + incentivesController, ]); return token; }; -export const deployVariableDebtToken = async ([name, symbol, underlyingAsset, poolAddress]: [ - string, - string, - tEthereumAddress, - tEthereumAddress -]) => { +export const deployVariableDebtToken = async ([ + name, + symbol, + underlyingAsset, + poolAddress, + incentivesController, +]: [string, string, tEthereumAddress, tEthereumAddress, tEthereumAddress]) => { const token = await deployContract(eContractid.VariableDebtToken, [ poolAddress, underlyingAsset, name, symbol, + incentivesController, ]); return token; }; -export const deployGenericAToken = async ([poolAddress, underlyingAssetAddress, name, symbol]: [ - tEthereumAddress, - tEthereumAddress, - string, - string -]) => { +export const deployGenericAToken = async ([ + poolAddress, + underlyingAssetAddress, + name, + symbol, + incentivesController, +]: [tEthereumAddress, tEthereumAddress, string, string, tEthereumAddress]) => { const token = await deployContract(eContractid.AToken, [ poolAddress, underlyingAssetAddress, name, symbol, + incentivesController, ]); return token; @@ -491,20 +497,20 @@ export const buildPermitParams = ( ) => ({ types: { EIP712Domain: [ - { name: "name", type: "string" }, - { name: "version", type: "string" }, - { name: "chainId", type: "uint256" }, - { name: "verifyingContract", type: "address" }, + {name: 'name', type: 'string'}, + {name: 'version', type: 'string'}, + {name: 'chainId', type: 'uint256'}, + {name: 'verifyingContract', type: 'address'}, ], Permit: [ - { name: "owner", type: "address" }, - { name: "spender", type: "address" }, - { name: "value", type: "uint256" }, - { name: "nonce", type: "uint256" }, - { name: "deadline", type: "uint256" }, + {name: 'owner', type: 'address'}, + {name: 'spender', type: 'address'}, + {name: 'value', type: 'uint256'}, + {name: 'nonce', type: 'uint256'}, + {name: 'deadline', type: 'uint256'}, ], }, - primaryType: "Permit" as const, + primaryType: 'Permit' as const, domain: { name: tokenName, version: revision, @@ -520,16 +526,12 @@ export const buildPermitParams = ( }, }); - export const getSignatureFromTypedData = ( privateKey: string, typedData: any // TODO: should be TypedData, from eth-sig-utils, but TS doesn't accept it ): ECDSASignature => { - const signature = signTypedData_v4( - Buffer.from(privateKey.substring(2, 66), "hex"), - { - data: typedData, - } - ); + const signature = signTypedData_v4(Buffer.from(privateKey.substring(2, 66), 'hex'), { + data: typedData, + }); return fromRpcSig(signature); -}; \ No newline at end of file +}; diff --git a/test/__setup.spec.ts b/test/__setup.spec.ts index e2db2752..e893d6af 100644 --- a/test/__setup.spec.ts +++ b/test/__setup.spec.ts @@ -175,7 +175,8 @@ const initReserves = async ( lendingPoolAddressesProvider: LendingPoolAddressesProvider, lendingPool: LendingPool, lendingPoolConfigurator: LendingPoolConfigurator, - aavePool: AavePools + aavePool: AavePools, + incentivesController: tEthereumAddress ) => { if (aavePool !== AavePools.proto && aavePool !== AavePools.secondary) { console.log(`Invalid Aave pool ${aavePool}`); @@ -230,6 +231,7 @@ const initReserves = async ( `stableDebt${assetSymbol === 'WETH' ? 'ETH' : assetSymbol}`, tokenAddress, lendingPool.address, + incentivesController, ]); const variableDebtToken = await deployVariableDebtToken([ @@ -237,6 +239,7 @@ const initReserves = async ( `variableDebt${assetSymbol === 'WETH' ? 'ETH' : assetSymbol}`, tokenAddress, lendingPool.address, + incentivesController, ]); const aToken = await deployGenericAToken([ @@ -244,6 +247,7 @@ const initReserves = async ( tokenAddress, `Aave interest bearing ${assetSymbol === 'WETH' ? 'ETH' : assetSymbol}`, `a${assetSymbol === 'WETH' ? 'ETH' : assetSymbol}`, + incentivesController, ]); if (process.env.POOL === AavePools.secondary) { @@ -481,7 +485,8 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { addressesProvider, lendingPoolProxy, lendingPoolConfiguratorProxy, - AavePools.proto + AavePools.proto, + ZERO_ADDRESS ); await enableReservesToBorrow( reservesParams, From 1a7933762fc7280f9d4653021a632292136de251 Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 15 Sep 2020 17:13:29 +0300 Subject: [PATCH 03/11] fix Upgradeability tests setup --- test/upgradeability.spec.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/upgradeability.spec.ts b/test/upgradeability.spec.ts index b2f50179..6139f3a4 100644 --- a/test/upgradeability.spec.ts +++ b/test/upgradeability.spec.ts @@ -10,6 +10,7 @@ import { import {MockAToken} from '../types/MockAToken'; import {MockStableDebtToken} from '../types/MockStableDebtToken'; import {MockVariableDebtToken} from '../types/MockVariableDebtToken'; +import {ZERO_ADDRESS} from '../helpers/constants'; makeSuite('Upgradeability', (testEnv: TestEnv) => { const {CALLER_NOT_LENDING_POOL_MANAGER} = ProtocolErrors; @@ -24,16 +25,29 @@ makeSuite('Upgradeability', (testEnv: TestEnv) => { dai.address, 'Aave Interest bearing DAI updated', 'aDAI', + ZERO_ADDRESS, ]); const stableDebtTokenInstance = await deployContract( eContractid.MockStableDebtToken, - [pool.address, dai.address, 'Aave stable debt bearing DAI updated', 'stableDebtDAI'] + [ + pool.address, + dai.address, + 'Aave stable debt bearing DAI updated', + 'stableDebtDAI', + ZERO_ADDRESS, + ] ); const variableDebtTokenInstance = await deployContract( eContractid.MockVariableDebtToken, - [pool.address, dai.address, 'Aave variable debt bearing DAI updated', 'variableDebtDAI'] + [ + pool.address, + dai.address, + 'Aave variable debt bearing DAI updated', + 'variableDebtDAI', + ZERO_ADDRESS, + ] ); newATokenAddress = aTokenInstance.address; From 3340bb14fb61055f95a921000abdef0e76f68a7b Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 15 Sep 2020 17:40:40 +0300 Subject: [PATCH 04/11] typo 0x0 -> 0 --- contracts/tokenization/ERC20.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/tokenization/ERC20.sol b/contracts/tokenization/ERC20.sol index 91194087..24257c3e 100644 --- a/contracts/tokenization/ERC20.sol +++ b/contracts/tokenization/ERC20.sol @@ -177,7 +177,7 @@ contract ERC20 is Context, IERC20, IERC20Detailed { uint256 oldRecipientBalance = _balances[recipient]; _balances[recipient] = _balances[recipient].add(amount); - if (address(_incentivesController) != address(0x0)) { + if (address(_incentivesController) != address(0)) { uint256 totalSupply = _totalSupply; _incentivesController.handleAction(sender, totalSupply, oldSenderBalance); _incentivesController.handleAction(recipient, totalSupply, oldRecipientBalance); @@ -196,7 +196,7 @@ contract ERC20 is Context, IERC20, IERC20Detailed { uint256 oldAccountBalance = _balances[account]; _balances[account] = oldAccountBalance.add(amount); - if (address(_incentivesController) != address(0x0)) { + if (address(_incentivesController) != address(0)) { _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance); } @@ -214,7 +214,7 @@ contract ERC20 is Context, IERC20, IERC20Detailed { uint256 oldAccountBalance = _balances[account]; _balances[account] = oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance'); - if (address(_incentivesController) != address(0x0)) { + if (address(_incentivesController) != address(0)) { _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance); } From 6af1e0923f5fc7fa2db0d11c1cdaaefb2cf3a7dc Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 15 Sep 2020 17:49:53 +0300 Subject: [PATCH 05/11] fix _incentivesController call in _transfer --- contracts/tokenization/ERC20.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/tokenization/ERC20.sol b/contracts/tokenization/ERC20.sol index 24257c3e..d7d33ab9 100644 --- a/contracts/tokenization/ERC20.sol +++ b/contracts/tokenization/ERC20.sol @@ -180,7 +180,9 @@ contract ERC20 is Context, IERC20, IERC20Detailed { if (address(_incentivesController) != address(0)) { uint256 totalSupply = _totalSupply; _incentivesController.handleAction(sender, totalSupply, oldSenderBalance); - _incentivesController.handleAction(recipient, totalSupply, oldRecipientBalance); + if (sender != recipient) { + _incentivesController.handleAction(recipient, totalSupply, oldRecipientBalance); + } } emit Transfer(sender, recipient, amount); } From fba2f03c03ef7626dbb67968ed2b720753566746 Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 15 Sep 2020 18:08:28 +0300 Subject: [PATCH 06/11] rename ERC20 to IncentivizedERC20 --- contracts/tokenization/AToken.sol | 28 ++++++++++--------- .../{ERC20.sol => IncentivizedERC20.sol} | 2 +- contracts/tokenization/base/DebtTokenBase.sol | 6 ++-- 3 files changed, 19 insertions(+), 17 deletions(-) rename contracts/tokenization/{ERC20.sol => IncentivizedERC20.sol} (99%) diff --git a/contracts/tokenization/AToken.sol b/contracts/tokenization/AToken.sol index 3c248fdb..b829350c 100644 --- a/contracts/tokenization/AToken.sol +++ b/contracts/tokenization/AToken.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: agpl-3.0 pragma solidity ^0.6.8; -import {ERC20} from './ERC20.sol'; +import {IncentivizedERC20} from './IncentivizedERC20.sol'; import {LendingPool} from '../lendingpool/LendingPool.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; @@ -18,9 +18,9 @@ import {SafeERC20} from '../misc/SafeERC20.sol'; * @dev Implementation of the interest bearing token for the DLP protocol. * @author Aave */ -contract AToken is VersionedInitializable, ERC20, IAToken { +contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { using WadRayMath for uint256; - using SafeERC20 for ERC20; + using SafeERC20 for IncentivizedERC20; uint256 public constant UINT_MAX_VALUE = uint256(-1); address public immutable UNDERLYING_ASSET_ADDRESS; @@ -47,7 +47,7 @@ contract AToken is VersionedInitializable, ERC20, IAToken { string memory tokenName, string memory tokenSymbol, address incentivesController - ) public ERC20(tokenName, tokenSymbol, 18, incentivesController) { + ) public IncentivizedERC20(tokenName, tokenSymbol, 18, incentivesController) { POOL = pool; UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress; } @@ -102,8 +102,7 @@ contract AToken is VersionedInitializable, ERC20, IAToken { _burn(user, scaledAmount); //transfers the underlying to the target - ERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(receiverOfUnderlying, amount); - + IncentivizedERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(receiverOfUnderlying, amount); emit Burn(msg.sender, receiverOfUnderlying, amount, index); } @@ -144,11 +143,16 @@ contract AToken is VersionedInitializable, ERC20, IAToken { /** * @dev calculates the balance of the user, which is the - * principal balance + interest generated by the principal balance + * principal balance + interest generated by the principal balance * @param user the user for which the balance is being calculated * @return the total balance of the user **/ - function balanceOf(address user) public override(ERC20, IERC20) view returns (uint256) { + function balanceOf(address user) + public + override(IncentivizedERC20, IERC20) + view + returns (uint256) + { return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS)); } @@ -183,16 +187,14 @@ contract AToken is VersionedInitializable, ERC20, IAToken { * does that too. * @return the current total supply **/ - function totalSupply() public override(ERC20, IERC20) view returns (uint256) { + function totalSupply() public override(IncentivizedERC20, IERC20) view returns (uint256) { uint256 currentSupplyScaled = super.totalSupply(); if (currentSupplyScaled == 0) { return 0; } - return - currentSupplyScaled - .rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS)); + return currentSupplyScaled.rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS)); } /** @@ -218,7 +220,7 @@ contract AToken is VersionedInitializable, ERC20, IAToken { onlyLendingPool returns (uint256) { - ERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(target, amount); + IncentivizedERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(target, amount); return amount; } diff --git a/contracts/tokenization/ERC20.sol b/contracts/tokenization/IncentivizedERC20.sol similarity index 99% rename from contracts/tokenization/ERC20.sol rename to contracts/tokenization/IncentivizedERC20.sol index d7d33ab9..0d3aa311 100644 --- a/contracts/tokenization/ERC20.sol +++ b/contracts/tokenization/IncentivizedERC20.sol @@ -12,7 +12,7 @@ import {IAaveIncentivesController} from '../interfaces/IAaveIncentivesController * @notice Basic ERC20 implementation * @author Aave **/ -contract ERC20 is Context, IERC20, IERC20Detailed { +contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { using SafeMath for uint256; IAaveIncentivesController internal immutable _incentivesController; diff --git a/contracts/tokenization/base/DebtTokenBase.sol b/contracts/tokenization/base/DebtTokenBase.sol index f09e92d4..ceb51688 100644 --- a/contracts/tokenization/base/DebtTokenBase.sol +++ b/contracts/tokenization/base/DebtTokenBase.sol @@ -8,7 +8,7 @@ import {ILendingPool} from '../../interfaces/ILendingPool.sol'; import { VersionedInitializable } from '../../libraries/openzeppelin-upgradeability/VersionedInitializable.sol'; -import {ERC20} from '../ERC20.sol'; +import {IncentivizedERC20} from '../IncentivizedERC20.sol'; import {Errors} from '../../libraries/helpers/Errors.sol'; /** @@ -17,7 +17,7 @@ import {Errors} from '../../libraries/helpers/Errors.sol'; * @author Aave */ -abstract contract DebtTokenBase is ERC20, VersionedInitializable { +abstract contract DebtTokenBase is IncentivizedERC20, VersionedInitializable { address internal immutable UNDERLYING_ASSET; ILendingPool internal immutable POOL; mapping(address => uint256) internal _usersData; @@ -40,7 +40,7 @@ abstract contract DebtTokenBase is ERC20, VersionedInitializable { string memory name, string memory symbol, address incentivesController - ) public ERC20(name, symbol, 18, incentivesController) { + ) public IncentivizedERC20(name, symbol, 18, incentivesController) { POOL = ILendingPool(pool); UNDERLYING_ASSET = underlyingAssetAddress; } From ee6d01743932604579f6758993f118103fce537d Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 15 Sep 2020 18:11:43 +0300 Subject: [PATCH 07/11] update package-lock --- package-lock.json | 156 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 149 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0b5eff61..80de030a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -801,6 +801,21 @@ "readdirp": "~3.4.0" } }, + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, "readdirp": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", @@ -862,6 +877,21 @@ "util.promisify": "^1.0.0" }, "dependencies": { + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -3970,6 +4000,21 @@ "sync-request": "^6.0.0" }, "dependencies": { + "ethereumjs-util": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.0.tgz", + "integrity": "sha512-vb0XN9J2QGdZGIEKG2vXM+kUdEivUfU6Wmi5y0cg+LRhDYKnXIZ/Lz7XjFbHRR9VIKq2lVGLzGBkA++y2nOdOQ==", + "dev": true, + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "ethjs-util": "0.1.6", + "keccak": "^2.0.0", + "rlp": "^2.2.3", + "secp256k1": "^3.0.1" + } + }, "ethers": { "version": "4.0.47", "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.47.tgz", @@ -4239,6 +4284,23 @@ "requires": { "bn.js": "^4.11.8", "ethereumjs-util": "^6.0.0" + }, + "dependencies": { + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + } } }, "ethereumjs-account": { @@ -4250,6 +4312,23 @@ "ethereumjs-util": "^6.0.0", "rlp": "^2.2.1", "safe-buffer": "^5.1.1" + }, + "dependencies": { + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + } } }, "ethereumjs-block": { @@ -4368,6 +4447,23 @@ "lru-cache": "^5.1.1", "rlp": "^2.2.2", "semaphore": "^1.1.0" + }, + "dependencies": { + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + } } }, "ethereumjs-common": { @@ -4384,21 +4480,67 @@ "requires": { "ethereumjs-common": "^1.5.0", "ethereumjs-util": "^6.0.0" + }, + "dependencies": { + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + } } }, "ethereumjs-util": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.0.tgz", - "integrity": "sha512-vb0XN9J2QGdZGIEKG2vXM+kUdEivUfU6Wmi5y0cg+LRhDYKnXIZ/Lz7XjFbHRR9VIKq2lVGLzGBkA++y2nOdOQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.0.2.tgz", + "integrity": "sha512-ATAP02eJLpAlWGfiKQddNrRfZpwXiTFhRN2EM/yLXMCdBW/xjKYblNKcx8GLzzrjXg0ymotck+lam1nuV90arQ==", "dev": true, "requires": { "@types/bn.js": "^4.11.3", - "bn.js": "^4.11.0", + "bn.js": "^5.1.2", "create-hash": "^1.1.2", "ethjs-util": "0.1.6", - "keccak": "^2.0.0", - "rlp": "^2.2.3", - "secp256k1": "^3.0.1" + "keccak": "^3.0.0", + "rlp": "^2.2.4", + "secp256k1": "^4.0.1" + }, + "dependencies": { + "bn.js": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", + "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", + "dev": true + }, + "keccak": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz", + "integrity": "sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA==", + "dev": true, + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "secp256k1": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", + "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", + "dev": true, + "requires": { + "elliptic": "^6.5.2", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + } } }, "ethers": { From 5b7b4c517f9b55841b8e5343030e0bdbbf826b8c Mon Sep 17 00:00:00 2001 From: andyk Date: Tue, 15 Sep 2020 18:25:34 +0300 Subject: [PATCH 08/11] fix ci:test env --- buidler.config.ts | 4 ++-- helpers/buidler-constants.ts | 3 +++ helpers/constants.ts | 36 +++++++++++++++--------------------- test/atoken-permit.spec.ts | 2 +- 4 files changed, 21 insertions(+), 24 deletions(-) create mode 100644 helpers/buidler-constants.ts diff --git a/buidler.config.ts b/buidler.config.ts index dd736359..03f42645 100644 --- a/buidler.config.ts +++ b/buidler.config.ts @@ -1,8 +1,8 @@ -import {usePlugin, BuidlerConfig} from '@nomiclabs/buidler/config'; +import {usePlugin} from '@nomiclabs/buidler/config'; // @ts-ignore import {accounts} from './test-wallets.js'; import {eEthereumNetwork} from './helpers/types'; -import { BUIDLEREVM_CHAINID, COVERAGE_CHAINID } from './helpers/constants'; +import {BUIDLEREVM_CHAINID, COVERAGE_CHAINID} from './helpers/buidler-constants'; usePlugin('@nomiclabs/buidler-ethers'); usePlugin('buidler-typechain'); diff --git a/helpers/buidler-constants.ts b/helpers/buidler-constants.ts new file mode 100644 index 00000000..5eb044c3 --- /dev/null +++ b/helpers/buidler-constants.ts @@ -0,0 +1,3 @@ +export const TEST_SNAPSHOT_ID = '0x1'; +export const BUIDLEREVM_CHAINID = 31337; +export const COVERAGE_CHAINID = 1337; diff --git a/helpers/constants.ts b/helpers/constants.ts index 54a10de5..457a7f1d 100644 --- a/helpers/constants.ts +++ b/helpers/constants.ts @@ -1,22 +1,17 @@ import { - iAssetBase, - iAavePoolAssets, - IMarketRates, - iAssetAggregatorBase, AavePools, + eEthereumNetwork, + iAavePoolAssets, + iAssetAggregatorBase, + iAssetBase, + iBasicDistributionParams, + IMarketRates, iMultiPoolsAssets, IReserveParams, tEthereumAddress, - iBasicDistributionParams, - eEthereumNetwork, } from './types'; import BigNumber from 'bignumber.js'; -import {getParamPerPool, getParamPerNetwork} from './contracts-helpers'; - -export const TEST_SNAPSHOT_ID = '0x1'; - -export const BUIDLEREVM_CHAINID = 31337; -export const COVERAGE_CHAINID = 1337; +import {getParamPerNetwork, getParamPerPool} from './contracts-helpers'; // ---------------- // MATH @@ -536,17 +531,16 @@ export const getFeeDistributionParamsCommon = ( }; }; -export const getATokenDomainSeparatorPerNetwork = ( - network: eEthereumNetwork -): tEthereumAddress => +export const getATokenDomainSeparatorPerNetwork = (network: eEthereumNetwork): tEthereumAddress => getParamPerNetwork( { - [eEthereumNetwork.coverage]: "0x95b73a72c6ecf4ccbbba5178800023260bad8e75cdccdb8e4827a2977a37c820", + [eEthereumNetwork.coverage]: + '0x95b73a72c6ecf4ccbbba5178800023260bad8e75cdccdb8e4827a2977a37c820', [eEthereumNetwork.buidlerevm]: - "0x76cbbf8aa4b11a7c207dd79ccf8c394f59475301598c9a083f8258b4fafcfa86", - [eEthereumNetwork.kovan]: "", - [eEthereumNetwork.ropsten]: "", - [eEthereumNetwork.main]: "", + '0x76cbbf8aa4b11a7c207dd79ccf8c394f59475301598c9a083f8258b4fafcfa86', + [eEthereumNetwork.kovan]: '', + [eEthereumNetwork.ropsten]: '', + [eEthereumNetwork.main]: '', }, network - ); \ No newline at end of file + ); diff --git a/test/atoken-permit.spec.ts b/test/atoken-permit.spec.ts index ef5a39e0..397eddb1 100644 --- a/test/atoken-permit.spec.ts +++ b/test/atoken-permit.spec.ts @@ -2,7 +2,6 @@ import { MAX_UINT_AMOUNT, ZERO_ADDRESS, getATokenDomainSeparatorPerNetwork, - BUIDLEREVM_CHAINID, } from '../helpers/constants'; import {buildPermitParams, getSignatureFromTypedData} from '../helpers/contracts-helpers'; import {expect} from 'chai'; @@ -11,6 +10,7 @@ import {eEthereumNetwork} from '../helpers/types'; import {makeSuite, TestEnv} from './helpers/make-suite'; import {BRE} from '../helpers/misc-utils'; import {waitForTx} from './__setup.spec'; +import {BUIDLEREVM_CHAINID} from '../helpers/buidler-constants'; const {parseEther} = ethers.utils; From 214c51f36596b2c5b82bb0a5e021c58eb7863c6c Mon Sep 17 00:00:00 2001 From: andyk Date: Wed, 16 Sep 2020 10:22:46 +0300 Subject: [PATCH 09/11] disallow liquiditySwap with freezed reserve as to --- contracts/libraries/helpers/Errors.sol | 3 ++- contracts/libraries/logic/ValidationLogic.sol | 11 +++++++---- deployed-contracts.json | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/contracts/libraries/helpers/Errors.sol b/contracts/libraries/helpers/Errors.sol index 48c53ba6..99818c3e 100644 --- a/contracts/libraries/helpers/Errors.sol +++ b/contracts/libraries/helpers/Errors.sol @@ -88,6 +88,7 @@ library Errors { NOT_ENOUGH_LIQUIDITY, NO_ACTIVE_RESERVE, HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD, - INVALID_EQUAL_ASSETS_TO_SWAP + INVALID_EQUAL_ASSETS_TO_SWAP, + NO_UNFREEZED_RESERVE } } diff --git a/contracts/libraries/logic/ValidationLogic.sol b/contracts/libraries/logic/ValidationLogic.sol index 336e18ad..f4024946 100644 --- a/contracts/libraries/logic/ValidationLogic.sol +++ b/contracts/libraries/logic/ValidationLogic.sol @@ -452,16 +452,19 @@ library ValidationLogic { address fromAsset, address toAsset ) internal view returns (uint256, string memory) { - if (!fromReserve.configuration.getActive() || !toReserve.configuration.getActive()) { - return (uint256(Errors.LiquidationErrors.NO_ACTIVE_RESERVE), Errors.NO_ACTIVE_RESERVE); - } - if (fromAsset == toAsset) { return ( uint256(Errors.LiquidationErrors.INVALID_EQUAL_ASSETS_TO_SWAP), Errors.INVALID_EQUAL_ASSETS_TO_SWAP ); } + (bool isToActive, bool isToFreezed, , ) = toReserve.configuration.getFlags(); + if (!fromReserve.configuration.getActive() || !isToActive) { + return (uint256(Errors.LiquidationErrors.NO_ACTIVE_RESERVE), Errors.NO_ACTIVE_RESERVE); + } + if (isToFreezed) { + return (uint256(Errors.LiquidationErrors.NO_UNFREEZED_RESERVE), Errors.NO_UNFREEZED_RESERVE); + } return (uint256(Errors.LiquidationErrors.NO_ERROR), Errors.NO_ERRORS); } diff --git a/deployed-contracts.json b/deployed-contracts.json index 9981b17c..e29e363d 100644 --- a/deployed-contracts.json +++ b/deployed-contracts.json @@ -1,7 +1,7 @@ { "MintableERC20": { "buidlerevm": { - "address": "0x58F132FBB86E21545A4Bace3C19f1C05d86d7A22", + "address": "0x2D8553F9ddA85A9B3259F6Bf26911364B85556F5", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { @@ -495,4 +495,4 @@ "address": "0xBEF0d4b9c089a5883741fC14cbA352055f35DDA2" } } -} +} \ No newline at end of file From 1b61edc6ffe89d82a2918c942b7704dd1b0a6e3e Mon Sep 17 00:00:00 2001 From: andyk Date: Wed, 16 Sep 2020 11:10:25 +0300 Subject: [PATCH 10/11] add tests on liquiditySwap validation logic --- deployed-contracts.json | 2 +- helpers/types.ts | 1 + test/collateral-swap.spec.ts | 73 +++++++++++++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/deployed-contracts.json b/deployed-contracts.json index e29e363d..00c4ef4f 100644 --- a/deployed-contracts.json +++ b/deployed-contracts.json @@ -1,7 +1,7 @@ { "MintableERC20": { "buidlerevm": { - "address": "0x2D8553F9ddA85A9B3259F6Bf26911364B85556F5", + "address": "0x58F132FBB86E21545A4Bace3C19f1C05d86d7A22", "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6" }, "localhost": { diff --git a/helpers/types.ts b/helpers/types.ts index 966180d4..bad17f9b 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -67,6 +67,7 @@ export enum ProtocolErrors { NO_VARIABLE_RATE_LOAN_IN_RESERVE = '18', // 'User does not have a variable rate loan in progress on this reserve' UNDERLYING_BALANCE_NOT_GREATER_THAN_0 = '19', // 'The underlying balance needs to be greater than 0' DEPOSIT_ALREADY_IN_USE = '20', // 'User deposit is already being used as collateral' + INVALID_EQUAL_ASSETS_TO_SWAP = '56', // User can't use same reserve as destination of liquidity swap // require error messages - LendingPool NOT_ENOUGH_STABLE_BORROW_BALANCE = '21', // 'User does not have any stable rate loan for this reserve' diff --git a/test/collateral-swap.spec.ts b/test/collateral-swap.spec.ts index cbae27f5..7e692659 100644 --- a/test/collateral-swap.spec.ts +++ b/test/collateral-swap.spec.ts @@ -13,12 +13,63 @@ const {expect} = require('chai'); makeSuite('LendingPool SwapDeposit function', (testEnv: TestEnv) => { let _mockSwapAdapter = {} as MockSwapAdapter; - const {HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD} = ProtocolErrors; + const { + HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD, + NO_UNFREEZED_RESERVE, + NO_ACTIVE_RESERVE, + INVALID_EQUAL_ASSETS_TO_SWAP, + } = ProtocolErrors; before(async () => { _mockSwapAdapter = await getMockSwapAdapter(); }); + it('Should not allow to swap if from equal to', async () => { + const {pool, weth} = testEnv; + + await expect( + pool.swapLiquidity( + _mockSwapAdapter.address, + weth.address, + weth.address, + '1'.toString(), + '0x10' + ) + ).to.be.revertedWith(INVALID_EQUAL_ASSETS_TO_SWAP); + }); + + it('Should not allow to swap if from or to reserves are not active', async () => { + const {pool, weth, dai, configurator} = testEnv; + + await configurator.deactivateReserve(weth.address); + + await expect( + pool.swapLiquidity( + _mockSwapAdapter.address, + weth.address, + dai.address, + '1'.toString(), + '0x10' + ) + ).to.be.revertedWith(NO_ACTIVE_RESERVE); + await configurator.activateReserve(weth.address); + + await configurator.deactivateReserve(dai.address); + + await expect( + pool.swapLiquidity( + _mockSwapAdapter.address, + weth.address, + dai.address, + '1'.toString(), + '0x10' + ) + ).to.be.revertedWith(NO_ACTIVE_RESERVE); + + //cleanup state + await configurator.activateReserve(dai.address); + }); + it('Deposits WETH into the reserve', async () => { const {pool, weth, users} = testEnv; const amountToDeposit = ethers.utils.parseEther('1'); @@ -151,7 +202,7 @@ makeSuite('LendingPool SwapDeposit function', (testEnv: TestEnv) => { }); it('Should set usage as collateral to false if no leftovers after swap', async () => { - const {pool, weth, dai, aEth, users} = testEnv; + const {pool, weth, dai, users} = testEnv; const userAddress = await pool.signer.getAddress(); // add more liquidity to allow user 0 to swap everything he has @@ -195,4 +246,22 @@ makeSuite('LendingPool SwapDeposit function', (testEnv: TestEnv) => { 'usageAsCollateralEnabled are not set to false' ); }); + it('Should not allow to swap if to reserve are freezed', async () => { + const {pool, weth, dai, configurator} = testEnv; + + await configurator.freezeReserve(dai.address); + + await expect( + pool.swapLiquidity( + _mockSwapAdapter.address, + weth.address, + dai.address, + '1'.toString(), + '0x10' + ) + ).to.be.revertedWith(NO_UNFREEZED_RESERVE); + + //cleanup state + await configurator.unfreezeReserve(dai.address); + }); }); From 83e0318bc6147a0a772a4e0a7c0085857df860fa Mon Sep 17 00:00:00 2001 From: andyk Date: Wed, 16 Sep 2020 13:30:57 +0300 Subject: [PATCH 11/11] add logic to set usageAsCollateralEnabled for the user if his balance was 0 before swap --- .../LendingPoolLiquidationManager.sol | 5 +++++ test/collateral-swap.spec.ts | 17 ++++------------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/contracts/lendingpool/LendingPoolLiquidationManager.sol b/contracts/lendingpool/LendingPoolLiquidationManager.sol index 6adc18d9..dfdcaaf6 100644 --- a/contracts/lendingpool/LendingPoolLiquidationManager.sol +++ b/contracts/lendingpool/LendingPoolLiquidationManager.sol @@ -514,6 +514,11 @@ contract LendingPoolLiquidationManager is VersionedInitializable { address(vars.toReserveAToken), vars.amountToReceive ); + + if (vars.toReserveAToken.balanceOf(msg.sender) == 0) { + usersConfig[msg.sender].setUsingAsCollateral(toReserve.id, true); + } + vars.toReserveAToken.mint(msg.sender, vars.amountToReceive, toReserve.liquidityIndex); toReserve.updateInterestRates( toAsset, diff --git a/test/collateral-swap.spec.ts b/test/collateral-swap.spec.ts index 7e692659..c2bd9024 100644 --- a/test/collateral-swap.spec.ts +++ b/test/collateral-swap.spec.ts @@ -83,6 +83,7 @@ makeSuite('LendingPool SwapDeposit function', (testEnv: TestEnv) => { .deposit(weth.address, amountToDeposit, await signer.getAddress(), '0'); } }); + it('User tries to swap more then he can, revert expected', async () => { const {pool, weth, dai} = testEnv; await expect( @@ -96,19 +97,6 @@ makeSuite('LendingPool SwapDeposit function', (testEnv: TestEnv) => { ).to.be.revertedWith('55'); }); - it('User tries to swap asset on equal asset, revert expected', async () => { - const {pool, weth} = testEnv; - await expect( - pool.swapLiquidity( - _mockSwapAdapter.address, - weth.address, - weth.address, - ethers.utils.parseEther('0.1'), - '0x10' - ) - ).to.be.revertedWith('56'); - }); - it('User tries to swap more then available on the reserve', async () => { const {pool, weth, dai, users, aEth, deployer} = testEnv; @@ -185,6 +173,9 @@ makeSuite('LendingPool SwapDeposit function', (testEnv: TestEnv) => { reserveBalanceDAIBefore.add(amountToReturn).toString(), 'was received incorrect amount if reserve funds' ); + expect( + (await pool.getUserReserveData(dai.address, userAddress)).usageAsCollateralEnabled + ).to.be.equal(true, 'usage as collateral was not enabled on destination reserve for the user'); }); it('User tries to drop HF below one', async () => {