Skip to content

Commit

Permalink
Merge pull request #74 from Havven/havven_upgrades
Browse files Browse the repository at this point in the history
small changes to havven to add basic functions and cleanup
  • Loading branch information
zyzek authored Jun 29, 2018
2 parents 8eaad3a + 3fd8e90 commit b30191e
Show file tree
Hide file tree
Showing 21 changed files with 684 additions and 208 deletions.
5 changes: 3 additions & 2 deletions contracts/ExternStateToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ contract ExternStateToken is SafeDecimalMath, SelfDestructible, Proxyable {
* @param _tokenState The TokenState contract address.
* @param _owner The owner of this contract.
*/
constructor(address _proxy, string _name, string _symbol, uint _totalSupply,
TokenState _tokenState, address _owner)
constructor(address _proxy, TokenState _tokenState,
string _name, string _symbol, uint _totalSupply,
address _owner)
SelfDestructible(_owner)
Proxyable(_proxy, _owner)
public
Expand Down
22 changes: 12 additions & 10 deletions contracts/FeeToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ contract FeeToken is ExternStateToken {
uint constant MAX_TRANSFER_FEE_RATE = UNIT / 10;
/* The address with the authority to distribute fees. */
address public feeAuthority;
/* The address that fees will be pooled in. */
address public constant FEE_ADDRESS = 0xfeefeefeefeefeefeefeefeefeefeefeefeefeef;


/* ========== CONSTRUCTOR ========== */
Expand All @@ -63,10 +65,10 @@ contract FeeToken is ExternStateToken {
* @param _feeAuthority The address which has the authority to withdraw fees from the accumulated pool.
* @param _owner The owner of this contract.
*/
constructor(address _proxy, string _name, string _symbol, uint _totalSupply,
constructor(address _proxy, TokenState _tokenState, string _name, string _symbol, uint _totalSupply,
uint _transferFeeRate, address _feeAuthority, address _owner)
ExternStateToken(_proxy, _name, _symbol, _totalSupply,
new TokenState(_owner, address(this)),
ExternStateToken(_proxy, _tokenState,
_name, _symbol, _totalSupply,
_owner)
public
{
Expand Down Expand Up @@ -158,7 +160,7 @@ contract FeeToken is ExternStateToken {
view
returns (uint)
{
return tokenState.balanceOf(address(this));
return tokenState.balanceOf(FEE_ADDRESS);
}

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

/* Emit events for both the transfer itself and the fee. */
emitTransfer(from, to, amount);
emitTransfer(from, address(this), fee);
emitTransfer(from, FEE_ADDRESS, fee);

return true;
}
Expand Down Expand Up @@ -264,11 +266,11 @@ contract FeeToken is ExternStateToken {
}

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

emitFeesWithdrawn(account, value);
emitTransfer(address(this), account, value);
emitTransfer(FEE_ADDRESS, account, value);

return true;
}
Expand All @@ -288,10 +290,10 @@ contract FeeToken is ExternStateToken {

/* safeSub ensures the donor has sufficient balance. */
tokenState.setBalanceOf(sender, safeSub(balance, n));
tokenState.setBalanceOf(address(this), safeAdd(tokenState.balanceOf(address(this)), n));
tokenState.setBalanceOf(FEE_ADDRESS, safeAdd(tokenState.balanceOf(FEE_ADDRESS), n));

emitFeesDonated(sender, n);
emitTransfer(sender, address(this), n);
emitTransfer(sender, FEE_ADDRESS, n);

return true;
}
Expand Down
148 changes: 119 additions & 29 deletions contracts/Havven.sol
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ contract Havven is ExternStateToken {

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

Expand All @@ -210,16 +210,51 @@ contract Havven is ExternStateToken {
* If the provided address is 0x0, then a fresh one will be constructed with the contract owning all tokens.
* @param _owner The owner of this contract.
*/
constructor(address _proxy, TokenState _tokenState, address _owner, address _oracle, uint _price)
ExternStateToken(_proxy, TOKEN_NAME, TOKEN_SYMBOL, HAVVEN_SUPPLY, _tokenState, _owner)
/* Owned is initialised in ExternStateToken */
constructor(address _proxy, TokenState _tokenState, address _owner, address _oracle,
uint _price, address[] _issuers, Havven _oldHavven)
ExternStateToken(_proxy, _tokenState, TOKEN_NAME, TOKEN_SYMBOL, HAVVEN_SUPPLY, _owner)
public
{
oracle = _oracle;
feePeriodStartTime = now;
lastFeePeriodStartTime = now - feePeriodDuration;
price = _price;
lastPriceUpdateTime = now;

uint i;
if (_oldHavven == address(0)) {
feePeriodStartTime = now;
lastFeePeriodStartTime = now - feePeriodDuration;
for (i = 0; i < _issuers.length; i++) {
isIssuer[_issuers[i]] = true;
}
} else {
feePeriodStartTime = _oldHavven.feePeriodStartTime();
lastFeePeriodStartTime = _oldHavven.lastFeePeriodStartTime();

uint cbs;
uint lab;
uint lm;
(cbs, lab, lm) = _oldHavven.totalIssuanceData();
totalIssuanceData.currentBalanceSum = cbs;
totalIssuanceData.lastAverageBalance = lab;
totalIssuanceData.lastModified = lm;

for (i = 0; i < _issuers.length; i++) {
address issuer = _issuers[i];
isIssuer[issuer] = true;
uint nomins = _oldHavven.nominsIssued(issuer);
if (nomins == 0) {
// It is not valid in general to skip those with no currently-issued nomins.
// But for this release, issuers with nonzero issuanceData have current issuance.
continue;
}
(cbs, lab, lm) = _oldHavven.issuanceData(issuer);
nominsIssued[issuer] = nomins;
issuanceData[issuer].currentBalanceSum = cbs;
issuanceData[issuer].lastAverageBalance = lab;
issuanceData[issuer].lastModified = lm;
}
}

}

/* ========== SETTERS ========== */
Expand Down Expand Up @@ -372,11 +407,7 @@ contract Havven is ExternStateToken {
returns (bool)
{
address sender = messageSender;
/* If they have enough available Havvens, it could be that
* their havvens are escrowed, however the transfer would then
* fail. This means that escrowed havvens are locked first,
* and then the actual transferable ones. */
require(nominsIssued[sender] == 0 || value <= availableHavvens(sender));
require(nominsIssued[sender] == 0 || value <= transferableHavvens(sender));
/* Perform the transfer: if there is a problem,
* an exception will be thrown in this call. */
_transfer_byProxy(sender, to, value);
Expand All @@ -393,7 +424,7 @@ contract Havven is ExternStateToken {
returns (bool)
{
address sender = messageSender;
require(nominsIssued[sender] == 0 || value <= availableHavvens(from));
require(nominsIssued[from] == 0 || value <= transferableHavvens(from));
/* Perform the transfer: if there is a problem,
* an exception will be thrown in this call. */
_transferFrom_byProxy(sender, from, to, value);
Expand All @@ -406,7 +437,7 @@ contract Havven is ExternStateToken {
* and then deposit it into their nomin account.
*/
function withdrawFees()
public
external
optionalProxy
{
address sender = messageSender;
Expand Down Expand Up @@ -451,20 +482,20 @@ contract Havven is ExternStateToken {
internal
{
/* update the total balances first */
totalIssuanceData = updatedIssuanceData(lastTotalSupply, totalIssuanceData);
totalIssuanceData = computeIssuanceData(lastTotalSupply, totalIssuanceData);

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

issuanceData[account] = updatedIssuanceData(preBalance, issuanceData[account]);
issuanceData[account] = computeIssuanceData(preBalance, issuanceData[account]);
}


/**
* @notice Compute the new IssuanceData on the old balance
*/
function updatedIssuanceData(uint preBalance, IssuanceData preIssuance)
function computeIssuanceData(uint preBalance, IssuanceData preIssuance)
internal
view
returns (IssuanceData)
Expand Down Expand Up @@ -609,41 +640,100 @@ contract Havven is ExternStateToken {
if (issued > max) {
return 0;
} else {
return max - issued;
return safeSub(max, issued);
}
}

/**
* @notice The total havvens owned by this account, both escrowed and unescrowed,
* against which nomins can be issued.
* This includes those already being used as collateral (locked), and those
* available for further issuance (unlocked).
*/
function collateral(address account)
public
view
returns (uint)
{
uint bal = tokenState.balanceOf(account);
if (escrow != address(0)) {
bal = safeAdd(bal, escrow.balanceOf(account));
}
return bal;
}

/**
* @notice Havvens that are locked, which can exceed the user's total balance + escrowed
* @notice The collateral that would be locked by issuance, which can exceed the account's actual collateral.
*/
function lockedHavvens(address account)
function issuanceDraft(address account)
public
view
returns (uint)
{
if (nominsIssued[account] == 0) {
uint issued = nominsIssued[account];
if (issued == 0) {
return 0;
}
return USDtoHAV(safeDiv_dec(nominsIssued[account], issuanceRatio));
return USDtoHAV(safeDiv_dec(issued, issuanceRatio));
}

/**
* @notice Havvens that are not locked, available for issuance
* @notice Collateral that has been locked due to issuance, and cannot be
* transferred to other addresses. This is capped at the account's total collateral.
*/
function availableHavvens(address account)
function lockedCollateral(address account)
public
view
returns (uint)
{
uint locked = lockedHavvens(account);
uint bal = tokenState.balanceOf(account);
if (escrow != address(0)) {
bal += escrow.balanceOf(account);
uint debt = issuanceDraft(account);
uint collat = collateral(account);
if (debt > collat) {
return collat;
}
if (locked > bal) {
return debt;
}

/**
* @notice Collateral that is not locked and available for issuance.
*/
function unlockedCollateral(address account)
public
view
returns (uint)
{
uint locked = lockedCollateral(account);
uint collat = collateral(account);
return safeSub(collat, locked);
}

/**
* @notice The number of havvens that are free to be transferred by an account.
* @dev If they have enough available Havvens, it could be that
* their havvens are escrowed, however the transfer would then
* fail. This means that escrowed havvens are locked first,
* and then the actual transferable ones.
*/
function transferableHavvens(address account)
public
view
returns (uint)
{
uint draft = issuanceDraft(account);
uint collat = collateral(account);
// In the case where the issuanceDraft exceeds the collateral, nothing is free
if (draft > collat) {
return 0;
}
return bal - locked;

uint bal = balanceOf(account);
// In the case where the draft exceeds the escrow, but not the whole collateral
// return the fraction of the balance that remains free
if (draft > safeSub(collat, bal)) {
return safeSub(collat, draft);
}
// In the case where the draft doesn't exceed the escrow, return the entire balance
return bal;
}

/**
Expand Down
23 changes: 13 additions & 10 deletions contracts/Nomin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ date: 2018-05-29
-----------------------------------------------------------------
MODULE DESCRIPTION
---------------------------------------------------------------- -
-----------------------------------------------------------------
Havven-backed nomin stablecoin contract.
Expand All @@ -25,8 +25,8 @@ value of their havvens to issue H * Cmax nomins. Where Cmax is
some value less than 1.
A configurable fee is charged on nomin transfers and deposited
into a common pot, which havven holders may withdraw from once per
fee period.
into a common pot, which havven holders may withdraw from once
per fee period.
-----------------------------------------------------------------
*/
Expand Down Expand Up @@ -57,16 +57,19 @@ contract Nomin is FeeToken {

/* ========== CONSTRUCTOR ========== */

constructor(address _proxy, Havven _havven, address _owner)
FeeToken(_proxy, TOKEN_NAME, TOKEN_SYMBOL, 0, // Zero nomins initially exist.
constructor(address _proxy, TokenState _tokenState, Havven _havven,
uint _totalSupply,
address _owner)
FeeToken(_proxy, _tokenState,
TOKEN_NAME, TOKEN_SYMBOL, _totalSupply,
TRANSFER_FEE_RATE,
_havven, // The havven contract is the fee authority.
_owner)
public
{
require(_proxy != 0 && address(_havven) != 0 && _owner != 0);
// It should not be possible to transfer to the nomin contract itself.
frozen[this] = true;
// It should not be possible to transfer to the fee pool directly (or confiscate its balance).
frozen[FEE_ADDRESS] = true;
havven = _havven;
}

Expand Down Expand Up @@ -157,11 +160,11 @@ contract Nomin is FeeToken {

// Confiscate the balance in the account and freeze it.
uint balance = tokenState.balanceOf(target);
tokenState.setBalanceOf(address(this), safeAdd(tokenState.balanceOf(address(this)), balance));
tokenState.setBalanceOf(FEE_ADDRESS, safeAdd(tokenState.balanceOf(FEE_ADDRESS), balance));
tokenState.setBalanceOf(target, 0);
frozen[target] = true;
emitAccountFrozen(target, balance);
emitTransfer(target, address(this), balance);
emitTransfer(target, FEE_ADDRESS, balance);
}

/* The owner may allow a previously-frozen contract to once
Expand All @@ -170,7 +173,7 @@ contract Nomin is FeeToken {
external
optionalProxy_onlyOwner
{
require(frozen[target] && target != address(this));
require(frozen[target] && target != FEE_ADDRESS);
frozen[target] = false;
emitAccountUnfrozen(target);
}
Expand Down
1 change: 1 addition & 0 deletions tests/contract_interfaces/fee_token_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def __init__(self, contract, name):
self.contract = contract
self.contract_name = name

self.FEE_ADDRESS = lambda: self.contract.functions.FEE_ADDRESS().call()
self.feePool = lambda: self.contract.functions.feePool().call()
self.feeAuthority = lambda: self.contract.functions.feeAuthority().call()
self.transferFeeRate = lambda: self.contract.functions.transferFeeRate().call()
Expand Down
Loading

0 comments on commit b30191e

Please sign in to comment.