From 0182437051ce3c57cbcc3c60e56fe6ae959ea1ed Mon Sep 17 00:00:00 2001 From: itofarina Date: Tue, 3 Oct 2023 12:37:10 -0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=B8=20escrow:=20return=20reserve=20on?= =?UTF-8?q?=20external=20stream=20cancel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: danilo neves cruz --- .changeset/thin-bugs-do.md | 5 +++++ .gas-snapshot | 8 ++++---- contracts/periphery/EscrowedEXA.sol | 16 +++++++++++----- test/EscrowedEXA.t.sol | 17 +++++++---------- 4 files changed, 27 insertions(+), 19 deletions(-) create mode 100644 .changeset/thin-bugs-do.md diff --git a/.changeset/thin-bugs-do.md b/.changeset/thin-bugs-do.md new file mode 100644 index 000000000..3c42d392b --- /dev/null +++ b/.changeset/thin-bugs-do.md @@ -0,0 +1,5 @@ +--- +"@exactly/protocol": patch +--- + +🚸 escrow: return reserve on external stream cancel diff --git a/.gas-snapshot b/.gas-snapshot index 983fc39fe..8ce400c42 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -95,8 +95,8 @@ DebtPreviewerTest:testPreviewMaxRatioWithdrawWithSameAssetLeverage() (gas: 10205 DebtPreviewerTest:testPreviewSameAssetInvalidLeverageShouldCapRatio() (gas: 910754) EscrowedEXATest:testCancelExternalStreams() (gas: 452302) EscrowedEXATest:testCancelExternalStreamsWithesEXACancel() (gas: 903873) -EscrowedEXATest:testCancelFromStreamAndGetReserveBack() (gas: 457589) -EscrowedEXATest:testCancelFromStreamJustCreated() (gas: 412791) +EscrowedEXATest:testCancelFromStreamAndGetReserveBack() (gas: 451714) +EscrowedEXATest:testCancelFromStreamJustCreated() (gas: 404796) EscrowedEXATest:testCancelShouldDeleteReserves() (gas: 471143) EscrowedEXATest:testCancelShouldGiveReservesBack() (gas: 676562) EscrowedEXATest:testCancelTwiceShouldRevert() (gas: 456028) @@ -122,10 +122,10 @@ EscrowedEXATest:testVestToAnother() (gas: 403826) EscrowedEXATest:testVestToAnotherAndCancel() (gas: 489580) EscrowedEXATest:testVestWithPermitReserve() (gas: 458876) EscrowedEXATest:testVestZero() (gas: 14151) -EscrowedEXATest:testWithdrawFromStreamAndGetReserveBack() (gas: 329816) +EscrowedEXATest:testWithdrawFromStreamAndGetReserveBack() (gas: 329850) EscrowedEXATest:testWithdrawFromUnknownStream() (gas: 898481) EscrowedEXATest:testWithdrawMaxFromMultipleStreams() (gas: 1007617) -EscrowedEXATest:testWithdrawMaxShouldGiveReserveBackWhenDepleted() (gas: 327828) +EscrowedEXATest:testWithdrawMaxShouldGiveReserveBackWhenDepleted() (gas: 327862) EscrowedEXATest:testWithdrawMaxWithInvalidSender() (gas: 339700) InterestRateModelTest:testFixedBorrowRate() (gas: 8089) InterestRateModelTest:testFloatingBorrowRate() (gas: 6236) diff --git a/contracts/periphery/EscrowedEXA.sol b/contracts/periphery/EscrowedEXA.sol index dadb21f16..f6cf0c4c4 100644 --- a/contracts/periphery/EscrowedEXA.sol +++ b/contracts/periphery/EscrowedEXA.sol @@ -157,11 +157,7 @@ contract EscrowedEXA is ERC20VotesUpgradeable, AccessControlUpgradeable { /// @param streamId streamId to withdraw from. function withdrawMax(uint256 streamId) internal { if (sablier.withdrawableAmountOf(streamId) != 0) sablier.withdrawMax(streamId, msg.sender); - if (sablier.isDepleted(streamId)) { - uint256 reserve = reserves[streamId]; - delete reserves[streamId]; - exa.safeTransfer(msg.sender, reserve); - } + if (sablier.isDepleted(streamId)) returnReserve(streamId, msg.sender); } /// @notice Checks if a stream is valid through its reserve. Reverts with `InvalidStream` if it is not. @@ -170,6 +166,15 @@ contract EscrowedEXA is ERC20VotesUpgradeable, AccessControlUpgradeable { if (reserves[streamId] == 0) revert InvalidStream(); } + /// @notice Returns the reserve to the recipient. + /// @param streamId streamId of the reserve to return. + /// @param recipient recipient of the reserve. + function returnReserve(uint256 streamId, address recipient) internal { + uint256 reserve = reserves[streamId]; + delete reserves[streamId]; + exa.safeTransfer(recipient, reserve); + } + /// @notice Hook called when a recipient cancels a stream. /// @notice Mints esEXA to the recipient with the remaining EXA received from the canceled stream. /// @param streamId streamId of the cancelled stream. @@ -179,6 +184,7 @@ contract EscrowedEXA is ERC20VotesUpgradeable, AccessControlUpgradeable { assert(msg.sender == address(sablier)); checkStream(streamId); _mint(recipient, senderAmount); + returnReserve(streamId, recipient); } /// @notice Sets the vesting period. diff --git a/test/EscrowedEXA.t.sol b/test/EscrowedEXA.t.sol index 0ba4ba0ef..3d7bbee2f 100644 --- a/test/EscrowedEXA.t.sol +++ b/test/EscrowedEXA.t.sol @@ -416,18 +416,17 @@ contract EscrowedEXATest is ForkTest { uint256 amount = 1_000 ether; uint256 reserve = amount.mulWadDown(esEXA.reserveRatio()); esEXA.mint(amount, address(this)); - uint256[] memory streams = new uint256[](1); - streams[0] = esEXA.vest(uint128(amount), address(this)); + uint256 streamId = esEXA.vest(uint128(amount), address(this)); uint256 exaBefore = exa.balanceOf(address(this)); vm.warp(block.timestamp + esEXA.vestingPeriod() / 2); - esEXA.sablier().cancel(streams[0]); - - assertEq(esEXA.balanceOf(address(this)), amount / 2, "half esEXA sholud be back"); + esEXA.sablier().cancel(streamId); - esEXA.withdrawMax(streams); - assertEq(exa.balanceOf(address(this)), exaBefore + amount / 2 + reserve, "amount/2 + reserve should be back"); + assertEq(esEXA.balanceOf(address(this)), amount / 2, "half esEXA should be back"); + assertEq(exa.balanceOf(address(this)), exaBefore + reserve, "reserve should be back"); + esEXA.sablier().withdrawMax(streamId, address(this)); + assertEq(exa.balanceOf(address(this)), exaBefore + reserve + amount / 2, "amount/2 should be back"); } function testCancelFromStreamJustCreated() external { @@ -441,9 +440,7 @@ contract EscrowedEXATest is ForkTest { esEXA.sablier().cancel(streams[0]); - assertEq(esEXA.balanceOf(address(this)), amount, "amount esEXA sholud be back"); - - esEXA.withdrawMax(streams); + assertEq(esEXA.balanceOf(address(this)), amount, "amount esEXA should be back"); assertEq(exa.balanceOf(address(this)), exaBefore + reserve, "reserve should be back"); }