diff --git a/protocol/contracts/core/VaultEarlyWithdraw.sol b/protocol/contracts/core/VaultEarlyWithdraw.sol index 1ed15c70f..b357079f2 100644 --- a/protocol/contracts/core/VaultEarlyWithdraw.sol +++ b/protocol/contracts/core/VaultEarlyWithdraw.sol @@ -18,10 +18,14 @@ contract VaultEarlyWithdraw is Pausable, Ownable { IERC20 immutable public templeToken; mapping(address => bool) public validVaults; + // @notice Enforce a minimum withdraw amount to circumvent very small rounding issues on exit + uint256 public minWithdrawAmount = 1_000; + event TokenRecovered(address indexed token, address indexed to, uint256 amount); event EarlyWithdraw(address indexed addr, uint256 amount); + event MinWithdrawAmountSet(uint256 amount); - error ExpectedNonZero(); + error MinAmountNotMet(); error InvalidVault(address _vaultAddress); error InvalidAddress(address _addr); error SendFailed(); @@ -42,12 +46,17 @@ contract VaultEarlyWithdraw is Pausable, Ownable { _unpause(); } + function setMinWithdrawAmount(uint256 amount) external onlyOwner { + minWithdrawAmount = amount; + emit MinWithdrawAmountSet(amount); + } + /** * @notice User with vaulted token in one of the whitelisted vaults can withdraw early. */ function withdraw(address _vault, uint256 _templeAmount) external whenNotPaused { if (!validVaults[_vault]) revert InvalidVault(_vault); - if (_templeAmount == 0) revert ExpectedNonZero(); + if (_templeAmount < minWithdrawAmount) revert MinAmountNotMet(); // Pull the user's vaulted temple, and send to the owner IERC20(_vault).safeTransferFrom(msg.sender, owner(), _templeAmount); diff --git a/protocol/scripts/deploys/helpers.ts b/protocol/scripts/deploys/helpers.ts index 5d5a44b8c..138b5d926 100644 --- a/protocol/scripts/deploys/helpers.ts +++ b/protocol/scripts/deploys/helpers.ts @@ -302,7 +302,7 @@ export const DEPLOYED_CONTRACTS: { [key: string]: DeployedContracts } = { OPS_MANAGER_LIB: '0x248bA5985053ee399a76B5822AdeB12FA0ab1424', JOINING_FEE: '0x8A17403B929ed1B6B50ea880d9C93068a5105D4C', VAULT_PROXY: '0x6f5bB7cC4F3D6628d0095545552757AB377FE15C', - VAULT_EARLY_WITHDRAW: '0x7C6f1b4891ff8CAcCeC97DbbD9Df3b773d88A03E', + VAULT_EARLY_WITHDRAW: '0x24719d3AF60e1B622a29317d29E5Ce283617DeEC', TREASURY_IV: '0xae8a796bd9437Bd266664e8e9B8428B25A7D2477', GENERIC_ZAPS: '0x388d3C524724541800FD74041136caB40FD4DAfE', TEMPLE_ZAPS: '0xb7C30F132DBbBbB1C2b81d9D66a010FB7c72Ff9c', diff --git a/protocol/test/core/vault-early-withdraw-tests.ts b/protocol/test/core/vault-early-withdraw-tests.ts index 98da5073e..52c45dd6b 100644 --- a/protocol/test/core/vault-early-withdraw-tests.ts +++ b/protocol/test/core/vault-early-withdraw-tests.ts @@ -138,9 +138,25 @@ describe("Temple Core Vault - Early Withdraw", async () => { .withArgs(await ben.getAddress()); }); - it("cannot withdraw zero amounts", async () => { - await expect(vaultEarlyWithdraw.connect(alan).withdraw(vault.address, 0)) - .to.be.revertedWithCustomError(vaultEarlyWithdraw, "ExpectedNonZero"); + it("cannot withdraw below the min amount", async () => { + await vault.connect(alan).deposit(toAtto(100)); + + await expect(vaultEarlyWithdraw.connect(alan).withdraw(vault.address, 999)) + .to.be.revertedWithCustomError(vaultEarlyWithdraw, "MinAmountNotMet"); + + // Succeeds for 1000 (the min amount) + await vaultEarlyWithdraw.connect(alan).withdraw(vault.address, 1000); + }); + + it("owner can update min withdraw amount", async () => { + await expect(vaultEarlyWithdraw.connect(alan).setMinWithdrawAmount(1)) + .to.be.revertedWith("Ownable: caller is not the owner"); + + await expect(vaultEarlyWithdraw.connect(owner).setMinWithdrawAmount(1)) + .to.emit(vaultEarlyWithdraw, "MinWithdrawAmountSet") + .withArgs(1); + + expect(await vaultEarlyWithdraw.minWithdrawAmount()).to.eq(1); }); it("cannot withdraw if not enough temple balance", async () => {