diff --git a/packages/contracts/src/token/ERC1155/MultiTokenVault.sol b/packages/contracts/src/token/ERC1155/MultiTokenVault.sol index b387b2d47..29832be63 100644 --- a/packages/contracts/src/token/ERC1155/MultiTokenVault.sol +++ b/packages/contracts/src/token/ERC1155/MultiTokenVault.sol @@ -123,10 +123,6 @@ abstract contract MultiTokenVault is return address(ASSET); } - function assetIERC20() public view virtual returns (IERC20 assetIERC20_) { - return ASSET; - } - /** * @inheritdoc IMultiTokenVault */ @@ -311,14 +307,21 @@ abstract contract MultiTokenVault is emit Withdraw(caller, receiver, owner, depositPeriod, assets, shares); } + /** + * @inheritdoc ERC1155SupplyUpgradeable + */ function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal virtual override(ERC1155SupplyUpgradeable, ERC1155PausableUpgradeable) + whenNotPaused { - super._update(from, to, ids, values); + ERC1155SupplyUpgradeable._update(from, to, ids, values); } + /** + * @inheritdoc ERC1155Upgradeable + */ function balanceOf(address account, uint256 id) public view @@ -326,9 +329,12 @@ abstract contract MultiTokenVault is override(ERC1155Upgradeable, IERC1155) returns (uint256) { - return super.balanceOf(account, id); + return ERC1155Upgradeable.balanceOf(account, id); } + /** + * @inheritdoc ERC1155Upgradeable + */ function balanceOfBatch(address[] memory accounts, uint256[] memory ids) public view @@ -336,9 +342,12 @@ abstract contract MultiTokenVault is override(ERC1155Upgradeable, IERC1155) returns (uint256[] memory) { - return super.balanceOfBatch(accounts, ids); + return ERC1155Upgradeable.balanceOfBatch(accounts, ids); } + /** + * @inheritdoc ERC1155Upgradeable + */ function isApprovedForAll(address account, address operator) public view @@ -346,21 +355,30 @@ abstract contract MultiTokenVault is override(ERC1155Upgradeable, IERC1155) returns (bool) { - return super.isApprovedForAll(account, operator); + return ERC1155Upgradeable.isApprovedForAll(account, operator); } + /** + * @inheritdoc ERC1155Upgradeable + */ function setApprovalForAll(address operator, bool approved) public virtual override(ERC1155Upgradeable, IERC1155) { - super.setApprovalForAll(operator, approved); + ERC1155Upgradeable.setApprovalForAll(operator, approved); } + /** + * @inheritdoc ERC1155Upgradeable + */ function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) public virtual override(ERC1155Upgradeable, IERC1155) { - super.safeTransferFrom(from, to, id, amount, data); + ERC1155Upgradeable.safeTransferFrom(from, to, id, amount, data); } + /** + * @inheritdoc ERC1155Upgradeable + */ function safeBatchTransferFrom( address from, address to, @@ -368,7 +386,7 @@ abstract contract MultiTokenVault is uint256[] memory amounts, bytes memory data ) public virtual override(ERC1155Upgradeable, IERC1155) { - super.safeBatchTransferFrom(from, to, ids, amounts, data); + ERC1155Upgradeable.safeBatchTransferFrom(from, to, ids, amounts, data); } /** diff --git a/packages/contracts/src/token/ERC1155/RedeemOptimizerFIFO.sol b/packages/contracts/src/token/ERC1155/RedeemOptimizerFIFO.sol index 6b07bea87..4cccb851e 100644 --- a/packages/contracts/src/token/ERC1155/RedeemOptimizerFIFO.sol +++ b/packages/contracts/src/token/ERC1155/RedeemOptimizerFIFO.sol @@ -79,7 +79,7 @@ contract RedeemOptimizerFIFO is IRedeemOptimizer { // Iterate over the from/to period range, inclusive of from and to. for (uint256 depositPeriod = fromDepositPeriod; depositPeriod <= toDepositPeriod; ++depositPeriod) { - uint256 sharesAtPeriod = vault.balanceOf(owner, depositPeriod); + uint256 sharesAtPeriod = vault.sharesAtPeriod(owner, depositPeriod); uint256 amountAtPeriod = amountType == AmountType.Shares ? sharesAtPeriod : vault.convertToAssetsForDepositPeriod(sharesAtPeriod, depositPeriod, redeemPeriod); diff --git a/packages/contracts/test/src/timelock/README.md b/packages/contracts/test/src/timelock/TIMELOCK.md similarity index 65% rename from packages/contracts/test/src/timelock/README.md rename to packages/contracts/test/src/timelock/TIMELOCK.md index afe9adf70..2f99bc3d2 100644 --- a/packages/contracts/test/src/timelock/README.md +++ b/packages/contracts/test/src/timelock/TIMELOCK.md @@ -57,39 +57,3 @@ function unlock(address account, uint256 lockReleasePeriod, uint256 value) publi _burn(account, lockReleasePeriod, value); } ``` ----- -## Rolling Over Investments - -Rolling over your investment means that instead of withdrawing your Principal and Interest at Maturity, you automatically reinvest them into the -same Product for an additional "tenor" period of time. - -### Rollover Example -Alice invests $1,000 with a 30-day Tenor. After 30 days, her investment matures, giving her 1 day to redeem. If she doesn't redeem within that -window, her $1,000 plus the Interest automatically rolls over into a new 30-day Tenor. - -### Rollover Implementation -The `rolloverUnlocked` function reinvests unlocked tokens by rolling them over into a new lock period. In the TimelockIERC1155 implementation, -this function burns the unlocked tokens and mints new tokens for the new lock period. -```Solidity -/** - * @notice Rolls over a specified amount of unlocked tokens for a new lock period. - * @param account The address of the account whose tokens are to be rolled over. - * @param lockReleasePeriod The period during which these tokens will be released. - * @param value The amount of tokens to be rolled over. - */ -function rolloverUnlocked(address account, uint256 lockReleasePeriod, uint256 value) public virtual override onlyOwner -{ - uint256 unlockableAmount = previewUnlock(account, lockReleasePeriod); - - if (value > unlockableAmount) { - revert InsufficientLockedBalance(unlockableAmount, value); - } - - _burn(account, lockReleasePeriod, value); - - uint256 rolloverLockReleasePeriod = lockReleasePeriod + lockDuration; - - _mint(account, rolloverLockReleasePeriod, value, ""); -} - -``` diff --git a/packages/contracts/CBL_README.md b/packages/contracts/test/src/token/CBL_README.md similarity index 100% rename from packages/contracts/CBL_README.md rename to packages/contracts/test/src/token/CBL_README.md diff --git a/packages/contracts/test/src/yield/README.md b/packages/contracts/test/src/yield/CALC_INTEREST.md similarity index 68% rename from packages/contracts/test/src/yield/README.md rename to packages/contracts/test/src/yield/CALC_INTEREST.md index 25e7b9e51..864b66e1c 100644 --- a/packages/contracts/test/src/yield/README.md +++ b/packages/contracts/test/src/yield/CALC_INTEREST.md @@ -152,76 +152,3 @@ function convertToAssetsAtPeriod(uint256 sharesInWei, uint256 numTimePeriodsElap } ``` ----- -# Rolling Over Investment with Discounting - -Rolling over your investment means that instead of withdrawing your Principal and Interest at Maturity, you automatically reinvest them into the -same Product for an additional "tenor" period of time. - -When you roll over, the interest earned during the first period is added to your original Principal (P1), forming a new Principal (P2) -for the next period. You can choose to fully or partially roll over your investment, with P2 including any amount you reinvest from P1. - -## Rollover Example - -Alice invests $1,000 in a Credbull product with a 30-day Tenor and 12% APY. She chooses to fully roll over her investment for another 30 days -after the first Tenor. - -### Investment Period 1 -Alice earns **$10** in interest for investment period 1. - -`Simple Interest = 0.12 * $1,000 * 30 / 360 = $10` - -### Investment Period 2 -Rather than redeeming, Alice rolls-over her full investment. - -1. Her new Principal(P2) for investment period 2 is **$1,010.** This includes her -original Principal(P1) and the interest from period 1. - -`Principal (P2) = $1,000 + $10 = $1,010` - -2. To calculate the Discounted Principal for P2 we just need the Price and Principal(P2) at rollover of 30 days. - -``` -Price(Day 30) = 1 + 0.12 * 30 / 360 = $1.01 -Discounted(P2) = P(P2) / Price = $1,010 / $1.01 = $1,000 -``` - - -### Rollover Implementation -The TimelockInterestVault `rolloverUnlocked` function reinvests unlocked tokens into a new period. Rollover is implemented as follows: - -- The new Principal (P2) for period 2 is calculated as the original Principal (P1) plus the Interest (P1) earned during the first period. -The function `convertToAssets`, which is defined as Principal + Interest, is used to calculate this. -- Next, we use `convertToShares` with Principal (P2) to calculate the new shares for period 2. These new shares represent the -Discounted Principal (P2) for the second period. -- The 'Discounted Principal' (P2) might be less than the 'Discounted Principal' (P1) due to interest accrual. In such cases, `rolloverUnlocked` -calculates the difference and burns any "excess" amount of shares. -- Finally, we call TimelockIERC1155's `rolloverUnlocked` function to remove the lock on the shares from period 1 and lock them for period 2. - - -```Solidity - -/** -* @notice Rolls over a specified amount of unlocked tokens for a new lock period. - * @param account The address of the account whose tokens are to be rolled over. - * @param lockReleasePeriod The period during which these tokens will be released. - * @param value The amount of tokens to be rolled over. - */ -function rolloverUnlocked(address account, uint256 lockReleasePeriod, uint256 value) public override onlyOwner { - uint256 principalAndInterestFirstPeriod = convertToAssets(value); // principal + first period interest - - uint256 sharesForNextPeriod = convertToShares(principalAndInterestFirstPeriod); // discounted principal for rollover period - - if (value > sharesForNextPeriod) { - uint256 excessValue = value - sharesForNextPeriod; - - // Burn from ERC1155 (Timelock) - ERC1155._burn(account, lockReleasePeriod, excessValue); - - // Burn from ERC4626 (Vault) - SimpleInterestVault._burnInternal(account, excessValue); - } - - TimelockIERC1155.rolloverUnlocked(account, lockReleasePeriod, sharesForNextPeriod); -} -``` \ No newline at end of file diff --git a/packages/contracts/test/test/token/ERC1155/IMultiTokenVaultTestBase.t.sol b/packages/contracts/test/test/token/ERC1155/IMultiTokenVaultTestBase.t.sol index 62235bc9c..87f44bdab 100644 --- a/packages/contracts/test/test/token/ERC1155/IMultiTokenVaultTestBase.t.sol +++ b/packages/contracts/test/test/token/ERC1155/IMultiTokenVaultTestBase.t.sol @@ -229,7 +229,7 @@ abstract contract IMultiTokenVaultTestBase is Test { ); assertEq( prevReceiverVaultBalance + actualSharesAtPeriod, - vault.balanceOf(account, testParams.depositPeriod), + vault.sharesAtPeriod(account, testParams.depositPeriod), _assertMsg( "receiver did not receive the correct vault shares - balanceOf ", vault, testParams.depositPeriod ) diff --git a/packages/contracts/test/test/yield/LiquidContinuousMultiTokenVaultTestBase.t.sol b/packages/contracts/test/test/yield/LiquidContinuousMultiTokenVaultTestBase.t.sol index 56a0fa0a0..582b78e64 100644 --- a/packages/contracts/test/test/yield/LiquidContinuousMultiTokenVaultTestBase.t.sol +++ b/packages/contracts/test/test/yield/LiquidContinuousMultiTokenVaultTestBase.t.sol @@ -51,7 +51,7 @@ abstract contract LiquidContinuousMultiTokenVaultTestBase is IMultiTokenVaultTes assertEq( actualSharesAtPeriod, - vault.balanceOf(receiver, testParams.depositPeriod), + vault.sharesAtPeriod(receiver, testParams.depositPeriod), _assertMsg( "!!! receiver did not receive the correct vault shares - balanceOf ", vault, testParams.depositPeriod )