-
Notifications
You must be signed in to change notification settings - Fork 0
/
GoodDollarExchangeProvider.sol
234 lines (193 loc) · 8.83 KB
/
GoodDollarExchangeProvider.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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;
import { PausableUpgradeable } from "openzeppelin-contracts-upgradeable/contracts/security/PausableUpgradeable.sol";
import { IGoodDollarExchangeProvider } from "contracts/interfaces/IGoodDollarExchangeProvider.sol";
import { IGoodDollarExpansionController } from "contracts/interfaces/IGoodDollarExpansionController.sol";
import { BancorExchangeProvider } from "./BancorExchangeProvider.sol";
import { UD60x18, unwrap, wrap } from "prb/math/UD60x18.sol";
/**
* @title GoodDollarExchangeProvider
* @notice Provides exchange functionality for the GoodDollar system.
*/
contract GoodDollarExchangeProvider is IGoodDollarExchangeProvider, BancorExchangeProvider, PausableUpgradeable {
/* ========================================================= */
/* ==================== State Variables ==================== */
/* ========================================================= */
// Address of the Expansion Controller contract.
IGoodDollarExpansionController public expansionController;
// Address of the GoodDollar DAO contract.
// solhint-disable-next-line var-name-mixedcase
address public AVATAR;
/* ===================================================== */
/* ==================== Constructor ==================== */
/* ===================================================== */
/**
* @dev Should be called with disable=true in deployments when it's accessed through a Proxy.
* Call this with disable=false during testing, when used without a proxy.
* @param disable Set to true to run `_disableInitializers()` inherited from
* openzeppelin-contracts-upgradeable/Initializable.sol
*/
constructor(bool disable) BancorExchangeProvider(disable) {}
/// @inheritdoc IGoodDollarExchangeProvider
function initialize(
address _broker,
address _reserve,
address _expansionController,
address _avatar
) public initializer {
BancorExchangeProvider._initialize(_broker, _reserve);
__Pausable_init();
setExpansionController(_expansionController);
setAvatar(_avatar);
}
/* =================================================== */
/* ==================== Modifiers ==================== */
/* =================================================== */
modifier onlyAvatar() {
require(msg.sender == AVATAR, "Only Avatar can call this function");
_;
}
modifier onlyExpansionController() {
require(msg.sender == address(expansionController), "Only ExpansionController can call this function");
_;
}
/* ============================================================ */
/* ==================== Mutative Functions ==================== */
/* ============================================================ */
/// @inheritdoc IGoodDollarExchangeProvider
function setAvatar(address _avatar) public onlyOwner {
require(_avatar != address(0), "Avatar address must be set");
AVATAR = _avatar;
emit AvatarUpdated(_avatar);
}
/// @inheritdoc IGoodDollarExchangeProvider
function setExpansionController(address _expansionController) public onlyOwner {
require(_expansionController != address(0), "ExpansionController address must be set");
expansionController = IGoodDollarExpansionController(_expansionController);
emit ExpansionControllerUpdated(_expansionController);
}
/**
* @inheritdoc BancorExchangeProvider
* @dev Only callable by the GoodDollar DAO contract.
*/
function setExitContribution(bytes32 exchangeId, uint32 exitContribution) external override onlyAvatar {
return _setExitContribution(exchangeId, exitContribution);
}
/**
* @inheritdoc BancorExchangeProvider
* @dev Only callable by the GoodDollar DAO contract.
*/
function createExchange(PoolExchange calldata _exchange) external override onlyAvatar returns (bytes32 exchangeId) {
return _createExchange(_exchange);
}
/**
* @inheritdoc BancorExchangeProvider
* @dev Only callable by the GoodDollar DAO contract.
*/
function destroyExchange(
bytes32 exchangeId,
uint256 exchangeIdIndex
) external override onlyAvatar returns (bool destroyed) {
return _destroyExchange(exchangeId, exchangeIdIndex);
}
/// @inheritdoc BancorExchangeProvider
function swapIn(
bytes32 exchangeId,
address tokenIn,
address tokenOut,
uint256 amountIn
) public override onlyBroker whenNotPaused returns (uint256 amountOut) {
amountOut = BancorExchangeProvider.swapIn(exchangeId, tokenIn, tokenOut, amountIn);
}
/// @inheritdoc BancorExchangeProvider
function swapOut(
bytes32 exchangeId,
address tokenIn,
address tokenOut,
uint256 amountOut
) public override onlyBroker whenNotPaused returns (uint256 amountIn) {
amountIn = BancorExchangeProvider.swapOut(exchangeId, tokenIn, tokenOut, amountOut);
}
/**
* @inheritdoc IGoodDollarExchangeProvider
* @dev Calculates the amount of G$ tokens that need to be minted as a result of the expansion
* while keeping the current price the same.
* calculation: amountToMint = (tokenSupply * reserveRatio - tokenSupply * newRatio) / newRatio
*/
function mintFromExpansion(
bytes32 exchangeId,
uint256 reserveRatioScalar
) external onlyExpansionController whenNotPaused returns (uint256 amountToMint) {
require(reserveRatioScalar > 0, "Reserve ratio scalar must be greater than 0");
PoolExchange memory exchange = getPoolExchange(exchangeId);
UD60x18 scaledRatio = wrap(uint256(exchange.reserveRatio) * 1e10);
UD60x18 newRatio = scaledRatio.mul(wrap(reserveRatioScalar));
uint32 newRatioUint = uint32(unwrap(newRatio) / 1e10);
require(newRatioUint > 0, "New ratio must be greater than 0");
UD60x18 numerator = wrap(exchange.tokenSupply).mul(scaledRatio);
numerator = numerator.sub(wrap(exchange.tokenSupply).mul(newRatio));
uint256 scaledAmountToMint = unwrap(numerator.div(newRatio));
exchanges[exchangeId].reserveRatio = newRatioUint;
exchanges[exchangeId].tokenSupply += scaledAmountToMint;
amountToMint = scaledAmountToMint / tokenPrecisionMultipliers[exchange.tokenAddress];
emit ReserveRatioUpdated(exchangeId, newRatioUint);
return amountToMint;
}
/**
* @inheritdoc IGoodDollarExchangeProvider
* @dev Calculates the amount of G$ tokens that need to be minted as a result of the reserve interest
* flowing into the reserve while keeping the current price the same.
* calculation: amountToMint = reserveInterest * tokenSupply / reserveBalance
*/
function mintFromInterest(
bytes32 exchangeId,
uint256 reserveInterest
) external onlyExpansionController whenNotPaused returns (uint256 amountToMint) {
PoolExchange memory exchange = getPoolExchange(exchangeId);
uint256 reserveinterestScaled = reserveInterest * tokenPrecisionMultipliers[exchange.reserveAsset];
uint256 amountToMintScaled = unwrap(
wrap(reserveinterestScaled).mul(wrap(exchange.tokenSupply)).div(wrap(exchange.reserveBalance))
);
amountToMint = amountToMintScaled / tokenPrecisionMultipliers[exchange.tokenAddress];
exchanges[exchangeId].tokenSupply += amountToMintScaled;
exchanges[exchangeId].reserveBalance += reserveinterestScaled;
return amountToMint;
}
/**
* @inheritdoc IGoodDollarExchangeProvider
* @dev Calculates the new reserve ratio needed to mint the G$ reward while keeping the current price the same.
* calculation: newRatio = reserveBalance / (tokenSupply + reward) * currentPrice
*/
function updateRatioForReward(bytes32 exchangeId, uint256 reward) external onlyExpansionController whenNotPaused {
PoolExchange memory exchange = getPoolExchange(exchangeId);
uint256 currentPriceScaled = currentPrice(exchangeId) * tokenPrecisionMultipliers[exchange.reserveAsset];
uint256 rewardScaled = reward * tokenPrecisionMultipliers[exchange.tokenAddress];
UD60x18 numerator = wrap(exchange.reserveBalance);
UD60x18 denominator = wrap(exchange.tokenSupply + rewardScaled).mul(wrap(currentPriceScaled));
uint256 newRatioScaled = unwrap(numerator.div(denominator));
uint32 newRatioUint = uint32(newRatioScaled / 1e10);
exchanges[exchangeId].reserveRatio = newRatioUint;
exchanges[exchangeId].tokenSupply += rewardScaled;
emit ReserveRatioUpdated(exchangeId, newRatioUint);
}
/**
* @inheritdoc IGoodDollarExchangeProvider
* @dev Only callable by the GoodDollar DAO contract.
*/
function pause() external virtual onlyAvatar {
_pause();
}
/**
* @inheritdoc IGoodDollarExchangeProvider
* @dev Only callable by the GoodDollar DAO contract.
*/
function unpause() external virtual onlyAvatar {
_unpause();
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}