From db5c66d1aed4864fc5c12d4e43f1066df6e35897 Mon Sep 17 00:00:00 2001 From: Ryan Sauge Date: Wed, 15 May 2024 15:40:50 +0200 Subject: [PATCH] Change filepath --- src/libraries/IncomeVaultInternal.sol | 76 +++++++++++++++++++ src/libraries/IncomeVaultInvariantStorage.sol | 29 +++++++ src/public/IncomeVaultOpen.sol | 2 +- src/public/IncomeVaultRestricted.sol | 2 +- 4 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 src/libraries/IncomeVaultInternal.sol create mode 100644 src/libraries/IncomeVaultInvariantStorage.sol diff --git a/src/libraries/IncomeVaultInternal.sol b/src/libraries/IncomeVaultInternal.sol new file mode 100644 index 0000000..8295118 --- /dev/null +++ b/src/libraries/IncomeVaultInternal.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +import "OZ/token/ERC20/utils/SafeERC20.sol"; +import "./IncomeVaultInvariantStorage.sol"; +import "CMTAT/interfaces/ICMTATSnapshot.sol"; +/** +* @title Internal functions +*/ +abstract contract IncomeVaultInternal is IncomeVaultInvariantStorage { + // CMTAT token + ICMTATSnapshot public CMTAT_TOKEN; + IERC20 public ERC20TokenPayment; + mapping(address => mapping (uint256 => bool)) public claimedDividend; + mapping(uint256 => uint256) public segregatedDividend; + mapping(uint256 => bool) public segregatedClaim; + uint256 public timeLimitToWithdraw; + + // Security + using SafeERC20 for IERC20; + + /** + * @param time dividend time + * @param tokenHolders addresses to compute dividend + * @param tokenHoldersBalance the sender balance + * @param tokenTotalSupply the total supply + */ + function _computeDividendBatch(uint256 time, address[] calldata tokenHolders, uint256[] memory tokenHoldersBalance, uint256 tokenTotalSupply) internal view returns(uint256[] memory tokenHolderDividend){ + tokenHolderDividend = new uint256[](tokenHolders.length); + uint256 dividendTotalSupply = segregatedDividend[time]; + for(uint256 i = 0; i < tokenHolders.length; ++i){ + if(tokenHoldersBalance[i] > 0) { + tokenHolderDividend[i] = (tokenHoldersBalance[i] * dividendTotalSupply) / tokenTotalSupply; + } + } + } + + /** + * @param time dividend time + * @param senderBalance token holder balance + * @param tokenTotalSupply the total supply + */ + function _computeDividend(uint256 time, uint256 senderBalance, uint256 tokenTotalSupply) internal view returns(uint256 tokenHolderDividend){ + if (senderBalance == 0){ + revert IncomeVault_NoDividendToClaim(); + } + /** + SenderBalance = 300 + totalSupply = 900 + Dividend total supply= 200 + dividend = (300 * 200) / 900 = 60000 / 900 = 600/9 = 66.6 = 66 + */ + uint256 dividendTotalSupply = segregatedDividend[time]; + + tokenHolderDividend = (senderBalance * dividendTotalSupply) / tokenTotalSupply; + } + + /** + * @param time dividend time + * @param tokenHolder addresses to send the dividends + * @param tokenHolderDividend the computed dividends + */ + function _transferDividend(uint256 time, address tokenHolder, uint256 tokenHolderDividend) internal{ + // Before ERC-20 transfer to avoid re-entrancy attack + claimedDividend[tokenHolder][time] = true; + emit DividendClaimed(time, tokenHolder, tokenHolderDividend); + // transfer + // We don't revert if SenderBalance == 0 to record the claim + if(tokenHolderDividend != 0){ + // Will revert in case of failure + // We should put that in a try catch for the batch version ??? + ERC20TokenPayment.safeTransfer(tokenHolder, tokenHolderDividend); + } + } +} diff --git a/src/libraries/IncomeVaultInvariantStorage.sol b/src/libraries/IncomeVaultInvariantStorage.sol new file mode 100644 index 0000000..e06a6eb --- /dev/null +++ b/src/libraries/IncomeVaultInvariantStorage.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +abstract contract IncomeVaultInvariantStorage { + // Role + bytes32 public constant INCOME_VAULT_OPERATOR_ROLE = keccak256("INCOME_VAULT_OPERATOR_ROLE"); + bytes32 public constant INCOME_VAULT_DEPOSIT_ROLE = keccak256("INCOME_VAULT_DEPOSIT_ROLE"); + bytes32 public constant INCOME_VAULT_DISTRIBUTE_ROLE = keccak256("INCOME_VAULT_DEPOSIT_ROLE"); + bytes32 public constant INCOME_VAULT_WITHDRAW_ROLE = keccak256("INCOME_VAULT_WITHDRAW_ROLE"); + + // errors + error IncomeVault_ClaimNotActivated(); + error IncomeVault_DividendAlreadyClaimed(); + error IncomeVault_NoDividendToClaim(); + error IncomeVault_AdminWithAddressZeroNotAllowed(); + error IncomeVault_TokenPaymentWithAddressZeroNotAllowed(); + error IncomeVault_CMTATWithAddressZeroNotAllowed(); + error IncomeVault_FailApproval(); + error IncomeVault_NoAmountSend(); + error IncomeVault_NotEnoughAmount(); + error IncomeVault_TokenBalanceIsZero(); + error IncomeVault_TooLateToWithdraw(uint256 currentTime); + error IncomeVault_TooEarlyToWithdraw(uint256 currentTime); + + // event + event newDeposit(uint256 indexed time, address indexed sender, uint256 dividend); + event DividendClaimed(uint256 indexed time, address indexed sender, uint256 dividend); +} \ No newline at end of file diff --git a/src/public/IncomeVaultOpen.sol b/src/public/IncomeVaultOpen.sol index 9f88418..1968674 100644 --- a/src/public/IncomeVaultOpen.sol +++ b/src/public/IncomeVaultOpen.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import "lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol"; -import "../lib/IncomeVaultInternal.sol"; +import "../libraries/IncomeVaultInternal.sol"; import "CMTAT/modules/wrapper/controllers/ValidationModule.sol"; /** * @title public function diff --git a/src/public/IncomeVaultRestricted.sol b/src/public/IncomeVaultRestricted.sol index 7736962..d7b2c2f 100644 --- a/src/public/IncomeVaultRestricted.sol +++ b/src/public/IncomeVaultRestricted.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import "OZ/token/ERC20/utils/SafeERC20.sol"; import "CMTAT/modules/wrapper/controllers/ValidationModule.sol"; -import "../lib/IncomeVaultInternal.sol"; +import "../libraries/IncomeVaultInternal.sol"; /** * @title restricted functions