From 7b30b634baf692ecca6333dfd8becb2be2533395 Mon Sep 17 00:00:00 2001 From: Nicola Miotto Date: Fri, 12 Apr 2024 10:27:51 +0200 Subject: [PATCH] customizable redemption to address (#139) `redeem` can now be called with any `to` address. --- contracts/InternalMarket/InternalMarket.sol | 5 ++-- test/Integration.ts | 26 ++++++++++----------- test/InternalMarket.ts | 24 +++++++++---------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/contracts/InternalMarket/InternalMarket.sol b/contracts/InternalMarket/InternalMarket.sol index 8ecff95..6e434ad 100644 --- a/contracts/InternalMarket/InternalMarket.sol +++ b/contracts/InternalMarket/InternalMarket.sol @@ -95,10 +95,11 @@ contract InternalMarket is Initializable, HasRole, InternalMarketBase { /** * @dev Redeem the locked tokens. + * @param to The address of the receiver. * @param amount The amount of tokens to redeem. */ - function redeem(uint amount) public { - _redeem(_msgSender(), amount); + function redeem(address to, uint amount) public { + _redeem(to, amount); } /** diff --git a/test/Integration.ts b/test/Integration.ts index e50b1f2..eeb77cd 100644 --- a/test/Integration.ts +++ b/test/Integration.ts @@ -1088,7 +1088,7 @@ describe("Integration", async () => { e(4) ); - await internalMarket.connect(user1).redeem(e(10)); + await internalMarket.redeem(user1.address, e(10)); // Chaining two changeTokenBalances seems to execute the "redeem" // function twice. Anyway, this second redeem should fail. /* @@ -1111,7 +1111,7 @@ describe("Integration", async () => { ); expect(await tokenMock.balanceOf(internalMarket.address)).equal(0); - await expect(internalMarket.connect(user1).redeem(e(4))).revertedWith( + await expect(internalMarket.redeem(user1.address, e(4))).revertedWith( "Redemption controller: amount exceeds redeemable balance" ); @@ -1121,17 +1121,17 @@ describe("Integration", async () => { await internalMarket.connect(user2).withdraw(free2.address, e(90)); await timeTravel(redemptionStartDays - offerDurationDays, true); // then tries to redeem but fails because not enough balance. - await expect(internalMarket.connect(user2).redeem(e(90))).revertedWith( + await expect(internalMarket.redeem(user2.address, e(90))).revertedWith( "ERC20: burn amount exceeds balance" ); // then tries to redeem 6 and succeeds. - await internalMarket.connect(user2).redeem(e(6)); + await internalMarket.redeem(user2.address, e(6)); // then 4 after the redeem window and fails await timeTravel(redemptionWindowDays, true); - await expect(internalMarket.connect(user2).redeem(e(4))).revertedWith( + await expect(internalMarket.redeem(user2.address, e(4))).revertedWith( "Redemption controller: amount exceeds redeemable balance" ); }); @@ -1156,12 +1156,12 @@ describe("Integration", async () => { // user2 offer all tokens, hoping to redeem all of them... await internalMarket.connect(user1).makeOffer(e(20)); await timeTravel(redemptionStartDays, true); - await expect(internalMarket.connect(user1).redeem(e(20))).revertedWith( + await expect(internalMarket.redeem(user1.address, e(20))).revertedWith( "Redemption controller: amount exceeds redeemable balance" ); // ...but they can only redeem those that were minted directly to them - await internalMarket.connect(user1).redeem(e(10)); + await internalMarket.redeem(user1.address, e(10)); expect(await tokenMock.balanceOf(user1.address)).equal( e(INITIAL_USDC + 10) ); @@ -1247,7 +1247,7 @@ describe("Integration", async () => { // 53 days later (60 since beginning) user1 redeems 3 tokens await timeTravel(redemptionStartDays - offerDurationDays, true); daysSinceMinting += redemptionStartDays - offerDurationDays; - await internalMarket.connect(user1).redeem(e(3)); + await internalMarket.redeem(user1.address, e(3)); tokensRedeemed += 3; // at the end of the redemption window, redemption of the 7 remaining @@ -1255,7 +1255,7 @@ describe("Integration", async () => { await timeTravel(redemptionWindowDays, true); daysSinceMinting += redemptionWindowDays; - await expect(internalMarket.connect(user1).redeem(e(7))).revertedWith( + await expect(internalMarket.redeem(user1.address, e(7))).revertedWith( "Redemption controller: amount exceeds redeemable balance" ); @@ -1265,7 +1265,7 @@ describe("Integration", async () => { // after 60 days, user1 redeems 4 tokens await timeTravel(redemptionStartDays, true); daysSinceMinting += redemptionStartDays; - await internalMarket.connect(user1).redeem(e(4)); + await internalMarket.redeem(user1.address, e(4)); tokensRedeemed += 4; // redemption window expires @@ -1287,7 +1287,7 @@ describe("Integration", async () => { // 67 days later, redemption fails await timeTravel(redemptionStartDays + redemptionWindowDays, true); - await expect(internalMarket.connect(user1).redeem(e(3))).revertedWith( + await expect(internalMarket.redeem(user1.address, e(3))).revertedWith( "Redemption controller: amount exceeds redeemable balance" ); @@ -1311,7 +1311,7 @@ describe("Integration", async () => { ); // user1 redeems their only token and withdraws the others, sobbing again - await internalMarket.connect(user1).redeem(e(1)); + await internalMarket.redeem(user1.address, e(1)); tokensRedeemed += 1; await internalMarket.connect(user1).withdraw(user1.address, e(13)); await internalMarket.connect(user1).deposit(e(13)); @@ -1538,7 +1538,7 @@ describe("Integration", async () => { }); await timeTravel(redemptionStartDays, true); - await internalMarket.connect(user1).redeem(e(20)); + await internalMarket.redeem(user1.address, e(20)); await check({ // -20 internalSupply: 95, diff --git a/test/InternalMarket.ts b/test/InternalMarket.ts index 16e6b37..68eebef 100644 --- a/test/InternalMarket.ts +++ b/test/InternalMarket.ts @@ -219,7 +219,7 @@ describe("InternalMarket", async () => { await mineEVMBlock(); }); it("should call afterRedeem on redemptionController", async () => { - await internalMarket.connect(alice).redeem(50); + await internalMarket.redeem(alice.address, 50); expect(redemption.afterRedeem).calledWith(alice.address, 50); }); @@ -237,7 +237,7 @@ describe("InternalMarket", async () => { }); it("should burn the 10 DAO tokens for 10 USDC of the reserve", async () => { - await internalMarket.connect(alice).redeem(parseEther("10")); + await internalMarket.redeem(alice.address, parseEther("10")); expect(governanceToken.burn).calledWith( internalMarket.address, parseEther("10") @@ -263,7 +263,7 @@ describe("InternalMarket", async () => { }); it("should burn 10 DAO token for 20 USDC", async () => { - await internalMarket.connect(alice).redeem(parseEther("10")); + await internalMarket.redeem(alice.address, parseEther("10")); expect(governanceToken.burn).calledWith( internalMarket.address, @@ -290,7 +290,7 @@ describe("InternalMarket", async () => { }); it("should burn the 10 DAO tokens for 11.222444 USDC", async () => { - await internalMarket.connect(alice).redeem(parseEther("10")); + await internalMarket.redeem(alice.address, parseEther("10")); expect(governanceToken.burn).calledWith( internalMarket.address, parseEther("10") @@ -316,7 +316,7 @@ describe("InternalMarket", async () => { }); it("should burn the 11 DAO tokens for 5.5 USDC", async () => { - await internalMarket.connect(alice).redeem(parseEther("11")); + await internalMarket.redeem(alice.address, parseEther("11")); expect(governanceToken.burn).calledWith( internalMarket.address, parseEther("11") @@ -329,7 +329,7 @@ describe("InternalMarket", async () => { }); it("should exchange the 1 DAO token sat for 0 USDC sats", async () => { - await internalMarket.connect(alice).redeem(1); + await internalMarket.redeem(alice.address, 1); expect(governanceToken.burn).calledWith(internalMarket.address, 1); expect(usdc.transferFrom).calledWith( reserve.address, @@ -361,7 +361,7 @@ describe("InternalMarket", async () => { .reverts("ERC20: burn amount exceeds balance"); // smock2 bug causes this error rather than the faked one await expect( - internalMarket.connect(alice).redeem(parseEther("70")) + internalMarket.redeem(alice.address, parseEther("70")) ).revertedWith("function returned an unexpected amount of data"); }); @@ -372,13 +372,13 @@ describe("InternalMarket", async () => { .whenCalledWith(alice.address, parseEther("10")) .reverts("ERC20: burn amount exceeds balance"); await expect( - internalMarket.connect(alice).redeem(parseEther("60")) + internalMarket.redeem(alice.address, parseEther("60")) ).revertedWith("function returned an unexpected amount of data"); }); describe("when user redeems 50 tokens", async () => { beforeEach(async () => { - await internalMarket.connect(alice).redeem(parseEther("50")); + await internalMarket.redeem(alice.address, parseEther("50")); }); it("should burn 50 tokens from market", async () => { @@ -408,13 +408,13 @@ describe("InternalMarket", async () => { it("should fail when the user redeems 70 tokens", async () => { await expect( - internalMarket.connect(alice).redeem(parseEther("70")) + internalMarket.redeem(alice.address, parseEther("70")) ).revertedWith("function returned an unexpected amount of data"); }); describe("when the user redeems 60 tokens", async () => { beforeEach(async () => { - await internalMarket.connect(alice).redeem(parseEther("60")); + await internalMarket.redeem(alice.address, parseEther("60")); }); it("should burn 10 tokens from alice", async () => { @@ -442,7 +442,7 @@ describe("InternalMarket", async () => { describe("when the user redeems 50 tokens", async () => { beforeEach(async () => { - await internalMarket.connect(alice).redeem(parseEther("50")); + await internalMarket.redeem(alice.address, parseEther("50")); }); it("should not burn 10 tokens from alice", async () => {