From ff16f12ea1ab1c658da3282048bb352712dc19da Mon Sep 17 00:00:00 2001 From: Dzmitry Lahunouski <22351223+killroy192@users.noreply.github.com> Date: Sun, 3 May 2026 11:07:51 +0100 Subject: [PATCH 1/4] draft fix --- src/vault/Interest.lib.sol | 18 +++++++++++ src/vault/Vault.sol | 55 +++++++++++++++++++------------- src/vault/Vault.types.sol | 7 +++++ src/vault/VaultSpend.lib.sol | 2 +- test/Vault.spend11.t.sol | 61 +++++++++++++++++++++++++++++++++++- 5 files changed, 120 insertions(+), 23 deletions(-) diff --git a/src/vault/Interest.lib.sol b/src/vault/Interest.lib.sol index e100c07..85d1996 100644 --- a/src/vault/Interest.lib.sol +++ b/src/vault/Interest.lib.sol @@ -99,6 +99,24 @@ library InterestLib { state.interestStates[token].assetsSupply -= assets; } + /** + * @notice Burns shares proportional to `feeAssets` without reducing `assetsSupply`. + * @dev Used to distribute protocol fees that stay in the vault as real tokens + * but were already counted in `assetsSupply`. Burning shares makes + * remaining shares worth more, effectively distributing the fee as interest. + * @param state The state of the vault + * @param feeAssets The fee amount (in asset units) to redistribute + * @param token The token to burn shares for + */ + function burnSharesForFee(State storage state, uint208 feeAssets, address token) external { + uint208 sharesSupply = state.interestStates[token].sharesSupply; + uint208 assetsSupply = state.interestStates[token].assetsSupply; + if (sharesSupply == 0 || assetsSupply == 0 || feeAssets == 0) return; + uint208 sharesToBurn = _convertToShares(sharesSupply, assetsSupply, feeAssets, Math.Rounding.Ceil); + if (sharesToBurn > sharesSupply) sharesToBurn = sharesSupply; + state.interestStates[token].sharesSupply = sharesSupply - sharesToBurn; + } + /** * @notice Returns the assets per `scale()` shares * @param state The state of the vault diff --git a/src/vault/Vault.sol b/src/vault/Vault.sol index cdf624c..6141446 100644 --- a/src/vault/Vault.sol +++ b/src/vault/Vault.sol @@ -5,8 +5,9 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; -import {AccessManagedUpgradeable} from - "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; +import { + AccessManagedUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; // not upgradable contracts & interfaces import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; @@ -16,7 +17,8 @@ import { CommitmentRecord, DepositParams, Transaction, - ICommitmentsRecipient + ICommitmentsRecipient, + InterestState } from "src/vault/Vault.types.sol"; // libs @@ -77,7 +79,13 @@ contract Vault is revert ProtocolManagerAuthorityMismatch(); } VaultInitLib.init_unchained( - _getStorage(), verifiers, trustedForwarder, protocolManager, dropMerkleRoot, token, strategyVault + _getStorage(), + verifiers, + trustedForwarder, + protocolManager, + dropMerkleRoot, + token, + strategyVault ); } @@ -175,11 +183,10 @@ contract Vault is } /// @inheritdoc IVault - function deposit(DepositParams calldata depositParams, uint256[24] calldata proof) - external - nonReentrant - whenNotPaused - { + function deposit( + DepositParams calldata depositParams, + uint256[24] calldata proof + ) external nonReentrant whenNotPaused { VaultDepositLib.deposit(_getStorage(), _msgSender(), depositParams, proof); } @@ -193,8 +200,9 @@ contract Vault is bytes32 s ) external nonReentrant whenNotPaused { address from = _msgSender(); - try IERC20Permit(depositParams.token).permit(from, address(this), depositParams.amount, deadline, v, r, s) {} - catch {} + try + IERC20Permit(depositParams.token).permit(from, address(this), depositParams.amount, deadline, v, r, s) + {} catch {} VaultDepositLib.deposit(_getStorage(), from, depositParams, proof); } @@ -204,11 +212,12 @@ contract Vault is } /// @inheritdoc IVault - function spendAndCall(address to, Transaction calldata transaction, uint256[24] calldata proof, bytes calldata data) - external - nonReentrant - whenNotPaused - { + function spendAndCall( + address to, + Transaction calldata transaction, + uint256[24] calldata proof, + bytes calldata data + ) external nonReentrant whenNotPaused { VaultSpendLib.spend(_getStorage(), _msgSender(), transaction, proof); ICommitmentsRecipient(to).onCommitmentsReceived(_msgSender(), transaction, data); } @@ -226,11 +235,10 @@ contract Vault is } /// @inheritdoc IVault - function previewFactors(address token, uint256[] calldata poseidonHashes) - external - view - returns (uint256[] memory factors) - { + function previewFactors( + address token, + uint256[] calldata poseidonHashes + ) external view returns (uint256[] memory factors) { return InputModifierLib.previewFactors(_getStorage(), token, poseidonHashes); } @@ -268,4 +276,9 @@ contract Vault is function getTokenDistributor() external view returns (address) { return address(_getStorage().tokenDistributor); } + + /// @inheritdoc IVault + function getInterestState(address token) external view returns (InterestState memory) { + return _getStorage().interestStates[token]; + } } diff --git a/src/vault/Vault.types.sol b/src/vault/Vault.types.sol index 22a8790..764a35e 100644 --- a/src/vault/Vault.types.sol +++ b/src/vault/Vault.types.sol @@ -259,6 +259,13 @@ interface IVault is IVaultEvents { * @return The address of the TokenDistributor contract */ function getTokenDistributor() external view returns (address); + + /** + * @notice Returns the interest state for a token + * @param token The token to get the interest state for + * @return interestState The interest state for the token + */ + function getInterestState(address token) external view returns (InterestState memory); } /** diff --git a/src/vault/VaultSpend.lib.sol b/src/vault/VaultSpend.lib.sol index 340f237..9afd2eb 100644 --- a/src/vault/VaultSpend.lib.sol +++ b/src/vault/VaultSpend.lib.sol @@ -217,7 +217,7 @@ library VaultSpendLib { processPublicOutputs(state, transaction, totalWithdraw); if (protocolFee > 0) { - InterestLib.modifyAssets(state, protocolFee, true, token); + InterestLib.burnSharesForFee(state, protocolFee, token); } emit IVaultEvents.Spend( diff --git a/test/Vault.spend11.t.sol b/test/Vault.spend11.t.sol index ec7c8d4..8624691 100644 --- a/test/Vault.spend11.t.sol +++ b/test/Vault.spend11.t.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.21; import {VaultTest} from "./VaultTest.util.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import { - Transaction, OutputsOwners, PublicOutput, IVaultErrors, ICommitmentsRecipient + Transaction, OutputsOwners, PublicOutput, IVaultErrors, ICommitmentsRecipient, InterestState } from "src/vault/Vault.types.sol"; import {IVaultEvents} from "src/vault/Vault.types.sol"; import {Fees} from "src/ProtocolManager.types.sol"; @@ -233,4 +233,63 @@ contract VaultSpend11Test is VaultTest, IVaultEvents { assertEq(recipient.calls(), 1); } + + function test_spend11_deposit_spend_redeposit_assetsSupply() public { + uint208 depositAmount = 4000e18; + uint208 publicOutAmount = 500e18; + + mockToken.mint(alice, depositAmount); + uint208 spendFee = _maxSpendFee(); + protocolManager.setMaxTVL(address(mockToken), depositAmount); + protocolManager.setFees(address(mockToken), Fees({deposit: 0, spend: spendFee, withdraw: 0})); + protocolManager.setInvestmentStrategyAssetsOffset(address(mockToken), depositAmount); + + uint256[3] memory depositHashes = [uint256(10001), uint256(10002), uint256(10003)]; + createDeposit(alice, depositAmount, 0, 0, depositHashes, [alice, bob, charlie]); + + InterestState memory stateAfterDeposit = vault.getInterestState(address(mockToken)); + assertEq(stateAfterDeposit.assetsSupply, depositAmount, "assetsSupply after deposit"); + assertEq(stateAfterDeposit.sharesSupply, depositAmount, "sharesSupply after deposit"); + + uint256 outputHash = 20001; + Transaction memory txData = + _buildSpend11(address(mockToken), depositHashes[0], outputHash, alice, publicOutAmount, alice, spendFee); + spend11Verifier.setVerificationResult(true); + vm.prank(alice); + vault.spend(txData, getDummyProof()); + + InterestState memory stateAfterSpend = vault.getInterestState(address(mockToken)); + uint208 expectedAssetsAfterSpend = depositAmount - publicOutAmount; + assertEq( + stateAfterSpend.assetsSupply, + expectedAssetsAfterSpend, + "assetsSupply after spend must equal deposit minus withdrawal, fee must not inflate it" + ); + assertLt( + stateAfterSpend.sharesSupply, + stateAfterSpend.assetsSupply, + "sharesSupply should be less than assetsSupply (fee accrued as interest)" + ); + assertEq( + stateAfterSpend.assetsSupply, + uint208(mockToken.balanceOf(address(vault))), + "assetsSupply must match actual token balance in vault" + ); + + // Re-deposit the full withdrawn amount — must not be blocked by inflated assetsSupply + uint256[3] memory reDepositHashes = [uint256(30001), uint256(30002), uint256(30003)]; + createDeposit(alice, publicOutAmount, 0, 0, reDepositHashes, [alice, bob, charlie]); + + InterestState memory stateAfterReDeposit = vault.getInterestState(address(mockToken)); + assertEq( + stateAfterReDeposit.assetsSupply, + expectedAssetsAfterSpend + publicOutAmount, + "assetsSupply after re-deposit should be previous + new deposit" + ); + assertEq( + stateAfterReDeposit.assetsSupply, + uint208(mockToken.balanceOf(address(vault))), + "assetsSupply must match actual token balance after re-deposit" + ); + } } From 27b46349cbc7cff94c3687f83645dba4ee9d7552 Mon Sep 17 00:00:00 2001 From: Dzmitry Lahunouski <22351223+killroy192@users.noreply.github.com> Date: Sun, 3 May 2026 11:08:15 +0100 Subject: [PATCH 2/4] format --- src/Administrator.sol | 10 +++---- src/token/TokenDistributor.sol | 32 ++++++++++------------- src/vault/Interest.lib.sol | 48 +++++++++++++++------------------- src/vault/Vault.sol | 47 ++++++++++++++------------------- src/vault/VaultDeposit.lib.sol | 39 +++++++++++---------------- test/Vault.spend11.t.sol | 7 ++++- 6 files changed, 79 insertions(+), 104 deletions(-) diff --git a/src/Administrator.sol b/src/Administrator.sol index 1cade3d..57ded4f 100644 --- a/src/Administrator.sol +++ b/src/Administrator.sol @@ -58,7 +58,7 @@ contract Administrator is AccessManager { if (admin == msg.sender) { revert AdminMustDifferFromCaller(); } - (bool isAdmin, ) = hasRole(ADMIN_ROLE, msg.sender); + (bool isAdmin,) = hasRole(ADMIN_ROLE, msg.sender); if (!isAdmin) { revert GovernanceNotAuthorized(); } @@ -108,14 +108,10 @@ contract Administrator is AccessManager { _setTargetFunctionRole(vault, IVault.collectCuratorPerformanceFee.selector, RolesLib.PROTOCOL_MANAGER); address protocolManager = IVault(vault).getManager(); _setTargetFunctionRole( - protocolManager, - IProtocolManager.setCuratorPerformanceFee.selector, - RolesLib.PROTOCOL_MANAGER + protocolManager, IProtocolManager.setCuratorPerformanceFee.selector, RolesLib.PROTOCOL_MANAGER ); _setTargetFunctionRole( - protocolManager, - IProtocolManager.setInvestmentStrategyAssetsOffset.selector, - RolesLib.PROTOCOL_MANAGER + protocolManager, IProtocolManager.setInvestmentStrategyAssetsOffset.selector, RolesLib.PROTOCOL_MANAGER ); _setTargetFunctionRole(protocolManager, IProtocolManager.setFees.selector, RolesLib.PROTOCOL_MANAGER); _setTargetFunctionRole(protocolManager, IProtocolManager.setMaxTVL.selector, RolesLib.PROTOCOL_MANAGER); diff --git a/src/token/TokenDistributor.sol b/src/token/TokenDistributor.sol index 23bc06c..2e9f7b3 100644 --- a/src/token/TokenDistributor.sol +++ b/src/token/TokenDistributor.sol @@ -81,7 +81,7 @@ contract TokenDistributor is ITokenDistributor, Ownable, ReentrancyGuard { /// @inheritdoc ITokenDistributor function previewDistribute(uint208 anchorAmount) external view returns (uint256 toMint) { - (toMint, ) = _compute(TDV, anchorAmount); + (toMint,) = _compute(TDV, anchorAmount); } /** @@ -102,11 +102,8 @@ contract TokenDistributor is ITokenDistributor, Ownable, ReentrancyGuard { uint208 leftTDVInLayer = uint208(mintingLayers[layer].TDVLayerCap * 10 ** anchorDecimals) - newTDV; if (anchorAmountToProcess > leftTDVInLayer) { - toMintAcc += (leftTDVInLayer * anchorToTokenPow).mulDiv( - mintingLayers[layer].multiplier, - LAYER_POW, - Math.Rounding.Floor - ); + toMintAcc += + (leftTDVInLayer * anchorToTokenPow).mulDiv(mintingLayers[layer].multiplier, LAYER_POW, Math.Rounding.Floor); layer = ++layer; anchorAmountToProcess -= leftTDVInLayer; newTDV = newTDV + leftTDVInLayer; @@ -114,9 +111,7 @@ contract TokenDistributor is ITokenDistributor, Ownable, ReentrancyGuard { // base formula: anchorAmount * layer_multiplier // with decimals: anchorAmount * 10 ** (token_decimals - decimals) * layer_multiplier / 10 ** LAYER_DECIMALS toMintAcc += (uint256(anchorAmountToProcess) * anchorToTokenPow).mulDiv( - mintingLayers[layer].multiplier, - LAYER_POW, - Math.Rounding.Floor + mintingLayers[layer].multiplier, LAYER_POW, Math.Rounding.Floor ); newTDV = newTDV + anchorAmountToProcess; anchorAmountToProcess = 0; @@ -161,19 +156,20 @@ contract TokenDistributor is ITokenDistributor, Ownable, ReentrancyGuard { } /// @inheritdoc ITokenDistributor - function claim( - bytes32[] calldata proof, - uint256 amount - ) external nonReentrant onlyNotClaimed(msg.sender) onlyValidProof(msg.sender, amount, proof) { + function claim(bytes32[] calldata proof, uint256 amount) + external + nonReentrant + onlyNotClaimed(msg.sender) + onlyValidProof(msg.sender, amount, proof) + { claimed[msg.sender] = true; Token(token).mint(msg.sender, amount); } - function migrateDropTokensAndTDV( - address[] calldata accounts, - uint256[] calldata amounts, - uint208 newTDV - ) external onlyOwner { + function migrateDropTokensAndTDV(address[] calldata accounts, uint256[] calldata amounts, uint208 newTDV) + external + onlyOwner + { if (TDV != 0) { revert("migrated"); } diff --git a/src/vault/Interest.lib.sol b/src/vault/Interest.lib.sol index 85d1996..8744a56 100644 --- a/src/vault/Interest.lib.sol +++ b/src/vault/Interest.lib.sol @@ -40,12 +40,11 @@ library InterestLib { * @param token The token to preview the assets supply for * @return The new assets supply */ - function previewModifyAssets( - State storage state, - uint208 assets, - bool isGain, - address token - ) public view returns (uint208) { + function previewModifyAssets(State storage state, uint208 assets, bool isGain, address token) + public + view + returns (uint208) + { if (isGain) { return state.interestStates[token].assetsSupply + assets; } else { @@ -90,10 +89,7 @@ library InterestLib { function withdraw(State storage state, uint208 assets, address token) external { if (assets == 0) revert AmountIsZero(); uint208 shares = _convertToShares( - state.interestStates[token].sharesSupply, - state.interestStates[token].assetsSupply, - assets, - Math.Rounding.Ceil + state.interestStates[token].sharesSupply, state.interestStates[token].assetsSupply, assets, Math.Rounding.Ceil ); state.interestStates[token].sharesSupply -= shares; state.interestStates[token].assetsSupply -= assets; @@ -134,11 +130,11 @@ library InterestLib { * @param token The token to get the assets per `scale()` shares for * @return The assets per `scale()` shares */ - function previewAssetsPerScaledShare( - State storage state, - uint208 assets, - address token - ) public view returns (uint208) { + function previewAssetsPerScaledShare(State storage state, uint208 assets, address token) + public + view + returns (uint208) + { return _convertToAssets(state.interestStates[token].sharesSupply, assets, scale(), Math.Rounding.Floor); } @@ -150,12 +146,11 @@ library InterestLib { * @param rounding The rounding direction * @return The amount of assets */ - function _convertToAssets( - uint256 sharesSupply, - uint208 assetsSupply, - uint208 shares, - Math.Rounding rounding - ) private pure returns (uint208) { + function _convertToAssets(uint256 sharesSupply, uint208 assetsSupply, uint208 shares, Math.Rounding rounding) + private + pure + returns (uint208) + { if (sharesSupply == 0 || assetsSupply == 0) { return shares; } @@ -170,12 +165,11 @@ library InterestLib { * @param rounding The rounding direction * @return The amount of shares */ - function _convertToShares( - uint208 sharesSupply, - uint208 assetsSupply, - uint208 assets, - Math.Rounding rounding - ) private pure returns (uint208) { + function _convertToShares(uint208 sharesSupply, uint208 assetsSupply, uint208 assets, Math.Rounding rounding) + private + pure + returns (uint208) + { if (sharesSupply == 0 || assetsSupply == 0) { return assets; } diff --git a/src/vault/Vault.sol b/src/vault/Vault.sol index 6141446..3151d30 100644 --- a/src/vault/Vault.sol +++ b/src/vault/Vault.sol @@ -5,9 +5,8 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; -import { - AccessManagedUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; +import {AccessManagedUpgradeable} from + "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol"; // not upgradable contracts & interfaces import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; @@ -79,13 +78,7 @@ contract Vault is revert ProtocolManagerAuthorityMismatch(); } VaultInitLib.init_unchained( - _getStorage(), - verifiers, - trustedForwarder, - protocolManager, - dropMerkleRoot, - token, - strategyVault + _getStorage(), verifiers, trustedForwarder, protocolManager, dropMerkleRoot, token, strategyVault ); } @@ -183,10 +176,11 @@ contract Vault is } /// @inheritdoc IVault - function deposit( - DepositParams calldata depositParams, - uint256[24] calldata proof - ) external nonReentrant whenNotPaused { + function deposit(DepositParams calldata depositParams, uint256[24] calldata proof) + external + nonReentrant + whenNotPaused + { VaultDepositLib.deposit(_getStorage(), _msgSender(), depositParams, proof); } @@ -200,9 +194,8 @@ contract Vault is bytes32 s ) external nonReentrant whenNotPaused { address from = _msgSender(); - try - IERC20Permit(depositParams.token).permit(from, address(this), depositParams.amount, deadline, v, r, s) - {} catch {} + try IERC20Permit(depositParams.token).permit(from, address(this), depositParams.amount, deadline, v, r, s) {} + catch {} VaultDepositLib.deposit(_getStorage(), from, depositParams, proof); } @@ -212,12 +205,11 @@ contract Vault is } /// @inheritdoc IVault - function spendAndCall( - address to, - Transaction calldata transaction, - uint256[24] calldata proof, - bytes calldata data - ) external nonReentrant whenNotPaused { + function spendAndCall(address to, Transaction calldata transaction, uint256[24] calldata proof, bytes calldata data) + external + nonReentrant + whenNotPaused + { VaultSpendLib.spend(_getStorage(), _msgSender(), transaction, proof); ICommitmentsRecipient(to).onCommitmentsReceived(_msgSender(), transaction, data); } @@ -235,10 +227,11 @@ contract Vault is } /// @inheritdoc IVault - function previewFactors( - address token, - uint256[] calldata poseidonHashes - ) external view returns (uint256[] memory factors) { + function previewFactors(address token, uint256[] calldata poseidonHashes) + external + view + returns (uint256[] memory factors) + { return InputModifierLib.previewFactors(_getStorage(), token, poseidonHashes); } diff --git a/src/vault/VaultDeposit.lib.sol b/src/vault/VaultDeposit.lib.sol index 99a2a0f..af7b0b1 100644 --- a/src/vault/VaultDeposit.lib.sol +++ b/src/vault/VaultDeposit.lib.sol @@ -25,15 +25,14 @@ library VaultDepositLib { * @param depositParams The deposit parameters. * @return netAmount The net deposit value excluding fees. */ - function computeDepositValues( - State storage state, - DepositParams calldata depositParams - ) internal view returns (uint208) { + function computeDepositValues(State storage state, DepositParams calldata depositParams) + internal + view + returns (uint208) + { IProtocolManager manager = state.manager; uint208 computedProtocolFee = VaultMathLib.calculateShareUp( - depositParams.amount, - manager.getFees(depositParams.token).deposit, - manager.getFeeScale() + depositParams.amount, manager.getFees(depositParams.token).deposit, manager.getFeeScale() ); if (depositParams.protocolFee < computedProtocolFee) { @@ -54,11 +53,8 @@ library VaultDepositLib { function preDeposit(State storage state, DepositParams calldata depositParams) private { (uint208 strategyDelta, bool isStrategyGain) = InvestmentVaultLib.snapshotStrategyDelta(state, depositParams.token); - (uint208 assetsDelta, bool isGain) = VaultMathLib.calculateAssetsDelta( - strategyDelta, - isStrategyGain, - depositParams.protocolFee - ); + (uint208 assetsDelta, bool isGain) = + VaultMathLib.calculateAssetsDelta(strategyDelta, isStrategyGain, depositParams.protocolFee); InterestLib.modifyAssets(state, assetsDelta, isGain, depositParams.token); } @@ -78,8 +74,7 @@ library VaultDepositLib { revert IVaultErrors.CommitmentAlreadyUsed(poseidonHash); } state.commitmentsMap[depositParams.token][poseidonHash] = CommitmentRecord( - depositParams.depositCommitmentParams[i].owner, - InterestLib.assetsPerScaledShare(state, depositParams.token) + depositParams.depositCommitmentParams[i].owner, InterestLib.assetsPerScaledShare(state, depositParams.token) ); emit IVaultEvents.CommitmentCreated( depositParams.depositCommitmentParams[i].owner, @@ -98,17 +93,14 @@ library VaultDepositLib { * @param depositParams The deposit parameters. * @param proof The proof of the deposit. */ - function deposit( - State storage state, - address from, - DepositParams calldata depositParams, - uint256[24] calldata proof - ) external { + function deposit(State storage state, address from, DepositParams calldata depositParams, uint256[24] calldata proof) + external + { IERC20 t = IERC20(depositParams.token); uint208 netAmount = computeDepositValues(state, depositParams); if ( - state.interestStates[depositParams.token].assetsSupply + depositParams.amount - depositParams.forwarderFee > - state.manager.getMaxTVL(depositParams.token) + state.interestStates[depositParams.token].assetsSupply + depositParams.amount - depositParams.forwarderFee + > state.manager.getMaxTVL(depositParams.token) ) { revert IVaultErrors.AmountExceedsMaxTVL( state.interestStates[depositParams.token].assetsSupply, @@ -118,8 +110,7 @@ library VaultDepositLib { } if ( !state.verifiers.depositVerifier().verify( - proof, - InputsLib.depositInputs(depositParams.depositCommitmentParams, netAmount) + proof, InputsLib.depositInputs(depositParams.depositCommitmentParams, netAmount) ) ) { revert IVaultErrors.InvalidZKProof(); diff --git a/test/Vault.spend11.t.sol b/test/Vault.spend11.t.sol index 8624691..614693d 100644 --- a/test/Vault.spend11.t.sol +++ b/test/Vault.spend11.t.sol @@ -4,7 +4,12 @@ pragma solidity >=0.8.21; import {VaultTest} from "./VaultTest.util.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import { - Transaction, OutputsOwners, PublicOutput, IVaultErrors, ICommitmentsRecipient, InterestState + Transaction, + OutputsOwners, + PublicOutput, + IVaultErrors, + ICommitmentsRecipient, + InterestState } from "src/vault/Vault.types.sol"; import {IVaultEvents} from "src/vault/Vault.types.sol"; import {Fees} from "src/ProtocolManager.types.sol"; From 5bb59452b5dd512a855dd8c756619491bdd15ddf Mon Sep 17 00:00:00 2001 From: Dzmitry Lahunouski <22351223+killroy192@users.noreply.github.com> Date: Tue, 5 May 2026 12:51:08 +0100 Subject: [PATCH 3/4] revert formatting --- src/Administrator.sol | 10 +++++++--- src/token/TokenDistributor.sol | 32 ++++++++++++++++++-------------- src/vault/Vault.sol | 2 +- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/Administrator.sol b/src/Administrator.sol index 57ded4f..1cade3d 100644 --- a/src/Administrator.sol +++ b/src/Administrator.sol @@ -58,7 +58,7 @@ contract Administrator is AccessManager { if (admin == msg.sender) { revert AdminMustDifferFromCaller(); } - (bool isAdmin,) = hasRole(ADMIN_ROLE, msg.sender); + (bool isAdmin, ) = hasRole(ADMIN_ROLE, msg.sender); if (!isAdmin) { revert GovernanceNotAuthorized(); } @@ -108,10 +108,14 @@ contract Administrator is AccessManager { _setTargetFunctionRole(vault, IVault.collectCuratorPerformanceFee.selector, RolesLib.PROTOCOL_MANAGER); address protocolManager = IVault(vault).getManager(); _setTargetFunctionRole( - protocolManager, IProtocolManager.setCuratorPerformanceFee.selector, RolesLib.PROTOCOL_MANAGER + protocolManager, + IProtocolManager.setCuratorPerformanceFee.selector, + RolesLib.PROTOCOL_MANAGER ); _setTargetFunctionRole( - protocolManager, IProtocolManager.setInvestmentStrategyAssetsOffset.selector, RolesLib.PROTOCOL_MANAGER + protocolManager, + IProtocolManager.setInvestmentStrategyAssetsOffset.selector, + RolesLib.PROTOCOL_MANAGER ); _setTargetFunctionRole(protocolManager, IProtocolManager.setFees.selector, RolesLib.PROTOCOL_MANAGER); _setTargetFunctionRole(protocolManager, IProtocolManager.setMaxTVL.selector, RolesLib.PROTOCOL_MANAGER); diff --git a/src/token/TokenDistributor.sol b/src/token/TokenDistributor.sol index 2e9f7b3..23bc06c 100644 --- a/src/token/TokenDistributor.sol +++ b/src/token/TokenDistributor.sol @@ -81,7 +81,7 @@ contract TokenDistributor is ITokenDistributor, Ownable, ReentrancyGuard { /// @inheritdoc ITokenDistributor function previewDistribute(uint208 anchorAmount) external view returns (uint256 toMint) { - (toMint,) = _compute(TDV, anchorAmount); + (toMint, ) = _compute(TDV, anchorAmount); } /** @@ -102,8 +102,11 @@ contract TokenDistributor is ITokenDistributor, Ownable, ReentrancyGuard { uint208 leftTDVInLayer = uint208(mintingLayers[layer].TDVLayerCap * 10 ** anchorDecimals) - newTDV; if (anchorAmountToProcess > leftTDVInLayer) { - toMintAcc += - (leftTDVInLayer * anchorToTokenPow).mulDiv(mintingLayers[layer].multiplier, LAYER_POW, Math.Rounding.Floor); + toMintAcc += (leftTDVInLayer * anchorToTokenPow).mulDiv( + mintingLayers[layer].multiplier, + LAYER_POW, + Math.Rounding.Floor + ); layer = ++layer; anchorAmountToProcess -= leftTDVInLayer; newTDV = newTDV + leftTDVInLayer; @@ -111,7 +114,9 @@ contract TokenDistributor is ITokenDistributor, Ownable, ReentrancyGuard { // base formula: anchorAmount * layer_multiplier // with decimals: anchorAmount * 10 ** (token_decimals - decimals) * layer_multiplier / 10 ** LAYER_DECIMALS toMintAcc += (uint256(anchorAmountToProcess) * anchorToTokenPow).mulDiv( - mintingLayers[layer].multiplier, LAYER_POW, Math.Rounding.Floor + mintingLayers[layer].multiplier, + LAYER_POW, + Math.Rounding.Floor ); newTDV = newTDV + anchorAmountToProcess; anchorAmountToProcess = 0; @@ -156,20 +161,19 @@ contract TokenDistributor is ITokenDistributor, Ownable, ReentrancyGuard { } /// @inheritdoc ITokenDistributor - function claim(bytes32[] calldata proof, uint256 amount) - external - nonReentrant - onlyNotClaimed(msg.sender) - onlyValidProof(msg.sender, amount, proof) - { + function claim( + bytes32[] calldata proof, + uint256 amount + ) external nonReentrant onlyNotClaimed(msg.sender) onlyValidProof(msg.sender, amount, proof) { claimed[msg.sender] = true; Token(token).mint(msg.sender, amount); } - function migrateDropTokensAndTDV(address[] calldata accounts, uint256[] calldata amounts, uint208 newTDV) - external - onlyOwner - { + function migrateDropTokensAndTDV( + address[] calldata accounts, + uint256[] calldata amounts, + uint208 newTDV + ) external onlyOwner { if (TDV != 0) { revert("migrated"); } diff --git a/src/vault/Vault.sol b/src/vault/Vault.sol index 3151d30..15015f0 100644 --- a/src/vault/Vault.sol +++ b/src/vault/Vault.sol @@ -82,7 +82,7 @@ contract Vault is ); } - function upgradeCallBack() external reinitializer(4) {} + function upgradeCallBack() external reinitializer(5) {} /// @inheritdoc IVault function approveUpgrade(address newImplementation, bytes32 paramsHash) external restricted { From a7a0de2a39d6f0ef05ae1dcd0fbbd40388b427b9 Mon Sep 17 00:00:00 2001 From: Dzmitry Lahunouski <22351223+killroy192@users.noreply.github.com> Date: Tue, 5 May 2026 13:38:51 +0100 Subject: [PATCH 4/4] deploy --- .../chain-8453/deployed_addresses.json | 13 +++--- ignition/modules/VaultUpgrade.module.ts | 41 +++++++++++++++---- scripts/deployVaultUpgrade.ts | 4 +- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/ignition/deployments/chain-8453/deployed_addresses.json b/ignition/deployments/chain-8453/deployed_addresses.json index 0da9797..0cdf6f1 100644 --- a/ignition/deployments/chain-8453/deployed_addresses.json +++ b/ignition/deployments/chain-8453/deployed_addresses.json @@ -16,16 +16,15 @@ "Main#Forwarder": "0xf3430970980d60BEEcEcC5f72b3868B891127c83", "Main#InvoiceFactory": "0xaAc5Dfc0Cb60b5F829e67749a72101A9d7C67FaD", "Main#TokenDistributorDeployLib": "0xe279F01DF9a69695D3203009be176dbB36106241", - "Main#InterestLib": "0xe8426F0675C9Da7340b4E8801cDE6a5C59f23Eb7", + "Main#InterestLib": "0xB2F91663c103CC8742720b1BFC0A094e9e3B3a92", "Main#VaultMathLib": "0x67bCC483fAaD61c3B7eBAdb31c884b8Aba9A13Ff", "Main#InputsLib": "0x8B77b2Bf6A592361aF18803d785c25a1BBE1104c", "Main#InvestmentVaultLib": "0x3B0775204AF03c2E9940CfbCB9fbeA0949CadA7B", - "Main#InputModifierLib": "0x47F8FB8136BFAFb9791D4C0CBf8e0764b02D619e", + "Main#InputModifierLib": "0x83d234D764a86c0485bab15E2827b9bF430abD2A", "Main#VaultInitLib": "0x262166E3d46dA3a796BF7BE448bdddfd11FFB56f", - "Main#VaultDepositLib": "0x60d879820974964b793B60a9FA14103B7f9527B0", - "Main#VaultSpendLib": "0x94CE27CDFD55208678FB250FeD21B7f1FfdEc4Ff", - "Main#VaultImplementation": "0xF136A71D02675DC797863DBab7fB4E30b63d7680", + "Main#VaultDepositLib": "0x51b2787EF2379a4Dc06e71Ef442Eb4be80A0300d", + "Main#VaultSpendLib": "0x8649b2650B98F96F334793FEAF7F3D5Db7ad77A7", + "Main#VaultImplementation": "0x1b366810246346c7e9f746D5306F6BC9bF65b67D", "Main#VaultProxy": "0xE0Eefe4AA0cB32740aDFD8083AbeC255AaC3b379", - "Main#Vault": "0xE0Eefe4AA0cB32740aDFD8083AbeC255AaC3b379", - "Main#ProtocolManager": "0xCC499487F6a1780dB9410a65408B98E63346aCfe" + "Main#Vault": "0xE0Eefe4AA0cB32740aDFD8083AbeC255AaC3b379" } diff --git a/ignition/modules/VaultUpgrade.module.ts b/ignition/modules/VaultUpgrade.module.ts index 5c4c91e..2b6ff76 100644 --- a/ignition/modules/VaultUpgrade.module.ts +++ b/ignition/modules/VaultUpgrade.module.ts @@ -1,17 +1,43 @@ import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; export default buildModule("VaultUpgrade", (m) => { - const interestLib = m.contractAt("InterestLib", "0xe8426F0675C9Da7340b4E8801cDE6a5C59f23Eb7"); + const vaultMathLib = m.contractAt("VaultMathLib", "0x67bCC483fAaD61c3B7eBAdb31c884b8Aba9A13Ff"); const investmentVaultLib = m.contractAt("InvestmentVaultLib", "0x3B0775204AF03c2E9940CfbCB9fbeA0949CadA7B"); + const vaultInitLib = m.contractAt("VaultInitLib", "0x262166E3d46dA3a796BF7BE448bdddfd11FFB56f"); + const inputsLib = m.contractAt("InputsLib", "0x8B77b2Bf6A592361aF18803d785c25a1BBE1104c"); + + const interestLib = m.library("InterestLib", { + id: `InterestLib_6`, + }); - const inputModifierLib = m.contractAt("InputModifierLib", "0x47F8FB8136BFAFb9791D4C0CBf8e0764b02D619e"); - - const vaultDepositLib = m.contractAt("VaultDepositLib", "0x60d879820974964b793B60a9FA14103B7f9527B0"); + const inputModifierLib = m.library("InputModifierLib", { + id: `InputModifierLib_6`, + libraries: { VaultMathLib: vaultMathLib, InterestLib: interestLib, InvestmentVaultLib: investmentVaultLib }, + after: [inputsLib], + }); - const vaultSpendLib = m.contractAt("VaultSpendLib", "0x94CE27CDFD55208678FB250FeD21B7f1FfdEc4Ff"); + const vaultDepositLib = m.library("VaultDepositLib", { + id: `VaultDepositLib_6`, + libraries: { + VaultMathLib: vaultMathLib, + InterestLib: interestLib, + InvestmentVaultLib: investmentVaultLib, + InputsLib: inputsLib, + }, + after: [inputModifierLib], + }); - const vaultInitLib = m.contractAt("VaultInitLib", "0x262166E3d46dA3a796BF7BE448bdddfd11FFB56f"); + const vaultSpendLib = m.library("VaultSpendLib", { + id: `VaultSpendLib_6`, + libraries: { + VaultMathLib: vaultMathLib, + InterestLib: interestLib, + InvestmentVaultLib: investmentVaultLib, + InputsLib: inputsLib, + }, + after: [vaultDepositLib], + }); const newImplementation = m.contract("Vault", [], { libraries: { @@ -22,7 +48,8 @@ export default buildModule("VaultUpgrade", (m) => { VaultSpendLib: vaultSpendLib, InterestLib: interestLib, }, - id: `Vault_5`, + id: `Vault_6`, + after: [vaultSpendLib], }); return { diff --git a/scripts/deployVaultUpgrade.ts b/scripts/deployVaultUpgrade.ts index ae3947e..8a8bdd3 100644 --- a/scripts/deployVaultUpgrade.ts +++ b/scripts/deployVaultUpgrade.ts @@ -2,7 +2,7 @@ import hre from "hardhat"; import { NetworkConfig } from "hardhat/types"; import { mkCreateXSalt } from "./mkCreateXSalt"; import VaultUpgradeModule from "../ignition/modules/VaultUpgrade.module"; -import { deploymentChange } from "./deploymentChange"; +// import { deploymentChange } from "./deploymentChange"; import { getIgnitionConfig } from "./getIgnitionConfig"; type Params = { @@ -41,7 +41,7 @@ async function main() { `New vault implementation deployed. Address: ${newImplementationAddress}, empty bytes params hash: 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`, ); - await deploymentChange(deployer, params.donor); + // await deploymentChange(deployer, params.donor); } main().catch(console.error);