From 0440580dcfcc879eb417d07033c30ed33af3e3a2 Mon Sep 17 00:00:00 2001 From: Jakub Nowakowski Date: Wed, 28 Aug 2024 15:12:43 +0200 Subject: [PATCH] Include unallocated balance in releaseDeposit function We were not including the unallocated balance in the releaseDeposit function. If there was no Mezo deposit, but some funds were donated to the contract they would be stuck there forever. This change allows the contract to release both unallocated balance and Mezo deposit. --- solidity/contracts/MezoAllocator.sol | 9 +- solidity/test/MezoAllocator.test.ts | 144 +++++++++++++++++++++++---- 2 files changed, 133 insertions(+), 20 deletions(-) diff --git a/solidity/contracts/MezoAllocator.sol b/solidity/contracts/MezoAllocator.sol index f456f76f9..43bfbf33f 100644 --- a/solidity/contracts/MezoAllocator.sol +++ b/solidity/contracts/MezoAllocator.sol @@ -268,9 +268,12 @@ contract MezoAllocator is IDispatcher, Ownable2StepUpgradeable { .getDeposit(address(this), address(tbtc), depositId) .balance; - emit DepositReleased(depositId, amount); - depositBalance = 0; - mezoPortal.withdraw(address(tbtc), depositId); + if (amount > 0) { + emit DepositReleased(depositId, amount); + depositBalance = 0; + mezoPortal.withdraw(address(tbtc), depositId); + } + tbtc.safeTransfer(address(stbtc), tbtc.balanceOf(address(this))); } diff --git a/solidity/test/MezoAllocator.test.ts b/solidity/test/MezoAllocator.test.ts index b8960e6ae..946aac5aa 100644 --- a/solidity/test/MezoAllocator.test.ts +++ b/solidity/test/MezoAllocator.test.ts @@ -733,32 +733,142 @@ describe("MezoAllocator", () => { }) context("when the caller is governance", () => { + beforeAfterSnapshotWrapper() + + context("when there is no deposit", () => { + beforeAfterSnapshotWrapper() + + context("when there is no donation", () => { + beforeAfterSnapshotWrapper() + + let tx: ContractTransactionResponse + + before(async () => { + tx = await mezoAllocator.connect(governance).releaseDeposit() + }) + + it("should not emit DepositReleased event", async () => { + await expect(tx).to.not.emit(mezoAllocator, "DepositReleased") + }) + + it("should not call MezoPortal.withdraw function", async () => { + await expect(tx).to.not.emit(mezoPortal, "WithdrawFully") + }) + }) + + context("when there is a donation", () => { + beforeAfterSnapshotWrapper() + + const donationAmount = to1e18(2) + + let tx: ContractTransactionResponse + + before(async () => { + await tbtc.mint(await mezoAllocator.getAddress(), donationAmount) + + tx = await mezoAllocator.connect(governance).releaseDeposit() + }) + + it("should not emit DepositReleased event", async () => { + await expect(tx).to.not.emit(mezoAllocator, "DepositReleased") + }) + + it("should transfer tBTC to stBTC contract", async () => { + await expect(tx).to.changeTokenBalances( + tbtc, + [mezoAllocator, stbtc], + [-donationAmount, donationAmount], + ) + }) + + it("should not call MezoPortal.withdraw function", async () => { + await expect(tx).to.not.emit(mezoPortal, "WithdrawFully") + }) + }) + }) + context("when there is a deposit", () => { - let tx: ContractTransactionResponse + beforeAfterSnapshotWrapper() + + const depositAmount = to1e18(5) before(async () => { - await tbtc.mint(await stbtc.getAddress(), to1e18(5)) + await tbtc.mint(await stbtc.getAddress(), depositAmount) await mezoAllocator.connect(maintainer).allocate() - tx = await mezoAllocator.connect(governance).releaseDeposit() }) - it("should emit DepositReleased event", async () => { - await expect(tx) - .to.emit(mezoAllocator, "DepositReleased") - .withArgs(1, to1e18(5)) - }) + context("when there is no donation", () => { + beforeAfterSnapshotWrapper() + + let tx: ContractTransactionResponse + + before(async () => { + tx = await mezoAllocator.connect(governance).releaseDeposit() + }) + + it("should emit DepositReleased event", async () => { + await expect(tx) + .to.emit(mezoAllocator, "DepositReleased") + .withArgs(1, depositAmount) + }) + + it("should decrease tracked deposit balance amount to zero", async () => { + const depositBalance = await mezoAllocator.depositBalance() + expect(depositBalance).to.equal(0) + }) + + it("should transfer tBTC to stBTC contract", async () => { + await expect(tx).to.changeTokenBalances( + tbtc, + [mezoPortal, stbtc], + [-depositAmount, depositAmount], + ) + }) - it("should decrease tracked deposit balance amount to zero", async () => { - const depositBalance = await mezoAllocator.depositBalance() - expect(depositBalance).to.equal(0) + it("should call MezoPortal.withdraw function", async () => { + await expect(tx) + .to.emit(mezoPortal, "WithdrawFully") + .withArgs(await tbtc.getAddress(), 1) + }) }) - it("should decrease Mezo Portal balance", async () => { - await expect(tx).to.changeTokenBalances( - tbtc, - [mezoPortal, stbtc], - [-to1e18(5), to1e18(5)], - ) + context("when there is a donation", () => { + beforeAfterSnapshotWrapper() + + const donationAmount = to1e18(2) + + let tx: ContractTransactionResponse + + before(async () => { + await tbtc.mint(await mezoAllocator.getAddress(), donationAmount) + + tx = await mezoAllocator.connect(governance).releaseDeposit() + }) + + it("should emit DepositReleased event", async () => { + await expect(tx) + .to.emit(mezoAllocator, "DepositReleased") + .withArgs(1, depositAmount) + }) + + it("should decrease tracked deposit balance amount to zero", async () => { + const depositBalance = await mezoAllocator.depositBalance() + expect(depositBalance).to.equal(0) + }) + + it("should transfer tBTC to stBTC contract", async () => { + await expect(tx).to.changeTokenBalances( + tbtc, + [mezoPortal, mezoAllocator, stbtc], + [-depositAmount, -donationAmount, depositAmount + donationAmount], + ) + }) + + it("should call MezoPortal.withdraw function", async () => { + await expect(tx) + .to.emit(mezoPortal, "WithdrawFully") + .withArgs(await tbtc.getAddress(), 1) + }) }) }) })