Skip to content

Commit

Permalink
Minor updates to comments
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasia committed Oct 5, 2024
1 parent e5be74c commit c92081a
Show file tree
Hide file tree
Showing 7 changed files with 32 additions and 123 deletions.
40 changes: 29 additions & 11 deletions packages/contracts/src/token/ERC1155/MultiTokenVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,6 @@ abstract contract MultiTokenVault is
return address(ASSET);
}

function assetIERC20() public view virtual returns (IERC20 assetIERC20_) {
return ASSET;
}

/**
* @inheritdoc IMultiTokenVault
*/
Expand Down Expand Up @@ -311,64 +307,86 @@ 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
virtual
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
virtual
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
virtual
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,
uint256[] memory ids,
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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, "");
}
```
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down

0 comments on commit c92081a

Please sign in to comment.