Skip to content

Commit b30191e

Browse files
authored
Merge pull request #74 from Havven/havven_upgrades
small changes to havven to add basic functions and cleanup
2 parents 8eaad3a + 3fd8e90 commit b30191e

21 files changed

+684
-208
lines changed

contracts/ExternStateToken.sol

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ contract ExternStateToken is SafeDecimalMath, SelfDestructible, Proxyable {
5757
* @param _tokenState The TokenState contract address.
5858
* @param _owner The owner of this contract.
5959
*/
60-
constructor(address _proxy, string _name, string _symbol, uint _totalSupply,
61-
TokenState _tokenState, address _owner)
60+
constructor(address _proxy, TokenState _tokenState,
61+
string _name, string _symbol, uint _totalSupply,
62+
address _owner)
6263
SelfDestructible(_owner)
6364
Proxyable(_proxy, _owner)
6465
public

contracts/FeeToken.sol

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ contract FeeToken is ExternStateToken {
4949
uint constant MAX_TRANSFER_FEE_RATE = UNIT / 10;
5050
/* The address with the authority to distribute fees. */
5151
address public feeAuthority;
52+
/* The address that fees will be pooled in. */
53+
address public constant FEE_ADDRESS = 0xfeefeefeefeefeefeefeefeefeefeefeefeefeef;
5254

5355

5456
/* ========== CONSTRUCTOR ========== */
@@ -63,10 +65,10 @@ contract FeeToken is ExternStateToken {
6365
* @param _feeAuthority The address which has the authority to withdraw fees from the accumulated pool.
6466
* @param _owner The owner of this contract.
6567
*/
66-
constructor(address _proxy, string _name, string _symbol, uint _totalSupply,
68+
constructor(address _proxy, TokenState _tokenState, string _name, string _symbol, uint _totalSupply,
6769
uint _transferFeeRate, address _feeAuthority, address _owner)
68-
ExternStateToken(_proxy, _name, _symbol, _totalSupply,
69-
new TokenState(_owner, address(this)),
70+
ExternStateToken(_proxy, _tokenState,
71+
_name, _symbol, _totalSupply,
7072
_owner)
7173
public
7274
{
@@ -158,7 +160,7 @@ contract FeeToken is ExternStateToken {
158160
view
159161
returns (uint)
160162
{
161-
return tokenState.balanceOf(address(this));
163+
return tokenState.balanceOf(FEE_ADDRESS);
162164
}
163165

164166
/* ========== MUTATIVE FUNCTIONS ========== */
@@ -178,11 +180,11 @@ contract FeeToken is ExternStateToken {
178180
/* Insufficient balance will be handled by the safe subtraction. */
179181
tokenState.setBalanceOf(from, safeSub(tokenState.balanceOf(from), safeAdd(amount, fee)));
180182
tokenState.setBalanceOf(to, safeAdd(tokenState.balanceOf(to), amount));
181-
tokenState.setBalanceOf(address(this), safeAdd(tokenState.balanceOf(address(this)), fee));
183+
tokenState.setBalanceOf(FEE_ADDRESS, safeAdd(tokenState.balanceOf(FEE_ADDRESS), fee));
182184

183185
/* Emit events for both the transfer itself and the fee. */
184186
emitTransfer(from, to, amount);
185-
emitTransfer(from, address(this), fee);
187+
emitTransfer(from, FEE_ADDRESS, fee);
186188

187189
return true;
188190
}
@@ -264,11 +266,11 @@ contract FeeToken is ExternStateToken {
264266
}
265267

266268
/* Safe subtraction ensures an exception is thrown if the balance is insufficient. */
267-
tokenState.setBalanceOf(address(this), safeSub(tokenState.balanceOf(address(this)), value));
269+
tokenState.setBalanceOf(FEE_ADDRESS, safeSub(tokenState.balanceOf(FEE_ADDRESS), value));
268270
tokenState.setBalanceOf(account, safeAdd(tokenState.balanceOf(account), value));
269271

270272
emitFeesWithdrawn(account, value);
271-
emitTransfer(address(this), account, value);
273+
emitTransfer(FEE_ADDRESS, account, value);
272274

273275
return true;
274276
}
@@ -288,10 +290,10 @@ contract FeeToken is ExternStateToken {
288290

289291
/* safeSub ensures the donor has sufficient balance. */
290292
tokenState.setBalanceOf(sender, safeSub(balance, n));
291-
tokenState.setBalanceOf(address(this), safeAdd(tokenState.balanceOf(address(this)), n));
293+
tokenState.setBalanceOf(FEE_ADDRESS, safeAdd(tokenState.balanceOf(FEE_ADDRESS), n));
292294

293295
emitFeesDonated(sender, n);
294-
emitTransfer(sender, address(this), n);
296+
emitTransfer(sender, FEE_ADDRESS, n);
295297

296298
return true;
297299
}

contracts/Havven.sol

Lines changed: 119 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ contract Havven is ExternStateToken {
188188

189189
/* A quantity of nomins greater than this ratio
190190
* may not be issued against a given value of havvens. */
191-
uint public issuanceRatio = 5 * UNIT / 100;
191+
uint public issuanceRatio = UNIT / 5;
192192
/* No more nomins may be issued than the value of havvens backing them. */
193193
uint constant MAX_ISSUANCE_RATIO = UNIT;
194194

@@ -210,16 +210,51 @@ contract Havven is ExternStateToken {
210210
* If the provided address is 0x0, then a fresh one will be constructed with the contract owning all tokens.
211211
* @param _owner The owner of this contract.
212212
*/
213-
constructor(address _proxy, TokenState _tokenState, address _owner, address _oracle, uint _price)
214-
ExternStateToken(_proxy, TOKEN_NAME, TOKEN_SYMBOL, HAVVEN_SUPPLY, _tokenState, _owner)
215-
/* Owned is initialised in ExternStateToken */
213+
constructor(address _proxy, TokenState _tokenState, address _owner, address _oracle,
214+
uint _price, address[] _issuers, Havven _oldHavven)
215+
ExternStateToken(_proxy, _tokenState, TOKEN_NAME, TOKEN_SYMBOL, HAVVEN_SUPPLY, _owner)
216216
public
217217
{
218218
oracle = _oracle;
219-
feePeriodStartTime = now;
220-
lastFeePeriodStartTime = now - feePeriodDuration;
221219
price = _price;
222220
lastPriceUpdateTime = now;
221+
222+
uint i;
223+
if (_oldHavven == address(0)) {
224+
feePeriodStartTime = now;
225+
lastFeePeriodStartTime = now - feePeriodDuration;
226+
for (i = 0; i < _issuers.length; i++) {
227+
isIssuer[_issuers[i]] = true;
228+
}
229+
} else {
230+
feePeriodStartTime = _oldHavven.feePeriodStartTime();
231+
lastFeePeriodStartTime = _oldHavven.lastFeePeriodStartTime();
232+
233+
uint cbs;
234+
uint lab;
235+
uint lm;
236+
(cbs, lab, lm) = _oldHavven.totalIssuanceData();
237+
totalIssuanceData.currentBalanceSum = cbs;
238+
totalIssuanceData.lastAverageBalance = lab;
239+
totalIssuanceData.lastModified = lm;
240+
241+
for (i = 0; i < _issuers.length; i++) {
242+
address issuer = _issuers[i];
243+
isIssuer[issuer] = true;
244+
uint nomins = _oldHavven.nominsIssued(issuer);
245+
if (nomins == 0) {
246+
// It is not valid in general to skip those with no currently-issued nomins.
247+
// But for this release, issuers with nonzero issuanceData have current issuance.
248+
continue;
249+
}
250+
(cbs, lab, lm) = _oldHavven.issuanceData(issuer);
251+
nominsIssued[issuer] = nomins;
252+
issuanceData[issuer].currentBalanceSum = cbs;
253+
issuanceData[issuer].lastAverageBalance = lab;
254+
issuanceData[issuer].lastModified = lm;
255+
}
256+
}
257+
223258
}
224259

225260
/* ========== SETTERS ========== */
@@ -372,11 +407,7 @@ contract Havven is ExternStateToken {
372407
returns (bool)
373408
{
374409
address sender = messageSender;
375-
/* If they have enough available Havvens, it could be that
376-
* their havvens are escrowed, however the transfer would then
377-
* fail. This means that escrowed havvens are locked first,
378-
* and then the actual transferable ones. */
379-
require(nominsIssued[sender] == 0 || value <= availableHavvens(sender));
410+
require(nominsIssued[sender] == 0 || value <= transferableHavvens(sender));
380411
/* Perform the transfer: if there is a problem,
381412
* an exception will be thrown in this call. */
382413
_transfer_byProxy(sender, to, value);
@@ -393,7 +424,7 @@ contract Havven is ExternStateToken {
393424
returns (bool)
394425
{
395426
address sender = messageSender;
396-
require(nominsIssued[sender] == 0 || value <= availableHavvens(from));
427+
require(nominsIssued[from] == 0 || value <= transferableHavvens(from));
397428
/* Perform the transfer: if there is a problem,
398429
* an exception will be thrown in this call. */
399430
_transferFrom_byProxy(sender, from, to, value);
@@ -406,7 +437,7 @@ contract Havven is ExternStateToken {
406437
* and then deposit it into their nomin account.
407438
*/
408439
function withdrawFees()
409-
public
440+
external
410441
optionalProxy
411442
{
412443
address sender = messageSender;
@@ -451,20 +482,20 @@ contract Havven is ExternStateToken {
451482
internal
452483
{
453484
/* update the total balances first */
454-
totalIssuanceData = updatedIssuanceData(lastTotalSupply, totalIssuanceData);
485+
totalIssuanceData = computeIssuanceData(lastTotalSupply, totalIssuanceData);
455486

456487
if (issuanceData[account].lastModified < feePeriodStartTime) {
457488
hasWithdrawnFees[account] = false;
458489
}
459490

460-
issuanceData[account] = updatedIssuanceData(preBalance, issuanceData[account]);
491+
issuanceData[account] = computeIssuanceData(preBalance, issuanceData[account]);
461492
}
462493

463494

464495
/**
465496
* @notice Compute the new IssuanceData on the old balance
466497
*/
467-
function updatedIssuanceData(uint preBalance, IssuanceData preIssuance)
498+
function computeIssuanceData(uint preBalance, IssuanceData preIssuance)
468499
internal
469500
view
470501
returns (IssuanceData)
@@ -609,41 +640,100 @@ contract Havven is ExternStateToken {
609640
if (issued > max) {
610641
return 0;
611642
} else {
612-
return max - issued;
643+
return safeSub(max, issued);
644+
}
645+
}
646+
647+
/**
648+
* @notice The total havvens owned by this account, both escrowed and unescrowed,
649+
* against which nomins can be issued.
650+
* This includes those already being used as collateral (locked), and those
651+
* available for further issuance (unlocked).
652+
*/
653+
function collateral(address account)
654+
public
655+
view
656+
returns (uint)
657+
{
658+
uint bal = tokenState.balanceOf(account);
659+
if (escrow != address(0)) {
660+
bal = safeAdd(bal, escrow.balanceOf(account));
613661
}
662+
return bal;
614663
}
615664

616665
/**
617-
* @notice Havvens that are locked, which can exceed the user's total balance + escrowed
666+
* @notice The collateral that would be locked by issuance, which can exceed the account's actual collateral.
618667
*/
619-
function lockedHavvens(address account)
668+
function issuanceDraft(address account)
620669
public
621670
view
622671
returns (uint)
623672
{
624-
if (nominsIssued[account] == 0) {
673+
uint issued = nominsIssued[account];
674+
if (issued == 0) {
625675
return 0;
626676
}
627-
return USDtoHAV(safeDiv_dec(nominsIssued[account], issuanceRatio));
677+
return USDtoHAV(safeDiv_dec(issued, issuanceRatio));
628678
}
629679

630680
/**
631-
* @notice Havvens that are not locked, available for issuance
681+
* @notice Collateral that has been locked due to issuance, and cannot be
682+
* transferred to other addresses. This is capped at the account's total collateral.
632683
*/
633-
function availableHavvens(address account)
684+
function lockedCollateral(address account)
634685
public
635686
view
636687
returns (uint)
637688
{
638-
uint locked = lockedHavvens(account);
639-
uint bal = tokenState.balanceOf(account);
640-
if (escrow != address(0)) {
641-
bal += escrow.balanceOf(account);
689+
uint debt = issuanceDraft(account);
690+
uint collat = collateral(account);
691+
if (debt > collat) {
692+
return collat;
642693
}
643-
if (locked > bal) {
694+
return debt;
695+
}
696+
697+
/**
698+
* @notice Collateral that is not locked and available for issuance.
699+
*/
700+
function unlockedCollateral(address account)
701+
public
702+
view
703+
returns (uint)
704+
{
705+
uint locked = lockedCollateral(account);
706+
uint collat = collateral(account);
707+
return safeSub(collat, locked);
708+
}
709+
710+
/**
711+
* @notice The number of havvens that are free to be transferred by an account.
712+
* @dev If they have enough available Havvens, it could be that
713+
* their havvens are escrowed, however the transfer would then
714+
* fail. This means that escrowed havvens are locked first,
715+
* and then the actual transferable ones.
716+
*/
717+
function transferableHavvens(address account)
718+
public
719+
view
720+
returns (uint)
721+
{
722+
uint draft = issuanceDraft(account);
723+
uint collat = collateral(account);
724+
// In the case where the issuanceDraft exceeds the collateral, nothing is free
725+
if (draft > collat) {
644726
return 0;
645727
}
646-
return bal - locked;
728+
729+
uint bal = balanceOf(account);
730+
// In the case where the draft exceeds the escrow, but not the whole collateral
731+
// return the fraction of the balance that remains free
732+
if (draft > safeSub(collat, bal)) {
733+
return safeSub(collat, draft);
734+
}
735+
// In the case where the draft doesn't exceed the escrow, return the entire balance
736+
return bal;
647737
}
648738

649739
/**

contracts/Nomin.sol

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ date: 2018-05-29
1414
1515
-----------------------------------------------------------------
1616
MODULE DESCRIPTION
17-
---------------------------------------------------------------- -
17+
-----------------------------------------------------------------
1818
1919
Havven-backed nomin stablecoin contract.
2020
@@ -25,8 +25,8 @@ value of their havvens to issue H * Cmax nomins. Where Cmax is
2525
some value less than 1.
2626
2727
A configurable fee is charged on nomin transfers and deposited
28-
into a common pot, which havven holders may withdraw from once per
29-
fee period.
28+
into a common pot, which havven holders may withdraw from once
29+
per fee period.
3030
3131
-----------------------------------------------------------------
3232
*/
@@ -57,16 +57,19 @@ contract Nomin is FeeToken {
5757

5858
/* ========== CONSTRUCTOR ========== */
5959

60-
constructor(address _proxy, Havven _havven, address _owner)
61-
FeeToken(_proxy, TOKEN_NAME, TOKEN_SYMBOL, 0, // Zero nomins initially exist.
60+
constructor(address _proxy, TokenState _tokenState, Havven _havven,
61+
uint _totalSupply,
62+
address _owner)
63+
FeeToken(_proxy, _tokenState,
64+
TOKEN_NAME, TOKEN_SYMBOL, _totalSupply,
6265
TRANSFER_FEE_RATE,
6366
_havven, // The havven contract is the fee authority.
6467
_owner)
6568
public
6669
{
6770
require(_proxy != 0 && address(_havven) != 0 && _owner != 0);
68-
// It should not be possible to transfer to the nomin contract itself.
69-
frozen[this] = true;
71+
// It should not be possible to transfer to the fee pool directly (or confiscate its balance).
72+
frozen[FEE_ADDRESS] = true;
7073
havven = _havven;
7174
}
7275

@@ -157,11 +160,11 @@ contract Nomin is FeeToken {
157160

158161
// Confiscate the balance in the account and freeze it.
159162
uint balance = tokenState.balanceOf(target);
160-
tokenState.setBalanceOf(address(this), safeAdd(tokenState.balanceOf(address(this)), balance));
163+
tokenState.setBalanceOf(FEE_ADDRESS, safeAdd(tokenState.balanceOf(FEE_ADDRESS), balance));
161164
tokenState.setBalanceOf(target, 0);
162165
frozen[target] = true;
163166
emitAccountFrozen(target, balance);
164-
emitTransfer(target, address(this), balance);
167+
emitTransfer(target, FEE_ADDRESS, balance);
165168
}
166169

167170
/* The owner may allow a previously-frozen contract to once
@@ -170,7 +173,7 @@ contract Nomin is FeeToken {
170173
external
171174
optionalProxy_onlyOwner
172175
{
173-
require(frozen[target] && target != address(this));
176+
require(frozen[target] && target != FEE_ADDRESS);
174177
frozen[target] = false;
175178
emitAccountUnfrozen(target);
176179
}

tests/contract_interfaces/fee_token_interface.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ def __init__(self, contract, name):
99
self.contract = contract
1010
self.contract_name = name
1111

12+
self.FEE_ADDRESS = lambda: self.contract.functions.FEE_ADDRESS().call()
1213
self.feePool = lambda: self.contract.functions.feePool().call()
1314
self.feeAuthority = lambda: self.contract.functions.feeAuthority().call()
1415
self.transferFeeRate = lambda: self.contract.functions.transferFeeRate().call()

0 commit comments

Comments
 (0)