From ebc97e936b33122b6dbd39801352d4c869a04b3d Mon Sep 17 00:00:00 2001 From: Cody Hatfield Date: Thu, 15 Sep 2022 11:20:43 -0700 Subject: [PATCH] fix: Allow higher for sale price on reclaim (#90) --- .../pco-license/facets/CFAReclaimerFacet.sol | 18 ---- test/pco-license/CFAReclaimerFacet.ts | 102 +++++++++++------- 2 files changed, 61 insertions(+), 59 deletions(-) diff --git a/contracts/pco-license/facets/CFAReclaimerFacet.sol b/contracts/pco-license/facets/CFAReclaimerFacet.sol index 71acb2f4..fc42cad2 100644 --- a/contracts/pco-license/facets/CFAReclaimerFacet.sol +++ b/contracts/pco-license/facets/CFAReclaimerFacet.sol @@ -87,24 +87,6 @@ contract CFAReclaimerFacet is CFABasePCOFacetModifiers { } ISuperToken paymentToken = ds.paramsStore.getPaymentToken(); - { - uint256 requiredBuffer = cs.cfaV1.cfa.getDepositRequiredForFlowRate( - paymentToken, - newContributionRate - ); - - require( - paymentToken.balanceOf(msg.sender) >= - newForSalePrice + requiredBuffer, - "CFAReclaimerFacet: Insufficient balance" - ); - require( - paymentToken.allowance(msg.sender, address(this)) >= - newForSalePrice + requiredBuffer, - "CFAReclaimerFacet: Insufficient allowance" - ); - } - { // Check operator permissions (, uint8 permissions, int96 flowRateAllowance) = cs diff --git a/test/pco-license/CFAReclaimerFacet.ts b/test/pco-license/CFAReclaimerFacet.ts index b278d3eb..386517b4 100644 --- a/test/pco-license/CFAReclaimerFacet.ts +++ b/test/pco-license/CFAReclaimerFacet.ts @@ -64,7 +64,7 @@ describe("CFAReclaimerFacet", async function () { ).to.have.been.calledWith(user, bidder, await basePCOFacet.licenseId()); }); - it("should revert if insufficient balance", async () => { + it("should succeed if for sale price is higher than claim price", async () => { const { basePCOFacet, mockParamsStore, @@ -74,6 +74,50 @@ describe("CFAReclaimerFacet", async function () { } = await BaseFixtures.afterPayerDelete(); const { bidder, user } = await getNamedAccounts(); + const contributionRate = BigNumber.from(200); + const forSalePrice = await rateToPurchasePrice( + mockParamsStore, + contributionRate + ); + const requiredBuffer = await ethersjsSf.cfaV1.contract + .connect(await ethers.getSigner(bidder)) + .getDepositRequiredForFlowRate(paymentToken.address, contributionRate); + + const reclaimPrice = await basePCOFacet + .connect(await ethers.getSigner(bidder)) + .reclaimPrice(); + + // Allow spending of reclaimPrice + const op2 = paymentToken.approve({ + amount: reclaimPrice.add(requiredBuffer), + receiver: basePCOFacet.address, + }); + await op2.exec(await ethers.getSigner(bidder)); + + // Approve flow creation + const op3 = ethersjsSf.cfaV1.updateFlowOperatorPermissions({ + superToken: paymentToken.address, + flowOperator: basePCOFacet.address, + permissions: 1, + flowRateAllowance: contributionRate.toString(), + }); + await op3.exec(await ethers.getSigner(bidder)); + + const txn = await basePCOFacet + .connect(await ethers.getSigner(bidder)) + .reclaim(contributionRate, forSalePrice); + await txn.wait(); + await expect(txn).to.emit(basePCOFacet, "LicenseReclaimed"); + expect( + mockLicense["safeTransferFrom(address,address,uint256)"] + ).to.have.been.calledWith(user, bidder, await basePCOFacet.licenseId()); + }); + + it("should revert if insufficient balance", async () => { + const { basePCOFacet, mockParamsStore, paymentToken, ethersjsSf } = + await BaseFixtures.afterPayerDelete(); + const { bidder } = await getNamedAccounts(); + const contributionRate = BigNumber.from(100); const forSalePrice = await rateToPurchasePrice( mockParamsStore, @@ -82,7 +126,6 @@ describe("CFAReclaimerFacet", async function () { const requiredBuffer = await ethersjsSf.cfaV1.contract .connect(await ethers.getSigner(bidder)) .getDepositRequiredForFlowRate(paymentToken.address, contributionRate); - const totalCollateral = forSalePrice.add(requiredBuffer); const reclaimPrice = await basePCOFacet .connect(await ethers.getSigner(bidder)) @@ -90,7 +133,7 @@ describe("CFAReclaimerFacet", async function () { // Allow spending of reclaimPrice const op2 = paymentToken.approve({ - amount: reclaimPrice.add(totalCollateral), + amount: reclaimPrice.add(requiredBuffer), receiver: basePCOFacet.address, }); await op2.exec(await ethers.getSigner(bidder)); @@ -118,28 +161,19 @@ describe("CFAReclaimerFacet", async function () { basePCOFacet .connect(await ethers.getSigner(bidder)) .reclaim(contributionRate, forSalePrice) - ).to.be.revertedWith("CFAReclaimerFacet: Insufficient balance"); + ).to.be.revertedWith("SuperfluidToken: move amount exceeds balance"); }); it("should revert if insufficient allowance", async () => { - const { - basePCOFacet, - mockParamsStore, - paymentToken, - ethersjsSf, - mockLicense, - } = await BaseFixtures.afterPayerDelete(); - const { bidder, user } = await getNamedAccounts(); + const { basePCOFacet, mockParamsStore, paymentToken, ethersjsSf } = + await BaseFixtures.afterPayerDelete(); + const { bidder } = await getNamedAccounts(); const contributionRate = BigNumber.from(100); const forSalePrice = await rateToPurchasePrice( mockParamsStore, contributionRate ); - const requiredBuffer = await ethersjsSf.cfaV1.contract - .connect(await ethers.getSigner(bidder)) - .getDepositRequiredForFlowRate(paymentToken.address, contributionRate); - const totalCollateral = forSalePrice.add(requiredBuffer); const reclaimPrice = await basePCOFacet .connect(await ethers.getSigner(bidder)) @@ -165,18 +199,13 @@ describe("CFAReclaimerFacet", async function () { basePCOFacet .connect(await ethers.getSigner(bidder)) .reclaim(contributionRate, forSalePrice) - ).to.be.revertedWith("CFAReclaimerFacet: Insufficient allowance"); + ).to.be.revertedWith("SuperToken: transfer amount exceeds allowance"); }); it("should revert if permission not granted to create flow", async () => { - const { - basePCOFacet, - mockParamsStore, - paymentToken, - ethersjsSf, - mockLicense, - } = await BaseFixtures.afterPayerDelete(); - const { bidder, user } = await getNamedAccounts(); + const { basePCOFacet, mockParamsStore, paymentToken, ethersjsSf } = + await BaseFixtures.afterPayerDelete(); + const { bidder } = await getNamedAccounts(); const contributionRate = BigNumber.from(100); const forSalePrice = await rateToPurchasePrice( @@ -186,7 +215,6 @@ describe("CFAReclaimerFacet", async function () { const requiredBuffer = await ethersjsSf.cfaV1.contract .connect(await ethers.getSigner(bidder)) .getDepositRequiredForFlowRate(paymentToken.address, contributionRate); - const totalCollateral = forSalePrice.add(requiredBuffer); const reclaimPrice = await basePCOFacet .connect(await ethers.getSigner(bidder)) @@ -194,7 +222,7 @@ describe("CFAReclaimerFacet", async function () { // Allow spending of reclaimPrice const op2 = paymentToken.approve({ - amount: reclaimPrice.add(totalCollateral), + amount: reclaimPrice.add(requiredBuffer), receiver: basePCOFacet.address, }); await op2.exec(await ethers.getSigner(bidder)); @@ -209,14 +237,9 @@ describe("CFAReclaimerFacet", async function () { }); it("should revert if flow permission doesn't have enough allowance", async () => { - const { - basePCOFacet, - mockParamsStore, - paymentToken, - ethersjsSf, - mockLicense, - } = await BaseFixtures.afterPayerDelete(); - const { bidder, user } = await getNamedAccounts(); + const { basePCOFacet, mockParamsStore, paymentToken, ethersjsSf } = + await BaseFixtures.afterPayerDelete(); + const { bidder } = await getNamedAccounts(); const contributionRate = BigNumber.from(100); const forSalePrice = await rateToPurchasePrice( @@ -226,7 +249,6 @@ describe("CFAReclaimerFacet", async function () { const requiredBuffer = await ethersjsSf.cfaV1.contract .connect(await ethers.getSigner(bidder)) .getDepositRequiredForFlowRate(paymentToken.address, contributionRate); - const totalCollateral = forSalePrice.add(requiredBuffer); const reclaimPrice = await basePCOFacet .connect(await ethers.getSigner(bidder)) @@ -234,7 +256,7 @@ describe("CFAReclaimerFacet", async function () { // Allow spending of reclaimPrice const op2 = paymentToken.approve({ - amount: reclaimPrice.add(totalCollateral), + amount: reclaimPrice.add(requiredBuffer), receiver: basePCOFacet.address, }); await op2.exec(await ethers.getSigner(bidder)); @@ -270,11 +292,10 @@ describe("CFAReclaimerFacet", async function () { const requiredBuffer = await ethersjsSf.cfaV1.contract .connect(await ethers.getSigner(bidder)) .getDepositRequiredForFlowRate(paymentToken.address, contributionRate); - const totalCollateral = forSalePrice.add(requiredBuffer); // Allow spending of reclaimPrice const op2 = paymentToken.approve({ - amount: totalCollateral.toString(), + amount: requiredBuffer.toString(), receiver: basePCOFacet.address, }); await op2.exec(await ethers.getSigner(bidder)); @@ -309,7 +330,6 @@ describe("CFAReclaimerFacet", async function () { const requiredBuffer = await ethersjsSf.cfaV1.contract .connect(await ethers.getSigner(bidder)) .getDepositRequiredForFlowRate(paymentToken.address, contributionRate); - const totalCollateral = forSalePrice.add(requiredBuffer); const reclaimPrice = await basePCOFacet .connect(await ethers.getSigner(bidder)) @@ -317,7 +337,7 @@ describe("CFAReclaimerFacet", async function () { // Allow spending of reclaimPrice const op2 = paymentToken.approve({ - amount: reclaimPrice.add(totalCollateral), + amount: reclaimPrice.add(requiredBuffer), receiver: basePCOFacet.address, }); await op2.exec(await ethers.getSigner(bidder));