feat: Added imports in a different file as too long, implemented getLimits and getTokens

This commit is contained in:
domenicodev
2024-01-23 16:54:38 +01:00
parent 94644ac735
commit ba289d5bd1
2 changed files with 235 additions and 42 deletions

View File

@@ -2,7 +2,8 @@
pragma experimental ABIEncoderV2;
pragma solidity ^0.8.13;
import {IERC20, ISwapAdapter} from "src/interfaces/ISwapAdapter.sol";
/// @dev Wrapped imports (incl. ISwapAdapter and IERC20) are included in utils
import "./AngleUtils.sol";
/// @title AngleAdapter
contract AngleAdapter is ISwapAdapter {
@@ -13,6 +14,7 @@ contract AngleAdapter is ISwapAdapter {
transmuter = _transmuter;
}
/// @inheritdoc ISwapAdapter
function price(
bytes32,
IERC20 _sellToken,
@@ -38,11 +40,42 @@ contract AngleAdapter is ISwapAdapter {
revert NotImplemented("TemplateSwapAdapter.swap");
}
/// @inheritdoc ISwapAdapter
/// @dev mint may have no limits, but we underestimate them to make sure, with the same amount of sellToken.
/// We use the quoteIn (incl. fee), because calculating fee requires a part of the implementation of
/// the Angle Diamond Storage, and therefore redundant functions and excessive contract size, with an high complexity.
function getLimits(bytes32, IERC20 sellToken, IERC20 buyToken)
external
view
override
returns (uint256[] memory limits)
{
revert NotImplemented("TemplateSwapAdapter.getLimits");
limits = new uint256[](2);
address sellTokenAddress = address(sellToken);
address buyTokenAddress = address(buyToken);
if(buyTokenAddress == transmuter.agToken()) { // mint(buy agToken)
Collateral memory collatInfo = transmuter.getCollateralInfo(sellTokenAddress);
if(collatInfo.isManaged > 0) {
limits[0] = LibManager.maxAvailable(collatInfo.managerData.config);
}
else {
limits[0] = sellToken.balanceOf(address(transmuter));
}
limits[1] = transmuter.quoteIn(limits[0], sellTokenAddress, buyTokenAddress);
}
else { // burn(sell agToken)
Collateral memory collatInfo = transmuter.getCollateralInfo(buyTokenAddress);
uint256 collatLimit;
if(collatInfo.isManaged > 0) {
collatLimit = LibManager.maxAvailable(collatInfo.managerData.config);
}
else {
collatLimit = buyToken.balanceOf(address(transmuter));
}
limits[0] = transmuter.quoteIn(collatLimit, buyTokenAddress, sellTokenAddress);
limits[1] = collatLimit;
}
}
function getCapabilities(bytes32 poolId, IERC20 sellToken, IERC20 buyToken)
@@ -52,11 +85,21 @@ contract AngleAdapter is ISwapAdapter {
revert NotImplemented("TemplateSwapAdapter.getCapabilities");
}
function getTokens(bytes32 poolId)
/// @inheritdoc ISwapAdapter
/// @dev Since Angle has no pool IDs but supports 3 tokens(agToken and the collaterals),
/// we return all the available collaterals and the agToken(agEUR)
function getTokens(bytes32)
external
view
override
returns (IERC20[] memory tokens)
{
revert NotImplemented("TemplateSwapAdapter.getTokens");
address[] memory collateralsAddresses = transmuter.getCollateralList();
tokens = new IERC20[](collateralsAddresses.length + 1);
for(uint256 i = 0; i < collateralsAddresses.length; i++) {
tokens[i] = IERC20(collateralsAddresses[i]);
}
tokens[collateralsAddresses.length] = IERC20(transmuter.agToken());
}
function getPoolIds(uint256 offset, uint256 limit)
@@ -85,39 +128,6 @@ contract AngleAdapter is ISwapAdapter {
}
abstract contract ITransmuter {
struct Tuple6871229 {
address facetAddress;
uint8 action;
bytes4[] functionSelectors;
}
struct Tuple1236461 {
address facetAddress;
bytes4[] functionSelectors;
}
struct Tuple3550792 {
uint8 isManaged;
uint8 isMintLive;
uint8 isBurnLive;
uint8 decimals;
uint8 onlyWhitelisted;
uint216 normalizedStables;
uint64[] xFeeMint;
int64[] yFeeMint;
uint64[] xFeeBurn;
int64[] yFeeBurn;
bytes oracleConfig;
bytes whitelistData;
Tuple5479340 managerData;
}
struct Tuple5479340 {
address[] subCollaterals;
bytes config;
}
function diamondCut(Tuple6871229[] memory _diamondCut, address _init, bytes memory _calldata) external {}
function implementation() external view returns (address) {}
@@ -129,8 +139,6 @@ abstract contract ITransmuter {
function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory _facetFunctionSelectors) {}
function facets() external view returns (Tuple1236461[] memory facets_) {}
function accessControlManager() external view returns (address) {}
function agToken() external view returns (address) {}
@@ -141,7 +149,7 @@ abstract contract ITransmuter {
function getCollateralDecimals(address collateral) external view returns (uint8) {}
function getCollateralInfo(address collateral) external view returns (Tuple3550792 memory) {}
function getCollateralInfo(address collateral) external view returns (Collateral memory) {}
function getCollateralList() external view returns (address[] memory) {}
@@ -203,8 +211,6 @@ abstract contract ITransmuter {
function setAccessControlManager(address _newAccessControlManager) external {}
function setCollateralManager(address collateral, Tuple5479340 memory managerData) external {}
function setOracle(address collateral, bytes memory oracleConfig) external {}
function setWhitelistStatus(address collateral, uint8 whitelistStatus, bytes memory whitelistData) external {}

View File

@@ -0,0 +1,187 @@
// 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";
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));
}
}