From c93137075cdff2ff627aab8d11d4c15bc569af52 Mon Sep 17 00:00:00 2001 From: Kevin Britz Date: Tue, 18 Jul 2023 16:05:23 -0700 Subject: [PATCH] parameter invariant cleanup --- packages/perennial/contracts/Market.sol | 50 ++----------------- .../contracts/types/MarketParameter.sol | 26 +++++++++- .../contracts/types/RiskParameter.sol | 34 ++++++++++++- .../perennial/test/unit/market/Market.test.ts | 48 +++++++++--------- 4 files changed, 85 insertions(+), 73 deletions(-) diff --git a/packages/perennial/contracts/Market.sol b/packages/perennial/contracts/Market.sol index 52f4f2df0..0caa277e8 100644 --- a/packages/perennial/contracts/Market.sol +++ b/packages/perennial/contracts/Market.sol @@ -119,21 +119,7 @@ contract Market is IMarket, Instance { /// @notice Updates the parameter set of the market /// @param newParameter The new parameter set function updateParameter(MarketParameter memory newParameter) external onlyOwner { - ProtocolParameter memory protocolParameter = IMarketFactory(address(factory())).parameter(); - - if (newParameter.fundingFee.gt(protocolParameter.maxCut)) revert MarketInvalidMarketParameterError(1); - if (newParameter.interestFee.gt(protocolParameter.maxCut)) revert MarketInvalidMarketParameterError(2); - if (newParameter.positionFee.gt(protocolParameter.maxCut)) revert MarketInvalidMarketParameterError(3); - if (newParameter.settlementFee.gt(protocolParameter.maxFeeAbsolute)) - revert MarketInvalidMarketParameterError(4); - if (newParameter.oracleFee.add(newParameter.riskFee).gt(UFixed6Lib.ONE)) - revert MarketInvalidMarketParameterError(5); - if (reward.isZero() && ( - !newParameter.makerRewardRate.isZero() || - !newParameter.longRewardRate.isZero() || - !newParameter.shortRewardRate.isZero() - )) revert MarketInvalidMarketParameterError(6); - + newParameter.validate(IMarketFactory(address(factory())).parameter(), reward); _parameter.store(newParameter); emit ParameterUpdated(newParameter); } @@ -141,38 +127,8 @@ contract Market is IMarket, Instance { /// @notice Updates the risk parameter set of the market /// @param newRiskParameter The new risk parameter set function updateRiskParameter(RiskParameter memory newRiskParameter) external onlyCoordinator { - ProtocolParameter memory protocolParameter = IMarketFactory(address(factory())).parameter(); - - if (newRiskParameter.maintenance.lt(protocolParameter.minMaintenance)) - revert MarketInvalidRiskParameterError(1); - if (newRiskParameter.takerFee.gt(protocolParameter.maxFee)) revert MarketInvalidRiskParameterError(2); - if (newRiskParameter.takerSkewFee.gt(protocolParameter.maxFee)) revert MarketInvalidRiskParameterError(3); - if (newRiskParameter.takerImpactFee.gt(protocolParameter.maxFee)) revert MarketInvalidRiskParameterError(4); - if (newRiskParameter.makerFee.gt(protocolParameter.maxFee)) revert MarketInvalidRiskParameterError(5); - if (newRiskParameter.makerImpactFee.gt(protocolParameter.maxFee)) revert MarketInvalidRiskParameterError(6); - if (newRiskParameter.efficiencyLimit.lt(protocolParameter.minEfficiency)) - revert MarketInvalidRiskParameterError(7); - if (newRiskParameter.liquidationFee.gt(protocolParameter.maxCut)) revert MarketInvalidRiskParameterError(8); - if (newRiskParameter.minLiquidationFee.gt(protocolParameter.maxFeeAbsolute)) - revert MarketInvalidRiskParameterError(9); - if (newRiskParameter.maxLiquidationFee.gt(protocolParameter.maxFeeAbsolute)) - revert MarketInvalidRiskParameterError(10); - if (newRiskParameter.utilizationCurve.minRate.gt(protocolParameter.maxRate)) - revert MarketInvalidRiskParameterError(11); - if (newRiskParameter.utilizationCurve.maxRate.gt(protocolParameter.maxRate)) - revert MarketInvalidRiskParameterError(12); - if (newRiskParameter.utilizationCurve.targetRate.gt(protocolParameter.maxRate)) - revert MarketInvalidRiskParameterError(13); - if (newRiskParameter.utilizationCurve.targetUtilization.gt(UFixed6Lib.ONE)) - revert MarketInvalidRiskParameterError(14); - if (newRiskParameter.pController.max.gt(protocolParameter.maxRate)) - revert MarketInvalidRiskParameterError(15); - if ( - newRiskParameter.minMaintenance.gt(protocolParameter.maxFeeAbsolute) || - newRiskParameter.minMaintenance.lt(newRiskParameter.minLiquidationFee) - ) revert MarketInvalidRiskParameterError(16); - - _updateRiskParameter(newRiskParameter); + newRiskParameter.validate(IMarketFactory(address(factory())).parameter()); + _updateRiskParameter(newRiskParameter); // TODO: unpack } /// @notice Updates the reward token of the market diff --git a/packages/perennial/contracts/types/MarketParameter.sol b/packages/perennial/contracts/types/MarketParameter.sol index e88279d97..e3a331e73 100644 --- a/packages/perennial/contracts/types/MarketParameter.sol +++ b/packages/perennial/contracts/types/MarketParameter.sol @@ -3,9 +3,11 @@ pragma solidity ^0.8.13; import "@equilibria/perennial-v2-payoff/contracts/interfaces/IPayoffProvider.sol"; import "@equilibria/perennial-v2-oracle/contracts/interfaces/IOracleProvider.sol"; +import "@equilibria/root/token/types/Token18.sol"; import "@equilibria/root/number/types/UFixed6.sol"; import "@equilibria/root/curve/types/UJumpRateUtilizationCurve6.sol"; import "@equilibria/root-v2/contracts/PController6.sol"; +import "./ProtocolParameter.sol"; /// @dev MarketParameter type struct MarketParameter { @@ -22,7 +24,7 @@ struct MarketParameter { bool makerCloseAlways; bool closed; } - +using MarketParameterLib for MarketParameter global; struct StoredMarketParameter { uint24 fundingFee; // <= 1677% uint24 interestFee; // <= 1677% @@ -38,6 +40,28 @@ struct StoredMarketParameter { struct MarketParameterStorage { StoredMarketParameter value; } using MarketParameterStorageLib for MarketParameterStorage global; +library MarketParameterLib { + error MarketInvalidMarketParameterError(uint256 code); + + function validate( + MarketParameter memory self, + ProtocolParameter memory protocolParameter, + Token18 reward + ) internal pure { + if (self.settlementFee.gt(protocolParameter.maxFeeAbsolute)) revert MarketInvalidMarketParameterError(2); + + if (self.fundingFee.max(self.interestFee).max(self.positionFee).gt(protocolParameter.maxCut)) + revert MarketInvalidMarketParameterError(3); + + if (self.oracleFee.add(self.riskFee).gt(UFixed6Lib.ONE)) revert MarketInvalidMarketParameterError(8); + + if ( + reward.isZero() && + (!self.makerRewardRate.isZero() || !self.longRewardRate.isZero() || !self.shortRewardRate.isZero()) + ) revert MarketInvalidMarketParameterError(9); + } +} + library MarketParameterStorageLib { error MarketParameterStorageInvalidError(); diff --git a/packages/perennial/contracts/types/RiskParameter.sol b/packages/perennial/contracts/types/RiskParameter.sol index a5a2989da..b25d55625 100644 --- a/packages/perennial/contracts/types/RiskParameter.sol +++ b/packages/perennial/contracts/types/RiskParameter.sol @@ -6,6 +6,7 @@ import "@equilibria/perennial-v2-oracle/contracts/interfaces/IOracleProvider.sol import "@equilibria/root/number/types/UFixed6.sol"; import "@equilibria/root/curve/types/UJumpRateUtilizationCurve6.sol"; import "@equilibria/root-v2/contracts/PController6.sol"; +import "./ProtocolParameter.sol"; /// @dev RiskParameter type struct RiskParameter { @@ -27,7 +28,7 @@ struct RiskParameter { uint256 staleAfter; bool makerReceiveOnly; } - +using RiskParameterLib for RiskParameter global; struct StoredRiskParameter { /* slot 1 */ uint48 makerLimit; // <= 281m @@ -59,6 +60,37 @@ struct StoredRiskParameter { struct RiskParameterStorage { StoredRiskParameter value; } using RiskParameterStorageLib for RiskParameterStorage global; +library RiskParameterLib { + error MarketInvalidRiskParameterError(uint256 code); + + function validate(RiskParameter memory self, ProtocolParameter memory protocolParameter) internal pure { + if ( + self.takerFee.max(self.takerSkewFee).max(self.takerImpactFee).max(self.makerFee).max(self.makerImpactFee) + .gt(protocolParameter.maxFee) + ) revert MarketInvalidRiskParameterError(1); + + if ( + self.minLiquidationFee.max(self.maxLiquidationFee).max(self.minMaintenance) + .gt(protocolParameter.maxFeeAbsolute) + ) revert MarketInvalidRiskParameterError(2); + + if (self.liquidationFee.gt(protocolParameter.maxCut)) revert MarketInvalidRiskParameterError(3); + + if ( + self.utilizationCurve.minRate.max(self.utilizationCurve.maxRate).max(self.utilizationCurve.targetRate).max(self.pController.max) + .gt(protocolParameter.maxRate) + ) revert MarketInvalidRiskParameterError(4); + + if (self.maintenance.lt(protocolParameter.minMaintenance)) revert MarketInvalidRiskParameterError(5); + + if (self.efficiencyLimit.lt(protocolParameter.minEfficiency)) revert MarketInvalidRiskParameterError(6); + + if (self.utilizationCurve.targetUtilization.gt(UFixed6Lib.ONE)) revert MarketInvalidRiskParameterError(7); + + if (self.minMaintenance.lt(self.minLiquidationFee)) revert MarketInvalidRiskParameterError(8); + } +} + library RiskParameterStorageLib { error RiskParameterStorageInvalidError(); diff --git a/packages/perennial/test/unit/market/Market.test.ts b/packages/perennial/test/unit/market/Market.test.ts index c06c7284c..37dd8b1ea 100644 --- a/packages/perennial/test/unit/market/Market.test.ts +++ b/packages/perennial/test/unit/market/Market.test.ts @@ -542,7 +542,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateParameter(newMarketParameter)) .to.revertedWithCustomError(market, 'MarketInvalidMarketParameterError') - .withArgs(4) + .withArgs(2) }) }) @@ -570,7 +570,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateParameter(newMarketParameter)) .to.revertedWithCustomError(market, 'MarketInvalidMarketParameterError') - .withArgs(1) + .withArgs(3) }) it('interestFee -> fail', async () => { @@ -581,7 +581,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateParameter(newMarketParameter)) .to.revertedWithCustomError(market, 'MarketInvalidMarketParameterError') - .withArgs(2) + .withArgs(3) }) it('positionFee -> fail', async () => { @@ -618,7 +618,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateParameter(newMarketParameter)) .to.revertedWithCustomError(market, 'MarketInvalidMarketParameterError') - .withArgs(5) + .withArgs(8) }) }) @@ -644,7 +644,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateParameter(newMarketParameter)) .to.revertedWithCustomError(market, 'MarketInvalidMarketParameterError') - .withArgs(6) + .withArgs(9) }) it('longRewardRate -> fail', async () => { @@ -656,7 +656,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateParameter(newMarketParameter)) .to.revertedWithCustomError(market, 'MarketInvalidMarketParameterError') - .withArgs(6) + .withArgs(9) }) it('shortRewardRate -> fail', async () => { @@ -668,7 +668,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateParameter(newMarketParameter)) .to.revertedWithCustomError(market, 'MarketInvalidMarketParameterError') - .withArgs(6) + .withArgs(9) }) }) @@ -806,7 +806,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(2) + .withArgs(1) }) it('takerSkewFee -> fail', async () => { @@ -817,7 +817,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(3) + .withArgs(1) }) it('takerImpactFee -> fail', async () => { @@ -828,7 +828,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(4) + .withArgs(1) }) it('makerFee -> fail', async () => { @@ -839,7 +839,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(5) + .withArgs(1) }) it('makerImpactFee -> fail', async () => { @@ -850,7 +850,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(6) + .withArgs(1) }) }) @@ -877,7 +877,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(9) + .withArgs(2) }) it('maxLiquidationFee -> fail', async () => { @@ -888,7 +888,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(10) + .withArgs(2) }) it('minMaintenance -> fail', async () => { @@ -899,7 +899,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(16) + .withArgs(2) }) }) @@ -924,7 +924,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(8) + .withArgs(3) }) }) @@ -961,7 +961,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(11) + .withArgs(4) }) it('utilizationCurve.maxRate -> fail', async () => { @@ -975,7 +975,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(12) + .withArgs(4) }) it('utilizationCurve.targetRate -> fail', async () => { @@ -989,7 +989,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(13) + .withArgs(4) }) it('pController.max -> fail', async () => { @@ -1003,7 +1003,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(15) + .withArgs(4) }) }) @@ -1028,7 +1028,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(1) + .withArgs(5) }) }) @@ -1053,7 +1053,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(7) + .withArgs(6) }) }) @@ -1082,7 +1082,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(14) + .withArgs(7) }) }) @@ -1117,7 +1117,7 @@ describe('Market', () => { } await expect(market.connect(owner).updateRiskParameter(newRiskParameter)) .to.revertedWithCustomError(market, 'MarketInvalidRiskParameterError') - .withArgs(16) + .withArgs(8) }) })