-
Notifications
You must be signed in to change notification settings - Fork 602
/
Copy pathIssuanceController.sol
345 lines (292 loc) · 10.2 KB
/
IssuanceController.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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
/*
-----------------------------------------------------------------
FILE INFORMATION
-----------------------------------------------------------------
file: IssuanceController.sol
version: 1.0
author: Kevin Brown
date: 2018-05-20
-----------------------------------------------------------------
MODULE DESCRIPTION
-----------------------------------------------------------------
Issuance controller contract. The issuance controller provides
a way for users to acquire nomins (Nomin.sol) by paying ETH
and a way for users to acquire havvens (Havven.sol) by paying
nomins.
This smart contract contains a balance of each currency, and
allows the owner of the contract (the Havven Foundation) to
manage the available balances of both currencies at their
discretion.
In future releases this functionality will gradually move away
from a centralised approach with the Havven foundation
controlling all of the currency to a decentralised exchange
approach where users can exchange these assets freely.
-----------------------------------------------------------------
*/
pragma solidity 0.4.24;
import "contracts/SelfDestructible.sol";
import "contracts/Pausable.sol";
import "contracts/SafeDecimalMath.sol";
import "contracts/Havven.sol";
import "contracts/Nomin.sol";
/**
* @title Issuance Controller Contract.
*/
contract IssuanceController is SafeDecimalMath, SelfDestructible, Pausable {
/* ========== STATE VARIABLES ========== */
Havven public havven;
Nomin public nomin;
// Address where the ether raised is transfered to
address public fundsWallet;
/* The address of the oracle which pushes the USD price havvens and ether to this contract */
address public oracle;
/* Do not allow the oracle to submit times any further forward into the future than
this constant. */
uint constant ORACLE_FUTURE_LIMIT = 10 minutes;
/* How long will the contract assume the price of any asset is correct */
uint public priceStalePeriod = 3 hours;
/* The time the prices were last updated */
uint public lastPriceUpdateTime;
/* The USD price of havvens denominated in UNIT */
uint public usdToHavPrice;
/* The USD price of ETH denominated in UNIT */
uint public usdToEthPrice;
/* ========== CONSTRUCTOR ========== */
/**
* @dev Constructor
* @param _owner The owner of this contract.
* @param _havven The Havven contract we'll interact with for balances and sending.
* @param _nomin The Nomin contract we'll interact with for balances and sending.
* @param _oracle The address which is able to update price information.
* @param _usdToEthPrice The current price of ETH in USD, expressed in UNIT.
* @param _usdToHavPrice The current price of Havven in USD, expressed in UNIT.
*/
constructor(
// Ownable
address _owner,
// Funds Wallet
address _fundsWallet,
// Other contracts needed
Havven _havven,
Nomin _nomin,
// Oracle values - Allows for price updates
address _oracle,
uint _usdToEthPrice,
uint _usdToHavPrice
)
/* Owned is initialised in SelfDestructible */
SelfDestructible(_owner)
Pausable(_owner)
public
{
fundsWallet = _fundsWallet;
havven = _havven;
nomin = _nomin;
oracle = _oracle;
usdToEthPrice = _usdToEthPrice;
usdToHavPrice = _usdToHavPrice;
lastPriceUpdateTime = now;
}
/* ========== SETTERS ========== */
/**
* @notice Set the funds wallet where ETH raised is held
*/
function setFundsWallet(address _fundsWallet)
external
onlyOwner
{
fundsWallet = _fundsWallet;
emit FundsWalletUpdated(fundsWallet);
}
/**
* @notice Set the Oracle that pushes the havven price to this contract
*/
function setOracle(address _oracle)
external
onlyOwner
{
oracle = _oracle;
emit OracleUpdated(oracle);
}
/**
* @notice Set the Nomin contract that the issuance controller uses to issue Nomins.
*/
function setNomin(Nomin _nomin)
external
onlyOwner
{
nomin = _nomin;
emit NominUpdated(_nomin);
}
/**
* @notice Set the Havven contract that the issuance controller uses to issue Havvens.
*/
function setHavven(Havven _havven)
external
onlyOwner
{
havven = _havven;
emit HavvenUpdated(_havven);
}
/**
* @notice Set the stale period on the updated price variables
*/
function setPriceStalePeriod(uint _time)
external
onlyOwner
{
priceStalePeriod = _time;
emit PriceStalePeriodUpdated(priceStalePeriod);
}
/* ========== MUTATIVE FUNCTIONS ========== */
/**
* @notice Access point for the oracle to update the prices of havvens / eth.
*/
function updatePrices(uint newEthPrice, uint newHavvenPrice, uint timeSent)
external
onlyOracle
{
/* Must be the most recently sent price, but not too far in the future.
* (so we can't lock ourselves out of updating the oracle for longer than this) */
require(lastPriceUpdateTime < timeSent && timeSent < now + ORACLE_FUTURE_LIMIT);
usdToEthPrice = newEthPrice;
usdToHavPrice = newHavvenPrice;
lastPriceUpdateTime = timeSent;
emit PricesUpdated(usdToEthPrice, usdToHavPrice, lastPriceUpdateTime);
}
/**
* @notice Fallback function (exchanges ETH to nUSD)
*/
function ()
external
payable
{
exchangeEtherForNomins();
}
/**
* @notice Exchange ETH to nUSD.
*/
function exchangeEtherForNomins()
public
payable
pricesNotStale
notPaused
returns (uint) // Returns the number of Nomins (nUSD) received
{
// The multiplication works here because usdToEthPrice is specified in
// 18 decimal places, just like our currency base.
uint requestedToPurchase = safeMul_dec(msg.value, usdToEthPrice);
// Store the ETH in our funds wallet
fundsWallet.transfer(msg.value);
// Send the nomins.
// Note: Fees are calculated by the Nomin contract, so when
// we request a specific transfer here, the fee is
// automatically deducted and sent to the fee pool.
nomin.transfer(msg.sender, requestedToPurchase);
// We don't emit our own events here because we assume that anyone
// who wants to watch what the Issuance Controller is doing can
// just watch ERC20 events from the Nomin contract filtered to our
// address.
return requestedToPurchase;
}
/**
* @notice Exchange nUSD for Havvens
*/
function exchangeNominsForHavvens(uint amount)
external
pricesNotStale
notPaused
returns (uint) // Returns the number of Havvens (HAV) received
{
// How many Havvens are they going to be receiving?
uint havvensToSend = havvensReceivedForNomins(amount);
// Ok, transfer the Nomins to our address.
nomin.transferFrom(msg.sender, this, amount);
// And send them the Havvens.
havven.transfer(msg.sender, havvensToSend);
// We don't emit our own events here because we assume that anyone
// who wants to watch what the Issuance Controller is doing can
// just watch ERC20 events from the Nomin and/or Havven contracts
// filtered to our address.
return havvensToSend;
}
/**
* @notice Withdraw havvens: Allows the owner to withdraw havvens from this contract if needed.
*/
function withdrawHavvens(uint amount)
external
onlyOwner
{
havven.transfer(owner, amount);
// We don't emit our own events here because we assume that anyone
// who wants to watch what the Issuance Controller is doing can
// just watch ERC20 events from the Nomin and/or Havven contracts
// filtered to our address.
}
/**
* @notice Withdraw nomins: Allows the owner to withdraw nomins from this contract if needed.
*/
function withdrawNomins(uint amount)
external
onlyOwner
{
nomin.transfer(owner, amount);
// We don't emit our own events here because we assume that anyone
// who wants to watch what the Issuance Controller is doing can
// just watch ERC20 events from the Nomin and/or Havven contracts
// filtered to our address.
}
/* ========== VIEWS ========== */
/**
* @notice Check if the prices haven't been updated for longer than the stale period.
*/
function pricesAreStale()
public
view
returns (bool)
{
return safeAdd(lastPriceUpdateTime, priceStalePeriod) < now;
}
/**
* @notice Calculate how many havvens you will receive if you transfer
* an amount of nomins.
*/
function havvensReceivedForNomins(uint amount)
public
view
returns (uint)
{
uint nominsReceived = nomin.amountReceived(amount);
return safeMul_dec(safeDiv_dec(nominsReceived, usdToHavPrice), UNIT);
}
/**
* @notice Calculate how many nomins you will receive if you transfer
* an amount of ether.
*/
function nominsReceivedForEther(uint amount)
public
view
returns (uint)
{
uint nominsTransferred = safeMul_dec(amount, usdToEthPrice);
return nomin.amountReceived(nominsTransferred);
}
/* ========== MODIFIERS ========== */
modifier onlyOracle
{
require(msg.sender == oracle);
_;
}
modifier pricesNotStale
{
require(!pricesAreStale());
_;
}
/* ========== EVENTS ========== */
event FundsWalletUpdated(address newFundsWallet);
event OracleUpdated(address newOracle);
event NominUpdated(Nomin newNominContract);
event HavvenUpdated(Havven newHavvenContract);
event PriceStalePeriodUpdated(uint priceStalePeriod);
event PricesUpdated(uint newEthPrice, uint newHavvenPrice, uint timeSent);
}