# ITIP-xx: Vault-based Batch Issuance *Using template [v0.1](https://github.com/IndexCoop/ITIPS/blob/main/itip-template.md)* ## Abstract Minting costs for our products are too damn high. We can control costs by packaging fewer components, but this is undesirable because it hurts diversification. This ITIP proposes an ERC-4626 vault mechanism which lets users avoid paying the gas costs of Flash Mint, while still giving them a claim to the newly minted index tokens as if they had executed the Flash Mint themselves. ## Background Information For a non-technical primer on how Flash Mint works, see this [blog post](https://indexcoop.com/blog/introducing-the-flash-mint-sdk). For a deeper understanding of how Index Protocol issuance modules work, see the [Set Protocol documentation](https://docs.tokensets.com/developers/guides-and-tutorials/protocol). Index Coop has several Flash Mint contracts deployed on Ethereum mainnet, summarized below. | Contract name | Compatible products | Example Tx | Gas used | Gas Cost ($) | | --------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------ | --------- | ------------:| | [Flash Mint 0x (Set V2)](https://etherscan.io/address/0xf42eCDC112365fF79a745B4cf7D4C266bd6E4b25) | DPI, MVI | [DPI issuance](https://etherscan.io/tx/0x2bdd66a23b86b80eb8d1fa488b2af97fdfc3358fadae1f841e131c90a9eb0649) | 2,015,383 | $120 | | [Flash Mint 0x (Index Protocol)](https://etherscan.io/address/0x9d648e5564b794b918d99c84b0fbf4b0bf36ce45) | dsETH | [dsETH redemption](https://etherscan.io/tx/0x2a4db6d785cc07c6f22e59287575d6605a579a620216f0d97a6155baffc7a3b9) | 756,643 | $20 | | [Flash Mint Leveraged](https://etherscan.io/address/0x981b21a2912a427f491f1e5b9bf9cca16fa794e1) | icETH | [icETH issuance](https://etherscan.io/tx/0xd082592f4ae36cc06e372020bd43b2c0a05f3073f21290f3646d5bfb57039733) | 1,308,241 | $40 | For composite indices the main driver of gas cost is the number of components. For leveraged indices we use an Aave Flash Loan, which adds gas and a flash loan fee of 0.09%. ## Proposed Solution ![](https://i.imgur.com/swPGNMQ.jpg) The diagram above shows a simple Vault + Keeper arrangement for icETH. A user can deposit USDC into a vault and receive "shares" in the form of ERC20-compatible `icETH-v` tokens. The keeper's job is to watch the vault contract for certain events emitted or conditions to be met. In the diagram, the keeper simply listens for a `deposit` event and then calls `issueExactSetFromERC20()` on the Flash Mint Leveraged Contract, using all the USDC in the vault. The newly issued icETH is sent to the vault address, however the amount of icETH in the vault has no immediate effect on its `totalAssets`, which is denominated in USDC. In practice we'd only Flash Mint the vault's USDC when it makes economic sense. For withdrawals, we let the user choose if they want a *fast* or *slow* withdrawal. In fast withdrawals, the user calls the vault contract's `redeem()` function directly, and pays the gas. Inside the `redeem()` function we check if there's enough USDC lying unused in the vault, and if there is, we burn the user's vault shares and return the corresponding amount of USDC. If there isn't enough USDC to cover the withdrawal, then the vault will attempt to Flash Redeem just enough icETH to return the user's share of USDC in the vault<sup>1</sup>. The user's vault shares are burned, and we subtract the withdrawn USDC from `totalAssets`. Deposits and Withdrawals are the only times we allow `totalAssets` to be updated, even though we know the icETH:USDC exchange rate constantly fluctuates. *Alternatively, we could charge a fee for fast withdrawals within X blocks at a guaranteed exchange rate* In a slow withdrawal request, the user specifies how long they are willing to wait to receive their USDC back from the vault (minimum of X blocks). Anytime before the user's deadline, our keeper can call `redeem()` on behalf of the user, paying the gas costs. If the keeper fails to call `redeem()` before the deadline, there is a public `rake()` function on the vault contract that anyone can call. `rake()` batches all of the unprocessed slow withdrawals and executes them in a gas efficient way. Whoever calls `rake()` receives an ETH reward to compensate for gas, similar to FLI's `ripcord()`. <sup>1</sup>*I think we'd need a USDC:TOKEN price oracle if we did it this way. I'm sure there's a way to design this to remove the oracle dependency. One possibilty is to save the USDC:TOKEN exchange rate on the vault contract each time a Flash Mint or Flash redeem is executed.* ## Open Questions - [ ] Can we use [EIP-2612: Permit Extension for EIP-20 Signed Approvals](https://eips.ethereum.org/EIPS/eip-2612) to make deposits and withdrawals free for the user? - TBD. [Solmate ERC20]([http](https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) is a good EIP-2612 implementation. - [ ] When should the vault's funds be used to perform a Flash Mint? - Every 7 days, at the same time every week. - [ ] How to handle withdrawals? - Basic option: Do it the same way as for deposits, and match deposits with withdrawals so only the net amounts need to be traded. - Advanced option A: Allow users to instantly redeem for vault asset (USDC) - Advanced option B: Allow users to instantly redeem for SetToken (ex. MMI) - [ ] Which product to build this for first? - MMI? ## Timeline March 15: Decide if this is worth doing. April 15: Finalize Vault specification. June 15: Complete code. Begin Audit. July 15: Deploy finalized vault contracts. ## Checkpoint 1 Before more in depth design of the contract flows lets make sure that all the work done to this point has been exhaustive. It should be clear what we're doing, why, and for who. All necessary information on external protocols should be gathered and potential solutions considered. At this point we should be in alignment with product on the non-technical requirements for this feature. It is up to the reviewer to determine whether we move onto the next step. **Reviewer**: TBD ## Appendix Everything below is from the template. ## Proposed Architecture Changes A diagram would be helpful here to see where new feature slot into the system. Additionally a brief description of any new contracts is helpful. ## Requirements These should be a distillation of the previous two sections taking into account the decided upon high-level implementation. Each flow should have high level requirements taking into account the needs of participants in the flow (users, managers, market makers, app devs, etc) ## User Flows - Highlight *each* external flow enabled by this feature. It's helpful to use diagrams (add them to the `assets` folder). Examples can be very helpful, make sure to highlight *who* is initiating this flow, *when* and *why*. A reviewer should be able to pick out what requirements are being covered by this flow. ## Checkpoint 2 Before we spec out the contract(s) in depth we want to make sure that we are aligned on all the technical requirements and flows for contract interaction. Again the who, what, when, why should be clearly illuminated for each flow. It is up to the reviewer to determine whether we move onto the next step. **Reviewer**: Reviewer: [] ## Specification ### [Contract Name] #### Inheritance - List inherited contracts #### Structs | Type | Name | Description | |------ |------ |------------- | |address|manager|Address of the manager| |uint256|iterations|Number of times manager has called contract| #### Constants | Type | Name | Description | Value | |------ |------ |------------- |------- | |uint256|ONE | The number one| 1 | #### Public Variables | Type | Name | Description | |------ |------ |------------- | |uint256|hodlers|Number of holders of this token| #### Modifiers > onlyManager(SetToken _setToken) #### Functions > issue(SetToken _setToken, uint256 quantity) external - Pseudo code ## Checkpoint 3 Before we move onto the implementation phase we want to make sure that we are aligned on the spec. All contracts should be specced out, their state and external function signatures should be defined. For more complex contracts, internal function definition is preferred in order to align on proper abstractions. Reviewer should take care to make sure that all stake holders (product, app engineering) have their needs met in this stage. **Reviewer**: ## Implementation [Link to implementation PR]() ## Documentation [Link to Documentation on feature]() ## Deployment [Link to Deployment script PR]() [Link to Deploy outputs PR]()