diff --git a/solidity/contracts/lib/ERC4626Fees.sol b/solidity/contracts/lib/ERC4626Fees.sol index b5b4beed1..7ac316a92 100644 --- a/solidity/contracts/lib/ERC4626Fees.sol +++ b/solidity/contracts/lib/ERC4626Fees.sol @@ -13,7 +13,7 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; abstract contract ERC4626Fees is ERC4626Upgradeable { using Math for uint256; - uint256 private constant _BASIS_POINT_SCALE = 1e4; + uint256 internal constant _BASIS_POINT_SCALE = 1e4; // === Overrides === diff --git a/solidity/contracts/stBTC.sol b/solidity/contracts/stBTC.sol index 5bd3c6e1c..da077b9cf 100644 --- a/solidity/contracts/stBTC.sol +++ b/solidity/contracts/stBTC.sol @@ -74,6 +74,9 @@ contract stBTC is ERC4626Fees, PausableOwnable { /// Reverts if the address is disallowed. error DisallowedAddress(); + /// Reverts if the fee basis points exceed the maximum value. + error ExceedsMaxFeeBasisPoints(); + /// Reverts if the treasury address is the same. error SameTreasury(); @@ -160,6 +163,9 @@ contract stBTC is ERC4626Fees, PausableOwnable { function updateEntryFeeBasisPoints( uint256 newEntryFeeBasisPoints ) external onlyOwner { + if (newEntryFeeBasisPoints > _BASIS_POINT_SCALE) { + revert ExceedsMaxFeeBasisPoints(); + } entryFeeBasisPoints = newEntryFeeBasisPoints; emit EntryFeeBasisPointsUpdated(newEntryFeeBasisPoints); @@ -170,18 +176,14 @@ contract stBTC is ERC4626Fees, PausableOwnable { function updateExitFeeBasisPoints( uint256 newExitFeeBasisPoints ) external onlyOwner { + if (newExitFeeBasisPoints > _BASIS_POINT_SCALE) { + revert ExceedsMaxFeeBasisPoints(); + } exitFeeBasisPoints = newExitFeeBasisPoints; emit ExitFeeBasisPointsUpdated(newExitFeeBasisPoints); } - /// @notice Returns the total amount of assets held by the vault across all - /// allocations and this contract. - function totalAssets() public view override returns (uint256) { - return - IERC20(asset()).balanceOf(address(this)) + dispatcher.totalAssets(); - } - /// @notice Calls `receiveApproval` function on spender previously approving /// the spender to withdraw from the caller multiple times, up to /// the `amount` amount. If this function is called again, it @@ -295,6 +297,13 @@ contract stBTC is ERC4626Fees, PausableOwnable { return super.redeem(shares, receiver, owner); } + /// @notice Returns the total amount of assets held by the vault across all + /// allocations and this contract. + function totalAssets() public view override returns (uint256) { + return + IERC20(asset()).balanceOf(address(this)) + dispatcher.totalAssets(); + } + /// @dev Returns the maximum amount of the underlying asset that can be /// deposited into the Vault for the receiver, through a deposit call. /// If the Vault is paused, returns 0. diff --git a/solidity/test/stBTC.test.ts b/solidity/test/stBTC.test.ts index 6b3e108ce..fed6991b0 100644 --- a/solidity/test/stBTC.test.ts +++ b/solidity/test/stBTC.test.ts @@ -1895,7 +1895,7 @@ describe("stBTC", () => { const validEntryFeeBasisPoints = 100n // 1% - context("when is called by governance", () => { + context("when called by the governance", () => { context("when entry fee basis points are valid", () => { beforeAfterSnapshotWrapper() @@ -1937,6 +1937,16 @@ describe("stBTC", () => { ) }) }) + + context("when entry fee basis points exceed 10000", () => { + beforeAfterSnapshotWrapper() + + it("should revert", async () => { + await expect( + stbtc.connect(governance).updateEntryFeeBasisPoints(10001n), + ).to.be.revertedWithCustomError(stbtc, "ExceedsMaxFeeBasisPoints") + }) + }) }) context("when is called by non-governance", () => { @@ -1953,7 +1963,7 @@ describe("stBTC", () => { const validExitFeeBasisPoints = 100n // 1% - context("when is called by governance", () => { + context("when called by the governance", () => { context("when exit fee basis points are valid", () => { beforeAfterSnapshotWrapper() @@ -1978,6 +1988,16 @@ describe("stBTC", () => { }) }) + context("when exit fee basis points exceed 10000", () => { + beforeAfterSnapshotWrapper() + + it("should revert", async () => { + await expect( + stbtc.connect(governance).updateExitFeeBasisPoints(10001n), + ).to.be.revertedWithCustomError(stbtc, "ExceedsMaxFeeBasisPoints") + }) + }) + context("when exit fee basis points are 0", () => { beforeAfterSnapshotWrapper()