diff --git a/contracts/arbitrum/connectors/basic-ERC4626/events.sol b/contracts/arbitrum/connectors/basic-ERC4626/events.sol new file mode 100644 index 0000000..f6e77ee --- /dev/null +++ b/contracts/arbitrum/connectors/basic-ERC4626/events.sol @@ -0,0 +1,38 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +contract Events { + event LogDeposit( + address indexed token, + uint256 underlyingAmt, + uint256 sharesReceieved, + uint256 getId, + uint256 setId + ); + + event LogMint( + address indexed token, + uint256 shareAmt, + uint256 tokensDeposited, + uint256 getId, + uint256 setId + ); + + event LogWithdraw( + address indexed token, + uint256 underlyingAmt, + uint256 sharedBurned, + address indexed to, + uint256 getId, + uint256 setId + ); + + event LogRedeem( + address indexed token, + uint256 shareAmt, + uint256 underlyingAmtReceieved, + address to, + uint256 getId, + uint256 setId + ); +} \ No newline at end of file diff --git a/contracts/arbitrum/connectors/basic-ERC4626/interface.sol b/contracts/arbitrum/connectors/basic-ERC4626/interface.sol new file mode 100644 index 0000000..07d8b62 --- /dev/null +++ b/contracts/arbitrum/connectors/basic-ERC4626/interface.sol @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC4626.sol) + +pragma solidity ^0.8.2; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +/** + * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in + * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. + * + * _Available since v4.7._ + */ +interface IERC4626 is IERC20 { + event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); + + event Withdraw( + address indexed sender, + address indexed receiver, + address indexed owner, + uint256 assets, + uint256 shares + ); + + function decimals() external view returns (uint8); + + /** + * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. + * + * - MUST be an ERC-20 token contract. + * - MUST NOT revert. + */ + function asset() external view returns (address assetTokenAddress); + + /** + * @dev Returns the total amount of the underlying asset that is “managed” by Vault. + * + * - SHOULD include any compounding that occurs from yield. + * - MUST be inclusive of any fees that are charged against assets in the Vault. + * - MUST NOT revert. + */ + function totalAssets() external view returns (uint256 totalManagedAssets); + + /** + * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal + * scenario where all the conditions are met. + * + * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. + * - MUST NOT show any variations depending on the caller. + * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + * - MUST NOT revert. + * + * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the + * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and + * from. + */ + function convertToShares(uint256 assets) external view returns (uint256 shares); + + /** + * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal + * scenario where all the conditions are met. + * + * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. + * - MUST NOT show any variations depending on the caller. + * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + * - MUST NOT revert. + * + * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the + * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and + * from. + */ + function convertToAssets(uint256 shares) external view returns (uint256 assets); + + /** + * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, + * through a deposit call. + * + * - MUST return a limited value if receiver is subject to some deposit limit. + * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. + * - MUST NOT revert. + */ + function maxDeposit(address receiver) external view returns (uint256 maxAssets); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given + * current on-chain conditions. + * + * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit + * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called + * in the same transaction. + * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the + * deposit would be accepted, regardless if the user has enough tokens approved, etc. + * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by depositing. + */ + function previewDeposit(uint256 assets) external view returns (uint256 shares); + + /** + * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. + * + * - MUST emit the Deposit event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + * deposit execution, and are accounted for during deposit. + * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not + * approving enough underlying tokens to the Vault contract, etc). + * + * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. + */ + function deposit(uint256 assets, address receiver) external returns (uint256 shares); + + /** + * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. + * - MUST return a limited value if receiver is subject to some mint limit. + * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. + * - MUST NOT revert. + */ + function maxMint(address receiver) external view returns (uint256 maxShares); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given + * current on-chain conditions. + * + * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call + * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the + * same transaction. + * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint + * would be accepted, regardless if the user has enough tokens approved, etc. + * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by minting. + */ + function previewMint(uint256 shares) external view returns (uint256 assets); + + /** + * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. + * + * - MUST emit the Deposit event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint + * execution, and are accounted for during mint. + * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not + * approving enough underlying tokens to the Vault contract, etc). + * + * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. + */ + function mint(uint256 shares, address receiver) external returns (uint256 assets); + + /** + * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the + * Vault, through a withdraw call. + * + * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. + * - MUST NOT revert. + */ + function maxWithdraw(address owner) external view returns (uint256 maxAssets); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, + * given current on-chain conditions. + * + * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw + * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if + * called + * in the same transaction. + * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though + * the withdrawal would be accepted, regardless if the user has enough shares, etc. + * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by depositing. + */ + function previewWithdraw(uint256 assets) external view returns (uint256 shares); + + /** + * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. + * + * - MUST emit the Withdraw event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + * withdraw execution, and are accounted for during withdraw. + * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner + * not having enough shares, etc). + * + * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. + * Those methods should be performed separately. + */ + function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); + + /** + * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, + * through a redeem call. + * + * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. + * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. + * - MUST NOT revert. + */ + function maxRedeem(address owner) external view returns (uint256 maxShares); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, + * given current on-chain conditions. + * + * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call + * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the + * same transaction. + * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the + * redemption would be accepted, regardless if the user has enough shares, etc. + * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by redeeming. + */ + function previewRedeem(uint256 shares) external view returns (uint256 assets); + + /** + * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. + * + * - MUST emit the Withdraw event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + * redeem execution, and are accounted for during redeem. + * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner + * not having enough shares, etc). + * + * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. + * Those methods should be performed separately. + */ + function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); +} \ No newline at end of file diff --git a/contracts/arbitrum/connectors/basic-ERC4626/main.sol b/contracts/arbitrum/connectors/basic-ERC4626/main.sol new file mode 100644 index 0000000..822a21a --- /dev/null +++ b/contracts/arbitrum/connectors/basic-ERC4626/main.sol @@ -0,0 +1,196 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +/** + * @title Basic D V2. + * @dev Deposit, Mint, Withdraw, & Redeem from ERC4626 DSA. + */ + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IERC4626 } from "./interface.sol"; +import { TokenInterface } from "../../common/interfaces.sol"; +import { DSMath } from "../../common/math.sol"; +import { Basic } from "../../common/basic.sol"; +import { Events } from "./events.sol"; + +abstract contract BasicConnector is Events, DSMath, Basic { + + /** + * @dev Deposit underlying asset to ERC4626 Vault. + * @notice Mints vault shares by depositing exactly amount of underlying assets + * @param vaultToken ERC4626 Token address. + * @param underlyingAmt The amount of the underlying asset to deposit. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens deposited. + */ + function deposit( + address vaultToken, + uint256 underlyingAmt, + uint256 getId, + uint256 setId + ) public returns (string memory _eventName, bytes memory _eventParam) { + uint256 _underlyingAmt = getUint(getId, underlyingAmt); + + IERC4626 vaultTokenContract = IERC4626(vaultToken); + TokenInterface _underlyingTokenContract = TokenInterface( + vaultTokenContract.asset() + ); + + _underlyingAmt = _underlyingAmt == type(uint256).max + ? _underlyingTokenContract.balanceOf(address(this)) + : _underlyingAmt; + + approve(_underlyingTokenContract, vaultToken, _underlyingAmt); + + // Deposit tokens for shares + uint256 _sharesReceieved = + vaultTokenContract.deposit(_underlyingAmt, address(this)); + + setUint(setId, _sharesReceieved); + + _eventName = "LogDeposit(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode( + vaultToken, + _underlyingAmt, + _sharesReceieved, + getId, + setId + ); + } + + /** + * @dev Mint underlying asset to ERC4626 Vault. + * @notice Mints vault shares by minting exactly amount of underlying assets + * @param vaultToken ERC4626 Token address. + * @param shareAmt The amount of the share to mint. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens minted. + */ + function mint( + address vaultToken, + uint256 shareAmt, + uint256 getId, + uint256 setId + ) public returns (string memory _eventName, bytes memory _eventParam) { + uint256 _shareAmt = getUint(getId, shareAmt); + + IERC4626 vaultTokenContract = IERC4626(vaultToken); + TokenInterface underlyingTokenContract = TokenInterface( + vaultTokenContract.asset() + ); + + uint256 _underlyingTokenAmount = vaultTokenContract.previewMint( + _shareAmt + ); + + approve(underlyingTokenContract, vaultToken, _underlyingTokenAmount); + + // Mint shares for tokens + uint256 _tokensDeposited = + vaultTokenContract.mint(_shareAmt, address(this)); + + setUint(setId, _tokensDeposited); + + _eventName = "LogMint(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode( + vaultToken, + _shareAmt, + _tokensDeposited, + getId, + setId + ); + } + + /** + * @dev Withdraw underlying asset from ERC4626 Vault. + * @notice Withdraw vault shares with exactly amount of underlying assets + * @param vaultToken ERC4626 Token address. + * @param underlyingAmt The amount of the token to withdraw. (For max: `uint256(-1)`) + * @param to The address of receiver. + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens withdrawn. + */ + function withdraw( + address vaultToken, + uint256 underlyingAmt, + address payable to, + uint256 getId, + uint256 setId + ) public returns (string memory _eventName, bytes memory _eventParam) { + uint256 _underlyingAmt = getUint(getId, underlyingAmt); + + IERC4626 vaultTokenContract = IERC4626(vaultToken); + TokenInterface underlyingTokenContract = TokenInterface( + vaultTokenContract.asset() + ); + + _underlyingAmt = _underlyingAmt == type(uint256).max + ? underlyingTokenContract.balanceOf(address(this)) + : _underlyingAmt; + + // Withdraw tokens for shares + uint256 _sharesBurned = + vaultTokenContract.withdraw(_underlyingAmt, to, address(this)); + + setUint(setId, _underlyingAmt); + + _eventName = "LogWithdraw(address,uint256,uint256,address,uint256,uint256)"; + _eventParam = abi.encode( + vaultToken, + _underlyingAmt, + _sharesBurned, + to, + getId, + setId + ); + } + + /** + * @dev Redeem underlying asset from ERC4626 Vault. + * @notice Redeem vault shares with exactly amount of underlying assets + * @param vaultToken ERC4626 Token address. + * @param shareAmt The amount of the token to redeem. (For max: `uint256(-1)`) + * @param to The address of receiver. + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens redeem. + */ + + function redeem( + address vaultToken, + uint256 shareAmt, + address payable to, + uint256 getId, + uint256 setId + ) public returns (string memory _eventName, bytes memory _eventParam) { + uint256 _shareAmt = getUint(getId, shareAmt); + + IERC4626 vaultTokenContract = IERC4626(vaultToken); + TokenInterface underlyingTokenContract = TokenInterface( + vaultTokenContract.asset() + ); + + _shareAmt = _shareAmt == type(uint256).max + ? vaultTokenContract.balanceOf(address(this)) + : _shareAmt; + + // Redeem tokens for shares + uint256 _underlyingAmtReceieved = + vaultTokenContract.redeem(_shareAmt, to, address(this)); + + setUint(setId, _shareAmt); + + _eventName = "LogRedeem(address,uint256,uint256,address,uint256,uint256)"; + _eventParam = abi.encode( + vaultToken, + _shareAmt, + _underlyingAmtReceieved, + to, + getId, + setId + ); + } +} + +contract ConnectV2BasicERC4626V2Arbitrum is BasicConnector { + string public constant name = "BASIC-ERC4626-v2.0"; +} \ No newline at end of file diff --git a/contracts/arbitrum/connectors/instapool_v5/main.sol b/contracts/arbitrum/connectors/instapool_v5/main.sol index 61a50a6..731baed 100644 --- a/contracts/arbitrum/connectors/instapool_v5/main.sol +++ b/contracts/arbitrum/connectors/instapool_v5/main.sol @@ -133,6 +133,6 @@ contract LiquidityResolver is Stores, Variables, Events { } -contract ConnectV2InstaPoolV5 is LiquidityResolver { +contract ConnectV2InstaPoolV5Arbitrum is LiquidityResolver { string public name = "Instapool-v5"; }