From 174c4e158afd4c475c2e4126f7f6db50784a1698 Mon Sep 17 00:00:00 2001 From: domenicodev Date: Wed, 24 Jan 2024 17:33:47 +0100 Subject: [PATCH] Revert "feat: Moved imports to main adapter file" This reverts commit 96915bd158e1ba9f1c8cbcdef3c25f8f2593920c. --- evm/src/angle/AngleAdapter.sol | 182 +-------------------------- evm/src/angle/AngleUtils.sol | 189 ++++++++++++++++++++++++++++ evm/test/AngleAdapter.t.sol | 223 --------------------------------- 3 files changed, 191 insertions(+), 403 deletions(-) create mode 100644 evm/src/angle/AngleUtils.sol delete mode 100644 evm/test/AngleAdapter.t.sol diff --git a/evm/src/angle/AngleAdapter.sol b/evm/src/angle/AngleAdapter.sol index 96aeab8..55b5d2f 100644 --- a/evm/src/angle/AngleAdapter.sol +++ b/evm/src/angle/AngleAdapter.sol @@ -2,10 +2,8 @@ pragma experimental ABIEncoderV2; pragma solidity ^0.8.13; -import {IERC20, ISwapAdapter} from "src/interfaces/ISwapAdapter.sol"; - -/// @dev custom reserve limit factor to prevent revert errors in OrderSide.Buy -uint256 constant RESERVE_LIMIT_FACTOR = 10; +/// @dev Wrapped imports (incl. ISwapAdapter and IERC20) are included in utils +import "./AngleUtils.sol"; /// @title AngleAdapter /// @dev Information about prices: When swapping collateral to agEUR, the trade price will not decrease(amountOut); @@ -211,182 +209,6 @@ contract AngleAdapter is ISwapAdapter { } } -interface IAgToken is IERC20 { - /*////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - MINTER ROLE ONLY FUNCTIONS - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/ - - /// @notice Lets a whitelisted contract mint agTokens - /// @param account Address to mint to - /// @param amount Amount to mint - function mint(address account, uint256 amount) external; - - /// @notice Burns `amount` tokens from a `burner` address after being asked to by `sender` - /// @param amount Amount of tokens to burn - /// @param burner Address to burn from - /// @param sender Address which requested the burn from `burner` - /// @dev This method is to be called by a contract with the minter right after being requested - /// to do so by a `sender` address willing to burn tokens from another `burner` address - /// @dev The method checks the allowance between the `sender` and the `burner` - function burnFrom(uint256 amount, address burner, address sender) external; - - /// @notice Burns `amount` tokens from a `burner` address - /// @param amount Amount of tokens to burn - /// @param burner Address to burn from - /// @dev This method is to be called by a contract with a minter right on the AgToken after being - /// requested to do so by an address willing to burn tokens from its address - function burnSelf(uint256 amount, address burner) external; - - /*////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - TREASURY ONLY FUNCTIONS - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/ - - /// @notice Adds a minter in the contract - /// @param minter Minter address to add - /// @dev Zero address checks are performed directly in the `Treasury` contract - function addMinter(address minter) external; - - /// @notice Removes a minter from the contract - /// @param minter Minter address to remove - /// @dev This function can also be called by a minter wishing to revoke itself - function removeMinter(address minter) external; - - /// @notice Sets a new treasury contract - /// @param _treasury New treasury address - function setTreasury(address _treasury) external; - - /*////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - EXTERNAL FUNCTIONS - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/ - - /// @notice Checks whether an address has the right to mint agTokens - /// @param minter Address for which the minting right should be checked - /// @return Whether the address has the right to mint agTokens or not - function isMinter(address minter) external view returns (bool); - - /// @notice Amount of decimals of the stablecoin - function decimals() external view returns (uint8); -} - -enum ManagerType { - EXTERNAL -} - -enum WhitelistType { - BACKED -} - -struct ManagerStorage { - IERC20[] subCollaterals; // Subtokens handled by the manager or strategies - bytes config; // Additional configuration data -} - -struct Collateral { - uint8 isManaged; // If the collateral is managed through external strategies - uint8 isMintLive; // If minting from this asset is unpaused - uint8 isBurnLive; // If burning to this asset is unpaused - uint8 decimals; // IERC20Metadata(collateral).decimals() - uint8 onlyWhitelisted; // If only whitelisted addresses can burn or redeem for this token - uint216 normalizedStables; // Normalized amount of stablecoins issued from this collateral - uint64[] xFeeMint; // Increasing exposures in [0,BASE_9[ - int64[] yFeeMint; // Mint fees at the exposures specified in `xFeeMint` - uint64[] xFeeBurn; // Decreasing exposures in ]0,BASE_9] - int64[] yFeeBurn; // Burn fees at the exposures specified in `xFeeBurn` - bytes oracleConfig; // Data about the oracle used for the collateral - bytes whitelistData; // For whitelisted collateral, data used to verify whitelists - ManagerStorage managerData; // For managed collateral, data used to handle the strategies -} - -struct TransmuterStorage { - IAgToken agToken; // agToken handled by the system - uint8 isRedemptionLive; // If redemption is unpaused - uint8 statusReentrant; // If call is reentrant or not - uint128 normalizedStables; // Normalized amount of stablecoins issued by the system - uint128 normalizer; // To reconcile `normalizedStables` values with the actual amount - address[] collateralList; // List of collateral assets supported by the system - uint64[] xRedemptionCurve; // Increasing collateral ratios > 0 - int64[] yRedemptionCurve; // Value of the redemption fees at `xRedemptionCurve` - mapping(address => Collateral) collaterals; // Maps a collateral asset to its parameters - mapping(address => uint256) isTrusted; // If an address is trusted to update the normalizer value - mapping(address => uint256) isSellerTrusted; // If an address is trusted to sell accruing reward tokens - mapping(WhitelistType => mapping(address => uint256)) isWhitelistedForType; -} - -interface IManager { - /// @notice Returns the amount of collateral managed by the Manager - /// @return balances Balances of all the subCollaterals handled by the manager - /// @dev MUST NOT revert - function totalAssets() external view returns (uint256[] memory balances, uint256 totalValue); - - /// @notice Hook to invest `amount` of `collateral` - /// @dev MUST revert if the manager cannot accept these funds - /// @dev MUST have received the funds beforehand - function invest(uint256 amount) external; - - /// @notice Sends `amount` of `collateral` to the `to` address - /// @dev Called when `agToken` are burnt and during redemptions - // @dev MUST revert if there are not funds enough available - /// @dev MUST be callable only by the transmuter - function release(address asset, address to, uint256 amount) external; - - /// @notice Gives the maximum amount of collateral immediately available for a transfer - /// @dev Useful for integrators using `quoteIn` and `quoteOut` - function maxAvailable() external view returns (uint256); -} - -/// @title LibManager -/// @author Angle Labs, Inc. -/// @dev Managed collateral assets may be handled through external smart contracts or directly through this library -/// @dev There is no implementation at this point for a managed collateral handled through this library, and -/// a new specific `ManagerType` would need to be added in this case -library LibManager { - /// @notice Checks to which address managed funds must be transferred - function transferRecipient(bytes memory config) internal view returns (address recipient) { - (ManagerType managerType, bytes memory data) = parseManagerConfig(config); - recipient = address(this); - if (managerType == ManagerType.EXTERNAL) return abi.decode(data, (address)); - } - - /// @notice Performs a transfer of `token` for a collateral that is managed to a `to` address - /// @dev `token` may not be the actual collateral itself, as some collaterals have subcollaterals associated - /// with it - /// @dev Eventually pulls funds from strategies - function release(address token, address to, uint256 amount, bytes memory config) internal { - (ManagerType managerType, bytes memory data) = parseManagerConfig(config); - if (managerType == ManagerType.EXTERNAL) abi.decode(data, (IManager)).release(token, to, amount); - } - - /// @notice Gets the balances of all the tokens controlled through `managerData` - /// @return balances An array of size `subCollaterals` with current balances of all subCollaterals - /// including the one corresponding to the `managerData` given - /// @return totalValue The value of all the `subCollaterals` in `collateral` - /// @dev `subCollaterals` must always have as first token (index 0) the collateral itself - function totalAssets(bytes memory config) internal view returns (uint256[] memory balances, uint256 totalValue) { - (ManagerType managerType, bytes memory data) = parseManagerConfig(config); - if (managerType == ManagerType.EXTERNAL) return abi.decode(data, (IManager)).totalAssets(); - } - - /// @notice Calls a hook if needed after new funds have been transfered to a manager - function invest(uint256 amount, bytes memory config) internal { - (ManagerType managerType, bytes memory data) = parseManagerConfig(config); - if (managerType == ManagerType.EXTERNAL) abi.decode(data, (IManager)).invest(amount); - } - - /// @notice Returns available underlying tokens, for instance if liquidity is fully used and - /// not withdrawable the function will return 0 - function maxAvailable(bytes memory config) internal view returns (uint256 available) { - (ManagerType managerType, bytes memory data) = parseManagerConfig(config); - if (managerType == ManagerType.EXTERNAL) return abi.decode(data, (IManager)).maxAvailable(); - } - - /// @notice Decodes the `managerData` associated to a collateral - function parseManagerConfig( - bytes memory config - ) internal pure returns (ManagerType managerType, bytes memory data) { - (managerType, data) = abi.decode(config, (ManagerType, bytes)); - } -} - abstract contract ITransmuter { function implementation() external view returns (address) {} diff --git a/evm/src/angle/AngleUtils.sol b/evm/src/angle/AngleUtils.sol new file mode 100644 index 0000000..a346784 --- /dev/null +++ b/evm/src/angle/AngleUtils.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +/** + * @dev Collection of wrapped interfaces, items and libraries for Angle + * we use them here to maintain integrity and readability of contracts + * as there are many different imports and items + */ + +import {IERC20, ISwapAdapter} from "src/interfaces/ISwapAdapter.sol"; + +/// @dev custom reserve limit factor to prevent revert errors in OrderSide.Buy +uint256 constant RESERVE_LIMIT_FACTOR = 10; + +interface IAgToken is IERC20 { + /*////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + MINTER ROLE ONLY FUNCTIONS + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/ + + /// @notice Lets a whitelisted contract mint agTokens + /// @param account Address to mint to + /// @param amount Amount to mint + function mint(address account, uint256 amount) external; + + /// @notice Burns `amount` tokens from a `burner` address after being asked to by `sender` + /// @param amount Amount of tokens to burn + /// @param burner Address to burn from + /// @param sender Address which requested the burn from `burner` + /// @dev This method is to be called by a contract with the minter right after being requested + /// to do so by a `sender` address willing to burn tokens from another `burner` address + /// @dev The method checks the allowance between the `sender` and the `burner` + function burnFrom(uint256 amount, address burner, address sender) external; + + /// @notice Burns `amount` tokens from a `burner` address + /// @param amount Amount of tokens to burn + /// @param burner Address to burn from + /// @dev This method is to be called by a contract with a minter right on the AgToken after being + /// requested to do so by an address willing to burn tokens from its address + function burnSelf(uint256 amount, address burner) external; + + /*////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + TREASURY ONLY FUNCTIONS + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/ + + /// @notice Adds a minter in the contract + /// @param minter Minter address to add + /// @dev Zero address checks are performed directly in the `Treasury` contract + function addMinter(address minter) external; + + /// @notice Removes a minter from the contract + /// @param minter Minter address to remove + /// @dev This function can also be called by a minter wishing to revoke itself + function removeMinter(address minter) external; + + /// @notice Sets a new treasury contract + /// @param _treasury New treasury address + function setTreasury(address _treasury) external; + + /*////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + EXTERNAL FUNCTIONS + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/ + + /// @notice Checks whether an address has the right to mint agTokens + /// @param minter Address for which the minting right should be checked + /// @return Whether the address has the right to mint agTokens or not + function isMinter(address minter) external view returns (bool); + + /// @notice Amount of decimals of the stablecoin + function decimals() external view returns (uint8); +} + +enum ManagerType { + EXTERNAL +} + +enum WhitelistType { + BACKED +} + +struct ManagerStorage { + IERC20[] subCollaterals; // Subtokens handled by the manager or strategies + bytes config; // Additional configuration data +} + +struct Collateral { + uint8 isManaged; // If the collateral is managed through external strategies + uint8 isMintLive; // If minting from this asset is unpaused + uint8 isBurnLive; // If burning to this asset is unpaused + uint8 decimals; // IERC20Metadata(collateral).decimals() + uint8 onlyWhitelisted; // If only whitelisted addresses can burn or redeem for this token + uint216 normalizedStables; // Normalized amount of stablecoins issued from this collateral + uint64[] xFeeMint; // Increasing exposures in [0,BASE_9[ + int64[] yFeeMint; // Mint fees at the exposures specified in `xFeeMint` + uint64[] xFeeBurn; // Decreasing exposures in ]0,BASE_9] + int64[] yFeeBurn; // Burn fees at the exposures specified in `xFeeBurn` + bytes oracleConfig; // Data about the oracle used for the collateral + bytes whitelistData; // For whitelisted collateral, data used to verify whitelists + ManagerStorage managerData; // For managed collateral, data used to handle the strategies +} + +struct TransmuterStorage { + IAgToken agToken; // agToken handled by the system + uint8 isRedemptionLive; // If redemption is unpaused + uint8 statusReentrant; // If call is reentrant or not + uint128 normalizedStables; // Normalized amount of stablecoins issued by the system + uint128 normalizer; // To reconcile `normalizedStables` values with the actual amount + address[] collateralList; // List of collateral assets supported by the system + uint64[] xRedemptionCurve; // Increasing collateral ratios > 0 + int64[] yRedemptionCurve; // Value of the redemption fees at `xRedemptionCurve` + mapping(address => Collateral) collaterals; // Maps a collateral asset to its parameters + mapping(address => uint256) isTrusted; // If an address is trusted to update the normalizer value + mapping(address => uint256) isSellerTrusted; // If an address is trusted to sell accruing reward tokens + mapping(WhitelistType => mapping(address => uint256)) isWhitelistedForType; +} + +interface IManager { + /// @notice Returns the amount of collateral managed by the Manager + /// @return balances Balances of all the subCollaterals handled by the manager + /// @dev MUST NOT revert + function totalAssets() external view returns (uint256[] memory balances, uint256 totalValue); + + /// @notice Hook to invest `amount` of `collateral` + /// @dev MUST revert if the manager cannot accept these funds + /// @dev MUST have received the funds beforehand + function invest(uint256 amount) external; + + /// @notice Sends `amount` of `collateral` to the `to` address + /// @dev Called when `agToken` are burnt and during redemptions + // @dev MUST revert if there are not funds enough available + /// @dev MUST be callable only by the transmuter + function release(address asset, address to, uint256 amount) external; + + /// @notice Gives the maximum amount of collateral immediately available for a transfer + /// @dev Useful for integrators using `quoteIn` and `quoteOut` + function maxAvailable() external view returns (uint256); +} + +/// @title LibManager +/// @author Angle Labs, Inc. +/// @dev Managed collateral assets may be handled through external smart contracts or directly through this library +/// @dev There is no implementation at this point for a managed collateral handled through this library, and +/// a new specific `ManagerType` would need to be added in this case +library LibManager { + /// @notice Checks to which address managed funds must be transferred + function transferRecipient(bytes memory config) internal view returns (address recipient) { + (ManagerType managerType, bytes memory data) = parseManagerConfig(config); + recipient = address(this); + if (managerType == ManagerType.EXTERNAL) return abi.decode(data, (address)); + } + + /// @notice Performs a transfer of `token` for a collateral that is managed to a `to` address + /// @dev `token` may not be the actual collateral itself, as some collaterals have subcollaterals associated + /// with it + /// @dev Eventually pulls funds from strategies + function release(address token, address to, uint256 amount, bytes memory config) internal { + (ManagerType managerType, bytes memory data) = parseManagerConfig(config); + if (managerType == ManagerType.EXTERNAL) abi.decode(data, (IManager)).release(token, to, amount); + } + + /// @notice Gets the balances of all the tokens controlled through `managerData` + /// @return balances An array of size `subCollaterals` with current balances of all subCollaterals + /// including the one corresponding to the `managerData` given + /// @return totalValue The value of all the `subCollaterals` in `collateral` + /// @dev `subCollaterals` must always have as first token (index 0) the collateral itself + function totalAssets(bytes memory config) internal view returns (uint256[] memory balances, uint256 totalValue) { + (ManagerType managerType, bytes memory data) = parseManagerConfig(config); + if (managerType == ManagerType.EXTERNAL) return abi.decode(data, (IManager)).totalAssets(); + } + + /// @notice Calls a hook if needed after new funds have been transfered to a manager + function invest(uint256 amount, bytes memory config) internal { + (ManagerType managerType, bytes memory data) = parseManagerConfig(config); + if (managerType == ManagerType.EXTERNAL) abi.decode(data, (IManager)).invest(amount); + } + + /// @notice Returns available underlying tokens, for instance if liquidity is fully used and + /// not withdrawable the function will return 0 + function maxAvailable(bytes memory config) internal view returns (uint256 available) { + (ManagerType managerType, bytes memory data) = parseManagerConfig(config); + if (managerType == ManagerType.EXTERNAL) return abi.decode(data, (IManager)).maxAvailable(); + } + + /// @notice Decodes the `managerData` associated to a collateral + function parseManagerConfig( + bytes memory config + ) internal pure returns (ManagerType managerType, bytes memory data) { + (managerType, data) = abi.decode(config, (ManagerType, bytes)); + } +} diff --git a/evm/test/AngleAdapter.t.sol b/evm/test/AngleAdapter.t.sol deleted file mode 100644 index fe0c7c8..0000000 --- a/evm/test/AngleAdapter.t.sol +++ /dev/null @@ -1,223 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.13; - -import "forge-std/Test.sol"; -import "openzeppelin-contracts/contracts/interfaces/IERC20.sol"; -import "src/angle/AngleAdapter.sol"; -import "src/interfaces/ISwapAdapterTypes.sol"; -import "src/libraries/FractionMath.sol"; - -contract AngleAdapterTest is Test, ISwapAdapterTypes { - using FractionMath for Fraction; - - AngleAdapter adapter; - IERC20 agEUR; - IERC20 constant EURC = IERC20(0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c); - ITransmuter constant transmuter = ITransmuter(0x00253582b2a3FE112feEC532221d9708c64cEFAb); - - uint256 constant TEST_ITERATIONS = 100; - - function setUp() public { - uint256 forkBlock = 18921770; - vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); - adapter = new - AngleAdapter(transmuter); - agEUR = IERC20(transmuter.agToken()); - - vm.label(address(adapter), "AngleAdapter"); - vm.label(address(agEUR), "agEUR"); - vm.label(address(EURC), "EURC"); - } - - function testPriceFuzzAngle(uint256 amount0, uint256 amount1) public { - bytes32 pair = bytes32(0); - uint256[] memory limits = adapter.getLimits(pair, EURC, agEUR); - vm.assume(amount0 < limits[0] && amount0 > 0); - vm.assume(amount1 < limits[0] && amount1 > 0); - - uint256[] memory amounts = new uint256[](2); - amounts[0] = amount0; - amounts[1] = amount1; - - Fraction[] memory prices = adapter.price(pair, EURC, agEUR, amounts); - - for (uint256 i = 0; i < prices.length; i++) { - assertGt(prices[i].numerator, 0); - assertGt(prices[i].denominator, 0); - } - } - - - function testPriceDecreasingAngle() public { - bytes32 pair = bytes32(0); - uint256[] memory amounts = new uint256[](TEST_ITERATIONS); - - for (uint256 i = 0; i < TEST_ITERATIONS; i++) { - amounts[i] = 1000 * (i+1) * 10 ** 18; - } - - Fraction[] memory prices = adapter.price(pair, agEUR, EURC, amounts); - - for (uint256 i = 0; i < TEST_ITERATIONS - 1; i++) { - assertEq(prices[i].compareFractions(prices[i + 1]), 1); - assertGt(prices[i].denominator, 0); - assertGt(prices[i + 1].denominator, 0); - } - } - - function testSwapFuzzAngleMint(uint256 specifiedAmount, bool isBuy) public { - OrderSide side = isBuy ? OrderSide.Buy : OrderSide.Sell; - - bytes32 pair = bytes32(0); - uint256[] memory limits = adapter.getLimits(pair, EURC, agEUR); - - if (side == OrderSide.Buy) { - vm.assume(specifiedAmount < limits[1] && specifiedAmount > 0); - - deal(address(EURC), address(this), type(uint256).max); - EURC.approve(address(adapter), type(uint256).max); - } else { - vm.assume(specifiedAmount < limits[0] && specifiedAmount > 0); - - deal(address(EURC), address(this), specifiedAmount); - EURC.approve(address(adapter), specifiedAmount); - } - - uint256 eurc_balance = EURC.balanceOf(address(this)); - uint256 agEUR_balance = agEUR.balanceOf(address(this)); - - Trade memory trade = - adapter.swap(pair, EURC, agEUR, side, specifiedAmount); - - if (trade.calculatedAmount > 0) { - if (side == OrderSide.Buy) { - assertEq( - specifiedAmount, - agEUR.balanceOf(address(this)) - agEUR_balance - ); - assertEq( - trade.calculatedAmount, - eurc_balance - EURC.balanceOf(address(this)) - ); - } else { - assertEq( - specifiedAmount, - eurc_balance - EURC.balanceOf(address(this)) - ); - assertEq( - trade.calculatedAmount, - agEUR.balanceOf(address(this)) - agEUR_balance - ); - } - } - } - - function testSwapFuzzAngleRedeem(uint256 specifiedAmount, bool isBuy) public { - OrderSide side = isBuy ? OrderSide.Buy : OrderSide.Sell; - - bytes32 pair = bytes32(0); - uint256[] memory limits = adapter.getLimits(pair, agEUR, EURC); - console.log(limits[0], limits[1]); - - if (side == OrderSide.Buy) { - vm.assume(specifiedAmount < limits[1] && specifiedAmount > 0); - - deal(address(agEUR), address(this), type(uint256).max); - agEUR.approve(address(adapter), type(uint256).max); - } else { - vm.assume(specifiedAmount < limits[0] && specifiedAmount > 0); - - deal(address(agEUR), address(this), specifiedAmount); - agEUR.approve(address(adapter), specifiedAmount); - } - - uint256 eurc_balance = EURC.balanceOf(address(this)); - uint256 agEUR_balance = agEUR.balanceOf(address(this)); - - Trade memory trade = - adapter.swap(pair, agEUR, EURC, side, specifiedAmount); - - if (trade.calculatedAmount > 0) { - if (side == OrderSide.Buy) { - assertEq( - specifiedAmount, - EURC.balanceOf(address(this)) - eurc_balance - ); - assertEq( - trade.calculatedAmount, - agEUR_balance - agEUR.balanceOf(address(this)) - ); - } else { - assertEq( - specifiedAmount, - agEUR_balance - agEUR.balanceOf(address(this)) - ); - assertEq( - trade.calculatedAmount, - EURC.balanceOf(address(this)) - eurc_balance - ); - } - } - } - - function testSwapSellIncreasingAngle() public { - executeIncreasingSwapsAngle(OrderSide.Sell); - } - - function executeIncreasingSwapsAngle(OrderSide side) internal { - bytes32 pair = bytes32(0); - - uint256[] memory amounts = new uint256[](TEST_ITERATIONS); - for (uint256 i = 0; i < TEST_ITERATIONS; i++) { - amounts[i] = - side == OrderSide.Sell ? (100 * i * 10 ** 18) : (100 * i * 10 ** 6); - } - - Trade[] memory trades = new Trade[](TEST_ITERATIONS); - uint256 beforeSwap; - for (uint256 i = 1; i < TEST_ITERATIONS; i++) { - beforeSwap = vm.snapshot(); - - if(side == OrderSide.Sell) { - deal(address(agEUR), address(this), amounts[i]); - agEUR.approve(address(adapter), amounts[i]); - } - else { - deal(address(agEUR), address(this), type(uint256).max); - agEUR.approve(address(adapter), type(uint256).max); - } - - trades[i] = adapter.swap(pair, agEUR, EURC, side, amounts[i]); - vm.revertTo(beforeSwap); - } - - for (uint256 i = 1; i < TEST_ITERATIONS - 1; i++) { - assertLe(trades[i].calculatedAmount, trades[i + 1].calculatedAmount); - assertEq(trades[i].price.compareFractions(trades[i + 1].price), 1); - } - } - - function testSwapBuyIncreasingAngle() public { - executeIncreasingSwapsAngle(OrderSide.Buy); - } - - function testGetCapabilitiesAngle(bytes32 pair, address t0, address t1) public { - Capability[] memory res = - adapter.getCapabilities(pair, IERC20(t0), IERC20(t1)); - - assertEq(res.length, 3); - } - - function testGetTokensAngle() public { - IERC20[] memory tokens = adapter.getTokens(bytes32(0)); - - assertGe(tokens.length, 2); - } - - function testGetLimits() public { - bytes32 pair = bytes32(0); - uint256[] memory limits = adapter.getLimits(pair, agEUR, EURC); - - assertEq(limits.length, 2); - } -}