From 2ed23e0c7ea669da65b818cf9f23cd0278ff532b Mon Sep 17 00:00:00 2001 From: Dmitry Date: Fri, 1 Dec 2023 15:43:02 +0100 Subject: [PATCH] Drafting distributions to Yield Modules Distribution to a Yield Module Vault is driven by distributions percentage set during the Vault addition or update by the Acre Manager. The concrete amount is calculated as the vault's percent allowance of the total TBTC balance in Acre (stBTC) contract. --- core/contracts/AcreRouter.sol | 92 +++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 20 deletions(-) diff --git a/core/contracts/AcreRouter.sol b/core/contracts/AcreRouter.sol index f9321b3cf..7932ba92d 100644 --- a/core/contracts/AcreRouter.sol +++ b/core/contracts/AcreRouter.sol @@ -14,6 +14,11 @@ import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; contract AcreRouter is OwnableUpgradeable { using SafeERC20 for IERC20; + struct Vault { + uint256 distribution; // percentage of TBTC in Acre to be distributed to the vault + bool approved; + } + IERC20 public immutable stBTC; IERC20 public immutable tBTC; @@ -24,7 +29,7 @@ contract AcreRouter is OwnableUpgradeable { /// ERC4626 standard and is approved by the Acre Manager it can be /// plugged into Acre. address[] public vaults; - mapping(address => bool) public isVault; + mapping(address => Vault) public vaultsInfo; /// @notice Acre Manager address. Only Acre Manager can set or remove Vaults. address public acreManager; @@ -48,25 +53,55 @@ contract AcreRouter is OwnableUpgradeable { /// @param manager Address of the Acre Manager. function setAcreManager(address manager) external onlyOwner { require(manager != address(0), "Cannot be zero address"); + acreManager = manager; emit AcreManagerSet(manager); } /// @notice Adds a vault to the list of approved vaults. /// @param vault Address of the vault to add. - function addVault(address vault) external onlyAcreManager { - require(!isVault[vault], "Vault already exists"); + /// @param percent Percentage of TBTC in Acre to be distributed to the vault. + function addVault(address vault, uint256 percent) external onlyAcreManager { + require(!vaultsInfo[vault].approved, "Vault already approved"); + vaults.push(vault); - isVault[vault] = true; + vaultsInfo[vault].distribution = percent; + vaultsInfo[vault].approved = true; + + require( + sumOfVaultsDistribution() <= 100, + "Total vaults distribution is greater than 100%" + ); + emit VaultAdded(vault); } + /// @notice Updates the distribution of a given vault. If the percentage of + /// all vaults distributions exceed 100% the transaction will revert. + /// In this case it is required to adjust the distribution of other + /// vault(s) first. + /// @param vault Address of the vault to update. + /// @param percent Percentage of TBTC in Acre to be distributed to the vault. + function updateVaultDistribution( + address vault, + uint256 percent + ) external onlyAcreManager { + require(vaultsInfo[vault].approved, "Vault is not approved"); + + vaultsInfo[vault].distribution = percent; + + require( + sumOfVaultsDistribution() <= 100, + "Total vaults distribution is greater than 100%" + ); + } + /// @notice Removes a vault from the list of approved vaults. /// @param vault Address of the vault to remove. function removeVault(address vault) external onlyAcreManager { - require(isVault[vault], "Not an vault"); + require(vaultsInfo[vault].approved, "Not a vault"); - delete isVault[vault]; + delete vaultsInfo[vault]; for (uint256 i = 0; i < vaults.length; i++) { if (vaults[i] == vault) { @@ -79,31 +114,45 @@ contract AcreRouter is OwnableUpgradeable { emit VaultRemoved(vault); } - /// @notice Routes funds from stBTC (Acre) to a given vault + /// @notice Routes funds from stBTC (Acre) to a given vault according to + /// the vault distribution. The amount of TBTC to deposit is + /// calculated as a percentage of the total amount of TBTC in Acre. /// @param vault Address of the vault to route the funds to. - /// @param amount Amount of TBTC to deposit. /// @param minSharesOut Minimum amount of shares to receive. function deposit( address vault, - uint256 amount, uint256 minSharesOut ) public returns (uint256 sharesOut) { require(msg.sender == address(stBTC), "stBTC only"); - if (!isVault[vault]) { - revert("Vault is not approved"); - } + require(vaultsInfo[vault].approved, "Vault is not approved"); + + uint256 totalBalance = tBTC.balanceOf(address(stBTC)); + uint256 vaultDistribution = vaultsInfo[vault].distribution; // percent + uint256 amountToDeposit = (totalBalance * vaultDistribution) / 100; - tBTC.safeTransferFrom(msg.sender, address(this), amount); - tBTC.safeIncreaseAllowance(vault, amount); + tBTC.safeTransferFrom(msg.sender, address(this), amountToDeposit); + tBTC.safeIncreaseAllowance(vault, amountToDeposit); // stBTC is the Acre contract where the shares will be minted to if ( - (sharesOut = IERC4626(vault).deposit(amount, address(stBTC))) < - minSharesOut + (sharesOut = IERC4626(vault).deposit( + amountToDeposit, + address(stBTC) + )) < minSharesOut ) { revert("Not enough shares received"); } } + // TODO: decide if we actually need this functionality. Is it be needed + // for automation of the deposit process by bots? + function depositBatch() public { + // require(msg.sender == address(stBTC), "stBTC only"); + // TODO: go through all the approved vaults and calulate the amount to + // deposit for each Vault based on their percent distribution. + // TODO: minSharesOut can be calculated for each Vault using + // Acre.previewDeposit(amount) function. + } + /// @notice Redeem TBTC from a vault and approves them to be collected /// by stBTC (Acre) /// @param vault Address of the vault to collect the assets from. @@ -118,10 +167,7 @@ contract AcreRouter is OwnableUpgradeable { uint256 minAssetsOut ) public returns (uint256 assetsOut) { require(msg.sender == address(stBTC), "stBTC only"); - - if (!isVault[vault]) { - revert("Vault is not approved"); - } + require(vaultsInfo[vault].approved, "Vault is not approved"); if ( (assetsOut = IERC4626(vault).redeem( @@ -134,4 +180,10 @@ contract AcreRouter is OwnableUpgradeable { } tBTC.safeIncreaseAllowance(address(stBTC), assetsOut); } + + function sumOfVaultsDistribution() internal view returns (uint256 sum) { + for (uint256 i = 0; i < vaults.length; i++) { + sum += vaultsInfo[vaults[i]].distribution; + } + } }