diff --git a/.gitignore b/.gitignore index 603dabe..e13c4e7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ node_modules/ broadcast/ lib/ out/ +docOut/ cache/ .~lock.test.odt# nethereum-gen.settings @@ -12,4 +13,4 @@ artifacts/ cache_hardhat/ #drawio *.bkp -*.dtmp \ No newline at end of file +*.dtmp diff --git a/CHANGELOG.md b/CHANGELOG.md index 4173bff..59fc447 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,60 +2,5 @@ Please follow [https://changelog.md/](https://changelog.md/) conventions. -## v1.0.3 - 20231122 - -- Upgrade the library CMTAT to the version [v2.3.1](https://github.com/CMTA/CMTAT/releases/tag/v2.3.1) -- Use custom errors instead of revert message (gas optimization) -- Add the rule `SanctionList` -- Upgrade OpenZeppelin to the version [v5.0.0](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.0.0) - -## v1.0.2 - 20230609 - -- Upgrade the library CMTAT to the vesion [v2.3.0](https://github.com/CMTA/CMTAT/releases/tag/v2.3.0) -- Set the number of runs for the optimizer to 200 for Hardhat and Foundry, see [https://docs.soliditylang.org/en/v0.8.17/using-the-compiler.html#optimizer-options](https://docs.soliditylang.org/en/v0.8.17/using-the-compiler.html#optimizer-options) - -## 1.0.2-rc.0 - 20230523 - -The release 1.0.2 contains mainly the different fixes and improvements related to the audit performed on the version 1.0.1. - -**Documentation** - -- Update the documentation for the release -- Add slither report -- Install hardhat in order to generic docgen documentation - -**General modifications** - -- Upgrade the library CMTAT to the latest version [2.3.0-rc.0](https://github.com/CMTA/CMTAT/releases/tag/2.3-Beta) ([pull/28](https://github.com/CMTA/RuleEngine/pull/28)) - - In RuleEngine, `ruleLength` is changed to `rulesCount()` -- Add the gasless suport / MetaTx module ([pull/27](https://github.com/CMTA/RuleEngine/pull/27)) -- RuleWhitelist: update RuleWhitelist to use code from IEIP1404Wrapper ([pull/29](https://github.com/CMTA/RuleEngine/pull/29)) - -**Audit report** - -This version also includes improvements suggested by the audit report, addressing the following findings: - -General - -- CVF-10: use a floating pragma for the version ([pull/25](https://github.com/CMTA/RuleEngine/pull/25)) -- CVF2, CVF-6: remove the function kill for the contracts RuleEngine and Whitelist ([pull/17](https://github.com/CMTA/RuleEngine/pull/17)) - -RuleEngine - -- CVF-1 / removeRule: add an additional argument with the rule index hint ([pull/23](https://github.com/CMTA/RuleEngine/pull/23)) -- CVF-7, CVF-8, CVF-9: check for duplicate rule ([pull/20](https://github.com/CMTA/RuleEngine/pull/20)) - -Whitelist - -- CVF-3: use a local variable for iterate inside a loop ([pull/18/](https://github.com/CMTA/RuleEngine/pull/18/)) -- CVF-4, CVF-5: remove useless conditional statement ([pull/19/](https://github.com/CMTA/RuleEngine/pull/19/)) -- CVF-15, CVF-16, CVF-17: improve readibility ([pull/24](https://github.com/CMTA/RuleEngine/pull/24)) - -## 1.0.1 - 20230122 - -- Update the library CMTAT to the version [2.2](https://github.com/CMTA/CMTAT/releases/tag/2.2) -- Update the library OpenZeppelin to the version [4.8.1](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v4.8.1) -- Improve integration test with CMTAT - ## 1.0.0 - 20221114 - ๐ŸŽ‰ first release! diff --git a/README.md b/README.md index caae243..007fd7c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,47 @@ # IncomeVault -Contracts to perform coupon payment -## Initialization +The `IncomeVault`is a prototype to perform coupon-payment dividend with a CMTAT and the snapshotModule + +## Introduction + +The dividends are deposited in a Vault. Once the claims are open, a token holder can then perform a claim to get his dividends for a given period. + +Currently, the vault supports only dividend under the form of another ERC-20 and it is suitable for the following use-case: + +- Dividends in ERC-20 compatible, which could be an ERC-20 stablecoin such as USDC or USDT for example +- Interest paid out at given intervals which shall be a configurable parameter (i.e. every 6 months, every 1 year) + +For the specific case where dividends are distributed in shares, meaning additional payout of the โ€œexistingโ€ CMTAT Token, it is not currently supported due to the following reasons: +\- With the current architecture, depending on when you decide to mint the new tokens, you will increase the total supply used to compute the token holder shares. Therefore, you will reduce the dividends distributed to the token holders. +\- In general, for yield tokens, the formula used can be different. + +## Compatibility + +- The dividends can be paid with ERC-20 tokens as described in the [ERC-20](https://eips.ethereum.org/EIPS/eip-20) specification +- The shares used to compute the dividends part have to be a smart contract implementing the interface `ICMTATSnapshot` as described in the CMTAT. This interface is responsible to provide information on the token holder's balance and the total supply for a specific time. + +## Audits + +The contracts are NOT audited, do not use them for production without auditing them !!!! + +A report performed with [Slither](https://github.com/crytic/slither) is available in [doc/audits/tools](./doc/audits/tools/slither-report.md) + +## Documentation + +Here a summary of the main documentation + +| Document | Link/Files | +| ----------------------- | ---------------------------------------- | +| Specification | [doc/specification](./doc/specification) | +| Technical documentation | [doc/technical](./doc/technical) | +| Toolchain | [doc/TOOLCHAIN.md](./doc/TOOLCHAIN.md) | +| Surya report | [doc/surya](./doc/surya/) | + +## Foundry + +The project is developed with [Foundry](https://book.getfoundry.sh) + +### Initialization You must first initialize the submodules, with @@ -21,19 +61,15 @@ See also the command's [documentation](https://book.getfoundry.sh/reference/forg -## Compilation +### Compilation The official documentation is available in the Foundry [website](https://book.getfoundry.sh/reference/forge/build-commands) ``` - forge build --contracts src/RuleEngine.sol + forge build --contracts src/IncomeVault.sol ``` -``` - forge build --contracts src/RuleWhiteList.sol -``` - -## Testing +### Testing You can run the tests with @@ -49,7 +85,9 @@ forge test --match-contract --match-test See also the test framework's [official documentation](https://book.getfoundry.sh/forge/tests), and that of the [test commands](https://book.getfoundry.sh/reference/forge/test-commands). -### Coverage +#### Coverage + +> Unfortunately, tests are performed with a proxy deployment and the coverage command does not work currently in this configuration * Perform a code coverage @@ -60,13 +98,13 @@ forge coverage --ffi * Generate LCOV report ``` -forge coverage --report lcov +forge coverage --ffi --report lcov ``` - Generate `index.html` ```bash -forge coverage --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage +forge coverage --ffi --report lcov && genhtml lcov.info --branch-coverage --output-dir coverage ``` See [Solidity Coverage in VS Code with Foundry](https://mirror.xyz/devanon.eth/RrDvKPnlD-pmpuW7hQeR5wWdVjklrpOgPCOA-PJkWFU) & [Foundry forge coverage](https://www.rareskills.io/post/foundry-forge-coverage) diff --git a/doc/audits/tools/slither-report.md b/doc/audits/tools/slither-report.md new file mode 100644 index 0000000..77a6c97 --- /dev/null +++ b/doc/audits/tools/slither-report.md @@ -0,0 +1,254 @@ +**THIS CHECKLIST IS NOT COMPLETE**. Use `--show-ignored-findings` to show all the results. +Summary + - [reentrancy-benign](#reentrancy-benign) (1 results) (Low) + - [pragma](#pragma) (1 results) (Informational) + - [dead-code](#dead-code) (1 results) (Informational) + - [solc-version](#solc-version) (2 results) (Informational) + - [naming-convention](#naming-convention) (11 results) (Informational) + - [unused-import](#unused-import) (6 results) (Informational) + - [unused-state](#unused-state) (1 results) (Informational) +## reentrancy-benign + +> Withdraw is protected by access control, reentrancy can not be used by an attacker + +Impact: Low +Confidence: Medium + - [ ] ID-0 + Reentrancy in [IncomeVaultRestricted.withdraw(uint256,uint256,address)](src/public/IncomeVaultRestricted.sol#L40-L51): + External calls: + - [result = ERC20TokenPayment.approve(address(this),amount)](src/public/IncomeVaultRestricted.sol#L41) + State variables written after the call(s): + - [segragatedDividend[time] -= amount](src/public/IncomeVaultRestricted.sol#L48) + +src/public/IncomeVaultRestricted.sol#L40-L51 + +## pragma + +> Concerns the CMTAT lib, will be fixed in the CMTAT lib. + +Impact: Informational +Confidence: High + - [ ] ID-1 + 2 different versions of Solidity are used: + - Version constraint ^0.8.0 is used by: + - lib/CMTAT/contracts/interfaces/ICMTATSnapshot.sol#3 + - lib/CMTAT/contracts/interfaces/draft-IERC1404/draft-IERC1404.sol#3 + - lib/CMTAT/contracts/interfaces/draft-IERC1404/draft-IERC1404Wrapper.sol#3 + - lib/CMTAT/contracts/interfaces/engine/IAuthorizationEngine.sol#3 + - lib/CMTAT/contracts/interfaces/engine/IRuleEngine.sol#3 + - Version constraint ^0.8.20 is used by: + - lib/CMTAT/contracts/libraries/Errors.sol#3 + - lib/CMTAT/contracts/modules/internal/EnforcementModuleInternal.sol#3 + - lib/CMTAT/contracts/modules/internal/ValidationModuleInternal.sol#3 + - lib/CMTAT/contracts/modules/security/AuthorizationModule.sol#3 + - lib/CMTAT/contracts/modules/wrapper/controllers/ValidationModule.sol#3 + - lib/CMTAT/contracts/modules/wrapper/core/EnforcementModule.sol#3 + - lib/CMTAT/contracts/modules/wrapper/core/PauseModule.sol#3 + - lib/CMTAT/contracts/modules/wrapper/extensions/MetaTxModule.sol#3 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/metatx/ERC2771ContextUpgradeable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol#4 + - lib/openzeppelin-contracts/contracts/access/IAccessControl.sol#4 + - lib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sol#3 + - lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol#4 + - lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol#4 + - lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol#4 + - lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#4 + - lib/openzeppelin-contracts/contracts/utils/Address.sol#4 + - lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol#4 + - src/IncomeVault.sol#3 + - src/lib/IncomeVaultInternal.sol#3 + - src/lib/IncomeVaultInvariantStorage.sol#3 + - src/public/IncomeVaultOpen.sol#3 + - src/public/IncomeVaultRestricted.sol#3 + +## dead-code + +> - Implemented to be gasless compatible (see MetaTxModule) +> +> - If we remove this function, we will have the following error: +> +> "Derived contract must override function "_msgData". Two or more base classes define function with same name and parameter types." + +Impact: Informational +Confidence: Medium + - [ ] ID-2 +[IncomeVault._msgData()](src/IncomeVault.sol#L96-L103) is never used and should be removed + +src/IncomeVault.sol#L96-L103 + +## solc-version + +> The version set in the config file is 0.8.22 + +Impact: Informational +Confidence: High + - [ ] ID-3 +Version constraint ^0.8.0 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html) + - FullInlinerNonExpressionSplitArgumentEvaluationOrder + - MissingSideEffectsOnSelectorAccess + - AbiReencodingHeadOverflowWithStaticArrayCleanup + - DirtyBytesArrayToStorage + - DataLocationChangeInInternalOverride + - NestedCalldataArrayAbiReencodingSizeValidation + - SignedImmutables + - ABIDecodeTwoDimensionalArrayMemory + - KeccakCaching. + It is used by: + - lib/CMTAT/contracts/interfaces/ICMTATSnapshot.sol#3 + - lib/CMTAT/contracts/interfaces/draft-IERC1404/draft-IERC1404.sol#3 + - lib/CMTAT/contracts/interfaces/draft-IERC1404/draft-IERC1404Wrapper.sol#3 + - lib/CMTAT/contracts/interfaces/engine/IAuthorizationEngine.sol#3 + - lib/CMTAT/contracts/interfaces/engine/IRuleEngine.sol#3 + + - [ ] ID-4 + Version constraint ^0.8.20 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html) + - VerbatimInvalidDeduplication + - FullInlinerNonExpressionSplitArgumentEvaluationOrder + - MissingSideEffectsOnSelectorAccess. + It is used by: + - lib/CMTAT/contracts/libraries/Errors.sol#3 + - lib/CMTAT/contracts/modules/internal/EnforcementModuleInternal.sol#3 + - lib/CMTAT/contracts/modules/internal/ValidationModuleInternal.sol#3 + - lib/CMTAT/contracts/modules/security/AuthorizationModule.sol#3 + - lib/CMTAT/contracts/modules/wrapper/controllers/ValidationModule.sol#3 + - lib/CMTAT/contracts/modules/wrapper/core/EnforcementModule.sol#3 + - lib/CMTAT/contracts/modules/wrapper/core/PauseModule.sol#3 + - lib/CMTAT/contracts/modules/wrapper/extensions/MetaTxModule.sol#3 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/metatx/ERC2771ContextUpgradeable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol#4 + - lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol#4 + - lib/openzeppelin-contracts/contracts/access/IAccessControl.sol#4 + - lib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sol#3 + - lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol#4 + - lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol#4 + - lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol#4 + - lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#4 + - lib/openzeppelin-contracts/contracts/utils/Address.sol#4 + - lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol#4 + - src/IncomeVault.sol#3 + - src/lib/IncomeVaultInternal.sol#3 + - src/lib/IncomeVaultInvariantStorage.sol#3 + - src/public/IncomeVaultOpen.sol#3 + - src/public/IncomeVaultRestricted.sol#3 + +## naming-convention +Impact: Informational +Confidence: High + - [ ] ID-5 +Variable [IncomeVaultInternal.CMTAT_TOKEN](src/lib/IncomeVaultInternal.sol#L13) is not in mixedCase + +src/lib/IncomeVaultInternal.sol#L13 + + + - [ ] ID-6 +Event [IncomeVaultInvariantStorage.newDeposit(uint256,address,uint256)](src/lib/IncomeVaultInvariantStorage.sol#L24) is not in CapWords + +src/lib/IncomeVaultInvariantStorage.sol#L24 + + + - [ ] ID-7 +Variable [IncomeVaultRestricted.__gap](src/public/IncomeVaultRestricted.sol#L102) is not in mixedCase + +src/public/IncomeVaultRestricted.sol#L102 + + + - [ ] ID-8 +Parameter [IncomeVault.__IncomeVault_init(address,IERC20,ICMTATSnapshot,IRuleEngine,IAuthorizationEngine).cmtat_token](src/IncomeVault.sol#L55) is not in mixedCase + +src/IncomeVault.sol#L55 + + + - [ ] ID-9 +Variable [IncomeVaultInternal.ERC20TokenPayment](src/lib/IncomeVaultInternal.sol#L14) is not in mixedCase + +src/lib/IncomeVaultInternal.sol#L14 + + + - [ ] ID-10 +Parameter [IncomeVault.initialize(address,IERC20,ICMTATSnapshot,IRuleEngine,IAuthorizationEngine).cmtat_token](src/IncomeVault.sol#L36) is not in mixedCase + +src/IncomeVault.sol#L36 + + + - [ ] ID-11 +Variable [IncomeVault.__gap](src/IncomeVault.sol#L112) is not in mixedCase + +src/IncomeVault.sol#L112 + + + - [ ] ID-12 +Parameter [IncomeVault.initialize(address,IERC20,ICMTATSnapshot,IRuleEngine,IAuthorizationEngine).ERC20TokenPayment_](src/IncomeVault.sol#L35) is not in mixedCase + +src/IncomeVault.sol#L35 + + + - [ ] ID-13 +Function [IncomeVault.__IncomeVault_init(address,IERC20,ICMTATSnapshot,IRuleEngine,IAuthorizationEngine)](src/IncomeVault.sol#L52-L79) is not in mixedCase + +src/IncomeVault.sol#L52-L79 + + + - [ ] ID-14 +Variable [IncomeVaultOpen.__gap](src/public/IncomeVaultOpen.sol#L73) is not in mixedCase + +src/public/IncomeVaultOpen.sol#L73 + + + - [ ] ID-15 +Parameter [IncomeVault.__IncomeVault_init(address,IERC20,ICMTATSnapshot,IRuleEngine,IAuthorizationEngine).ERC20TokenPayment_](src/IncomeVault.sol#L54) is not in mixedCase + +src/IncomeVault.sol#L54 + +## unused-import + +> Concerns the CMTAT lib, will be fixed in the CMTAT lib. + +Impact: Informational +Confidence: High + + - [ ] ID-16 +The following unused import(s) in lib/CMTAT/contracts/modules/security/AuthorizationModule.sol should be removed: + -import "../../../openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol"; (lib/CMTAT/contracts/modules/security/AuthorizationModule.sol#6) + + - [ ] ID-17 +The following unused import(s) in lib/CMTAT/contracts/modules/internal/ValidationModuleInternal.sol should be removed: + -import "../../interfaces/draft-IERC1404/draft-IERC1404Wrapper.sol"; (lib/CMTAT/contracts/modules/internal/ValidationModuleInternal.sol#7) + + - [ ] ID-18 +The following unused import(s) in lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol should be removed: + -import {IERC20Permit} from "../extensions/IERC20Permit.sol"; (lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#7) + + - [ ] ID-19 +The following unused import(s) in lib/CMTAT/contracts/modules/wrapper/core/PauseModule.sol should be removed: + -import "../../../../openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol"; (lib/CMTAT/contracts/modules/wrapper/core/PauseModule.sol#6) + + - [ ] ID-20 +The following unused import(s) in lib/CMTAT/contracts/modules/wrapper/extensions/MetaTxModule.sol should be removed: + -import "../../../../openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol"; (lib/CMTAT/contracts/modules/wrapper/extensions/MetaTxModule.sol#6) + + - [ ] ID-21 + The following unused import(s) in lib/CMTAT/contracts/modules/wrapper/controllers/ValidationModule.sol should be removed: + -import "../../../../openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol"; (lib/CMTAT/contracts/modules/wrapper/controllers/ValidationModule.sol#5) + +## unused-state + +> Keep in case of inheritance + +Impact: Informational +Confidence: High + - [ ] ID-22 +[IncomeVault.__gap](src/IncomeVault.sol#L112) is never used in [IncomeVault](src/IncomeVault.sol#L13-L113) + +src/IncomeVault.sol#L112 + diff --git a/doc/schema/Debt b/doc/schema/Debt deleted file mode 100644 index f28029c..0000000 --- a/doc/schema/Debt +++ /dev/null @@ -1 +0,0 @@ -3Vpdk6I4FP01Vu0+aAEhKo9+TU9v9Yxu6/bOPHWlJWq2gFgQW51fPwECkgQ/WtF1pl+aXEJCzj05995gDfT8zUOIlosv1MVezTLcTQ30a5YFmu02/xdbtqkFZoZ5SNzUZO4MY/IDC6MhrCvi4kjqyCj1GFnKxikNAjxlkg2FIV3L3WbUk2ddojnWDOMp8nTrv8Rli9RqAdDc3fiMyXyRTd2EdnrHR1lvsZRogVy6LpjAoAZ6IaUsvfI3PezF6GXApM992nM3f7MQB+yUB1z7v2cSwOHL8Mtw4b7D0fjvUd1KR3lH3kqsuI+XNCJMvDPbZkjgwO3EgPLW1ENRRKY10F0w3+MGk1+GdBW4OJ7L4C3+TuH2G2/UjYZhgMzyPb7dMNp2Zuhvig/0t6IVMRSybLqABjjrMcIh8THDoeg4owETrDEhb+ugCJwiugqn+AASpiPohcI5ZocgA2lH7ErUEaA/YMrfL9zyDmITcASg5QhWrHckgoIXiwJ9MluIPcTIu0xCJLg8z6fIZx1RwtebT+mIYcSmyymYjZCCIR4qUkYZxwbKQIYyUArWgYGyjnQ2i7DUh18U0NqZEtZ+gMFAY/AfjFOkZvVi2UnJ3PE5OdmfOqe5C5/QG9csicrII/Mg5jknUcy07jsOGeGi0BE3fOK68RjdEEfkB3pLxovpuIyXnSwZdmuwfxJBD+5LPjHelCmdmFTSkjL2GQ2raUHJi6ASjkH5ges5uKU5eJL4V/GlrD/rBWF4vETJll/z2CR7+GTZ0BywF2jHVvZK1i5sedMq2fNNYz/0EpYfBa6tAZfug7uDzswxuBvsHA27SwPihrBv4k58/b1g3wXBuBFjYLUaULTViJdHVkMOqvBISD3Zb0cDYOvE+HebWNe2FO40Y+zOCndtNW6q9NoT7qpSuizpvTLrcmrpvLsJwY6mYq0Tidi+KyKahqpizplpl2mae/TwVkTMJLxIxK6UTOnEvNtcKt9WFSRTAF7GleunS6Ze0l0qIlIt5yiqAM0junBZ3VZdOXYbFWgpufa5GqCWXgAqQe3aEmBrNOKEHTPEVlHPQ8QvVFlRYv2Vyqt8k1wsCXxPAKW+ql9YYN1AI/SaqiqNiCXCkiWiDY6lDvciEbnMG067JUdgvSC53fmN3TpXRCyHL6Xwpw7bMJR326MqnAloW+gmNuLeBVit8hXseJyOeHvm60XxNFazPnknLqd8omq/lJJlW7kKJTNNACS/mZXQuQ7a8iNX9K9euPcH3clrfzAajh8nr8/Dp4Hm3WiBlvHlyvc6U0aLjkycPorTXkJjh75Rxqhf4mlGlXMSumIeCXAv/zDxIU07/fBELTpMXanALU9OLL2I/Uw9l2+P3wp2qAgc/L9h18/BH4MpH/wFrbz7O/Kz1aOWMtqWAQjMqyFYfVIkTl+Suik7dElzIqd1+Aym5OCvuvOVOyuZ8q9K2TdieGa6o3AKmtdJbkz1fcU8H3yt0lyoqjho6XkOC1EQzUp0+H6TG6uy5IbnNnZTSekvI2+lucwas6+2/wCf3E+zIPr6F3Eeh3X9EC5JZV46/zxNXoejwXNnMnz+DTMa5UMaMK4WWnlz9/uL1G27n7GAwU8=5ZpRb9sgEMc/TR472cY4zmObdt2kTZuUaWv3MlGbOmjEZJgsyT79IMZxALvJssZxt5cWDoLL/35w50sHYDxb3XI0n75nKaaDwEtXA3A9CAIfwED+UpZ1aYFxXBoyTlI9qTZMyC+sjZ62LkiKC2OiYIwKMjeNCctznAjDhjhnS3PaI6PmU+cow45hkiDqWr+QVExLawBAVA+8wSSbVo+OYFiOzFA1W2+lmKKULXdM4GYAxpwxUbZmqzGmSr1KmPJzr1tGt38Zx7k45ANMoJs5ufhx922yvqHvfk3JBbjQq/xEdKF3/DZP2Ax/Rgsq5MADoihPsN6BWFe6yM3MVTNZU5KnmIMBuFpOicCTOUrUwFLyIG1TMaOy58vmA1vImem7h60BJd8zrqwfFkIug7W9KCnwoWw/slxoKvxQ9t1NVzvAXODVjkmLcIvlbgRfyyl6NPS0QzSSAMCyv6wdPNJTpruuBdqINFPZduladdnQwv+BEwLHCY7eOE8vFc5KcoqKgiSmuLwUV/a8BtXwiog7PVO17/U81b5e7XbWlfCq9xFzIneIeTUjl7u92+1sFnoFq2691qa31r1WpxVswRO8H0+BeIbFE/NAOQ+nxml2EdhxMWxwcWXjmCJBfpp3QJPb9RM+MpKLmrDIIkwazCXKfetP7R5ZeyFoLeRbC5XCOAttKNxu+3gwgQPmJwmEtNwNggjN1AHPH4r51sU7wJpA7rkaTnDKo9CSDrqn3A8aGLB99WynPHTEvJxJkUQpZ8/kGwWWfKErXwAa5ItPJR9sY/G+d+LZ7IGhd2b2ojbxvvZevDA+t3jDs4TnV7A1QHsvLj7DXsVnaMXn0M7sDo3P0IrPwL79Thyf43OQ6bdyefECM8eoX2T6FpnDY8mMrIWCbskctSc7/YvXdrIDIjfkdJvsVBg06de/kG3rFzbkOx3r59Y1BoH7kiL3J0x1CsHZdzxmlKmrK2eqKHH1SCi1TIiSLFd3qlRN3XJXSi2SIHqpB2YkTdVjGt3ReukGw+fxiO950EQ6cPP3qMEjJ6tx+E1Fjv/ZI2HoHpJuPeK+3W9+emYF8KqlAvjPeiqOzdssAq6jgN+pp9zSgeOOo/O8jWzHvIG0Sr03N9Pg7c3N9LZ7kpsNR1aWAI/MzWK7POB1m5v5bimlBzh1/4oAD8Rw1GsMt3HkrzEcdYyhW5T6LzGMDsSwCjk94TAGJj7waA6teBt2zeEp63sGhzV7T9dRTA5r9O4N8p69YuIdCmK/vm3zbRDtt8pDQfQtEKGd0J0aRLecN8EZRxkSEq3AS/GcFUQ4cP6zmfjQqob5TZl4U13hiK8CZLf+V4zSo/V/tICb3w==5Vtdc6M2FP01ntl2JjuA+PJj7CS72+l2OpN0tn1UjGyYxcgVcmLvr69kBEZXJMY22Ow0DzEIIcPVueeeeyWP0HS5+cTwKv5KI5KOHCvajNDdyHFs5DniQ7ZsixYvDIuGBUsi1Wnf8Jj8IKrRUq3rJCK51pFTmvJkpTfOaJaRGdfaMGP0Ve82p6n+rSu8IEbD4wynZuu3JOJx0eog5O8vfCbJIi6/2vfc4soSl73Vq+QxjuhrrQndj9CUUcqLo+VmSlJpvdIwxX0Pb1ytnoyRjLe5YebZX3+73Xyer7/c/cW9dP79X3qjRnnB6Vq98ZdcmjPFyVJ80hXJSLQzHJOmjxN5lSdLIj5G6EG9Gd+W9mIxXT6vhdEnr3HCyeMKz+SVV4EO0RbzZSrObHE4pxlX02274tx8m/LRCONkU2tSb/eJ0CXhbCu6qKuOF3z0iptKtAXK9q+1ubNUn7g+ba7qiBVeFtXoe4uKA2XUIwzsGAY2bEay6FZCVZwJu+d5MtNNxeg6i4j8EmHyCdkk/G95LN61OPtH9ZPHd5tat7utOmlt7Jyu2YwcxgvHbEH4O/2UE5BIcy9z6moT41nmvJRtjKSYJy+6UzbNlfqGP2ki3qxChmfpuEBjMN3Fe6u76j4EBkK+pQ0UIDBQYRhjoB10qtc+HU3IQNP069PtkwGpA86H81VBlvNkI4HVhzeGwFRhaPqi7aMGXywbO/dFt5nsouRFBJlM0hxOGcGRfIupZMAd9Q2P5gAG3QaScxqcyXb6IjmvL5KzaxS3J7xmkhMWZduCGS0LlQ3FncgKyob9zbsz7e4/CROTzgnrnDfHLXkTDYo3XV/nzTFEUFve9Gx9oDBox5sCMnhb67aSHfK3H9gPdN8YI00TiYNixE5J2TF5RbzLY4ZXeUz5l2xOJ5jP4g+FbJpKuAhvIOwX00nEtP+On4WG1uk6TRaZdBwBO4nNiSSLRIjUW3VhmUSRHGPCSJ78wM+78SSAlb3E4N5k5N21gvS7Hg5ZqlLe6ktHdW3bxF7WRzvwbG2S7POwWnah83lOeom6fl/0Zo0ur+CGxTAe0h3WtqASb00xljES0BE9a7PAQAmn30n2RDlOH9erVbqtO79wapyJifp5KKD0gg4owPMsp0sKKMPVWBv0BgzQH0GExtQXk3xXKUs/FaadPIsk2l/Io10vCAXr15oa1XBjyUeWpQ8IKKhLNZq5gjYFcd5GjilOXdTAJzAj60ybjg+Tdx7jlTzMOZEmWtVUoGyqqcKDJpX5VGnTfkyMQpBXjRvyqib5H/Zl4bJW12F8rKS8LuO9QyL+qrURJQMPavzxoCIwTCfHUJq3ro2AAGwkCx1pfFiDuYjGt81C6QVphAnK5wmVcf7GDo9C7RHEEkABZV2dWZw3Aus+ZIqAipfSfEVYdcpnHkixBkF9a/umUauHvky1xjariOfydSPvWu/ybq1c42ksf4DjO6xlty1mlzAcCGO7zluYOpayXejytt2OszujVbN00gsUj5MAp0LzDCg6LaEYDguJY4CfSgMcjUQXjuRdGIlmDTvHc/LEcJbPCXtgdPmhiD1FFl+maWYR7+o5GJyUapGqHnLCppADZ6+7kNMiCxPDJKuc6PbZayrM+KNQQWQnj9J0SlPKdjcKzMk/2ZkzkSDXrszn6kofkd0Fa80Naqkp0YUrht2tNLdJwwRvPKpTynhMFzTD6f2+tW57WGJ6ppzT5Y71KkaWGwSKFmXe8N15eCvNu5oEaLsuY7fk3TMJFeY2HqyG9syCjpnm7LaCfEt4HDH8ilMDUsJdOPBZbfozmkGfVU3ta5pNJKpDaQcJ9VCQV72OeBXILrctrfbn8Q0xC9Qdf7r8yL1+fuSYlXyTSI8SpYbAPEGkXl6Uhi25sYThQESpsRDkQinZeknJgyNBgdQ3HZsLC1uS/y/BWILs8A6KYFBodCGGfBgOTl7g9GDdre/NZ8fV+ffS8CFJSyjuhaJIeY8BadeAO4yjE4o+VSHmXNDAqBieChrXAQty4aVB47QAzXuZn0TMkPI+Fy5xjq+d+KHO67lvhYiTtuPZQwsQJzh2jwHC96GvwxpsW18PbDBSAOvCXe2yg/TkwZ8eHLohOPaGseUCP+lhkQ+Z1eg/qOlKg92hUxHB2Vt0bqyP9rgsSpeb5s+De/+7cJCZt82KXdxVPuxNVA1XmFnu7Jb/ORO9jQ0698tEPjV5IcpXhlTiRS78pcteVmpypOGnLpATugtDZrLSUxg6JlM5PaCUcDpcGxxY/guXUgJYKWmdccCVBL/ltu3DHi1O9798K7rvf0CI7v8D3VpRc9o4EP41zOQeYGzLNuYRSNreTDvJJJm2uZcbYQvQxLYYWSRwv/4kLBlLMsGhQEvyEmklr63db7/dVdIB42z1mcLF/BtJUNrxnGTVAdcdj/8EgP8SknUpGUROKZhRnJQidyt4wP8hKVTbljhBhbaREZIyvNCFMclzFDNNBiklr/q2KUn1ty7gDFmChximtvQHTthcnguAcLvwBeHZXL06DPxyJYNqtzxKMYcJea2JwE0HjCkhrBxlqzFKhfWUYcrnPu1Yrb6Mopy1eWDxdB/G7L6f/YNup930+d8fz6Ou1PIC06U88fjb4/BRfjFbKzvwj1+IYbxOcZ4gCjpg9DrHDD0sYCwWXrn/uWzOspTPXD6ckCXfmXydVAIYP8+okN4uGVeDpLwove4GfDwlOZMocEM+tw+pvhhRhlY1kTz0Z0QyxOiab1GrrgLTWgnk/LXm0YGUzWvOBEoIJYpmlfKtnflAmvodZvcss/+dx1z3d7hM2UcyfuAYtnds24Mm2/vOqWwPLNtbBkd5MhTkIWyewqLAsW5dWlqXz5wGs6EVZj/lTjF+kvvE+HpVnwijuE6/F0jBHaKYHxJRtSnnB/5ZnzzVJ1tlm1mpLVBzU9lObxZkSWO0H60M0hli+8kEJRqn2tio+T5ocL2SUZRChl90Jm6Cg3zDHcH8ZBX0+pEOvUqvUlGeWz5VJ05DkeuGhiYQ6ZpKy1iaNvCszn04Yn0LsV5P+C2Hi2JO2AimMI/5/ivGHd7xxmINCaL4y0Y2981XOOFZWgM0TPEsF2jn2BCAGYkIxzwNDuVChpNE6BhRxBkDTjb6BKoW4tAbMwSjTnDdikXeDEuTWqrcLl+qZc8mynF6buT3dYcdBVDGA2Q6LdBJ/B2ci6GcVgzlRXv5yW3LT97A652AoVRpuI+hvD+LoYzCxMp5bRnKpDrgG4pOTFChBVjQ25iHLWnOB5OSoS6IjVQIHoGNnMEg0LzTvyw2iiznPpJnJNw6590U98+ucnWZpcOYkboDN86+IwVmmAhHTghjJGvwMCNGQUvKqnVc9XlNvLaTQNoXrJ5ZsEYNBWsDJYSnKlcHlvldEVuc9XG2SfgNSf7XksMBRWd/N6lXuaaqcp+0mZ1vnB3Z5vD0EF1kegDunrKzbXqo0oFS5J03Pahz1DB8v0zRTT4TfegH6nYjTzd0GDR1u4GNmNPdNLj2DY8v+IMsEIUM3eaPFObFFNEr7QZi20OU4wS/4ITPT0c2WwbZRzfOfqrp+6FGNoNBdEa6ad0vOy35pqolPAdoAOuq1uA8jBT0DXybdWZbRgp9vSQKzfS5g5E40uC6tk3Wezs/ODIK4yDS7kj5oNRoPH36osq1bwADEZZXjC5lCz+FaXG65K51bO4b0dZ1W6T2KNCS+85Y83tgVy95eLipMLqw9B45RwqmyA11kLdM7+8NJtcx6oggeCOajhYp9u3Xzf24K85YZaWPVEN4BmVV1Pgbawi7vz82LTU2Ce67c/ZRy4kzFAD+H8VIPjDi28zKbRnJN9Oue+aGo28BNhTZlcla9xMlWbt6d5hx3LJLukevgvUIV1cecAaaI9Ut/aFA+5XCik+3fywvt2//5wDc/A8=dZHBDoIwDIafZndchXBH1IsnDp4XVtmSQcmYAX16IRviop7Wff/fdW0ZFO10sqJXF5JoGE/kxODAON9ByudjIQ9P0jz3oLFaBtMGKv3EAJNA71riEBkdkXG6j2FNXYe1i5iwlsbYdiMTV+1Fg1+gqoX5plctnfKUA2SbcEbdqLV0lu690orVHVoZlJA0fiAoGRSWyPmonQo0y/TWwfi84x/1/TOLnfuRMAfb2/MlWhGULw== \ No newline at end of file diff --git a/doc/schema/IncomeVault b/doc/schema/IncomeVault new file mode 100644 index 0000000..b686bc2 --- /dev/null +++ b/doc/schema/IncomeVault @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/doc/schema/drawio/Debt-claimWithdrawal.drawio.png b/doc/schema/drawio/Debt-claimWithdrawal.drawio.png deleted file mode 100644 index a59a779..0000000 Binary files a/doc/schema/drawio/Debt-claimWithdrawal.drawio.png and /dev/null differ diff --git a/doc/schema/drawio/IncomeVault-Global.drawio.png b/doc/schema/drawio/IncomeVault-Global.drawio.png new file mode 100644 index 0000000..740a12b Binary files /dev/null and b/doc/schema/drawio/IncomeVault-Global.drawio.png differ diff --git a/doc/schema/drawio/IncomeVault-RuleEngine.drawio.png b/doc/schema/drawio/IncomeVault-RuleEngine.drawio.png new file mode 100644 index 0000000..35b1f63 Binary files /dev/null and b/doc/schema/drawio/IncomeVault-RuleEngine.drawio.png differ diff --git a/doc/schema/drawio/IncomeVault-claimDividend.drawio.png b/doc/schema/drawio/IncomeVault-claimDividend.drawio.png new file mode 100644 index 0000000..e3e2436 Binary files /dev/null and b/doc/schema/drawio/IncomeVault-claimDividend.drawio.png differ diff --git a/doc/script/CMTATWithRuleEngineScript.s.sol b/doc/script/CMTATWithRuleEngineScript.s.sol deleted file mode 100644 index c06c63a..0000000 --- a/doc/script/CMTATWithRuleEngineScript.s.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -// Documentation : -// https://book.getfoundry.sh/tutorials/solidity-scripting -pragma solidity ^0.8.17; - -import "forge-std/Script.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; -import "src/RuleEngine.sol"; -import "src/rules/RuleWhitelist.sol"; -/** -@title Deploy a CMTAT, a RuleWhitelist and a RuleEngine -*/ -contract CMTATWithRuleEngineScript is Script { - function run() external { - // Get env variable - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address ADMIN = vm.addr(deployerPrivateKey); - address trustedForwarder = address(0x0); - vm.startBroadcast(deployerPrivateKey); - uint256 flag = 5; - uint48 initialDelay = 0; - uint8 decimals = 0; - // CMTAT - CMTAT_STANDALONE CMTAT_CONTRACT = new CMTAT_STANDALONE( - trustedForwarder, - ADMIN, - initialDelay, - "CMTA Token", - "CMTAT", - decimals, - "CMTAT_ISIN", - "https://cmta.ch", - IRuleEngine(address(0)), - "CMTAT_info", - flag - ); - console.log("CMTAT CMTAT_CONTRACT : ", address(CMTAT_CONTRACT)); - // whitelist - RuleWhitelist ruleWhitelist = new RuleWhitelist( - ADMIN, - trustedForwarder - ); - console.log("whitelist: ", address(ruleWhitelist)); - // ruleEngine - RuleEngine RULE_ENGINE = new RuleEngine(ADMIN, trustedForwarder); - console.log("RuleEngine : ", address(RULE_ENGINE)); - RULE_ENGINE.addRule(ruleWhitelist); - CMTAT_CONTRACT.setRuleEngine(RULE_ENGINE); - - vm.stopBroadcast(); - } -} diff --git a/doc/script/RuleEngineScript.s.sol b/doc/script/RuleEngineScript.s.sol deleted file mode 100644 index 71f6ff9..0000000 --- a/doc/script/RuleEngineScript.s.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -// Documentation : -// https://book.getfoundry.sh/tutorials/solidity-scripting -pragma solidity ^0.8.17; - -import "forge-std/Script.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; -import "src/RuleEngine.sol"; -import "src/rules/RuleWhitelist.sol"; -import "CMTAT/modules/wrapper/controllers/ValidationModule.sol"; - -/** -@title Deploy a RuleWhitelist and a RuleEngine. The CMTAT is considred already deployed -*/ -contract RuleEngineScript is Script { - function run() external { - // Get env variable - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address ADMIN = vm.addr(deployerPrivateKey); - address CMTAT_Address = vm.envAddress("CMTAT_ADDRESS"); - vm.startBroadcast(deployerPrivateKey); - //whitelist - RuleWhitelist ruleWhitelist = new RuleWhitelist(ADMIN, address(0)); - console.log("whitelist: ", address(ruleWhitelist)); - // ruleEngine - RuleEngine RULE_ENGINE = new RuleEngine(ADMIN, address(0)); - console.log("RuleEngine: ", address(RULE_ENGINE)); - RULE_ENGINE.addRule(ruleWhitelist); - // Configure the new ruleEngine for CMTAT - (bool success, ) = address(CMTAT_Address).call( - abi.encodeCall(ValidationModule.setRuleEngine, RULE_ENGINE) - ); - require(success); - vm.stopBroadcast(); - } -} diff --git a/doc/script/script_surya_graph.sh b/doc/script/script_surya_graph.sh new file mode 100755 index 0000000..31a1cef --- /dev/null +++ b/doc/script/script_surya_graph.sh @@ -0,0 +1,18 @@ +#/bin/bash +cd '../../' +DIR=$(pwd) +DIR_OUT=${DIR}/docOut/surya_graph +if ! [ -d "$DIR_OUT" ]; then + mkdir -p ./docOut/surya_graph +fi +cd './src' +DIR=$(pwd) +for i in $(find $DIR -type f); +do + #echo $i + filename=${i##*/} + ext=${i##*.} + if [[ $ext == 'sol' ]]; then + npx surya graph $i | dot -Tpng > ../docOut/surya_graph/surya_graph_$filename.png; + fi +done; \ No newline at end of file diff --git a/doc/script/script_surya_inheritance.sh b/doc/script/script_surya_inheritance.sh new file mode 100755 index 0000000..e4b8ca7 --- /dev/null +++ b/doc/script/script_surya_inheritance.sh @@ -0,0 +1,17 @@ +#/bin/bash +cd '../../' +DIR=$(pwd) +DIR_OUT=${DIR}/docOut/inheritance +if ! [ -d "$DIR_OUT" ]; then + mkdir -p ./docOut/surya_inheritance +fi +cd './src' +DIR=$(pwd) +for i in $(find $dir -type f); +do + filename=${i##*/} + ext=${i##*.} + if [[ $ext == 'sol' ]]; then + npx surya inheritance $i | dot -Tpng > ../docOut/surya_inheritance/surya_inheritance_$filename.png; + fi +done; \ No newline at end of file diff --git a/doc/script/script_surya_report.sh b/doc/script/script_surya_report.sh new file mode 100755 index 0000000..84769c9 --- /dev/null +++ b/doc/script/script_surya_report.sh @@ -0,0 +1,17 @@ +#/bin/bash +cd '../../' +DIR=$(pwd) +DIR_OUT=${DIR}/docOut/surya_report +if ! [ -d "$DIR_OUT" ]; then + mkdir ./docOut/surya_report +fi +cd './src' +DIR=$(pwd) +for i in $(find $dir -type f); +do + filename=${i##*/} + ext=${i##*.} + if [[ $ext == 'sol' ]]; then + npx surya mdreport ../docOut/surya_report/surya_report_$filename.md $i; + fi +done; \ No newline at end of file diff --git a/doc/specification.md b/doc/specification.md new file mode 100644 index 0000000..4a85b0e --- /dev/null +++ b/doc/specification.md @@ -0,0 +1,171 @@ +# Specification + +[TOC] + +## Introduction + + \0. On the CMTAT, the admin registers the dividend `time` to perform a snapshot and store the holderโ€™s balance at this specified time. + +1. An authorized address perform a deposit in the `IncomeVault` for a specific `time` +2. An authorized address open the claim for this specific `time` +3. Holder claims his dividends by calling the function `claimDividend` + +![IncomeVault-Global.drawio](../doc/schema/drawio/IncomeVault-Global.drawio.png) + +## Segregated Deposit + +Each deposit is segregated in its time value. A `time` is the dividends distribution date (Unix Timestamp) to the token holders. + +![Debt-Segragated Deposit.drawio](../doc/schema/drawio/Debt-Segragated Deposit.drawio.png) + +### ValidationModule + +A claim is considered as a transfer from the contract to the sender (token holder). +This transfer can be restricted with the ValidationModule + +This module is imported from the CMTAT which allows to : + +- Freeze/unfreeze an address +- Put the contract in the pause state +- Call the ruleEngine for additional rules + +If the ValidationModule refuses the transfer, the function is reverted. + + + +### RuleEngine + +As for the CMTAT, there is the possibility to configure a ruleEngine with rules to perform transfer rectriction/verification. As relevant rules, we have: + +- Whitelist +- Blacklist +- Sanctionlist +- ConditionalTransfer + + + + + +## Operation + +### Claim dividends + +The distribution of dividends is not automatic. A token holder has to claim his dividends by calling the function `claimDividend`, similar to the Lido protocol. When he claims his dividends, he precises the defined `time`. + +Therefore, a token holder has to know the different `time` when a deposit has been performed. + + + +A function `claimDividend` in batch is also available to claim dividends for several different time. + +### Schema + +This schema describes the different smart contracts called when a token holder claims his dividends. + +![IncomeVault-RuleEngine.drawio](../doc/schema/drawio/IncomeVault-RuleEngine.drawio.png) + +#### Formula + +The computation of dividends is performing according to the following formula + +``` +senderDividend = (senderCMTATBalance * dividendTotalSupply) / TokenTotalSupply; +``` + +The sender dividend will be rounded to the inferior integer. Thus, the issuer should put a โ€œlimitโ€ date to claim his dividend in order to withdraw the staying funds (due to rounding) from the smart contract. + +Example with USDC (6 decimal) and a CMTAT (0 decimal) + +tokenSupply CMTAT = 12โ€™351 + +The sender has 4221 tokens. + +21โ€™555.50 $ in USDC are deposited corresponding to a value of 21555500000 tokens since USDC has 6 decimals. + + We have: + +senderDividend = 4221 * 21555500000 / 12351 = 7366671969.880981297 = 7366671969 which correspond to **7366.671969**$ + +#### Schema + +Schema without the `ValidationModule` (see next paragraph) + +![IncomeVault-claimDividend.drawio](../doc/schema/drawio/IncomeVault-claimDividend.drawio.png) + + + +## Withdraw funds + +An authorized user can call the following functions to withdraw funds from the vault: + +``` +1. withdraw(uint256 time, uint256 amount, address withdrawAddress) public onlyRole(DEBT_VAULT_WITHDRAW_ROLE) +``` + +and + +``` +2. withdrawAll(uint256 amount, address withdrawAddress) public onlyRole(DEBT_VAULT_WITHDRAW_ROLE) +``` + +With the function 1, the funds are withdrawn only from the specific time. + +The second function allows to withdraw funds without a specific time, which can lead to an โ€œunstableโ€ state with the different pool of dividend. To be used only in case of emergency or if the vault is closed. + + + +## Distribute dividend + +An authorized user can also decide to distribute the dividend for a given time and a given list of addresses. + +In this situation, the token holder can not decide if he wants to receive his dividends (he is forced to accept) and can not choose the address where he wants to receive his dividends. + + + +Since the function is restricted by access control, it is not possible to use Chainlink Automation to perform an automatic call and distribute the dividends. +Moreover, the list of token holders has to be provided by the transactionโ€™s sender. + +## Improvement + +- An automatic distribution of dividend could be performed through [Chainlink Automation](https://docs.chain.link/chainlink-automation) but it requires several changes to allow that. +- Only ERC20 tokens are supported. We could extends this to support direct native (e.g ether) too. + +## Deployment + +The contract has to be deployed with a transparent proxy and the contract is compatible with the standard [ERC-2771](https://eips.ethereum.org/EIPS/eip-2771) for meta transactions. + + + +## Threat model & FAQ + +### Claim dividend several times + +> What if a holder tries to claim the same dividend several times? + +When a holder claims his dividends for a specific time, a boolean is set to true to indicate the claiming dividend. + +``` + claimedDividend[tokenHolder][time] = true; +``` + +This boolean is set inside the internal function `_transferDividend` + +Moreover, the functions to claim are protected against reentrancy attacks with the modifier `nonReentrant` from OpenZeppelin. + +### New dividend after claim + +> What happens if the authorized address deposit dividend after that a token holder has already claimed his dividends ? + +A token holder can not claim his dividends if the claim status is not opened. Moreover, you can not deposit new dividends if the status is on open (=true). + +The function `setStatusClaim` allows to open (true) or close(false) the claims for a specific time. + +If you close the claim (claim status = false) and deposit new dividends, the previous token holders will be penalized since the dividends total supply for this specific time has improved for all token holders which have not already claimed their dividends, + +In summary, when you have opened the claim, you should not deposit new dividends in the vault for a specific time. + +### Transfer fails + +> What happens if the token transfer fails? + +In this case, the whole transaction is reverted, and the smart contract still considers that dividends have not been claimed by the token holder (sender). diff --git a/doc/surya/graph/surya_graph_IncomeVault.png b/doc/surya/graph/surya_graph_IncomeVault.png deleted file mode 100644 index e0bd643..0000000 Binary files a/doc/surya/graph/surya_graph_IncomeVault.png and /dev/null differ diff --git a/doc/surya/graph/surya_graph_IncomeVaultOpen.png b/doc/surya/graph/surya_graph_IncomeVaultOpen.png deleted file mode 100644 index 5addb41..0000000 Binary files a/doc/surya/graph/surya_graph_IncomeVaultOpen.png and /dev/null differ diff --git a/doc/surya/graph/surya_graph_IncomeVaultRestricted.png b/doc/surya/graph/surya_graph_IncomeVaultRestricted.png deleted file mode 100644 index c141d54..0000000 Binary files a/doc/surya/graph/surya_graph_IncomeVaultRestricted.png and /dev/null differ diff --git a/doc/surya/surya_graph/surya_graph_IncomeVault.sol.png b/doc/surya/surya_graph/surya_graph_IncomeVault.sol.png new file mode 100644 index 0000000..7d58b08 Binary files /dev/null and b/doc/surya/surya_graph/surya_graph_IncomeVault.sol.png differ diff --git a/doc/surya/surya_graph/surya_graph_IncomeVaultInternal.sol.png b/doc/surya/surya_graph/surya_graph_IncomeVaultInternal.sol.png new file mode 100644 index 0000000..99ca209 Binary files /dev/null and b/doc/surya/surya_graph/surya_graph_IncomeVaultInternal.sol.png differ diff --git a/doc/surya/surya_graph/surya_graph_IncomeVaultInvariantStorage.sol.png b/doc/surya/surya_graph/surya_graph_IncomeVaultInvariantStorage.sol.png new file mode 100644 index 0000000..e166f8f Binary files /dev/null and b/doc/surya/surya_graph/surya_graph_IncomeVaultInvariantStorage.sol.png differ diff --git a/doc/surya/surya_graph/surya_graph_IncomeVaultOpen.sol.png b/doc/surya/surya_graph/surya_graph_IncomeVaultOpen.sol.png new file mode 100644 index 0000000..97d8638 Binary files /dev/null and b/doc/surya/surya_graph/surya_graph_IncomeVaultOpen.sol.png differ diff --git a/doc/surya/surya_graph/surya_graph_IncomeVaultRestricted.sol.png b/doc/surya/surya_graph/surya_graph_IncomeVaultRestricted.sol.png new file mode 100644 index 0000000..e0512d8 Binary files /dev/null and b/doc/surya/surya_graph/surya_graph_IncomeVaultRestricted.sol.png differ diff --git a/doc/surya/surya_inheritance/surya_inheritance_IncomeVault.sol.png b/doc/surya/surya_inheritance/surya_inheritance_IncomeVault.sol.png new file mode 100644 index 0000000..91f9858 Binary files /dev/null and b/doc/surya/surya_inheritance/surya_inheritance_IncomeVault.sol.png differ diff --git a/doc/surya/surya_inheritance/surya_inheritance_IncomeVaultInternal.sol.png b/doc/surya/surya_inheritance/surya_inheritance_IncomeVaultInternal.sol.png new file mode 100644 index 0000000..2247a3b Binary files /dev/null and b/doc/surya/surya_inheritance/surya_inheritance_IncomeVaultInternal.sol.png differ diff --git a/doc/surya/surya_inheritance/surya_inheritance_IncomeVaultInvariantStorage.sol.png b/doc/surya/surya_inheritance/surya_inheritance_IncomeVaultInvariantStorage.sol.png new file mode 100644 index 0000000..2f3a67e Binary files /dev/null and b/doc/surya/surya_inheritance/surya_inheritance_IncomeVaultInvariantStorage.sol.png differ diff --git a/doc/surya/surya_inheritance/surya_inheritance_IncomeVaultOpen.sol.png b/doc/surya/surya_inheritance/surya_inheritance_IncomeVaultOpen.sol.png new file mode 100644 index 0000000..945efb9 Binary files /dev/null and b/doc/surya/surya_inheritance/surya_inheritance_IncomeVaultOpen.sol.png differ diff --git a/doc/surya/surya_inheritance/surya_inheritance_IncomeVaultRestricted.sol.png b/doc/surya/surya_inheritance/surya_inheritance_IncomeVaultRestricted.sol.png new file mode 100644 index 0000000..2ae4a80 Binary files /dev/null and b/doc/surya/surya_inheritance/surya_inheritance_IncomeVaultRestricted.sol.png differ diff --git a/doc/surya/report/surya_report_IncomeVault.md b/doc/surya/surya_report/surya_report_IncomeVault.sol.md similarity index 94% rename from doc/surya/report/surya_report_IncomeVault.md rename to doc/surya/surya_report/surya_report_IncomeVault.sol.md index ae004fd..f1f45b3 100644 --- a/doc/surya/report/surya_report_IncomeVault.md +++ b/doc/surya/surya_report/surya_report_IncomeVault.sol.md @@ -5,7 +5,7 @@ | File Name | SHA-1 Hash | |-------------|--------------| -| src/IncomeVault.sol | 3a400ede203c2bc4ce3dc8aa0436867f6e445b6a | +| ./IncomeVault.sol | d13b93632cb6877222166951c4fd742a13602a4e | ### Contracts Description Table diff --git a/doc/surya/surya_report/surya_report_IncomeVaultInternal.sol.md b/doc/surya/surya_report/surya_report_IncomeVaultInternal.sol.md new file mode 100644 index 0000000..9f516bf --- /dev/null +++ b/doc/surya/surya_report/surya_report_IncomeVaultInternal.sol.md @@ -0,0 +1,29 @@ +## Sลซrya's Description Report + +### Files Description Table + + +| File Name | SHA-1 Hash | +|-------------|--------------| +| ./lib/IncomeVaultInternal.sol | c95adc49c29ca32ae1c38f9644a5ed2250d524fe | + + +### Contracts Description Table + + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| โ”” | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **IncomeVaultInternal** | Implementation | IncomeVaultInvariantStorage ||| +| โ”” | _computeDividendBatch | Internal ๐Ÿ”’ | | | +| โ”” | _computeDividend | Internal ๐Ÿ”’ | | | +| โ”” | _transferDividend | Internal ๐Ÿ”’ | ๐Ÿ›‘ | | + + +### Legend + +| Symbol | Meaning | +|:--------:|-----------| +| ๐Ÿ›‘ | Function can modify state | +| ๐Ÿ’ต | Function is payable | diff --git a/doc/surya/surya_report/surya_report_IncomeVaultInvariantStorage.sol.md b/doc/surya/surya_report/surya_report_IncomeVaultInvariantStorage.sol.md new file mode 100644 index 0000000..ce6b53b --- /dev/null +++ b/doc/surya/surya_report/surya_report_IncomeVaultInvariantStorage.sol.md @@ -0,0 +1,26 @@ +## Sลซrya's Description Report + +### Files Description Table + + +| File Name | SHA-1 Hash | +|-------------|--------------| +| ./lib/IncomeVaultInvariantStorage.sol | f15b8cd61cb0d00d67ebe7129cca3b00daab8cce | + + +### Contracts Description Table + + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| โ”” | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **IncomeVaultInvariantStorage** | Implementation | ||| + + +### Legend + +| Symbol | Meaning | +|:--------:|-----------| +| ๐Ÿ›‘ | Function can modify state | +| ๐Ÿ’ต | Function is payable | diff --git a/doc/surya/surya_report/surya_report_IncomeVaultOpen.sol.md b/doc/surya/surya_report/surya_report_IncomeVaultOpen.sol.md new file mode 100644 index 0000000..9e5adb8 --- /dev/null +++ b/doc/surya/surya_report/surya_report_IncomeVaultOpen.sol.md @@ -0,0 +1,28 @@ +## Sลซrya's Description Report + +### Files Description Table + + +| File Name | SHA-1 Hash | +|-------------|--------------| +| ./public/IncomeVaultOpen.sol | 724117dc19eecbc7994690a65fa955c381bc43f2 | + + +### Contracts Description Table + + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| โ”” | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **IncomeVaultOpen** | Implementation | ReentrancyGuardUpgradeable, ValidationModule, IncomeVaultInternal ||| +| โ”” | claimDividend | Public โ—๏ธ | ๐Ÿ›‘ | nonReentrant | +| โ”” | claimDividendBatch | Public โ—๏ธ | ๐Ÿ›‘ | nonReentrant | + + +### Legend + +| Symbol | Meaning | +|:--------:|-----------| +| ๐Ÿ›‘ | Function can modify state | +| ๐Ÿ’ต | Function is payable | diff --git a/doc/surya/surya_report/surya_report_IncomeVaultRestricted.sol.md b/doc/surya/surya_report/surya_report_IncomeVaultRestricted.sol.md new file mode 100644 index 0000000..2336c76 --- /dev/null +++ b/doc/surya/surya_report/surya_report_IncomeVaultRestricted.sol.md @@ -0,0 +1,31 @@ +## Sลซrya's Description Report + +### Files Description Table + + +| File Name | SHA-1 Hash | +|-------------|--------------| +| ./public/IncomeVaultRestricted.sol | 045808f76caaa6faec4a42805df928607c6845bd | + + +### Contracts Description Table + + +| Contract | Type | Bases | | | +|:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| +| โ”” | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | +|||||| +| **IncomeVaultRestricted** | Implementation | ValidationModule, IncomeVaultInternal ||| +| โ”” | deposit | Public โ—๏ธ | ๐Ÿ›‘ | onlyRole | +| โ”” | withdraw | Public โ—๏ธ | ๐Ÿ›‘ | onlyRole | +| โ”” | withdrawAll | Public โ—๏ธ | ๐Ÿ›‘ | onlyRole | +| โ”” | distributeDividend | Public โ—๏ธ | ๐Ÿ›‘ | onlyRole | +| โ”” | setStatusClaim | Public โ—๏ธ | ๐Ÿ›‘ | onlyRole | + + +### Legend + +| Symbol | Meaning | +|:--------:|-----------| +| ๐Ÿ›‘ | Function can modify state | +| ๐Ÿ’ต | Function is payable | diff --git a/doc/general.md b/doc/technical.md similarity index 82% rename from doc/general.md rename to doc/technical.md index d7666bc..afe364d 100644 --- a/doc/general.md +++ b/doc/technical.md @@ -35,12 +35,12 @@ Please see the OpenGSN [documentation](https://docs.opengsn.org/contracts/#recei ### IncomeVault -![surya_graph_IncomeVault](../doc/surya/graph/surya_graph_IncomeVault.png) +![surya_graph_IncomeVault](../doc/surya/surya_graph/surya_graph_IncomeVault.sol.png) ### IncomeVaultOpen -![surya_graph_IncomeVaultOpen](../doc/surya/graph/surya_graph_IncomeVaultOpen.png) +![surya_graph_IncomeVaultOpen](../doc/surya/surya_graph/surya_graph_IncomeVaultOpen.sol.png) ### IncomeVaultRestricted -![surya_graph_IncomeVaultRestricted](../doc/surya/graph/surya_graph_IncomeVaultRestricted.png) +![surya_graph_IncomeVaultRestricted](../doc/surya/surya_graph/surya_graph_IncomeVaultRestricted.sol.png) diff --git a/foundry.toml b/foundry.toml index c331288..039b692 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,5 +1,5 @@ [profile.default] -solc = "0.8.20" +solc = "0.8.22" src = 'src' out = 'out' libs = ['lib'] diff --git a/hardhat.config.js b/hardhat.config.js index b2dd4be..132197f 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -2,7 +2,7 @@ require("@nomicfoundation/hardhat-foundry"); require('solidity-docgen'); module.exports = { - solidity: "0.8.20", + solidity: "0.8.22", settings: { optimizer: { enabled: true, diff --git a/lcov.info b/lcov.info index 625cc22..b4bb18b 100644 --- a/lcov.info +++ b/lcov.info @@ -23,46 +23,48 @@ DA:63,0 DA:63,0 DA:65,0 DA:65,0 +DA:65,0 +DA:65,0 +BRDA:65,2,0,- +BRDA:65,2,1,- DA:66,0 DA:66,0 -DA:67,0 -DA:67,0 DA:68,0 DA:68,0 DA:69,0 DA:69,0 -DA:72,0 -DA:72,0 -DA:73,0 -DA:73,0 +DA:70,0 +DA:70,0 +DA:71,0 +DA:71,0 DA:74,0 DA:74,0 -DA:76,0 -DA:76,0 +DA:75,0 +DA:75,0 +DA:77,0 +DA:77,0 DA:78,0 DA:78,0 -DA:79,0 -DA:79,0 -FN:85,IncomeVault._msgSender +FN:84,IncomeVault._msgSender FNDA:0,IncomeVault._msgSender -DA:91,0 -DA:91,0 -DA:91,0 -FN:97,IncomeVault._msgData +DA:90,0 +DA:90,0 +DA:90,0 +FN:96,IncomeVault._msgData FNDA:0,IncomeVault._msgData -DA:103,0 -DA:103,0 -DA:103,0 -FN:106,IncomeVault._contextSuffixLength +DA:102,0 +DA:102,0 +DA:102,0 +FN:105,IncomeVault._contextSuffixLength FNDA:0,IncomeVault._contextSuffixLength -DA:109,0 -DA:109,0 -DA:109,0 +DA:108,0 +DA:108,0 +DA:108,0 FNF:5 FNH:0 -LF:19 +LF:18 LH:0 -BRF:4 +BRF:6 BRH:0 end_of_record TN: @@ -172,29 +174,29 @@ DA:55,0 DA:55,0 DA:56,0 DA:56,0 +DA:58,0 +DA:58,0 +DA:58,0 +DA:59,0 DA:59,0 DA:59,0 DA:59,0 DA:60,0 DA:60,0 DA:60,0 -DA:60,0 +BRDA:60,5,0,- +BRDA:60,5,1,- DA:61,0 DA:61,0 DA:61,0 -BRDA:61,5,0,- -BRDA:61,5,1,- -DA:62,0 -DA:62,0 -DA:62,0 +DA:64,0 +DA:64,0 +BRDA:64,6,0,- +BRDA:64,6,1,- DA:65,0 DA:65,0 -BRDA:65,6,0,- -BRDA:65,6,1,- -DA:66,0 -DA:66,0 -DA:69,0 -DA:69,0 +DA:68,0 +DA:68,0 FNF:2 FNH:0 LF:25 @@ -223,60 +225,74 @@ DA:31,0 DA:31,0 FN:40,IncomeVaultRestricted.withdraw FNDA:0,IncomeVaultRestricted.withdraw +DA:41,0 +DA:41,0 +DA:41,0 DA:42,0 DA:42,0 +BRDA:42,1,0,- +BRDA:42,1,1,- DA:43,0 DA:43,0 -BRDA:43,1,0,- -BRDA:43,1,1,- -DA:44,0 -DA:44,0 +DA:45,0 +DA:45,0 +BRDA:45,2,0,- +BRDA:45,2,1,- DA:46,0 DA:46,0 DA:48,0 DA:48,0 -FN:56,IncomeVaultRestricted.withdrawAll +DA:50,0 +DA:50,0 +FN:58,IncomeVaultRestricted.withdrawAll FNDA:0,IncomeVaultRestricted.withdrawAll -DA:58,0 -DA:58,0 +DA:59,0 +DA:59,0 +DA:59,0 DA:60,0 DA:60,0 -FN:68,IncomeVaultRestricted.distributeDividend +BRDA:60,3,0,- +BRDA:60,3,1,- +DA:61,0 +DA:61,0 +DA:64,0 +DA:64,0 +FN:72,IncomeVaultRestricted.distributeDividend FNDA:0,IncomeVaultRestricted.distributeDividend -DA:70,0 -DA:70,0 -BRDA:70,2,0,- -BRDA:70,2,1,- -DA:71,0 -DA:71,0 -DA:74,0 DA:74,0 DA:74,0 -DA:76,0 -DA:76,0 -DA:76,0 -DA:78,0 +BRDA:74,4,0,- +BRDA:74,4,1,- +DA:75,0 +DA:75,0 DA:78,0 DA:78,0 DA:78,0 DA:80,0 DA:80,0 -BRDA:80,3,0,- -BRDA:80,3,1,- +DA:80,0 +DA:82,0 DA:82,0 DA:82,0 -BRDA:82,4,0,- -BRDA:82,4,1,- -DA:83,0 -DA:83,0 -FN:95,IncomeVaultRestricted.setStatusClaim +DA:82,0 +DA:84,0 +DA:84,0 +BRDA:84,5,0,- +BRDA:84,5,1,- +DA:86,0 +DA:86,0 +BRDA:86,6,0,- +BRDA:86,6,1,- +DA:87,0 +DA:87,0 +FN:99,IncomeVaultRestricted.setStatusClaim FNDA:0,IncomeVaultRestricted.setStatusClaim -DA:96,0 -DA:96,0 +DA:100,0 +DA:100,0 FNF:5 FNH:0 -LF:22 +LF:26 LH:0 -BRF:10 +BRF:14 BRH:0 end_of_record diff --git a/lib/CMTAT b/lib/CMTAT index 5741a09..4774b7e 160000 --- a/lib/CMTAT +++ b/lib/CMTAT @@ -1 +1 @@ -Subproject commit 5741a09cbfd41ff127a73c7f3696b028c1bba6b8 +Subproject commit 4774b7e7aadae29994872c7a4a0445ed819bd570 diff --git a/src/IncomeVault.sol b/src/IncomeVault.sol index 1b5a1f4..5fb5271 100644 --- a/src/IncomeVault.sol +++ b/src/IncomeVault.sol @@ -57,12 +57,14 @@ contract IncomeVault is Initializable, ContextUpgradeable, IncomeVaultRestricted IAuthorizationEngine authorizationEngineIrrevocable ) internal onlyInitializing { if(admin == address(0)){ - revert AdminWithAddressZeroNotAllowed(); + revert IncomeVault_AdminWithAddressZeroNotAllowed(); } if(address(ERC20TokenPayment_) == address(0)){ - revert TokenPaymentWithAddressZeroNotAllowed(); - } - __Validation_init_unchained(ruleEngine_); + revert IncomeVault_TokenPaymentWithAddressZeroNotAllowed(); + } + if(address(ERC20TokenPayment_) == address(0)){ + revert IncomeVault_CMTATWithAddressZeroNotAllowed(); + } _grantRole(DEFAULT_ADMIN_ROLE, admin); _grantRole(INCOME_VAULT_OPERATOR_ROLE, admin); CMTAT_TOKEN = cmtat_token; @@ -70,13 +72,10 @@ contract IncomeVault is Initializable, ContextUpgradeable, IncomeVaultRestricted // Initialization __AccessControl_init_unchained(); - __Pausable_init_unchained(); - __Validation_init_unchained(ruleEngine_); - __AuthorizationModule_init_unchained(admin, authorizationEngineIrrevocable); // PauseModule_init_unchained is called before ValidationModule_init_unchained due to inheritance - __PauseModule_init_unchained(); - __ValidationModule_init_unchained(); + __Pausable_init_unchained(); + __Validation_init_unchained(ruleEngine_); } /** @@ -109,5 +108,6 @@ contract IncomeVault is Initializable, ContextUpgradeable, IncomeVaultRestricted return ERC2771ContextUpgradeable._contextSuffixLength(); } + // Use in case of inheritance uint256[50] private __gap; } diff --git a/src/public/IncomeVaultOpen.sol b/src/public/IncomeVaultOpen.sol index 87b8101..5bc591f 100644 --- a/src/public/IncomeVaultOpen.sol +++ b/src/public/IncomeVaultOpen.sol @@ -7,7 +7,7 @@ import "lib/CMTAT/openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyG import "../lib/IncomeVaultInternal.sol"; import "CMTAT/modules/wrapper/controllers/ValidationModule.sol"; /** -* @title Debt Vault to distribute dividend +* @title public function */ abstract contract IncomeVaultOpen is ReentrancyGuardUpgradeable, ValidationModule , IncomeVaultInternal { /** @@ -17,17 +17,17 @@ abstract contract IncomeVaultOpen is ReentrancyGuardUpgradeable, ValidationModu function claimDividend(uint256 time) public nonReentrant() { // Check if the claim is activated if(!segragatedClaim[time]){ - revert claimNotActivated(); + revert IncomeVault_claimNotActivated(); } address sender = _msgSender(); // At the beginning since no external call to do if (claimedDividend[sender][time]){ - revert dividendAlreadyClaimed(); + revert IncomeVault_dividendAlreadyClaimed(); } // External call to the CMTAT to retrieve the total supply and the sender balance (uint256 senderBalance, uint256 TokenTotalSupply) = CMTAT_TOKEN.snapshotInfo(time, sender); if (senderBalance == 0){ - revert noDividendToClaim(); + revert IncomeVault_noDividendToClaim(); } uint256 senderDividend = _computeDividend(time, senderBalance, TokenTotalSupply); @@ -48,13 +48,12 @@ abstract contract IncomeVaultOpen is ReentrancyGuardUpgradeable, ValidationModu // Check if the claim is activated for each times for(uint256 i = 0; i < times.length; ++i){ if(!segragatedClaim[times[i]]){ - revert claimNotActivated(); + revert IncomeVault_claimNotActivated(); } } address sender = _msgSender(); address[] memory senders = new address[](1); senders[0] = sender; - //function snapshotInfoBatch(uint256[] calldata times, address[] calldata addresses) public view returns (uint256[][] memory ownerBalances, uint256[] memory totalSupply) { // External call to the CMTAT to retrieve the total supply and the sender balance (uint256[][] memory senderBalances, uint256[] memory TokenTotalSupplys) = CMTAT_TOKEN.snapshotInfoBatch(times, senders); for(uint256 i = 0; i < times.length; ++i){ diff --git a/src/public/IncomeVaultRestricted.sol b/src/public/IncomeVaultRestricted.sol index 16bf857..645e7ad 100644 --- a/src/public/IncomeVaultRestricted.sol +++ b/src/public/IncomeVaultRestricted.sol @@ -7,8 +7,8 @@ import "CMTAT/modules/wrapper/controllers/ValidationModule.sol"; import "../lib/IncomeVaultInternal.sol"; /** -* @title Debt Vault to distribute dividend -*/// AuthorizationModuleStandalone +* @title restricted functions +*/ abstract contract IncomeVaultRestricted is ValidationModule, IncomeVaultInternal { @@ -23,7 +23,7 @@ abstract contract IncomeVaultRestricted is ValidationModule, IncomeVaultInternal function deposit(uint256 time, uint256 amount) public onlyRole(INCOME_VAULT_DEPOSIT_ROLE) { address sender = _msgSender(); if(amount == 0) { - revert noAmountSend(); + revert IncomeVault_noAmountSend(); } segragatedDividend[time] += amount; emit newDeposit(time, sender, amount); @@ -38,10 +38,12 @@ abstract contract IncomeVaultRestricted is ValidationModule, IncomeVaultInternal * @param withdrawAddress address to receive `amount`of tokens */ function withdraw(uint256 time, uint256 amount, address withdrawAddress) public onlyRole(INCOME_VAULT_WITHDRAW_ROLE) { - // TODO: check why it is necessary - ERC20TokenPayment.approve(address(this), amount); + bool result = ERC20TokenPayment.approve(address(this), amount); + if(!result){ + revert IncomeVault_FailApproval(); + } if(segragatedDividend[time] < amount) { - revert notEnoughAmount(); + revert IncomeVault_notEnoughAmount(); } segragatedDividend[time] -= amount; // Will revert in case of failure @@ -54,8 +56,10 @@ abstract contract IncomeVaultRestricted is ValidationModule, IncomeVaultInternal * @param withdrawAddress address to receive `amount`of tokens */ function withdrawAll(uint256 amount, address withdrawAddress) public onlyRole(INCOME_VAULT_WITHDRAW_ROLE) { - // TODO: check why it is necessary - ERC20TokenPayment.approve(address(this), amount); + bool result = ERC20TokenPayment.approve(address(this), amount); + if(!result){ + revert IncomeVault_FailApproval(); + } // Will revert in case of failure ERC20TokenPayment.safeTransferFrom(address(this), withdrawAddress, amount); } @@ -68,7 +72,7 @@ abstract contract IncomeVaultRestricted is ValidationModule, IncomeVaultInternal function distributeDividend(address[] calldata addresses, uint256 time) public onlyRole(INCOME_VAULT_DISTRIBUTE_ROLE) { // Check if the claim is activated if(!segragatedClaim[time]){ - revert claimNotActivated(); + revert IncomeVault_claimNotActivated(); } // Get info from the token (uint256[] memory tokenHolderBalance, uint256 totalSupply) = CMTAT_TOKEN.snapshotInfoBatch(time, addresses); @@ -95,17 +99,5 @@ abstract contract IncomeVaultRestricted is ValidationModule, IncomeVaultInternal function setStatusClaim(uint256 time, bool status) public onlyRole(INCOME_VAULT_OPERATOR_ROLE){ segragatedClaim[time] = status; } - - /*function setTokenPayment(IERC20 ERC20TokenPayment_) public onlyRole(DEBT_VAULT_OPERATOR_ROLE){ - if(address(ERC20TokenPayment_) != address(0)){ - ERC20TokenPayment = ERC20TokenPayment_; - } - }*/ - - /*function setTokenCMTAT(CMTAT_BASE cmtat_token) public onlyRole(DEBT_VAULT_OPERATOR_ROLE){ - if(address(cmtat_token) != address(0) && address(CMTAT_TOKEN) == address(0)){ - CMTAT_TOKEN = cmtat_token; - } - }*/ uint256[50] private __gap; }