diff --git a/contracts/p0/RToken.sol b/contracts/p0/RToken.sol index 2ba631e3a..e7ce86a0b 100644 --- a/contracts/p0/RToken.sol +++ b/contracts/p0/RToken.sol @@ -337,6 +337,7 @@ contract RTokenP0 is ComponentP0, ERC20PermitUpgradeable, IRToken { require(params.amtRate >= MIN_THROTTLE_RATE_AMT, "issuance amtRate too small"); require(params.amtRate <= MAX_THROTTLE_RATE_AMT, "issuance amtRate too big"); require(params.pctRate <= MAX_THROTTLE_PCT_AMT, "issuance pctRate too big"); + issuanceThrottle.useAvailable(totalSupply(), 0); emit IssuanceThrottleSet(issuanceThrottle.params, params); issuanceThrottle.params = params; } @@ -346,6 +347,7 @@ contract RTokenP0 is ComponentP0, ERC20PermitUpgradeable, IRToken { require(params.amtRate >= MIN_THROTTLE_RATE_AMT, "redemption amtRate too small"); require(params.amtRate <= MAX_THROTTLE_RATE_AMT, "redemption amtRate too big"); require(params.pctRate <= MAX_THROTTLE_PCT_AMT, "redemption pctRate too big"); + redemptionThrottle.useAvailable(totalSupply(), 0); emit RedemptionThrottleSet(redemptionThrottle.params, params); redemptionThrottle.params = params; } diff --git a/contracts/p1/RToken.sol b/contracts/p1/RToken.sol index 942e2d250..f68a43a53 100644 --- a/contracts/p1/RToken.sol +++ b/contracts/p1/RToken.sol @@ -442,6 +442,7 @@ contract RTokenP1 is ComponentP1, ERC20PermitUpgradeable, IRToken { require(params.amtRate >= MIN_THROTTLE_RATE_AMT, "issuance amtRate too small"); require(params.amtRate <= MAX_THROTTLE_RATE_AMT, "issuance amtRate too big"); require(params.pctRate <= MAX_THROTTLE_PCT_AMT, "issuance pctRate too big"); + issuanceThrottle.useAvailable(totalSupply(), 0); emit IssuanceThrottleSet(issuanceThrottle.params, params); issuanceThrottle.params = params; } @@ -451,6 +452,7 @@ contract RTokenP1 is ComponentP1, ERC20PermitUpgradeable, IRToken { require(params.amtRate >= MIN_THROTTLE_RATE_AMT, "redemption amtRate too small"); require(params.amtRate <= MAX_THROTTLE_RATE_AMT, "redemption amtRate too big"); require(params.pctRate <= MAX_THROTTLE_PCT_AMT, "redemption pctRate too big"); + redemptionThrottle.useAvailable(totalSupply(), 0); emit RedemptionThrottleSet(redemptionThrottle.params, params); redemptionThrottle.params = params; } diff --git a/test/RToken.test.ts b/test/RToken.test.ts index c13c235db..4652dec1c 100644 --- a/test/RToken.test.ts +++ b/test/RToken.test.ts @@ -224,6 +224,26 @@ describe(`RTokenP${IMPLEMENTATION} contract`, () => { ).to.be.revertedWith('issuance pctRate too big') }) + it('Should account for accrued value when updating issuance throttle parameters', async () => { + await advanceTime(12 * 5 * 60) // 60 minutes, charge fully + const issuanceThrottleParams = { amtRate: fp('60'), pctRate: fp('0.1') } + + await rToken.connect(owner).setIssuanceThrottleParams(issuanceThrottleParams) + const params = await rToken.issuanceThrottleParams() + expect(params[0]).to.equal(issuanceThrottleParams.amtRate) + expect(params[1]).to.equal(issuanceThrottleParams.pctRate) + + await Promise.all(tokens.map((t) => t.connect(addr1).approve(rToken.address, initialBal))) + await rToken.connect(addr1).issue(fp('20')) + expect(await rToken.issuanceAvailable()).to.equal(fp('40')) + + await setNextBlockTimestamp((await getLatestBlockTimestamp()) + 12 * 5 * 10) // 10 minutes + + issuanceThrottleParams.amtRate = fp('100') + await rToken.connect(owner).setIssuanceThrottleParams(issuanceThrottleParams) + expect(await rToken.issuanceAvailable()).to.equal(fp('50')) + }) + it('Should allow to update redemption throttle if Owner and perform validations', async () => { const redemptionThrottleParams = { amtRate: fp('1'), pctRate: fp('0.1') } await expect( @@ -262,6 +282,30 @@ describe(`RTokenP${IMPLEMENTATION} contract`, () => { ).to.be.revertedWith('redemption pctRate too big') }) + it('Should account for accrued value when updating redemption throttle parameters', async () => { + await advanceTime(12 * 5 * 60) // 60 minutes, charge fully + const issuanceThrottleParams = { amtRate: fp('100'), pctRate: fp('0.1') } + const redemptionThrottleParams = { amtRate: fp('60'), pctRate: fp('0.1') } + + await rToken.connect(owner).setIssuanceThrottleParams(issuanceThrottleParams) + await rToken.connect(owner).setRedemptionThrottleParams(redemptionThrottleParams) + const params = await rToken.redemptionThrottleParams() + expect(params[0]).to.equal(redemptionThrottleParams.amtRate) + expect(params[1]).to.equal(redemptionThrottleParams.pctRate) + + await Promise.all(tokens.map((t) => t.connect(addr1).approve(rToken.address, initialBal))) + await rToken.connect(addr1).issue(fp('100')) + expect(await rToken.redemptionAvailable()).to.equal(fp('60')) + await rToken.connect(addr1).redeem(fp('30')) + expect(await rToken.redemptionAvailable()).to.equal(fp('30')) + + await setNextBlockTimestamp((await getLatestBlockTimestamp()) + 12 * 5 * 10) // 10 minutes + + redemptionThrottleParams.amtRate = fp('100') + await rToken.connect(owner).setRedemptionThrottleParams(redemptionThrottleParams) + expect(await rToken.redemptionAvailable()).to.equal(fp('40')) + }) + it('Should return a price of 0 if the assets become unregistered', async () => { const startPrice = await basketHandler.price() @@ -367,6 +411,8 @@ describe(`RTokenP${IMPLEMENTATION} contract`, () => { await Promise.all( tokens.map((t) => t.connect(addr1).approve(rToken.address, MAX_THROTTLE_AMT_RATE)) ) + // advance time + await advanceTime(12 * 5 * 60) // 60 minutes, charge fully await rToken.connect(addr1).issue(MAX_THROTTLE_AMT_RATE) expect(await rToken.totalSupply()).to.equal(MAX_THROTTLE_AMT_RATE) expect(await rToken.basketsNeeded()).to.equal(MAX_THROTTLE_AMT_RATE) @@ -1370,6 +1416,9 @@ describe(`RTokenP${IMPLEMENTATION} contract`, () => { redemptionThrottleParams.pctRate = bn(0) await rToken.connect(owner).setRedemptionThrottleParams(redemptionThrottleParams) + // advance time + await advanceTime(12 * 5 * 60) // 60 minutes, charge fully + // Check redemption throttle expect(await rToken.redemptionAvailable()).to.equal(redemptionThrottleParams.amtRate) @@ -2183,6 +2232,9 @@ describe(`RTokenP${IMPLEMENTATION} contract`, () => { redemptionThrottleParams.pctRate = bn(0) await rToken.connect(owner).setRedemptionThrottleParams(redemptionThrottleParams) + // advance time + await advanceTime(12 * 5 * 60) // 60 minutes, charge fully + // Check redemption throttle expect(await rToken.redemptionAvailable()).to.equal(redemptionThrottleParams.amtRate) diff --git a/test/ZTradingExtremes.test.ts b/test/ZTradingExtremes.test.ts index 537ba8176..77ca05227 100644 --- a/test/ZTradingExtremes.test.ts +++ b/test/ZTradingExtremes.test.ts @@ -480,6 +480,9 @@ describeExtreme(`Trading Extreme Values (${SLOW ? 'slow mode' : 'fast mode'})`, const noThrottle = { amtRate: MAX_THROTTLE_AMT_RATE, pctRate: 0 } await rToken.setIssuanceThrottleParams(noThrottle) await rToken.setRedemptionThrottleParams(noThrottle) + + await advanceTime(12 * 5 * 60) // 60 minutes, charge fully + await rToken.connect(addr1).issue(rTokenSupply) expect(await rToken.balanceOf(addr1.address)).to.equal(rTokenSupply) @@ -651,6 +654,9 @@ describeExtreme(`Trading Extreme Values (${SLOW ? 'slow mode' : 'fast mode'})`, const noThrottle = { amtRate: MAX_THROTTLE_AMT_RATE, pctRate: 0 } await rToken.setIssuanceThrottleParams(noThrottle) await rToken.setRedemptionThrottleParams(noThrottle) + + await advanceTime(12 * 5 * 60) // 60 minutes, charge fully + await rToken.connect(addr1).issue(rTokenSupply) expect(await rToken.balanceOf(addr1.address)).to.equal(rTokenSupply)