/
GlobalPerpsMarketConfiguration.sol
220 lines (197 loc) · 8.38 KB
/
GlobalPerpsMarketConfiguration.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;
import "@synthetixio/core-contracts/contracts/utils/ERC2771Context.sol";
import {SafeCastU128} from "@synthetixio/core-contracts/contracts/utils/SafeCast.sol";
import {SetUtil} from "@synthetixio/core-contracts/contracts/utils/SetUtil.sol";
import {DecimalMath} from "@synthetixio/core-contracts/contracts/utils/DecimalMath.sol";
import {MathUtil} from "../utils/MathUtil.sol";
import {IFeeCollector} from "../interfaces/external/IFeeCollector.sol";
import {PerpsMarketFactory} from "./PerpsMarketFactory.sol";
/**
* @title This library contains all global perps market configuration data
*/
library GlobalPerpsMarketConfiguration {
using DecimalMath for uint256;
using PerpsMarketFactory for PerpsMarketFactory.Data;
using SetUtil for SetUtil.UintSet;
using SafeCastU128 for uint128;
bytes32 private constant _SLOT_GLOBAL_PERPS_MARKET_CONFIGURATION =
keccak256(abi.encode("io.synthetix.perps-market.GlobalPerpsMarketConfiguration"));
struct Data {
/**
* @dev fee collector contract
* @dev portion or all of the order fees are sent to fee collector contract based on quote.
*/
IFeeCollector feeCollector;
/**
* @dev Percentage share of fees for each referrer address
*/
mapping(address => uint256) referrerShare;
/**
* @dev mapping of configured synthMarketId to max collateral amount.
* @dev USD token synth market id = 0
*/
mapping(uint128 => uint256) maxCollateralAmounts;
/**
* @dev when deducting from user's margin which is made up of many synths, this priority governs which synth to sell for deduction
*/
uint128[] synthDeductionPriority;
/**
* @dev minimum configured keeper reward for the sender who liquidates the account
*/
uint256 minKeeperRewardUsd;
/**
* @dev maximum configured keeper reward for the sender who liquidates the account
*/
uint256 maxKeeperRewardUsd;
/**
* @dev maximum configured number of concurrent positions per account.
* @notice If set to zero it means no new positions can be opened, but existing positions can be increased or decreased.
* @notice If set to a larger number (larger than number of markets created) it means is unlimited.
*/
uint128 maxPositionsPerAccount;
/**
* @dev maximum configured number of concurrent collaterals per account.
* @notice If set to zero it means no new collaterals can be added accounts, but existing collaterals can be increased or decreased.
* @notice If set to a larger number (larger than number of collaterals enabled) it means is unlimited.
*/
uint128 maxCollateralsPerAccount;
/**
* @dev used together with minKeeperRewardUsd to get the minumum keeper reward for the sender who settles, or liquidates the account
*/
uint256 minKeeperProfitRatioD18;
/**
* @dev used together with maxKeeperRewardUsd to get the maximum keeper reward for the sender who settles, or liquidates the account
*/
uint256 maxKeeperScalingRatioD18;
/**
* @dev set of supported collateral types. By supported we mean collateral types that have a maxCollateralAmount > 0
*/
SetUtil.UintSet supportedCollateralTypes;
/**
* @dev interest rate gradient applied to utilization prior to hitting the gradient breakpoint
*/
uint128 lowUtilizationInterestRateGradient;
/**
* @dev breakpoint at which the interest rate gradient changes from low to high
*/
uint128 interestRateGradientBreakpoint;
/**
* @dev interest rate gradient applied to utilization after hitting the gradient breakpoint
*/
uint128 highUtilizationInterestRateGradient;
}
function load() internal pure returns (Data storage globalMarketConfig) {
bytes32 s = _SLOT_GLOBAL_PERPS_MARKET_CONFIGURATION;
assembly {
globalMarketConfig.slot := s
}
}
function loadInterestRateParameters() internal view returns (uint128, uint128, uint128) {
Data storage self = load();
return (
self.lowUtilizationInterestRateGradient,
self.interestRateGradientBreakpoint,
self.highUtilizationInterestRateGradient
);
}
function minimumKeeperRewardCap(
Data storage self,
uint256 costOfExecutionInUsd
) internal view returns (uint256) {
return
MathUtil.max(
costOfExecutionInUsd + self.minKeeperRewardUsd,
costOfExecutionInUsd.mulDecimal(self.minKeeperProfitRatioD18 + DecimalMath.UNIT)
);
}
function maximumKeeperRewardCap(
Data storage self,
uint256 availableMarginInUsd
) internal view returns (uint256) {
// Note: if availableMarginInUsd is zero, it means the account was flagged, so the maximumKeeperRewardCap will just be maxKeeperRewardUsd
if (availableMarginInUsd == 0) {
return self.maxKeeperRewardUsd;
}
return
MathUtil.min(
availableMarginInUsd.mulDecimal(self.maxKeeperScalingRatioD18),
self.maxKeeperRewardUsd
);
}
/**
* @dev returns the keeper reward based on total keeper rewards from all markets compared against min/max
*/
function keeperReward(
Data storage self,
uint256 keeperRewards,
uint256 costOfExecutionInUsd,
uint256 availableMarginInUsd
) internal view returns (uint256) {
uint256 minCap = minimumKeeperRewardCap(self, costOfExecutionInUsd);
uint256 maxCap = maximumKeeperRewardCap(self, availableMarginInUsd);
return MathUtil.min(MathUtil.max(minCap, keeperRewards + costOfExecutionInUsd), maxCap);
}
function updateSynthDeductionPriority(
Data storage self,
uint128[] memory newSynthDeductionPriority
) internal {
delete self.synthDeductionPriority;
for (uint256 i = 0; i < newSynthDeductionPriority.length; i++) {
self.synthDeductionPriority.push(newSynthDeductionPriority[i]);
}
}
function collectFees(
Data storage self,
uint256 orderFees,
address referrer,
PerpsMarketFactory.Data storage factory
) internal returns (uint256 referralFees, uint256 feeCollectorFees) {
referralFees = _collectReferrerFees(self, orderFees, referrer, factory);
uint256 remainingFees = orderFees - referralFees;
if (remainingFees == 0 || self.feeCollector == IFeeCollector(address(0))) {
return (referralFees, 0);
}
uint256 feeCollectorQuote = self.feeCollector.quoteFees(
factory.perpsMarketId,
remainingFees,
ERC2771Context._msgSender()
);
if (feeCollectorQuote == 0) {
return (referralFees, 0);
}
if (feeCollectorQuote > remainingFees) {
feeCollectorQuote = remainingFees;
}
factory.withdrawMarketUsd(address(self.feeCollector), feeCollectorQuote);
return (referralFees, feeCollectorQuote);
}
function updateCollateral(
Data storage self,
uint128 synthMarketId,
uint256 maxCollateralAmount
) internal {
self.maxCollateralAmounts[synthMarketId] = maxCollateralAmount;
bool isSupportedCollateral = self.supportedCollateralTypes.contains(synthMarketId);
if (maxCollateralAmount > 0 && !isSupportedCollateral) {
self.supportedCollateralTypes.add(synthMarketId.to256());
} else if (maxCollateralAmount == 0 && isSupportedCollateral) {
self.supportedCollateralTypes.remove(synthMarketId.to256());
}
}
function _collectReferrerFees(
Data storage self,
uint256 fees,
address referrer,
PerpsMarketFactory.Data storage factory
) private returns (uint256 referralFeesSent) {
if (referrer == address(0)) {
return 0;
}
uint256 referrerShareRatio = self.referrerShare[referrer];
if (referrerShareRatio > 0) {
referralFeesSent = fees.mulDecimal(referrerShareRatio);
factory.withdrawMarketUsd(referrer, referralFeesSent);
}
}
}