diff --git a/.github/workflows/ci-dev-contracts.yml b/.github/workflows/ci-dev-contracts.yml index c3d5f59dc..2e2854434 100644 --- a/.github/workflows/ci-dev-contracts.yml +++ b/.github/workflows/ci-dev-contracts.yml @@ -10,7 +10,6 @@ on: branches: - "main*" - "development*" - - "ops*" paths: - "packages/contracts/**" - ".github/workflows/ci-dev-contracts.yml" @@ -46,14 +45,14 @@ jobs: environment: testnet steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: - version: nightly + version: nightly-f625d0fa7c51e65b4bf1e8f7931cd1c6e2e285e9 # re-run the tests here on purpose to ensure quality. - name: Run tests @@ -79,7 +78,8 @@ jobs: run: yarn deploy:ci env: ENVIRONMENT: "testnet" - DEPLOYER_PRIVATE_KEY: ${{ secrets.BASE_SEPOLIA_DEPLOYER_PRIVATE_KEY }} + DEPLOYER_PRIVATE_KEY: ${{ secrets.ARBITRUM_SEPOLIA_DEPLOYER_PRIVATE_KEY }} + ARBITRUM_SEPOLIA_ETHERSCAN_API_KEY: ${{ secrets.ARBITRUM_SEPOLIA_ETHERSCAN_API_KEY }} + ETHERSCAN_API_KEY: ${{ secrets.ARBITRUM_SEPOLIA_ETHERSCAN_API_KEY }} ALCHEMY_API_KEY: ${{ secrets.TESTNET_ALCHEMY_API_KEY }} - BASE_SEPOLIA_ETHERSCAN_API_KEY: ${{ secrets.BASE_SEPOLIA_ETHERSCAN_API_KEY }} SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.TEST_SUPABASE_SERVICE_ROLE_KEY }} diff --git a/packages/contracts/CBL_README.md b/packages/contracts/CBL_README.md index 5bf243982..0caacda6c 100644 --- a/packages/contracts/CBL_README.md +++ b/packages/contracts/CBL_README.md @@ -112,3 +112,9 @@ error CBL__InvalidOwnerAddress(); error CBL__InvalidMinterAddress(); ``` +## Roadmap - Future Features + +### Governance & Governance Token ($gCBL) Contract + +Governance will be implemented in a separate Governance Token ($gCBL) smart contract. The $gCBL contract will include functions +related to governance and voting. Users lock $CBL to receive Governance Tokens ($gCBL) in return. \ No newline at end of file diff --git a/packages/contracts/Makefile b/packages/contracts/Makefile index 12a7d3c24..df460bbad 100755 --- a/packages/contracts/Makefile +++ b/packages/contracts/Makefile @@ -1,43 +1,44 @@ -# NOTE (JL,2024-05-20): Hierarchical Environment loading does not simply work with `make`. A custom -# solution may be possible, but given that `forge` auto-loads any `.env` file in the invocation +# NOTE (JL,2024-05-20): Hierarchical Environment loading does not simply work with `make`. A custom +# solution may be possible, but given that `forge` auto-loads any `.env` file in the invocation # directory and our deployment scripts depend on this, it seems unnecessary to change this now. -include .env -build:; forge build +DEPLOY_SCRIPT ?= script/DeployVaultFactory.s.sol:DeployVaultFactory -anvil: anvil +# How the deployer is authenticated. Can override to use `cast wallet` output or `--account x --password y` etc. +DEPLOYER_AUTHENTICATION ?= --private-key $(DEPLOYER_PRIVATE_KEY) deploy-local: $(MAKE) deploy RPC_URL=localhost EXTRA_FLAGS= -# TODO: change this to use the network config from foundry.toml -deploy-arbSepolia: +deploy-arbitrumSepolia: $(MAKE) deploy RPC_URL=arbitrumSepolia EXTRA_FLAGS="--verify --slow" -resume-arbSepolia: +resume-arbitrumSepolia: $(MAKE) deploy RPC_URL=arbitrumSepolia EXTRA_FLAGS="--verify --slow --resume" deploy-baseSepolia: $(MAKE) deploy RPC_URL=baseSepolia EXTRA_FLAGS="--verify --slow" -# NOTE (JL,2024-05-20): `forge` auto-includes any `.env` file in the invocation directory. -deploy: - @echo "Running deploy '$(RPC_URL)' for '$(ENVIRONMENT)' environment." - @rm -rf out && \ - forge script script/DeployVaultFactory.s.sol:DeployVaultFactory --rpc-url $(RPC_URL) \ - --private-key $(DEPLOYER_PRIVATE_KEY) --broadcast $(EXTRA_FLAGS) -vvvv && \ - yarn gen-types && \ - yarn db-export - # TODO: add --verifier flag. forge isn't happy --etherscan-api-key is empty. deploy-bitlayerTestnet: $(MAKE) deploy RPC_URL="https://testnet-rpc.bitlayer.org" \ - EXTRA_FLAGS='--etherscan-api-key "" --verifier-url "https://testnet-scan.bitlayer.org" --legacy --slow' + EXTRA_FLAGS='--etherscan-api-key "" --verifier-url "https://testnet-scan.bitlayer.org" --legacy --slow' +# NOTE (JL,2024-05-20): `forge` auto-includes any `.env` file in the invocation directory. +# NOTE (JL,2024-08-08): For verification, ensure that ETHERSCAN_API_KEY is explicitly set. +# NOTE (JL,2024-08-08): We run a `clean` and `build` to ensure the local build state is consistent. +deploy: + @echo "Running deploy script '$(DEPLOY_SCRIPT)' against '$(RPC_URL)' for '$(ENVIRONMENT)' environment." + @yarn clean + @yarn build + @forge script $(DEPLOY_SCRIPT) --rpc-url $(RPC_URL) $(DEPLOYER_AUTHENTICATION) --broadcast $(EXTRA_FLAGS) -vvvv + @yarn db-export + +# NOTE (JL,2024-08-08): Verify is a 'recovery' operation, requiring the `broadcast` directory contrents. So, no `clean`. +# Also, as such, we run `db-export` afterwards in case it resumed Contract Deployment. verify: - @echo "Running verify '$(RPC_URL)' for '$(ENVIRONMENT)' environment." - rm -rf out && \ - forge script script/DeployVaultFactory.s.sol:DeployVaultFactory --rpc-url $(RPC_URL) \ - --private-key $(DEPLOYER_PRIVATE_KEY) --verify --resume -vvvv && \ - yarn gen-types && \ - yarn db-export + @echo "Running verify of script '$(DEPLOY_SCRIPT)' against '$(RPC_URL)' for '$(ENVIRONMENT)' environment." + @yarn build + @forge script $(DEPLOY_SCRIPT) --rpc-url $(RPC_URL) $(DEPLOYER_AUTHENTICATION) --verify --resume -vvvv + @yarn db-export diff --git a/packages/contracts/foundry.toml b/packages/contracts/foundry.toml index 78f0a5257..e09ed1d6b 100755 --- a/packages/contracts/foundry.toml +++ b/packages/contracts/foundry.toml @@ -3,7 +3,7 @@ src = "src" out = "out" libs = ["lib"] fs_permissions = [{ access = "read-write", path = "./" }] -solc_version="0.8.23" +solc_version = "0.8.23" [fmt] bracket_spacing = true @@ -25,9 +25,9 @@ bitlayerTestnet = "https://testnet-rpc.bitlayer.org" [etherscan] -localhost = { key = "" , url="http://127.0.0.1:8545"} +localhost = { key = "", url = "http://127.0.0.1:8545" } base = { key = "${BASE_ETHERSCAN_API_KEY}" } baseSepolia = { key = "${BASE_SEPOLIA_ETHERSCAN_API_KEY}" } -arbitrumSepolia = { key = "${ARB_SEPOLIA_ETHERSCAN_API_KEY}", url = "https://api-sepolia.arbiscan.io/api", chain=421614 } -bitlayer = { key = "bitlayer", url="https://rpc.bitlayer.org" } -bitlayerTestnet = { key = "bitlayerTestnet", url="https://testnet-scan.bitlayer.org" } +arbitrumSepolia = { key = "${ARBITRUM_SEPOLIA_ETHERSCAN_API_KEY}", url = "https://api-sepolia.arbiscan.io/api?", chain = 421614 } +bitlayer = { key = "bitlayer", url = "https://rpc.bitlayer.org" } +bitlayerTestnet = { key = "bitlayerTestnet", url = "https://testnet-scan.bitlayer.org" } diff --git a/packages/contracts/package.json b/packages/contracts/package.json index defa086f2..c3034e60c 100755 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -8,15 +8,15 @@ "chain": "yarn rm-dbdata && anvil --config-out localhost.json", "clean": "forge clean && rm -rf broadcast cache deployments types localhost.json index.js", "dev": "yarn rm-dbdata && anvil --config-out localhost.json & make deploy-local", - "build": "forge build && yarn gen-types", + "build": "forge build && yarn gen-types && tsc", "test": "forge test", "format": "forge fmt && prettier './script/**/*.js' --write", "lint": "forge fmt && eslint --fix --ignore-path .gitignore && yarn solhint './*(test|src)/**/*.sol'", "db-check": "tsc && node ./script/utils/checkDb.js", "db-export": "tsc && node ./script/utils/exporter.js", "deploy": "make deploy-local", - "deploy:ci": "yarn db-export && make deploy-baseSepolia", - "gen-types": "yarn typechain --target ethers-v5 --out-dir ./types ./out/*.sol/*.json && tsc", + "deploy:ci": "make deploy-arbitrumSepolia", + "gen-types": "yarn typechain --target ethers-v5 --out-dir ./types ./out/*.sol/*.json", "postinstall": "yarn build && yarn gen-types" }, "packageManager": "yarn@3.2.3", @@ -28,7 +28,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "prettier": "^3.2.2", - "solhint": "^5.0.1", + "solhint": "^5.0.3", "typechain": "^8.3.2", "typescript": "^5.4.5" }, diff --git a/packages/contracts/resource/testnet.toml b/packages/contracts/resource/testnet.toml index 0be6685c8..d916272b3 100644 --- a/packages/contracts/resource/testnet.toml +++ b/packages/contracts/resource/testnet.toml @@ -4,7 +4,7 @@ [evm] # blockchain id, e.g. baseSepolia=84532, arbSepolia=421614 -chain_id = 84532 +chain_id = 421614 deploy_mocks = true [evm.address] diff --git a/packages/contracts/script/utils/exporter.js b/packages/contracts/script/utils/exporter.js index ef2003c7b..a38faa73d 100755 --- a/packages/contracts/script/utils/exporter.js +++ b/packages/contracts/script/utils/exporter.js @@ -102,6 +102,7 @@ async function clearExistingData(client) { await exportAddress(loadConfiguration()); } catch (e) { console.log(e); + process.exitCode = 1; } finally { console.log(`Finished exporting contracts`); } diff --git a/packages/contracts/src/CredbullFixedYieldVaultFactory.sol b/packages/contracts/src/CredbullFixedYieldVaultFactory.sol index 8301fe200..987f9a41a 100644 --- a/packages/contracts/src/CredbullFixedYieldVaultFactory.sol +++ b/packages/contracts/src/CredbullFixedYieldVaultFactory.sol @@ -19,8 +19,9 @@ contract CredbullFixedYieldVaultFactory is VaultFactory { { } /** - * @notice - Function to create a new vault. Should be called only by the owner - * @param params - The VaultParams + * @notice Function to create a new vault. + * @param params The [VaultParams] that defines the [Vault] to create. + * @param options A [string] of options, emitted in the [VaultDeployed] event. */ function createVault(CredbullFixedYieldVault.FixedYieldVaultParams memory params, string memory options) public diff --git a/packages/contracts/src/factory/VaultFactory.sol b/packages/contracts/src/factory/VaultFactory.sol index e5deb68e0..6384db69d 100644 --- a/packages/contracts/src/factory/VaultFactory.sol +++ b/packages/contracts/src/factory/VaultFactory.sol @@ -14,6 +14,21 @@ abstract contract VaultFactory is AccessControl { /// @notice Error to revert if custodian is not allowed error CredbullVaultFactory__CustodianNotAllowed(); + /// @notice Error to indicate that the provided owner address is invalid. + error CredbullVaultFactory__InvalidOwnerAddress(); + + /// @notice Error to indicate that the provided operator address is invalid. + error CredbullVaultFactory__InvalidOperatorAddress(); + + /// @notice Error to indicate that the provided custodian address is invalid. + error CredbullVaultFactory__InvalidCustodianAddress(); + + /// @notice Event to emit when a new custodian is allowed + event CustodianAllowed(address indexed custodian); + + /// @notice Event to emit when a custodian is removed + event CustodianRemoved(address indexed custodian); + /// @notice Address set that contains list of all vault address EnumerableSet.AddressSet internal allVaults; @@ -29,12 +44,22 @@ abstract contract VaultFactory is AccessControl { * @param custodians - Initial set of custodians allowable for the vaults */ constructor(address owner, address operator, address[] memory custodians) { + if (owner == address(0)) { + revert CredbullVaultFactory__InvalidOwnerAddress(); + } + + if (operator == address(0)) { + revert CredbullVaultFactory__InvalidOperatorAddress(); + } _grantRole(DEFAULT_ADMIN_ROLE, owner); _grantRole(OPERATOR_ROLE, operator); // set the allowed custodians directly in the constructor, without access restriction bool[] memory result = new bool[](custodians.length); for (uint256 i = 0; i < custodians.length; i++) { + if (custodians[i] == address(0)) { + revert CredbullVaultFactory__InvalidCustodianAddress(); + } result[i] = allowedCustodians.add(custodians[i]); } } @@ -58,6 +83,11 @@ abstract contract VaultFactory is AccessControl { /// @notice Add custodian address to the set function allowCustodian(address _custodian) public onlyRole(DEFAULT_ADMIN_ROLE) returns (bool) { + if (_custodian == address(0)) { + revert CredbullVaultFactory__InvalidCustodianAddress(); + } + + emit CustodianAllowed(_custodian); return allowedCustodians.add(_custodian); } @@ -65,6 +95,7 @@ abstract contract VaultFactory is AccessControl { function removeCustodian(address _custodian) public onlyRole(DEFAULT_ADMIN_ROLE) { if (allowedCustodians.contains(_custodian)) { allowedCustodians.remove(_custodian); + emit CustodianRemoved(_custodian); } } diff --git a/packages/contracts/src/plugin/MaxCapPlugin.sol b/packages/contracts/src/plugin/MaxCapPlugin.sol index f64b9eb72..0b36b0b19 100644 --- a/packages/contracts/src/plugin/MaxCapPlugin.sol +++ b/packages/contracts/src/plugin/MaxCapPlugin.sol @@ -6,6 +6,12 @@ pragma solidity ^0.8.20; abstract contract MaxCapPlugin { error CredbullVault__MaxCapReached(); + /// @notice Event emitted when the max cap is updated + event MaxCapUpdated(uint256 indexed maxCap); + + /// @notice Event emitted when the max cap check is updated + event MaxCapCheckUpdated(bool indexed checkMaxCap); + /// @notice - Params for the MaxCap Plugin struct MaxCapPluginParams { uint256 maxCap; @@ -33,12 +39,16 @@ abstract contract MaxCapPlugin { } /// @notice - Toggle the max cap check status - function _toggleMaxCapCheck(bool status) internal virtual { - checkMaxCap = status; + function _setCheckMaxCap(bool _checkMaxCapStatus) internal virtual { + checkMaxCap = _checkMaxCapStatus; + + emit MaxCapCheckUpdated(checkMaxCap); } /// @notice - Update the max cap value function _updateMaxCap(uint256 _value) internal virtual { maxCap = _value; + + emit MaxCapUpdated(maxCap); } } diff --git a/packages/contracts/src/plugin/WhiteListPlugin.sol b/packages/contracts/src/plugin/WhiteListPlugin.sol index 1420d7094..bcd95e307 100644 --- a/packages/contracts/src/plugin/WhiteListPlugin.sol +++ b/packages/contracts/src/plugin/WhiteListPlugin.sol @@ -11,6 +11,9 @@ abstract contract WhiteListPlugin { /// @notice Error to revert if the address is not whiteListed error CredbullVault__NotWhiteListed(address, uint256); + /// @notice Event emitted when the whiteList check is updated + event WhiteListCheckUpdated(bool indexed checkWhiteList); + /// @notice - Params for the WhiteList Plugin struct WhiteListPluginParams { address whiteListProvider; @@ -18,33 +21,34 @@ abstract contract WhiteListPlugin { } /// @notice - Address of the White List Provider. - IWhiteListProvider public whiteListProvider; + IWhiteListProvider public immutable WHITELIST_PROVIDER; /// @notice - Flag to check for whiteList bool public checkWhiteList; /// @notice - Deposit threshold amount to check for whiteListing - uint256 public depositThresholdForWhiteListing; + uint256 public immutable DEPOSIT_THRESHOLD_FOR_WHITE_LISTING; constructor(WhiteListPluginParams memory params) { if (params.whiteListProvider == address(0)) { revert CredbullVault__InvalidWhiteListProviderAddress(params.whiteListProvider); } - whiteListProvider = IWhiteListProvider(params.whiteListProvider); + WHITELIST_PROVIDER = IWhiteListProvider(params.whiteListProvider); checkWhiteList = true; // Set the check to true by default - depositThresholdForWhiteListing = params.depositThresholdForWhiteListing; + DEPOSIT_THRESHOLD_FOR_WHITE_LISTING = params.depositThresholdForWhiteListing; } /// @notice - Function to check for whiteListed address function _checkIsWhiteListed(address receiver, uint256 amount) internal view virtual { - if (checkWhiteList && amount >= depositThresholdForWhiteListing && !whiteListProvider.status(receiver)) { + if (checkWhiteList && amount >= DEPOSIT_THRESHOLD_FOR_WHITE_LISTING && !WHITELIST_PROVIDER.status(receiver)) { revert CredbullVault__NotWhiteListed(receiver, amount); } } /// @notice - Function to toggle check for whiteListed address - function _toggleWhiteListCheck(bool status) internal virtual { - checkWhiteList = status; + function _toggleWhiteListCheck() internal virtual { + checkWhiteList = !checkWhiteList; + emit WhiteListCheckUpdated(checkWhiteList); } } diff --git a/packages/contracts/src/plugin/WindowPlugin.sol b/packages/contracts/src/plugin/WindowPlugin.sol index 47c5b8b05..8cdffc8aa 100644 --- a/packages/contracts/src/plugin/WindowPlugin.sol +++ b/packages/contracts/src/plugin/WindowPlugin.sol @@ -9,6 +9,26 @@ abstract contract WindowPlugin { uint256 windowOpensAt, uint256 windowClosesAt, uint256 timestamp ); + /// @notice Error to revert when incorrect window values are provided + error WindowPlugin__IncorrectWindowValues( + uint256 depositOpen, uint256 depositClose, uint256 redeemOpen, uint256 redeemClose + ); + + /// @notice Event emitted when the window is updated + event WindowUpdated( + uint256 depositOpensAt, uint256 depositClosesAt, uint256 redemptionOpensAt, uint256 redemptionClosesAt + ); + + /// @notice Event emitted when the window check is updated + event WindowCheckUpdated(bool indexed checkWindow); + + modifier validateWindows(uint256 _depositOpen, uint256 _depositClose, uint256 _redeemOpen, uint256 _redeemClose) { + if (!(_depositOpen < _depositClose && _depositClose < _redeemOpen && _redeemOpen < _redeemClose)) { + revert WindowPlugin__IncorrectWindowValues(_depositOpen, _depositClose, _redeemOpen, _redeemClose); + } + _; + } + /// @notice A Window is essentially a Time Span, denoted by an Opening and Closing Time pair. struct Window { uint256 opensAt; @@ -36,7 +56,14 @@ abstract contract WindowPlugin { /// @notice - Flag to check for window bool public checkWindow; - constructor(WindowPluginParams memory params) { + constructor(WindowPluginParams memory params) + validateWindows( + params.depositWindow.opensAt, + params.depositWindow.closesAt, + params.redemptionWindow.opensAt, + params.redemptionWindow.closesAt + ) + { depositOpensAtTimestamp = params.depositWindow.opensAt; depositClosesAtTimestamp = params.depositWindow.closesAt; redemptionOpensAtTimestamp = params.redemptionWindow.opensAt; @@ -68,15 +95,19 @@ abstract contract WindowPlugin { function _updateWindow(uint256 _depositOpen, uint256 _depositClose, uint256 _redeemOpen, uint256 _redeemClose) internal virtual + validateWindows(_depositOpen, _depositClose, _redeemOpen, _redeemClose) { depositOpensAtTimestamp = _depositOpen; depositClosesAtTimestamp = _depositClose; redemptionOpensAtTimestamp = _redeemOpen; redemptionClosesAtTimestamp = _redeemClose; + + emit WindowUpdated(_depositOpen, _depositClose, _redeemOpen, _redeemClose); } /// @notice - Function to toggle check for window - function _toggleWindowCheck(bool status) internal { - checkWindow = status; + function _toggleWindowCheck() internal { + checkWindow = !checkWindow; + emit WindowCheckUpdated(checkWindow); } } diff --git a/packages/contracts/src/provider/whiteList/WhiteListProvider.sol b/packages/contracts/src/provider/whiteList/WhiteListProvider.sol index e472b1233..0682d3cff 100755 --- a/packages/contracts/src/provider/whiteList/WhiteListProvider.sol +++ b/packages/contracts/src/provider/whiteList/WhiteListProvider.sol @@ -3,8 +3,9 @@ pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IWhiteListProvider } from "./IWhiteListProvider.sol"; +import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; -contract WhiteListProvider is IWhiteListProvider, Ownable { +contract WhiteListProvider is IWhiteListProvider, Ownable2Step { error LengthMismatch(); /** @@ -30,6 +31,7 @@ contract WhiteListProvider is IWhiteListProvider, Ownable { uint256 length = _addresses.length; for (uint256 i; i < length;) { + if (_addresses[i] == address(0)) continue; isWhiteListed[_addresses[i]] = _statuses[i]; unchecked { diff --git a/packages/contracts/src/vault/FixedYieldVault.sol b/packages/contracts/src/vault/FixedYieldVault.sol index 2e09eb50b..d2c7d735c 100644 --- a/packages/contracts/src/vault/FixedYieldVault.sol +++ b/packages/contracts/src/vault/FixedYieldVault.sol @@ -13,6 +13,12 @@ import { MaxCapPlugin } from "../plugin/MaxCapPlugin.sol"; contract FixedYieldVault is MaturityVault, WhiteListPlugin, WindowPlugin, MaxCapPlugin, AccessControl { using Math for uint256; + /// @notice Error to indicate that the provided owner address is invalid. + error FixedYieldVault__InvalidOwnerAddress(); + + /// @notice Error to indicate that the provided operator address is invalid. + error FixedYieldVault__InvalidOperatorAddress(); + /// @notice - Hash of operator role bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); @@ -32,7 +38,7 @@ contract FixedYieldVault is MaturityVault, WhiteListPlugin, WindowPlugin, MaxCap } /// @dev The fixed yield value in percentage(100) that's promised to the users on deposit. - uint256 private _fixedYield; + uint256 private immutable FIXED_YIELD; constructor(FixedYieldVaultParams memory params) MaturityVault(params.maturityVault) @@ -40,10 +46,18 @@ contract FixedYieldVault is MaturityVault, WhiteListPlugin, WindowPlugin, MaxCap WindowPlugin(params.windowPlugin) MaxCapPlugin(params.maxCapPlugin) { + if (params.roles.owner == address(0)) { + revert FixedYieldVault__InvalidOwnerAddress(); + } + + if (params.roles.operator == address(0)) { + revert FixedYieldVault__InvalidOperatorAddress(); + } + _grantRole(DEFAULT_ADMIN_ROLE, params.roles.owner); _grantRole(OPERATOR_ROLE, params.roles.operator); - _fixedYield = params.promisedYield; + FIXED_YIELD = params.promisedYield; } /// @dev - Overridden deposit modifer @@ -70,7 +84,7 @@ contract FixedYieldVault is MaturityVault, WhiteListPlugin, WindowPlugin, MaxCap // @notice - Returns expected assets on maturity function expectedAssetsOnMaturity() public view override returns (uint256) { - return totalAssetDeposited.mulDiv(100 + _fixedYield, 100); + return totalAssetDeposited.mulDiv(100 + FIXED_YIELD, 100); } /// @notice Mature the vault @@ -79,23 +93,23 @@ contract FixedYieldVault is MaturityVault, WhiteListPlugin, WindowPlugin, MaxCap } /// @notice Toggle check for maturity - function toggleMaturityCheck(bool status) public onlyRole(DEFAULT_ADMIN_ROLE) { - _toggleMaturityCheck(status); + function setMaturityCheck(bool _setMaturityCheckStatus) public onlyRole(DEFAULT_ADMIN_ROLE) { + _setMaturityCheck(_setMaturityCheckStatus); } /// @notice Toggle check for whiteList - function toggleWhiteListCheck(bool status) public onlyRole(DEFAULT_ADMIN_ROLE) { - _toggleWhiteListCheck(status); + function toggleWhiteListCheck() public onlyRole(DEFAULT_ADMIN_ROLE) { + _toggleWhiteListCheck(); } /// @notice Toggle check for window - function toggleWindowCheck(bool status) public onlyRole(DEFAULT_ADMIN_ROLE) { - _toggleWindowCheck(status); + function toggleWindowCheck() public onlyRole(DEFAULT_ADMIN_ROLE) { + _toggleWindowCheck(); } /// @notice Toggle check for max cap - function toggleMaxCapCheck(bool status) public onlyRole(DEFAULT_ADMIN_ROLE) { - _toggleMaxCapCheck(status); + function setCheckMaxCap(bool _checkMaxCapStatus) public onlyRole(DEFAULT_ADMIN_ROLE) { + _setCheckMaxCap(_checkMaxCapStatus); } /// @notice Update max cap value diff --git a/packages/contracts/src/vault/MaturityVault.sol b/packages/contracts/src/vault/MaturityVault.sol index 2352fb7e5..f99881954 100644 --- a/packages/contracts/src/vault/MaturityVault.sol +++ b/packages/contracts/src/vault/MaturityVault.sol @@ -23,6 +23,12 @@ abstract contract MaturityVault is Vault { /// @notice Reverts on mature if there is not enough balance. error CredbullVault__NotEnoughBalanceToMature(); + /// @notice Event emitted when the vault matures. + event VaultMatured(uint256 indexed totalAssetDeposited); + + /// @notice Event emitted when the maturity check is updated. + event MaturityCheckUpdated(bool indexed checkMaturity); + /// @notice Determine if the vault is matured or not. bool public isMatured; @@ -48,6 +54,8 @@ abstract contract MaturityVault is Vault { totalAssetDeposited = currentBalance; isMatured = true; + + emit VaultMatured(totalAssetDeposited); } /// @notice - Returns expected assets on maturity @@ -73,9 +81,10 @@ abstract contract MaturityVault is Vault { /** * @notice Enables/disables the Maturity Check according to the [status] value. * @dev 'Toggling' means flipping the existing state. This is simply a mutator. - * @param status Boolean value to toggle */ - function _toggleMaturityCheck(bool status) internal { - checkMaturity = status; + function _setMaturityCheck(bool _setMaturityCheckStatus) internal { + checkMaturity = _setMaturityCheckStatus; + + emit MaturityCheckUpdated(checkMaturity); } } diff --git a/packages/contracts/src/vault/UpsideVault.sol b/packages/contracts/src/vault/UpsideVault.sol index 77b58b06f..7e3b35081 100644 --- a/packages/contracts/src/vault/UpsideVault.sol +++ b/packages/contracts/src/vault/UpsideVault.sol @@ -9,12 +9,16 @@ import { FixedYieldVault } from "./FixedYieldVault.sol"; contract UpsideVault is FixedYieldVault { using Math for uint256; + /// @notice Error to indicate that the provided share balance is insufficient. error CredbullVault__InsufficientShareBalance(); + /// @notice Error to indicate that the provided collateral percentage is invalid. + error CredbullVault__InvalidUpsidePercentage(); + struct UpsideVaultParams { FixedYieldVaultParams fixedYieldVault; IERC20 cblToken; - uint256 collateralPercentage; + uint256 upsidePercentage; } /// @notice address of the Credbull token CBL @@ -23,9 +27,9 @@ contract UpsideVault is FixedYieldVault { uint256 public twap = 100_00; /// @notice Percentage of collateral (100_00) is 100% - uint256 public collateralPercentage; + uint256 public upsidePercentage; - mapping(address account => uint256) private _collateralBalance; + mapping(address account => uint256) private _upsideBalance; /// @notice Total collateral deposited // uint256 public totalCollateralDeposited; @@ -40,7 +44,10 @@ contract UpsideVault is FixedYieldVault { uint256 private additionalPrecision; constructor(UpsideVaultParams memory params) FixedYieldVault(params.fixedYieldVault) { - collateralPercentage = params.collateralPercentage; + if (params.upsidePercentage > MAX_PERCENTAGE) { + revert CredbullVault__InvalidUpsidePercentage(); + } + upsidePercentage = params.upsidePercentage; token = params.cblToken; uint8 assetDecimal = _checkValidDecimalValue(address(params.fixedYieldVault.maturityVault.vault.asset)); @@ -67,7 +74,7 @@ contract UpsideVault is FixedYieldVault { uint256 collateral = getCollateralAmount(assets); - _collateralBalance[receiver] += collateral; + _upsideBalance[receiver] += collateral; totalAssetDeposited += assets; if (totalAssetDeposited > maxCap) { @@ -95,7 +102,7 @@ contract UpsideVault is FixedYieldVault { uint256 collateral = calculateTokenRedemption(shares, owner); - _collateralBalance[owner] -= collateral; + _upsideBalance[owner] -= collateral; totalAssetDeposited -= assets; SafeERC20.safeTransfer(token, receiver, collateral); @@ -108,8 +115,7 @@ contract UpsideVault is FixedYieldVault { /// @notice - Get the collateral amount to deposit for the given asset function getCollateralAmount(uint256 assets) public view virtual returns (uint256) { - return - ((assets * additionalPrecision).mulDiv(collateralPercentage, MAX_PERCENTAGE)).mulDiv(MAX_PERCENTAGE, twap); + return ((assets * additionalPrecision).mulDiv(upsidePercentage, MAX_PERCENTAGE)).mulDiv(MAX_PERCENTAGE, twap); } /// @notice - Get the collateral amount to redeem for the given shares @@ -119,7 +125,7 @@ contract UpsideVault is FixedYieldVault { } uint256 sharePercent = shares.mulDiv(PRECISION, balanceOf(account)); - return _collateralBalance[account].mulDiv(sharePercent, PRECISION); + return _upsideBalance[account].mulDiv(sharePercent, PRECISION); } /// @notice - Update the twap value diff --git a/packages/contracts/src/vault/Vault.sol b/packages/contracts/src/vault/Vault.sol index eb0b1174c..2d6321c99 100644 --- a/packages/contracts/src/vault/Vault.sol +++ b/packages/contracts/src/vault/Vault.sol @@ -24,7 +24,6 @@ abstract contract Vault is ERC4626, Pausable { error CredbullVault__TransferOutsideEcosystem(address); error CredbullVault__InvalidAssetAmount(uint256); error CredbullVault__UnsupportedDecimalValue(uint8); - error CredbullVault__NativeTransferNotAllowed(); /// @notice The set of parameters required to create a Credbull Vault instance. struct VaultParams { @@ -163,16 +162,6 @@ abstract contract Vault is ERC4626, Pausable { return VAULT_DECIMALS; } - /// @notice Revert any ETH transfer to contract - receive() external payable { - revert CredbullVault__NativeTransferNotAllowed(); - } - - /// @notice Revert any ETH transfer to contract - fallback() external payable { - revert CredbullVault__NativeTransferNotAllowed(); - } - /// @notice Withdraw any ERC20 tokens sent directly to contract. /// This should be implemented by the inherited contract and should be callable only by the admin. function _withdrawERC20(address[] calldata _tokens, address _to) internal { diff --git a/packages/contracts/test/src/CredbullFixedYieldVaultFactoryTest.t.sol b/packages/contracts/test/src/CredbullFixedYieldVaultFactoryTest.t.sol index feda253a0..ad63ed8a6 100644 --- a/packages/contracts/test/src/CredbullFixedYieldVaultFactoryTest.t.sol +++ b/packages/contracts/test/src/CredbullFixedYieldVaultFactoryTest.t.sol @@ -35,6 +35,18 @@ contract CredbullFixedYieldVaultFactoryTest is Test { params.whiteListPlugin.whiteListProvider = address(whiteListProvider); } + function test__ShouldRevertOnInvalidParams() public { + vm.prank(config.factoryParams.owner); + vm.expectRevert(VaultFactory.CredbullVaultFactory__InvalidOwnerAddress.selector); + new CredbullFixedYieldVaultFactory(address(0), config.factoryParams.operator, new address[](0)); + + vm.expectRevert(VaultFactory.CredbullVaultFactory__InvalidOperatorAddress.selector); + new CredbullFixedYieldVaultFactory(config.factoryParams.owner, address(0), new address[](0)); + + vm.expectRevert(VaultFactory.CredbullVaultFactory__InvalidCustodianAddress.selector); + new CredbullFixedYieldVaultFactory(config.factoryParams.owner, config.factoryParams.operator, new address[](1)); + } + function test__ShouldSuccefullyCreateFactoryFixedYield() public { address[] memory custodians = new address[](1); custodians[0] = config.factoryParams.custodian; @@ -53,7 +65,7 @@ contract CredbullFixedYieldVaultFactoryTest is Test { assertEq(vault.asset(), address(params.maturityVault.vault.asset)); assertEq(vault.name(), params.maturityVault.vault.shareName); assertEq(vault.symbol(), params.maturityVault.vault.shareSymbol); - assertEq(address(vault.whiteListProvider()), params.whiteListPlugin.whiteListProvider); + assertEq(address(vault.WHITELIST_PROVIDER()), params.whiteListPlugin.whiteListProvider); assertEq(vault.CUSTODIAN(), params.maturityVault.vault.custodian); } @@ -115,6 +127,8 @@ contract CredbullFixedYieldVaultFactoryTest is Test { function test__ShouldAllowAdminToAddCustodians() public { vm.prank(config.factoryParams.owner); + vm.expectEmit(); + emit VaultFactory.CustodianAllowed(params.maturityVault.vault.custodian); factory.allowCustodian(params.maturityVault.vault.custodian); assertTrue(factory.isCustodianAllowed(params.maturityVault.vault.custodian)); @@ -125,11 +139,22 @@ contract CredbullFixedYieldVaultFactoryTest is Test { factory.allowCustodian(params.maturityVault.vault.custodian); assertTrue(factory.isCustodianAllowed(params.maturityVault.vault.custodian)); + vm.expectEmit(); + emit VaultFactory.CustodianRemoved(params.maturityVault.vault.custodian); factory.removeCustodian(params.maturityVault.vault.custodian); assertTrue(!factory.isCustodianAllowed(params.maturityVault.vault.custodian)); vm.stopPrank(); } + function test__ShouldRevertOnInvalidCustodian() public { + vm.startPrank(config.factoryParams.owner); + factory.allowCustodian(params.maturityVault.vault.custodian); + + vm.expectRevert(VaultFactory.CredbullVaultFactory__InvalidCustodianAddress.selector); + factory.allowCustodian(address(0)); + vm.stopPrank(); + } + function test__ShouldRevertAllowAdmingIfNotOwner() public { vm.prank(makeAddr("random_addr")); vm.expectRevert(); diff --git a/packages/contracts/test/src/CredbullFixedYieldVaultTest.t.sol b/packages/contracts/test/src/CredbullFixedYieldVaultTest.t.sol index f46ee6559..7d64bec86 100644 --- a/packages/contracts/test/src/CredbullFixedYieldVaultTest.t.sol +++ b/packages/contracts/test/src/CredbullFixedYieldVaultTest.t.sol @@ -14,6 +14,7 @@ import { CredbullFixedYieldVault } from "@credbull/CredbullFixedYieldVault.sol"; import { CredbullWhiteListProvider } from "@credbull/CredbullWhiteListProvider.sol"; import { WhiteListProvider } from "@credbull/provider/whiteList/WhiteListProvider.sol"; import { WhiteListPlugin } from "@credbull/plugin/WhiteListPlugin.sol"; +import { FixedYieldVault } from "@credbull/vault/FixedYieldVault.sol"; import { ParamsFactory } from "@test/test/vault/utils/ParamsFactory.t.sol"; import { SimpleUSDC } from "@test/test/token/SimpleUSDC.t.sol"; @@ -52,13 +53,24 @@ contract CredbullFixedYieldVaultTest is Test { statuses[1] = true; vm.startPrank(params.roles.operator); - vault.whiteListProvider().updateStatus(whiteListAddresses, statuses); + vault.WHITELIST_PROVIDER().updateStatus(whiteListAddresses, statuses); vm.stopPrank(); SimpleUSDC(address(params.maturityVault.vault.asset)).mint(alice, INITIAL_BALANCE * precision); SimpleUSDC(address(params.maturityVault.vault.asset)).mint(bob, INITIAL_BALANCE * precision); } + function test__FixedYieldVault__RevertOnInvalidAddress() public { + params.roles.owner = address(0); + vm.expectRevert(abi.encodeWithSelector(FixedYieldVault.FixedYieldVault__InvalidOwnerAddress.selector)); + new CredbullFixedYieldVault(params); + + params.roles.owner = makeAddr("owner"); + params.roles.operator = address(0); + vm.expectRevert(abi.encodeWithSelector(FixedYieldVault.FixedYieldVault__InvalidOperatorAddress.selector)); + new CredbullFixedYieldVault(params); + } + function test__FixedYieldVault__ShouldAllowOwnerToChangeOperator() public { address newOperator = makeAddr("new_operator"); @@ -113,6 +125,7 @@ contract CredbullFixedYieldVaultTest is Test { } function test__FixedYieldVault__RevertMaturityToggleIfNotAdmin() public { + bool previousMaturityStatus = vault.checkMaturity(); vm.startPrank(params.roles.operator); vm.expectRevert( abi.encodeWithSelector( @@ -121,11 +134,11 @@ contract CredbullFixedYieldVaultTest is Test { vault.DEFAULT_ADMIN_ROLE() ) ); - vault.toggleMaturityCheck(false); + vault.setMaturityCheck(!previousMaturityStatus); vm.stopPrank(); vm.prank(params.roles.owner); - vault.toggleMaturityCheck(false); + vault.setMaturityCheck(!previousMaturityStatus); } function test__FixedYieldVault__RevertWhiteListToggleIfNotAdmin() public { @@ -137,11 +150,11 @@ contract CredbullFixedYieldVaultTest is Test { vault.DEFAULT_ADMIN_ROLE() ) ); - vault.toggleWhiteListCheck(false); + vault.toggleWhiteListCheck(); vm.stopPrank(); vm.prank(params.roles.owner); - vault.toggleWhiteListCheck(false); + vault.toggleWhiteListCheck(); } function test__FixedYieldVault__RevertWindowToggleIfNotAdmin() public { @@ -153,11 +166,11 @@ contract CredbullFixedYieldVaultTest is Test { vault.DEFAULT_ADMIN_ROLE() ) ); - vault.toggleWindowCheck(false); + vault.toggleWindowCheck(); vm.stopPrank(); vm.prank(params.roles.owner); - vault.toggleWindowCheck(false); + vault.toggleWindowCheck(); } function test__FixedYieldVault__ShouldCheckForWhiteListedAddresses() public { @@ -168,7 +181,7 @@ contract CredbullFixedYieldVaultTest is Test { statuses[0] = false; vm.startPrank(params.roles.operator); - vault.whiteListProvider().updateStatus(whiteListAddresses, statuses); + vault.WHITELIST_PROVIDER().updateStatus(whiteListAddresses, statuses); vm.stopPrank(); uint256 depositAmount = 1000 * precision; @@ -186,7 +199,7 @@ contract CredbullFixedYieldVaultTest is Test { statuses[0] = false; vm.startPrank(params.roles.operator); - vault.whiteListProvider().updateStatus(whiteListAddresses, statuses); + vault.WHITELIST_PROVIDER().updateStatus(whiteListAddresses, statuses); vm.stopPrank(); uint256 depositAmount = 1000 * precision; @@ -200,6 +213,7 @@ contract CredbullFixedYieldVaultTest is Test { } function test__FixedYieldVault__RevertMaxCapToggleIfNotAdmin() public { + bool previousStatus = vault.checkMaxCap(); vm.startPrank(params.roles.operator); vm.expectRevert( abi.encodeWithSelector( @@ -208,11 +222,11 @@ contract CredbullFixedYieldVaultTest is Test { vault.DEFAULT_ADMIN_ROLE() ) ); - vault.toggleMaxCapCheck(false); + vault.setCheckMaxCap(!previousStatus); vm.stopPrank(); vm.prank(params.roles.owner); - vault.toggleMaxCapCheck(false); + vault.setCheckMaxCap(!previousStatus); } function test__FixedYieldVault__RevertUdpateMaxCapIfNotAdmin() public { @@ -233,7 +247,7 @@ contract CredbullFixedYieldVaultTest is Test { function test__FixedYieldVault__RevertOpsIfVaultIsPaused() public { vm.startPrank(params.roles.owner); - vault.toggleWindowCheck(false); + vault.toggleWindowCheck(); vault.pauseVault(); vm.expectRevert(abi.encodeWithSelector(Pausable.EnforcedPause.selector)); vault.deposit(1000 * precision, alice); @@ -246,7 +260,7 @@ contract CredbullFixedYieldVaultTest is Test { function test__FixedYieldVault__ShouldAllowAdminToUnpauseVault() public { vm.startPrank(params.roles.owner); - vault.toggleWindowCheck(false); + vault.toggleWindowCheck(); vault.pauseVault(); vm.expectRevert(abi.encodeWithSelector(Pausable.EnforcedPause.selector)); vault.deposit(1000 * precision, alice); diff --git a/packages/contracts/test/src/CredbullFixedYieldVaultWithUpsideTest.t.sol b/packages/contracts/test/src/CredbullFixedYieldVaultWithUpsideTest.t.sol index 346d3553b..2e013d674 100644 --- a/packages/contracts/test/src/CredbullFixedYieldVaultWithUpsideTest.t.sol +++ b/packages/contracts/test/src/CredbullFixedYieldVaultWithUpsideTest.t.sol @@ -64,13 +64,20 @@ contract CredbullFixedYieldVaultWithUpsideTest is Test { statuses[0] = true; vm.startPrank(vaultParams.roles.operator); - vault.whiteListProvider().updateStatus(whiteListAddresses, statuses); + vault.WHITELIST_PROVIDER().updateStatus(whiteListAddresses, statuses); vm.stopPrank(); SimpleUSDC(address(vaultParams.maturityVault.vault.asset)).mint(alice, INITIAL_BALANCE * precision); SimpleToken(address(cblToken)).mint(alice, 200 ether); } + function test__UpsideVault__ShouldRevertOnInvalidUpsidePercentage() public { + CredbullFixedYieldVaultWithUpside.UpsideVaultParams memory params = upsideVaultParams; + params.upsidePercentage = 100_01; + vm.expectRevert(abi.encodeWithSelector(UpsideVault.CredbullVault__InvalidUpsidePercentage.selector)); + new CredbullFixedYieldVaultWithUpside(params); + } + function test__UpsideVault__VaultCreationShouldRevertOnUnsupportedDecimalValue() public { CredbullFixedYieldVaultWithUpside.UpsideVaultParams memory params = upsideVaultParams; params.cblToken = new DecimalToken(1e6, 19); diff --git a/packages/contracts/test/src/CredbullUpsideVaultFactoryTest.t.sol b/packages/contracts/test/src/CredbullUpsideVaultFactoryTest.t.sol index 10ba09de9..4133ca490 100644 --- a/packages/contracts/test/src/CredbullUpsideVaultFactoryTest.t.sol +++ b/packages/contracts/test/src/CredbullUpsideVaultFactoryTest.t.sol @@ -51,7 +51,7 @@ contract CredbullUpsideVaultFactoryTest is Test { assertEq(vault.asset(), address(params.fixedYieldVault.maturityVault.vault.asset)); assertEq(vault.name(), params.fixedYieldVault.maturityVault.vault.shareName); assertEq(vault.symbol(), params.fixedYieldVault.maturityVault.vault.shareSymbol); - assertEq(address(vault.whiteListProvider()), params.fixedYieldVault.whiteListPlugin.whiteListProvider); + assertEq(address(vault.WHITELIST_PROVIDER()), params.fixedYieldVault.whiteListPlugin.whiteListProvider); assertEq(vault.CUSTODIAN(), params.fixedYieldVault.maturityVault.vault.custodian); } } diff --git a/packages/contracts/test/src/plugin/MaxCapPluginTest.t.sol b/packages/contracts/test/src/plugin/MaxCapPluginTest.t.sol index 6289dba4a..0fb471310 100644 --- a/packages/contracts/test/src/plugin/MaxCapPluginTest.t.sol +++ b/packages/contracts/test/src/plugin/MaxCapPluginTest.t.sol @@ -66,6 +66,8 @@ contract MaxCapPluginTest is Test { assertTrue(newValue != currentValue); + vm.expectEmit(); + emit MaxCapPlugin.MaxCapUpdated(newValue); vault.updateMaxCap(newValue); assertTrue(vault.maxCap() == newValue); diff --git a/packages/contracts/test/src/plugin/WhiteListPluginTest.t.sol b/packages/contracts/test/src/plugin/WhiteListPluginTest.t.sol index 01b4ad946..bfa928e1b 100644 --- a/packages/contracts/test/src/plugin/WhiteListPluginTest.t.sol +++ b/packages/contracts/test/src/plugin/WhiteListPluginTest.t.sol @@ -50,7 +50,7 @@ contract WhiteListPluginTest is Test { statuses[1] = true; vm.startPrank(whiteListProvider.owner()); - vault.whiteListProvider().updateStatus(whiteListAddresses, statuses); + vault.WHITELIST_PROVIDER().updateStatus(whiteListAddresses, statuses); vm.stopPrank(); SimpleUSDC(address(vaultParams.asset)).mint(alice, INITIAL_BALANCE * precision); @@ -65,7 +65,7 @@ contract WhiteListPluginTest is Test { statuses[0] = false; vm.startPrank(whiteListProvider.owner()); - vault.whiteListProvider().updateStatus(whiteListAddresses, statuses); + vault.WHITELIST_PROVIDER().updateStatus(whiteListAddresses, statuses); vm.stopPrank(); uint256 depositAmount = 1000 * precision; @@ -116,10 +116,10 @@ contract WhiteListPluginTest is Test { statuses[0] = false; vm.startPrank(whiteListProvider.owner()); - vault.whiteListProvider().updateStatus(whiteListAddresses, statuses); + vault.WHITELIST_PROVIDER().updateStatus(whiteListAddresses, statuses); vm.stopPrank(); - vault.toggleWhiteListCheck(false); + vault.toggleWhiteListCheck(); deposit(alice, 10 * precision); assertEq(vault.balanceOf(alice), 10 * precision); @@ -127,7 +127,7 @@ contract WhiteListPluginTest is Test { function test__WhiteListVault__ShouldToggleWhiteList() public { bool beforeToggle = vault.checkWhiteList(); - vault.toggleWhiteListCheck(!beforeToggle); + vault.toggleWhiteListCheck(); bool afterToggle = vault.checkWhiteList(); assertEq(afterToggle, !beforeToggle); } diff --git a/packages/contracts/test/src/plugin/WindowPluginTest.t.sol b/packages/contracts/test/src/plugin/WindowPluginTest.t.sol index b5e1011a6..91f478eff 100644 --- a/packages/contracts/test/src/plugin/WindowPluginTest.t.sol +++ b/packages/contracts/test/src/plugin/WindowPluginTest.t.sol @@ -143,7 +143,7 @@ contract WindowPluginTest is Test { } function test__WindowVault__ShouldNotRevertOnWindowModifier() public { - vault.toggleWindowCheck(false); + vault.toggleWindowCheck(); deposit(alice, 10 * precision); assertEq(vault.balanceOf(alice), 10 * precision); @@ -151,7 +151,7 @@ contract WindowPluginTest is Test { function test__WindowVault__ShouldToggleWhiteList() public { bool beforeToggle = vault.checkWindow(); - vault.toggleWindowCheck(!beforeToggle); + vault.toggleWindowCheck(); bool afterToggle = vault.checkWindow(); assertEq(afterToggle, !beforeToggle); } @@ -172,6 +172,8 @@ contract WindowPluginTest is Test { assertTrue(currentWithdrawOpen != newWithdrawOpen); assertTrue(currentWithdrawClose != newWithdrawClose); + vm.expectEmit(); + emit WindowPlugin.WindowUpdated(newDepositOpen, newDepositClose, newWithdrawOpen, newWithdrawClose); vault.updateWindow(newDepositOpen, newDepositClose, newWithdrawOpen, newWithdrawClose); assertTrue(vault.depositOpensAtTimestamp() == newDepositOpen); @@ -180,6 +182,77 @@ contract WindowPluginTest is Test { assertTrue(vault.redemptionClosesAtTimestamp() == newWithdrawClose); } + function test__WindowVault__RevertOnIncorrectWindowParams() public { + // Withdraw window opens before deposit window + uint256 newDepositOpen = 100; + uint256 newDepositClose = 300; + uint256 newWithdrawOpen = 200; + uint256 newWithdrawClose = 400; + + // Deposit window closes before it opens + uint256 newDepositOpen_1 = 300; + uint256 newDepositClose_1 = 100; + uint256 newWithdrawOpen_1 = 200; + uint256 newWithdrawClose_1 = 400; + + WindowPlugin.WindowPluginParams memory localWindowParams = WindowPlugin.WindowPluginParams({ + depositWindow: WindowPlugin.Window({ opensAt: newDepositOpen, closesAt: newDepositClose }), + redemptionWindow: WindowPlugin.Window({ opensAt: newWithdrawOpen, closesAt: newWithdrawClose }) + }); + + vm.expectRevert( + abi.encodeWithSelector( + WindowPlugin.WindowPlugin__IncorrectWindowValues.selector, + newDepositOpen, + newDepositClose, + newWithdrawOpen, + newWithdrawClose + ) + ); + vault.updateWindow(newDepositOpen, newDepositClose, newWithdrawOpen, newWithdrawClose); + + vm.expectRevert( + abi.encodeWithSelector( + WindowPlugin.WindowPlugin__IncorrectWindowValues.selector, + newDepositOpen_1, + newDepositClose_1, + newWithdrawOpen_1, + newWithdrawClose_1 + ) + ); + vault.updateWindow(newDepositOpen_1, newDepositClose_1, newWithdrawOpen_1, newWithdrawClose_1); + + { + // Withdraw window closes before it opens + uint256 newDepositOpen_2 = 100; + uint256 newDepositClose_2 = 300; + uint256 newWithdrawOpen_2 = 400; + uint256 newWithdrawClose_2 = 200; + vm.expectRevert( + abi.encodeWithSelector( + WindowPlugin.WindowPlugin__IncorrectWindowValues.selector, + newDepositOpen_2, + newDepositClose_2, + newWithdrawOpen_2, + newWithdrawClose_2 + ) + ); + vault.updateWindow(newDepositOpen_2, newDepositClose_2, newWithdrawOpen_2, newWithdrawClose_2); + } + + //checking modifier on constructor + vm.expectRevert( + abi.encodeWithSelector( + WindowPlugin.WindowPlugin__IncorrectWindowValues.selector, + newDepositOpen, + newDepositClose, + newWithdrawOpen, + newWithdrawClose + ) + ); + new SimpleWindowVault(vaultParams, localWindowParams); + } + function deposit(address user, uint256 assets) internal returns (uint256 shares) { // first, approve the deposit vm.startPrank(user); diff --git a/packages/contracts/test/src/vault/MaturityVaultTest.t.sol b/packages/contracts/test/src/vault/MaturityVaultTest.t.sol index 981d4ddbf..3edf3ff60 100644 --- a/packages/contracts/test/src/vault/MaturityVaultTest.t.sol +++ b/packages/contracts/test/src/vault/MaturityVaultTest.t.sol @@ -50,6 +50,8 @@ contract MaturityVaultTest is Test { params.vault.asset.transferFrom(params.vault.custodian, address(vault), finalBalance); vm.stopPrank(); + vm.expectEmit(); + emit MaturityVault.VaultMatured(finalBalance); vault.mature(); // ---- Assert Vault burns shares and Alice receive asset with additional 10% --- @@ -113,16 +115,16 @@ contract MaturityVaultTest is Test { vm.startPrank(alice); vault.approve(address(vault), shares); - vault.toogleMaturityCheck(false); + vault.setMaturityCheck(!vault.checkMaturity()); vault.redeem(shares, alice, alice); assertEq(params.vault.asset.balanceOf(alice), INITIAL_BALANCE * precision); vm.stopPrank(); } - function test__MaturityVault__ShouldToggleMaturityCheck() public { + function test__MaturityVault__ShouldSetMaturityCheck() public { bool beforeToggle = vault.checkMaturity(); - vault.toogleMaturityCheck(!beforeToggle); + vault.setMaturityCheck(!beforeToggle); bool afterToggle = vault.checkMaturity(); assertEq(afterToggle, !beforeToggle); } diff --git a/packages/contracts/test/test/vault/SimpleMaturityVault.t.sol b/packages/contracts/test/test/vault/SimpleMaturityVault.t.sol index a680a0b68..cba7a133b 100644 --- a/packages/contracts/test/test/vault/SimpleMaturityVault.t.sol +++ b/packages/contracts/test/test/vault/SimpleMaturityVault.t.sol @@ -17,7 +17,7 @@ contract SimpleMaturityVault is MaturityVault { _; } - function toogleMaturityCheck(bool status) public { - _toggleMaturityCheck(status); + function setMaturityCheck(bool _setMaturityCheckStatus) public { + _setMaturityCheck(_setMaturityCheckStatus); } } diff --git a/packages/contracts/test/test/vault/SimpleMaxCapVault.t.sol b/packages/contracts/test/test/vault/SimpleMaxCapVault.t.sol index e53f9e5b0..c66ef8450 100644 --- a/packages/contracts/test/test/vault/SimpleMaxCapVault.t.sol +++ b/packages/contracts/test/test/vault/SimpleMaxCapVault.t.sol @@ -20,8 +20,8 @@ contract SimpleMaxCapVault is Vault, MaxCapPlugin { _; } - function toggleMaxCapCheck(bool status) public { - _toggleMaxCapCheck(status); + function setCheckMaxCap(bool _checkMaxCapStatus) public { + _setCheckMaxCap(_checkMaxCapStatus); } function updateMaxCap(uint256 _value) public { diff --git a/packages/contracts/test/test/vault/SimpleWhiteListVault.t.sol b/packages/contracts/test/test/vault/SimpleWhiteListVault.t.sol index 0252da92f..1f42f5491 100644 --- a/packages/contracts/test/test/vault/SimpleWhiteListVault.t.sol +++ b/packages/contracts/test/test/vault/SimpleWhiteListVault.t.sol @@ -20,7 +20,7 @@ contract SimpleWhiteListVault is Vault, WhiteListPlugin { _; } - function toggleWhiteListCheck(bool status) public { - _toggleWhiteListCheck(status); + function toggleWhiteListCheck() public { + _toggleWhiteListCheck(); } } diff --git a/packages/contracts/test/test/vault/SimpleWindowVault.t.sol b/packages/contracts/test/test/vault/SimpleWindowVault.t.sol index b97c1d8eb..2f256eac9 100644 --- a/packages/contracts/test/test/vault/SimpleWindowVault.t.sol +++ b/packages/contracts/test/test/vault/SimpleWindowVault.t.sol @@ -33,7 +33,7 @@ contract SimpleWindowVault is Vault, WindowPlugin { _updateWindow(_depositOpen, _depositClose, _withdrawOpen, _withdrawClose); } - function toggleWindowCheck(bool status) public { - _toggleWindowCheck(status); + function toggleWindowCheck() public { + _toggleWindowCheck(); } } diff --git a/packages/contracts/test/test/vault/utils/ParamsFactory.t.sol b/packages/contracts/test/test/vault/utils/ParamsFactory.t.sol index 3f99f4c20..fdc04bc61 100644 --- a/packages/contracts/test/test/vault/utils/ParamsFactory.t.sol +++ b/packages/contracts/test/test/vault/utils/ParamsFactory.t.sol @@ -40,7 +40,7 @@ contract ParamsFactory is Test { params = UpsideVault.UpsideVaultParams({ fixedYieldVault: createFixedYieldVaultParams(), cblToken: networkConfig.cblToken, - collateralPercentage: 20_00 + upsidePercentage: 20_00 }); } diff --git a/yarn.lock b/yarn.lock index 9192e8a91..070939767 100644 --- a/yarn.lock +++ b/yarn.lock @@ -642,7 +642,7 @@ __metadata: ethers: ^5.7.2 js-toml: ^1.0.0 prettier: ^3.2.2 - solhint: ^5.0.1 + solhint: ^5.0.3 typechain: ^8.3.2 typescript: ^5.4.5 languageName: unknown @@ -15705,9 +15705,9 @@ __metadata: languageName: node linkType: hard -"solhint@npm:^5.0.1": - version: 5.0.1 - resolution: "solhint@npm:5.0.1" +"solhint@npm:^5.0.3": + version: 5.0.3 + resolution: "solhint@npm:5.0.3" dependencies: "@solidity-parser/parser": ^0.18.0 ajv: ^6.12.6 @@ -15733,7 +15733,7 @@ __metadata: optional: true bin: solhint: solhint.js - checksum: ff961f5e3e62172b6e26cda758b4b2e266cd07fdc32f280bfbafeb9eda99177326515aaeb5dfff531eeb03c01e432488783f4406439e7524c8da1afa0235a44e + checksum: 30361c0099cded492059719e477cecadcc7390f87a1737904411e8dbf15277344a5892e0762738a025cefd78467455601db1d7d6ffb3d489a6ab6e296c6a7b92 languageName: node linkType: hard