diff --git a/lib/forge-std b/lib/forge-std index 51a29d7f..b78fdb5a 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 51a29d7f15c574189518afd42680d16f367b7d59 +Subproject commit b78fdb5aed571e20c8b1f52a67566ed5bac0be59 diff --git a/src/core/AccountManager.sol b/src/core/AccountManager.sol index c2120da3..4535eadf 100644 --- a/src/core/AccountManager.sol +++ b/src/core/AccountManager.sol @@ -47,6 +47,9 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { /// @notice Mapping of collateral enabled tokens mapping(address => bool) public isCollateralAllowed; + /// @notice Number of assets a sentiment account can hold - 1 + uint256 public assetCap; + /* -------------------------------------------------------------------------- */ /* CUSTOM MODIFIERS */ /* -------------------------------------------------------------------------- */ @@ -76,10 +79,11 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { /// @notice Initializes external dependencies function initDep() external adminOnly { - riskEngine = IRiskEngine(registry.getAddress('RISK_ENGINE')); - controller = IControllerFacade(registry.getAddress('CONTROLLER')); - accountFactory = - IAccountFactory(registry.getAddress('ACCOUNT_FACTORY')); + riskEngine = IRiskEngine(registry.getAddress("RISK_ENGINE")); + controller = IControllerFacade(registry.getAddress("CONTROLLER")); + accountFactory = IAccountFactory( + registry.getAddress("ACCOUNT_FACTORY") + ); } /** @@ -89,10 +93,15 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { Emits AccountAssigned(account, owner) event @param owner Owner of the newly opened account */ - function openAccount(address owner) external nonReentrant whenNotPaused { + function openAccount(address owner) + external + nonReentrant + whenNotPaused + returns (address) + { if (owner == address(0)) revert Errors.ZeroAddress(); address account; - uint length = inactiveAccountsOf[owner].length; + uint256 length = inactiveAccountsOf[owner].length; if (length == 0) { account = accountFactory.create(address(this)); IAccount(account).init(address(this)); @@ -104,6 +113,7 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { } IAccount(account).activate(); emit AccountAssigned(account, owner); + return account; } /** @@ -112,7 +122,11 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { Emits AccountClosed(account, owner) event @param _account Address of account to be closed */ - function closeAccount(address _account) public nonReentrant onlyOwner(_account) { + function closeAccount(address _account) + public + nonReentrant + onlyOwner(_account) + { IAccount account = IAccount(_account); if (account.activationBlock() == block.number) revert Errors.AccountDeactivationFailure(); @@ -145,12 +159,12 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { @param account Address of account @param amt Amount of Eth to withdraw */ - function withdrawEth(address account, uint amt) + function withdrawEth(address account, uint256 amt) external nonReentrant onlyOwner(account) { - if(!riskEngine.isWithdrawAllowed(account, address(0), amt)) + if (!riskEngine.isWithdrawAllowed(account, address(0), amt)) revert Errors.RiskThresholdBreached(); account.withdrawEth(msg.sender, amt); } @@ -163,16 +177,18 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { @param token Address of token @param amt Amount of token to deposit */ - function deposit(address account, address token, uint amt) - external - nonReentrant - whenNotPaused - onlyOwner(account) - { + function deposit( + address account, + address token, + uint256 amt + ) external nonReentrant whenNotPaused onlyOwner(account) { if (!isCollateralAllowed[token]) revert Errors.CollateralTypeRestricted(); - if (IAccount(account).hasAsset(token) == false) + if (IAccount(account).hasAsset(token) == false) { + if (IAccount(account).getAssets().length > assetCap) + revert Errors.MaxAssetCap(); IAccount(account).addAsset(token); + } token.safeTransferFrom(msg.sender, account, amt); } @@ -185,16 +201,15 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { @param token Address of token @param amt Amount of token to withdraw */ - function withdraw(address account, address token, uint amt) - external - nonReentrant - onlyOwner(account) - { + function withdraw( + address account, + address token, + uint256 amt + ) external nonReentrant onlyOwner(account) { if (!riskEngine.isWithdrawAllowed(account, token, amt)) revert Errors.RiskThresholdBreached(); account.withdraw(msg.sender, token, amt); - if (token.balanceOf(account) == 0) - IAccount(account).removeAsset(token); + if (token.balanceOf(account) == 0) IAccount(account).removeAsset(token); } /** @@ -206,16 +221,18 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { @param token Address of token @param amt Amount of token to borrow */ - function borrow(address account, address token, uint amt) - external - nonReentrant - whenNotPaused - onlyOwner(account) - { + function borrow( + address account, + address token, + uint256 amt + ) external nonReentrant whenNotPaused onlyOwner(account) { if (registry.LTokenFor(token) == address(0)) revert Errors.LTokenUnavailable(); - if (IAccount(account).hasAsset(token) == false) + if (IAccount(account).hasAsset(token) == false) { + if (IAccount(account).getAssets().length > assetCap) + revert Errors.MaxAssetCap(); IAccount(account).addAsset(token); + } if (ILToken(registry.LTokenFor(token)).lendTo(account, amt)) IAccount(account).addBorrow(token); if (!riskEngine.isAccountHealthy(account)) @@ -231,11 +248,11 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { @param token Address of token @param amt Amount of token to borrow */ - function repay(address account, address token, uint amt) - public - nonReentrant - onlyOwner(account) - { + function repay( + address account, + address token, + uint256 amt + ) public nonReentrant onlyOwner(account) { _repay(account, token, amt); } @@ -265,13 +282,9 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { address account, address token, address spender, - uint amt - ) - external - nonReentrant - onlyOwner(account) - { - if(address(controller.controllerFor(spender)) == address(0)) + uint256 amt + ) external nonReentrant onlyOwner(account) { + if (address(controller.controllerFor(spender)) == address(0)) revert Errors.FunctionCallRestricted(); account.safeApprove(token, spender, amt); } @@ -289,24 +302,25 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { function exec( address account, address target, - uint amt, + uint256 amt, bytes calldata data - ) - external - nonReentrant - onlyOwner(account) - { + ) external nonReentrant onlyOwner(account) { bool isAllowed; address[] memory tokensIn; address[] memory tokensOut; - (isAllowed, tokensIn, tokensOut) = - controller.canCall(target, (amt > 0), data); + (isAllowed, tokensIn, tokensOut) = controller.canCall( + target, + (amt > 0), + data + ); if (!isAllowed) revert Errors.FunctionCallRestricted(); _updateTokensIn(account, tokensIn); - (bool success,) = IAccount(account).exec(target, amt, data); + (bool success, ) = IAccount(account).exec(target, amt, data); if (!success) revert Errors.AccountInteractionFailure(account, target, amt, data); _updateTokensOut(account, tokensOut); + if (IAccount(account).getAssets().length > assetCap + 1) + revert Errors.MaxAssetCap(); if (!riskEngine.isAccountHealthy(account)) revert Errors.RiskThresholdBreached(); } @@ -317,8 +331,8 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { */ function settle(address account) external nonReentrant onlyOwner(account) { address[] memory borrows = IAccount(account).getBorrows(); - for (uint i; i < borrows.length; i++) { - _repay(account, borrows[i], type(uint).max); + for (uint256 i; i < borrows.length; i++) { + _repay(account, borrows[i], type(uint256).max); } } @@ -327,9 +341,7 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { @param user Address of user @return address[] List of inactive accounts */ - function getInactiveAccountsOf( - address user - ) + function getInactiveAccountsOf(address user) external view returns (address[] memory) @@ -344,8 +356,8 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { function _updateTokensIn(address account, address[] memory tokensIn) internal { - uint tokensInLen = tokensIn.length; - for(uint i; i < tokensInLen; ++i) { + uint256 tokensInLen = tokensIn.length; + for (uint256 i; i < tokensInLen; ++i) { if (IAccount(account).hasAsset(tokensIn[i]) == false) IAccount(account).addAsset(tokensIn[i]); } @@ -354,8 +366,8 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { function _updateTokensOut(address account, address[] memory tokensOut) internal { - uint tokensOutLen = tokensOut.length; - for(uint i; i < tokensOutLen; ++i) { + uint256 tokensOutLen = tokensOut.length; + for (uint256 i; i < tokensOutLen; ++i) { if (tokensOut[i].balanceOf(account) == 0) IAccount(account).removeAsset(tokensOut[i]); } @@ -364,12 +376,12 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { function _liquidate(address _account) internal { IAccount account = IAccount(_account); address[] memory accountBorrows = account.getBorrows(); - uint borrowLen = accountBorrows.length; + uint256 borrowLen = accountBorrows.length; ILToken LToken; - uint amt; + uint256 amt; - for(uint i; i < borrowLen; ++i) { + for (uint256 i; i < borrowLen; ++i) { address token = accountBorrows[i]; LToken = ILToken(registry.LTokenFor(token)); LToken.updateState(); @@ -377,16 +389,18 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { token.safeTransferFrom(msg.sender, address(LToken), amt); LToken.collectFrom(_account, amt); account.removeBorrow(token); + emit Repay(_account, msg.sender, token, amt); } account.sweepTo(msg.sender); } - function _repay(address account, address token, uint amt) - internal - { + function _repay( + address account, + address token, + uint256 amt + ) internal { ILToken LToken = ILToken(registry.LTokenFor(token)); - if (address(LToken) == address(0)) - revert Errors.LTokenUnavailable(); + if (address(LToken) == address(0)) revert Errors.LTokenUnavailable(); LToken.updateState(); if (amt == type(uint256).max) amt = LToken.getBorrowBalance(account); account.withdraw(address(LToken), token, amt); @@ -408,4 +422,12 @@ contract AccountManager is ReentrancyGuard, Pausable, IAccountManager { function toggleCollateralStatus(address token) external adminOnly { isCollateralAllowed[token] = !isCollateralAllowed[token]; } -} \ No newline at end of file + + /** + @notice Set asset cap + @param cap Number of assets + */ + function setAssetCap(uint256 cap) external adminOnly { + assetCap = cap - 1; + } +} diff --git a/src/interface/core/IAccountManager.sol b/src/interface/core/IAccountManager.sol index b6fdb7d1..d34c02e2 100644 --- a/src/interface/core/IAccountManager.sol +++ b/src/interface/core/IAccountManager.sol @@ -14,45 +14,83 @@ interface IAccountManager { address indexed account, address indexed owner, address indexed token, - uint amt + uint256 amt ); event Borrow( address indexed account, address indexed owner, address indexed token, - uint amt + uint256 amt ); function registry() external returns (IRegistry); + function riskEngine() external returns (IRiskEngine); + function accountFactory() external returns (IAccountFactory); + function controller() external returns (IControllerFacade); + function init(IRegistry) external; + function initDep() external; - function openAccount(address owner) external; + + function openAccount(address owner) external returns (address); + function closeAccount(address account) external; - function repay(address account, address token, uint amt) external; - function borrow(address account, address token, uint amt) external; - function deposit(address account, address token, uint amt) external; - function withdraw(address account, address token, uint amt) external; - function depositEth(address account) payable external; - function withdrawEth(address, uint) external; + + function repay( + address account, + address token, + uint256 amt + ) external; + + function borrow( + address account, + address token, + uint256 amt + ) external; + + function deposit( + address account, + address token, + uint256 amt + ) external; + + function withdraw( + address account, + address token, + uint256 amt + ) external; + + function depositEth(address account) external payable; + + function withdrawEth(address, uint256) external; + function liquidate(address) external; + function settle(address) external; + function exec( address account, address target, - uint amt, + uint256 amt, bytes calldata data ) external; + function approve( address account, address token, address spender, - uint amt + uint256 amt ) external; + function toggleCollateralStatus(address token) external; - function getInactiveAccountsOf( - address owner - ) external view returns (address[] memory); + + function setAssetCap(uint256) external; + + function getInactiveAccountsOf(address owner) + external + view + returns (address[] memory); } diff --git a/src/interface/core/IRegistry.sol b/src/interface/core/IRegistry.sol index ed6aaf6f..9c5395d0 100644 --- a/src/interface/core/IRegistry.sol +++ b/src/interface/core/IRegistry.sol @@ -23,4 +23,5 @@ interface IRegistry { function accountsOwnedBy(address user) external view returns (address[] memory); function getAddress(string calldata) external view returns (address); + function accounts(uint i) external view returns (address); } \ No newline at end of file diff --git a/src/interface/core/IRiskEngine.sol b/src/interface/core/IRiskEngine.sol index 2fbf7f01..1375c4c5 100644 --- a/src/interface/core/IRiskEngine.sol +++ b/src/interface/core/IRiskEngine.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; +import {IOracle} from "oracle/core/IOracle.sol"; interface IRiskEngine { function initDep() external; @@ -10,4 +11,5 @@ interface IRiskEngine { external view returns (bool); function isWithdrawAllowed(address account, address token, uint amt) external view returns (bool); + function oracle() external view returns (IOracle); } \ No newline at end of file diff --git a/src/test/account/Account.t.sol b/src/test/account/Account.t.sol index 3089ad36..93279639 100644 --- a/src/test/account/Account.t.sol +++ b/src/test/account/Account.t.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.17; import {Errors} from "../../utils/Errors.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; -contract AccountTest is TestBase { +contract AccountTest is BaseTest { IAccount public account; address public owner = cheats.addr(1); diff --git a/src/test/account/BorrowFlow.t.sol b/src/test/account/BorrowFlow.t.sol index 00259d46..e40a5c64 100644 --- a/src/test/account/BorrowFlow.t.sol +++ b/src/test/account/BorrowFlow.t.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -contract BorrowFlowTest is TestBase { +contract BorrowFlowTest is BaseTest { using FixedPointMathLib for uint; address public account; address public borrower = cheats.addr(1); diff --git a/src/test/account/DepositFlow.t.sol b/src/test/account/DepositFlow.t.sol index 88c4dcb6..f8581ba3 100644 --- a/src/test/account/DepositFlow.t.sol +++ b/src/test/account/DepositFlow.t.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; -contract DepositFlow is TestBase { +contract DepositFlow is BaseTest { address public account; address public borrower = cheats.addr(1); diff --git a/src/test/account/LiquidationFlow.t.sol b/src/test/account/LiquidationFlow.t.sol index b5cc2eaf..3518dc7d 100644 --- a/src/test/account/LiquidationFlow.t.sol +++ b/src/test/account/LiquidationFlow.t.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; -contract LiquidationFlowTest is TestBase { +contract LiquidationFlowTest is BaseTest { address public borrower = cheats.addr(1); address public maintainer = cheats.addr(2); address public account; diff --git a/src/test/account/OpenCloseFlow.t.sol b/src/test/account/OpenCloseFlow.t.sol index ecc6ce3a..5bf31ea2 100644 --- a/src/test/account/OpenCloseFlow.t.sol +++ b/src/test/account/OpenCloseFlow.t.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.17; import {Errors} from "../../utils/Errors.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; -contract OpenCloseFlowTest is TestBase { +contract OpenCloseFlowTest is BaseTest { address public account; address public user = cheats.addr(1); diff --git a/src/test/account/OriginationFee.t.sol b/src/test/account/OriginationFee.t.sol index 0bb0028a..786b5a35 100644 --- a/src/test/account/OriginationFee.t.sol +++ b/src/test/account/OriginationFee.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; @@ -10,7 +10,7 @@ import {LEther} from "../../tokens/LEther.sol"; import {LToken} from "../../tokens/LToken.sol"; import {Proxy} from "../../proxy/Proxy.sol"; -contract OriginationFeeTests is TestBase { +contract OriginationFeeTests is BaseTest { using FixedPointMathLib for uint96; using FixedPointMathLib for uint; diff --git a/src/test/account/RepayFlow.t.sol b/src/test/account/RepayFlow.t.sol index 7921b4ea..98880a9e 100644 --- a/src/test/account/RepayFlow.t.sol +++ b/src/test/account/RepayFlow.t.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {LToken} from "../../tokens/LToken.sol"; import {Registry} from "../../core/Registry.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; import {AccountManager} from "../../core/AccountManager.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -contract RepayFlowTest is TestBase { +contract RepayFlowTest is BaseTest { using FixedPointMathLib for uint; address public account; address public borrower = cheats.addr(1); diff --git a/src/test/account/RepayInParts.t.sol b/src/test/account/RepayInParts.t.sol index 872204ca..c73dfc43 100644 --- a/src/test/account/RepayInParts.t.sol +++ b/src/test/account/RepayInParts.t.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.17; import {ERC20} from "solmate/tokens/ERC20.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -contract RepayInParts is TestBase { +contract RepayInParts is BaseTest { using FixedPointMathLib for uint; address public account; diff --git a/src/test/accountManager/AccountManager.t.sol b/src/test/accountManager/AccountManager.t.sol index 7df33d70..d4038b8a 100644 --- a/src/test/accountManager/AccountManager.t.sol +++ b/src/test/accountManager/AccountManager.t.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.17; import {Errors} from "../../utils/Errors.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {IControllerFacade} from "controller/core/IControllerFacade.sol"; -contract AccountManagerTest is TestBase { +contract AccountManagerTest is BaseTest { using FixedPointMathLib for uint; address account; address public owner = cheats.addr(1); diff --git a/src/test/accountManager/AdminOnly.t.sol b/src/test/accountManager/AdminOnly.t.sol index 8da897f3..3f333af8 100644 --- a/src/test/accountManager/AdminOnly.t.sol +++ b/src/test/accountManager/AdminOnly.t.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.17; import {Errors} from "../../utils/Errors.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {console} from "../utils/console.sol"; import {IOwnable} from "../../interface/utils/IOwnable.sol"; -contract AccountManagerAdminOnlyTest is TestBase { +contract AccountManagerAdminOnlyTest is BaseTest { function setUp() public { setupContracts(); } diff --git a/src/test/accountManager/BorrowRepay.t.sol b/src/test/accountManager/BorrowRepay.t.sol index 5b906c03..277e77fe 100644 --- a/src/test/accountManager/BorrowRepay.t.sol +++ b/src/test/accountManager/BorrowRepay.t.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.17; import {Errors} from "../../utils/Errors.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {console} from "../utils/console.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -contract AccountManagerBorrowRepayTest is TestBase { +contract AccountManagerBorrowRepayTest is BaseTest { using FixedPointMathLib for uint; address account; address public owner = cheats.addr(1); diff --git a/src/test/accountManager/CapAssets.t.sol b/src/test/accountManager/CapAssets.t.sol new file mode 100644 index 00000000..db05489e --- /dev/null +++ b/src/test/accountManager/CapAssets.t.sol @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +import {Test} from "forge-std/Test.sol"; +import {IERC20} from "forge-std/interfaces/IERC20.sol"; +import {Proxy} from "../../proxy/Proxy.sol"; +import {AccountManager} from "../../core/AccountManager.sol"; +import {Registry} from "../../core/Registry.sol"; +import {ISwapRouterV3} from "controller/uniswap/ISwapRouterV3.sol"; + +contract CapAssetsArbiIntegrationTest is Test { + address uniV3Router = 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45; + + IERC20 WETH = IERC20(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1); + IERC20 USDC = IERC20(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8); + IERC20 USDT = IERC20(0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9); + IERC20 WBTC = IERC20(0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f); + IERC20 WSTETH = IERC20(0x5979D7b546E38E414F7E9822514be443A4800529); + IERC20 DAI = IERC20(0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1); + + Proxy accountManagerProxy = + Proxy(payable(0x62c5AA8277E49B3EAd43dC67453ec91DC6826403)); + AccountManager accountManager = + AccountManager(payable(0x62c5AA8277E49B3EAd43dC67453ec91DC6826403)); + Registry registry = Registry(0x17B07cfBAB33C0024040e7C299f8048F4a49679B); + + AccountManager newAccountManager; + + address user = 0x884ba7391637BfCE1D0B8C3aF6723477f6541e0e; + address account = 0x355eA7352DD7502f6B72b217DFEA526DAC0b0F3a; + + function setUp() public { + newAccountManager = new AccountManager(); + changePrank(0x92f473Ef0Cd07080824F5e6B0859ac49b3AEb215); + accountManagerProxy.changeImplementation(address(newAccountManager)); + accountManager.setAssetCap(2); + } + + function testOpenAccount() public { + address _account = accountManager.openAccount(address(this)); + assertEq(address(this), registry.ownerFor(_account)); + } + + function testCloseAccount() public { + changePrank(user); + accountManager.closeAccount(account); + } + + function testDepositOneAsset() public { + changePrank(user); + deal(address(WETH), user, 2e18, true); + WETH.approve(address(accountManager), 1e18); + accountManager.deposit(account, address(WETH), 1e18); + } + + function testDepositTwoAssets() public { + testDepositOneAsset(); + deal(address(USDC), user, 2e10, true); + USDC.approve(address(accountManager), 1e10); + accountManager.deposit(account, address(USDC), 1e10); + } + + function testFailDepositThreeAssets() public { + testDepositOneAsset(); + testDepositTwoAssets(); + deal(address(USDT), user, 2e10, true); + USDT.approve(address(accountManager), 1e10); + accountManager.deposit(account, address(USDT), 1e10); + } + + function testBorrowOneAsset() public { + testDepositOneAsset(); + accountManager.borrow(account, address(WETH), 1e18); + accountManager.borrow(account, address(WETH), 1e18); + } + + function testBorrowTwoAssets() public { + testBorrowOneAsset(); + accountManager.borrow(account, address(USDC), 1e6); + } + + function testFailBorrowThreeAssets() public { + testBorrowTwoAssets(); + accountManager.borrow(account, address(USDT), 1e6); + } + + function testSwapWETHUSDC() public { + testDepositOneAsset(); + bytes memory data = abi.encodeWithSignature( + "exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))", + getExactInputParams(address(WETH), address(USDC), account, 0, 1e17) + ); + accountManager.approve( + account, + address(WETH), + uniV3Router, + type(uint256).max + ); + accountManager.exec(account, uniV3Router, 0, data); + } + + function testSwapWETHUSDT() public { + testSwapWETHUSDC(); + bytes memory data = abi.encodeWithSignature( + "exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))", + getExactInputParams( + address(WETH), + address(USDT), + account, + 0, + WETH.balanceOf(account) + ) + ); + accountManager.exec(account, uniV3Router, 0, data); + } + + function testFailSwapWETHUSDT() public { + testSwapWETHUSDC(); + bytes memory data = abi.encodeWithSignature( + "exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))", + getExactInputParams(address(WETH), address(USDT), account, 0, 1e17) + ); + accountManager.exec(account, uniV3Router, 0, data); + } + + function testCollateralStatus() public { + assertTrue(accountManager.isCollateralAllowed(address(WETH))); + assertTrue(accountManager.isCollateralAllowed(address(USDC))); + assertTrue(accountManager.isCollateralAllowed(address(USDT))); + assertTrue(accountManager.isCollateralAllowed(address(WBTC))); + assertTrue(accountManager.isCollateralAllowed(address(DAI))); + } + + function getExactInputParams( + address tokenIn, + address tokenOut, + address recipient, + uint256 amountOut, + uint256 amountIn + ) private pure returns (ISwapRouterV3.ExactInputSingleParams memory data) { + data = ISwapRouterV3.ExactInputSingleParams( + tokenIn, + tokenOut, + 3000, + recipient, + amountIn, + amountOut, + 0 + ); + } +} diff --git a/src/test/accountManager/DepositWithdraw.t.sol b/src/test/accountManager/DepositWithdraw.t.sol index 072ead43..a5c0971b 100644 --- a/src/test/accountManager/DepositWithdraw.t.sol +++ b/src/test/accountManager/DepositWithdraw.t.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.17; import {Errors} from "../../utils/Errors.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -contract AccountManagerDepositWithdrawTest is TestBase { +contract AccountManagerDepositWithdrawTest is BaseTest { using FixedPointMathLib for uint; address account; address public owner = cheats.addr(1); diff --git a/src/test/attacks/CloseAccountAttack.sol b/src/test/attacks/CloseAccountAttack.sol index e647c21b..7ee3a7f3 100644 --- a/src/test/attacks/CloseAccountAttack.sol +++ b/src/test/attacks/CloseAccountAttack.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.17; import {Errors} from "../../utils/Errors.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; import {IAccountManager} from "../../interface/core/IAccountManager.sol"; @@ -27,7 +27,7 @@ contract CloseAccountAttacker { } } -contract CloseAccountAttack is TestBase { +contract CloseAccountAttack is BaseTest { IAccount public account; CloseAccountAttacker owner; diff --git a/src/test/integrations/Balancer.t.sol b/src/test/integrations/Balancer.t.sol index a224db46..eebc49b2 100644 --- a/src/test/integrations/Balancer.t.sol +++ b/src/test/integrations/Balancer.t.sol @@ -5,7 +5,7 @@ import {console} from "forge-std/console.sol"; import {IVault, IAsset} from "controller/balancer/IVault.sol"; import {IERC20} from "../../interface/tokens/IERC20.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; -import {IntegrationTestBase} from "./utils/IntegrationTestBase.sol"; +import {IntegrationBaseTest} from "./utils/IntegrationBaseTest.sol"; import {StableBalancerLPOracle} from "oracle/balancer/StableBalancerLPOracle.sol"; import {WeightedBalancerLPOracle} from "oracle/balancer/WeightedBalancerLPOracle.sol"; import {IVault as IVaultOracle} from "oracle/balancer/IVault.sol"; @@ -20,7 +20,7 @@ enum ExitKind { } enum SwapKind { GIVEN_IN, GIVEN_OUT } -contract BalancerIntegrationTest is IntegrationTestBase { +contract BalancerIntegrationTest is IntegrationBaseTest { address account; address user = cheats.addr(1); diff --git a/src/test/integrations/Compound.t.sol b/src/test/integrations/Compound.t.sol index 503d9fc8..c422829d 100644 --- a/src/test/integrations/Compound.t.sol +++ b/src/test/integrations/Compound.t.sol @@ -5,14 +5,14 @@ import {Errors} from "../../utils/Errors.sol"; import {IERC20} from "../../interface/tokens/IERC20.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; import {CTokenOracle} from "oracle/compound/CTokenOracle.sol"; -import {IntegrationTestBase} from "./utils/IntegrationTestBase.sol"; +import {IntegrationBaseTest} from "./utils/IntegrationBaseTest.sol"; import {CompoundController} from "controller/compound/CompoundController.sol"; interface ICERC20 { function exchangeRateCurrent() external returns (uint); } -contract CompoundIntegrationTest is IntegrationTestBase { +contract CompoundIntegrationTest is IntegrationBaseTest { address account; address user = cheats.addr(1); diff --git a/src/test/integrations/Weth.t.sol b/src/test/integrations/Weth.t.sol index 0ebdd64c..e72a3063 100644 --- a/src/test/integrations/Weth.t.sol +++ b/src/test/integrations/Weth.t.sol @@ -4,10 +4,10 @@ pragma solidity ^0.8.17; import {Errors} from "../../utils/Errors.sol"; import {IERC20} from "../../interface/tokens/IERC20.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; -import {IntegrationTestBase} from "./utils/IntegrationTestBase.sol"; +import {IntegrationBaseTest} from "./utils/IntegrationBaseTest.sol"; import {IAccountManager} from "../../interface/core/IAccountManager.sol"; -contract WethIntegrationTest is IntegrationTestBase { +contract WethIntegrationTest is IntegrationBaseTest { address account; address user = cheats.addr(1); diff --git a/src/test/integrations/Yearn.t.sol b/src/test/integrations/Yearn.t.sol index 8878270b..e782110a 100644 --- a/src/test/integrations/Yearn.t.sol +++ b/src/test/integrations/Yearn.t.sol @@ -4,10 +4,10 @@ pragma solidity ^0.8.17; import {IERC20} from "../../interface/tokens/IERC20.sol"; import {IAccount} from "../../interface/core/IAccount.sol"; import {YTokenOracle} from "oracle/yearn/YTokenOracle.sol"; -import {IntegrationTestBase} from "./utils/IntegrationTestBase.sol"; +import {IntegrationBaseTest} from "./utils/IntegrationBaseTest.sol"; import {YearnVaultController} from "controller/yearn/YearnController.sol"; -contract YearnIntegrationTest is IntegrationTestBase { +contract YearnIntegrationTest is IntegrationBaseTest { address account; address user = cheats.addr(1); diff --git a/src/test/integrations/aave/AaveEth.t.sol b/src/test/integrations/aave/AaveEth.t.sol index 5fe88d3a..327d60b0 100644 --- a/src/test/integrations/aave/AaveEth.t.sol +++ b/src/test/integrations/aave/AaveEth.t.sol @@ -5,11 +5,11 @@ import {Errors} from "../../../utils/Errors.sol"; import {ATokenOracle} from "oracle/aave/ATokenOracle.sol"; import {IERC20} from "../../../interface/tokens/IERC20.sol"; import {IAccount} from "../../../interface/core/IAccount.sol"; -import {IntegrationTestBase} from "../utils/IntegrationTestBase.sol"; +import {IntegrationBaseTest} from "../utils/IntegrationBaseTest.sol"; import {AaveEthController} from "controller/aave/AaveEthController.sol"; import {ILendingPoolAddressProvider} from "./interface/ILendingPoolAddressProvider.sol"; -contract AaveEthIntegrationTest is IntegrationTestBase { +contract AaveEthIntegrationTest is IntegrationBaseTest { address account; address user = cheats.addr(1); diff --git a/src/test/integrations/aave/AaveV2.t.sol b/src/test/integrations/aave/AaveV2.t.sol index 61c2b3e3..e4c48dcb 100644 --- a/src/test/integrations/aave/AaveV2.t.sol +++ b/src/test/integrations/aave/AaveV2.t.sol @@ -5,13 +5,13 @@ import {Errors} from "../../../utils/Errors.sol"; import {ATokenOracle} from "oracle/aave/ATokenOracle.sol"; import {IERC20} from "../../../interface/tokens/IERC20.sol"; import {IAccount} from "../../../interface/core/IAccount.sol"; -import {IntegrationTestBase} from "../utils/IntegrationTestBase.sol"; +import {IntegrationBaseTest} from "../utils/IntegrationBaseTest.sol"; import {AaveV2Controller} from "controller/aave/AaveV2Controller.sol"; import {IProtocolDataProvider} from "controller/aave/IProtocolDataProvider.sol"; import {ILendingPoolAddressProvider} from "./interface/ILendingPoolAddressProvider.sol"; -contract AaveV2IntegrationTest is IntegrationTestBase { +contract AaveV2IntegrationTest is IntegrationBaseTest { address account; address user = cheats.addr(1); diff --git a/src/test/integrations/aave/AaveV3.t.sol b/src/test/integrations/aave/AaveV3.t.sol index 4241963d..adcba1d0 100644 --- a/src/test/integrations/aave/AaveV3.t.sol +++ b/src/test/integrations/aave/AaveV3.t.sol @@ -5,12 +5,12 @@ import {Errors} from "../../../utils/Errors.sol"; import {ATokenOracle} from "oracle/aave/ATokenOracle.sol"; import {IERC20} from "../../../interface/tokens/IERC20.sol"; import {IAccount} from "../../../interface/core/IAccount.sol"; -import {ArbiIntegrationTestBase} from "../utils/ArbiIntegrationTestBase.sol"; +import {ArbiIntegrationBaseTest} from "../utils/ArbiIntegrationBaseTest.sol"; import {AaveV3Controller} from "controller/aave/AaveV3Controller.sol"; import {IPoolAddressProvider} from "./interface/IPoolAddressProvider.sol"; /// @notice runs only on arbitrum -contract AaveV3ArbiIntegrationTest is ArbiIntegrationTestBase { +contract AaveV3ArbiIntegrationTest is ArbiIntegrationBaseTest { address account; address user = cheats.addr(1); diff --git a/src/test/integrations/curve/Curve.t.sol b/src/test/integrations/curve/Curve.t.sol index 7e702b09..d0bdd0d6 100644 --- a/src/test/integrations/curve/Curve.t.sol +++ b/src/test/integrations/curve/Curve.t.sol @@ -4,13 +4,13 @@ pragma solidity ^0.8.17; import {Errors} from "../../../utils/Errors.sol"; import {IERC20} from "../../../interface/tokens/IERC20.sol"; import {IAccount} from "../../../interface/core/IAccount.sol"; -import {IntegrationTestBase} from "../utils/IntegrationTestBase.sol"; +import {IntegrationBaseTest} from "../utils/IntegrationBaseTest.sol"; interface IStableSwapPool { function get_dy(uint256, uint256, uint256) external view returns (uint256); } -contract CurveIntegrationTest is IntegrationTestBase { +contract CurveIntegrationTest is IntegrationBaseTest { address account; address user = cheats.addr(1); diff --git a/src/test/integrations/curve/CurveStableSwap.t.sol b/src/test/integrations/curve/CurveStableSwap.t.sol index e81fee97..2c1d4b61 100644 --- a/src/test/integrations/curve/CurveStableSwap.t.sol +++ b/src/test/integrations/curve/CurveStableSwap.t.sol @@ -4,14 +4,14 @@ pragma solidity ^0.8.17; import {Errors} from "../../../utils/Errors.sol"; import {IERC20} from "../../../interface/tokens/IERC20.sol"; import {IAccount} from "../../../interface/core/IAccount.sol"; -import {IntegrationTestBase} from "../utils/IntegrationTestBase.sol"; +import {IntegrationBaseTest} from "../utils/IntegrationBaseTest.sol"; import {StableSwap3PoolController} from "controller/curve/StableSwap3PoolController.sol"; interface IStableSwapPool { function get_dy(int128, int128, uint256) external view returns (uint256); } -contract CurveStableSwapIntegrationTest is IntegrationTestBase { +contract CurveStableSwapIntegrationTest is IntegrationBaseTest { address account; address user = cheats.addr(1); diff --git a/src/test/integrations/uniswap/UniV2Lp.t.sol b/src/test/integrations/uniswap/UniV2Lp.t.sol index bd5bb45e..e83975a1 100644 --- a/src/test/integrations/uniswap/UniV2Lp.t.sol +++ b/src/test/integrations/uniswap/UniV2Lp.t.sol @@ -3,11 +3,11 @@ pragma solidity ^0.8.17; import {IERC20} from "../../../interface/tokens/IERC20.sol"; import {UniV2LpOracle} from "oracle/uniswap/UniV2LPOracle.sol"; -import {IntegrationTestBase} from "../utils/IntegrationTestBase.sol"; +import {IntegrationBaseTest} from "../utils/IntegrationBaseTest.sol"; import {UniV2Controller} from "controller/uniswap/UniV2Controller.sol"; import {IUniV2Factory} from "controller/uniswap/IUniV2Factory.sol"; -contract UniV2LpIntegrationTest is IntegrationTestBase { +contract UniV2LpIntegrationTest is IntegrationBaseTest { address account; address user = cheats.addr(1); diff --git a/src/test/integrations/uniswap/UniV2Swap.t.sol b/src/test/integrations/uniswap/UniV2Swap.t.sol index 426e8bbe..11933f66 100644 --- a/src/test/integrations/uniswap/UniV2Swap.t.sol +++ b/src/test/integrations/uniswap/UniV2Swap.t.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.17; import {IERC20} from "../../../interface/tokens/IERC20.sol"; -import {IntegrationTestBase} from "../utils/IntegrationTestBase.sol"; +import {IntegrationBaseTest} from "../utils/IntegrationBaseTest.sol"; import {UniV2Controller} from "controller/uniswap/UniV2Controller.sol"; import {IUniV2Factory} from "controller/uniswap/IUniV2Factory.sol"; -contract UniV2SwapIntegrationTest is IntegrationTestBase { +contract UniV2SwapIntegrationTest is IntegrationBaseTest { address account; address user = cheats.addr(1); diff --git a/src/test/integrations/uniswap/UniV3.t.sol b/src/test/integrations/uniswap/UniV3.t.sol index b26182b1..6f720b75 100644 --- a/src/test/integrations/uniswap/UniV3.t.sol +++ b/src/test/integrations/uniswap/UniV3.t.sol @@ -3,11 +3,11 @@ pragma solidity ^0.8.17; import {IERC20} from "../../../interface/tokens/IERC20.sol"; import {IAccount} from "../../../interface/core/IAccount.sol"; -import {IntegrationTestBase} from "../utils/IntegrationTestBase.sol"; +import {IntegrationBaseTest} from "../utils/IntegrationBaseTest.sol"; import {ISwapRouterV3} from "controller/uniswap/ISwapRouterV3.sol"; import {UniV3Controller} from "controller/uniswap/UniV3Controller.sol"; -contract UniV3IntegrationTest is IntegrationTestBase { +contract UniV3IntegrationTest is IntegrationBaseTest { address account; address user = cheats.addr(1); diff --git a/src/test/integrations/utils/ArbiIntegrationTestBase.sol b/src/test/integrations/utils/ArbiIntegrationBaseTest.sol similarity index 91% rename from src/test/integrations/utils/ArbiIntegrationTestBase.sol rename to src/test/integrations/utils/ArbiIntegrationBaseTest.sol index d4c952e7..2fe8c0d6 100644 --- a/src/test/integrations/utils/ArbiIntegrationTestBase.sol +++ b/src/test/integrations/utils/ArbiIntegrationBaseTest.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {TestBase} from "../../utils/TestBase.sol"; +import {BaseTest} from "../../utils/BaseTest.sol"; import {WETHOracle} from "oracle/weth/WETHOracle.sol"; import {WETHController} from "controller/weth/WETHController.sol"; -contract ArbiIntegrationTestBase is TestBase { +contract ArbiIntegrationBaseTest is BaseTest { // Controller Contracts WETHController wEthController; diff --git a/src/test/integrations/utils/IntegrationTestBase.sol b/src/test/integrations/utils/IntegrationBaseTest.sol similarity index 98% rename from src/test/integrations/utils/IntegrationTestBase.sol rename to src/test/integrations/utils/IntegrationBaseTest.sol index ed780813..6c12517a 100644 --- a/src/test/integrations/utils/IntegrationTestBase.sol +++ b/src/test/integrations/utils/IntegrationBaseTest.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {TestBase} from "../../utils/TestBase.sol"; +import {BaseTest} from "../../utils/BaseTest.sol"; import {WETHOracle} from "oracle/weth/WETHOracle.sol"; import {WETHController} from "controller/weth/WETHController.sol"; import {ChainlinkOracle} from "oracle/chainlink/ChainlinkOracle.sol"; @@ -13,7 +13,7 @@ import {CurveCryptoSwapController} import {ICurveTriCryptoOracle} from "oracle/curve/CurveTriCryptoOracle.sol"; import {ICurvePool} from "oracle/curve/CurveTriCryptoOracle.sol"; -contract IntegrationTestBase is TestBase { +contract IntegrationBaseTest is BaseTest { // Controller Contracts WETHController wEthController; diff --git a/src/test/registry/Registry.t.sol b/src/test/registry/Registry.t.sol index c503cdbc..e7023409 100644 --- a/src/test/registry/Registry.t.sol +++ b/src/test/registry/Registry.t.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.17; import {Errors} from "../../utils/Errors.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {IRegistry} from "../../interface/core/IRegistry.sol"; -contract RegistryTest is TestBase { +contract RegistryTest is BaseTest { function setUp() public { setupContracts(); } diff --git a/src/test/riskEngine/RiskEngine.t.sol b/src/test/riskEngine/RiskEngine.t.sol index 31f0375f..5a5420ad 100644 --- a/src/test/riskEngine/RiskEngine.t.sol +++ b/src/test/riskEngine/RiskEngine.t.sol @@ -4,10 +4,10 @@ pragma solidity ^0.8.17; import {TestERC20} from "../utils/TestERC20.sol"; import {console} from "../utils/console.sol"; import {Errors} from "../../utils/Errors.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; -contract RiskEngineTest is TestBase { +contract RiskEngineTest is BaseTest { using FixedPointMathLib for uint; address account; diff --git a/src/test/tokens/LToken.t.sol b/src/test/tokens/LToken.t.sol index 3d408cf0..eed09a4d 100644 --- a/src/test/tokens/LToken.t.sol +++ b/src/test/tokens/LToken.t.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.17; import {Errors} from "../../utils/Errors.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; import {console} from "../utils/console.sol"; -contract LTokenTest is TestBase { +contract LTokenTest is BaseTest { address account; address owner = cheats.addr(1); diff --git a/src/test/tokens/LendingFlow.t.sol b/src/test/tokens/LendingFlow.t.sol index 9624df30..73f00072 100644 --- a/src/test/tokens/LendingFlow.t.sol +++ b/src/test/tokens/LendingFlow.t.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.17; import {Errors} from "../../utils/Errors.sol"; -import {TestBase} from "../utils/TestBase.sol"; +import {BaseTest} from "../utils/BaseTest.sol"; -contract LendingFlowTest is TestBase { +contract LendingFlowTest is BaseTest { function setUp() public { setupContracts(); diff --git a/src/test/utils/TestBase.sol b/src/test/utils/BaseTest.sol similarity index 97% rename from src/test/utils/TestBase.sol rename to src/test/utils/BaseTest.sol index 29d8cad3..64c53915 100644 --- a/src/test/utils/TestBase.sol +++ b/src/test/utils/BaseTest.sol @@ -23,7 +23,7 @@ import {DefaultRateModel} from "../../core/DefaultRateModel.sol"; import {IAccountManager} from "../../interface/core/IAccountManager.sol"; import {ControllerFacade} from "controller/core/ControllerFacade.sol"; -contract TestBase is Test { +contract BaseTest is Test { CheatCodes cheats = CheatCodes(HEVM_ADDRESS); uint lenderID = 5; @@ -78,7 +78,7 @@ contract TestBase is Test { deploy(); register(); - initialize(); + initializeDep(); mock(); } @@ -131,13 +131,14 @@ contract TestBase is Test { registry.setLToken(address(erc20), address(lErc20)); } - function initialize() private { + function initializeDep() private { riskEngine.initDep(); accountManager.initDep(); lEth.initDep('RATE_MODEL'); lErc20.initDep('RATE_MODEL'); accountManager.toggleCollateralStatus(address(erc20)); + accountManager.setAssetCap(16); } function mock() private { diff --git a/src/utils/Errors.sol b/src/utils/Errors.sol index bdcae41f..2a9847e9 100644 --- a/src/utils/Errors.sol +++ b/src/utils/Errors.sol @@ -6,6 +6,7 @@ library Errors { error MaxSupply(); error ZeroShares(); error ZeroAssets(); + error MaxAssetCap(); error ZeroAddress(); error MinimumShares(); error ContractPaused();