// File: @openzeppelin/contracts/math/SafeMath.sol
pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
// File: contracts/external/Decimal.sol
/**
* @title Decimal
* @author dYdX
*
* Library that defines a fixed-point number with 18 decimal places.
*/
library Decimal {
using SafeMath for uint256;
// ============ Constants ============
uint256 constant BASE = 10**18;
// ============ Structs ============
struct D256 {
uint256 value;
}
// ============ Static Functions ============
function zero()
internal
pure
returns (D256 memory)
{
return D256({ value: 0 });
}
function one()
internal
pure
returns (D256 memory)
{
return D256({ value: BASE });
}
function from(
uint256 a
)
internal
pure
returns (D256 memory)
{
return D256({ value: a.mul(BASE) });
}
function ratio(
uint256 a,
uint256 b
)
internal
pure
returns (D256 memory)
{
return D256({ value: getPartial(a, BASE, b) });
}
// ============ Self Functions ============
function add(
D256 memory self,
uint256 b
)
internal
pure
returns (D256 memory)
{
return D256({ value: self.value.add(b.mul(BASE)) });
}
function sub(
D256 memory self,
uint256 b
)
internal
pure
returns (D256 memory)
{
return D256({ value: self.value.sub(b.mul(BASE)) });
}
function sub(
D256 memory self,
uint256 b,
string memory reason
)
internal
pure
returns (D256 memory)
{
return D256({ value: self.value.sub(b.mul(BASE), reason) });
}
function mul(
D256 memory self,
uint256 b
)
internal
pure
returns (D256 memory)
{
return D256({ value: self.value.mul(b) });
}
function div(
D256 memory self,
uint256 b
)
internal
pure
returns (D256 memory)
{
return D256({ value: self.value.div(b) });
}
function pow(
D256 memory self,
uint256 b
)
internal
pure
returns (D256 memory)
{
if (b == 0) {
return from(1);
}
D256 memory temp = D256({ value: self.value });
for (uint256 i = 1; i < b; i++) {
temp = mul(temp, self);
}
return temp;
}
function add(
D256 memory self,
D256 memory b
)
internal
pure
returns (D256 memory)
{
return D256({ value: self.value.add(b.value) });
}
function sub(
D256 memory self,
D256 memory b
)
internal
pure
returns (D256 memory)
{
return D256({ value: self.value.sub(b.value) });
}
function sub(
D256 memory self,
D256 memory b,
string memory reason
)
internal
pure
returns (D256 memory)
{
return D256({ value: self.value.sub(b.value, reason) });
}
function mul(
D256 memory self,
D256 memory b
)
internal
pure
returns (D256 memory)
{
return D256({ value: getPartial(self.value, b.value, BASE) });
}
function div(
D256 memory self,
D256 memory b
)
internal
pure
returns (D256 memory)
{
return D256({ value: getPartial(self.value, BASE, b.value) });
}
function equals(D256 memory self, D256 memory b) internal pure returns (bool) {
return self.value == b.value;
}
function greaterThan(D256 memory self, D256 memory b) internal pure returns (bool) {
return compareTo(self, b) == 2;
}
function lessThan(D256 memory self, D256 memory b) internal pure returns (bool) {
return compareTo(self, b) == 0;
}
function greaterThanOrEqualTo(D256 memory self, D256 memory b) internal pure returns (bool) {
return compareTo(self, b) > 0;
}
function lessThanOrEqualTo(D256 memory self, D256 memory b) internal pure returns (bool) {
return compareTo(self, b) < 2;
}
function isZero(D256 memory self) internal pure returns (bool) {
return self.value == 0;
}
function asUint256(D256 memory self) internal pure returns (uint256) {
return self.value.div(BASE);
}
// ============ Core Methods ============
function getPartial(
uint256 target,
uint256 numerator,
uint256 denominator
)
private
pure
returns (uint256)
{
return target.mul(numerator).div(denominator);
}
function compareTo(
D256 memory a,
D256 memory b
)
private
pure
returns (uint256)
{
if (a.value == b.value) {
return 1;
}
return a.value > b.value ? 2 : 0;
}
}
// File: contracts/Constants.sol
pragma solidity ^0.5.17;
library Constants {
/* Chain */
uint256 private constant CHAIN_ID = 1; // Mainnet
/* Bootstrapping */
uint256 private constant BOOTSTRAPPING_PERIOD = 168; // 14 days
uint256 private constant BOOTSTRAPPING_PRICE = 11e17; // ESB price == 1.10 * WBTC
/* Oracle */
address private constant WBTC =
address(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599); // Wrapped BTC
uint256 private constant ORACLE_RESERVE_MINIMUM = 1e18;
/* Bonding */
uint256 private constant INITIAL_STAKE_MULTIPLE = 1e6; // 100 ESB -> 100M ESBS
/* Epoch */
struct EpochStrategy {
uint256 offset;
uint256 start;
uint256 period;
}
uint256 private constant EPOCH_START = 1610240400; // 01/10/2021 @ 1:00am (UTC)
uint256 private constant EPOCH_OFFSET = 0;
uint256 private constant EPOCH_PERIOD = 7200; // 2 hours
/* Governance */
uint256 private constant GOVERNANCE_PERIOD = 27; // 9 * 3 epochs since epoch period is reduced
uint256 private constant GOVERNANCE_EXPIRATION = 7; // 2 * 3 + 1 epochs
uint256 private constant GOVERNANCE_QUORUM = 20e16; // 20%
uint256 private constant GOVERNANCE_PROPOSAL_THRESHOLD = 5e15; // 0.5%
uint256 private constant GOVERNANCE_SUPER_MAJORITY = 66e16; // 66%
uint256 private constant GOVERNANCE_EMERGENCY_DELAY = 18; // 18 epochs (36 hours; same as ESG)
/* DAO */
uint256 private constant ADVANCE_INCENTIVE = 1e15; // 0.001 ESB // not making this too crazy
// uint256 private constant DAO_EXIT_LOCKUP_EPOCHS = 20; // 5 days
uint256 private constant DAO_EXIT_LOCKUP_EPOCHS = 60; // 5 days
/* Pool */
// uint256 private constant POOL_EXIT_LOCKUP_EPOCHS = 8; // 2 days
uint256 private constant POOL_EXIT_LOCKUP_EPOCHS = 24; // 2 days
/* Market */
uint256 private constant COUPON_EXPIRATION = 360; // 30 days
uint256 private constant DEBT_RATIO_CAP = 25e16; // 25%; inspired by DSD DIP-8
/* Regulator */
uint256 private constant SUPPLY_CHANGE_LIMIT = 5e16; // 5%
uint256 private constant COUPON_SUPPLY_CHANGE_LIMIT = 3e16; // 3% since we are expanding less too
uint256 private constant ORACLE_POOL_RATIO = 20; // 20%
uint256 private constant TREASURY_RATIO = 250; // 2.5%, until TREASURY_ADDRESS is set, this portion is sent to LP
// TODO: vote on recipient
address private constant TREASURY_ADDRESS =
address(0x0000000000000000000000000000000000000000);
function getWBTCAddress() internal pure returns (address) {
return WBTC;
}
function getOracleReserveMinimum() internal pure returns (uint256) {
return ORACLE_RESERVE_MINIMUM;
}
function getCurrentEpochStrategy()
internal
pure
returns (EpochStrategy memory)
{
return
EpochStrategy({
offset: EPOCH_OFFSET,
start: EPOCH_START,
period: EPOCH_PERIOD
});
}
function getInitialStakeMultiple() internal pure returns (uint256) {
return INITIAL_STAKE_MULTIPLE;
}
function getBootstrappingPeriod() internal pure returns (uint256) {
return BOOTSTRAPPING_PERIOD;
}
function getBootstrappingPrice()
internal
pure
returns (Decimal.D256 memory)
{
return Decimal.D256({value: BOOTSTRAPPING_PRICE});
}
function getGovernancePeriod() internal pure returns (uint256) {
return GOVERNANCE_PERIOD;
}
function getGovernanceExpiration() internal pure returns (uint256) {
return GOVERNANCE_EXPIRATION;
}
function getGovernanceQuorum() internal pure returns (Decimal.D256 memory) {
return Decimal.D256({value: GOVERNANCE_QUORUM});
}
function getGovernanceProposalThreshold()
internal
pure
returns (Decimal.D256 memory)
{
return Decimal.D256({value: GOVERNANCE_PROPOSAL_THRESHOLD});
}
function getGovernanceSuperMajority()
internal
pure
returns (Decimal.D256 memory)
{
return Decimal.D256({value: GOVERNANCE_SUPER_MAJORITY});
}
function getGovernanceEmergencyDelay() internal pure returns (uint256) {
return GOVERNANCE_EMERGENCY_DELAY;
}
function getAdvanceIncentive() internal pure returns (uint256) {
return ADVANCE_INCENTIVE;
}
function getDAOExitLockupEpochs() internal pure returns (uint256) {
return DAO_EXIT_LOCKUP_EPOCHS;
}
function getPoolExitLockupEpochs() internal pure returns (uint256) {
return POOL_EXIT_LOCKUP_EPOCHS;
}
function getCouponExpiration() internal pure returns (uint256) {
return COUPON_EXPIRATION;
}
function getDebtRatioCap() internal pure returns (Decimal.D256 memory) {
return Decimal.D256({value: DEBT_RATIO_CAP});
}
function getSupplyChangeLimit()
internal
pure
returns (Decimal.D256 memory)
{
return Decimal.D256({value: SUPPLY_CHANGE_LIMIT});
}
function getCouponSupplyChangeLimit()
internal
pure
returns (Decimal.D256 memory)
{
return Decimal.D256({value: COUPON_SUPPLY_CHANGE_LIMIT});
}
function getOraclePoolRatio() internal pure returns (uint256) {
return ORACLE_POOL_RATIO;
}
function getTreasuryRatio() internal pure returns (uint256) {
return TREASURY_RATIO;
}
function getChainId() internal pure returns (uint256) {
return CHAIN_ID;
}
function getTreasuryAddress() internal pure returns (address) {
return TREASURY_ADDRESS;
}
}
/* Constants for previous implementations are listed below:
// uint256 private constant BOOTSTRAPPING_PERIOD = 56; // 14 days
// uint256 private constant SUPPLY_CHANGE_LIMIT = 1e17; // 10%
// uint256 private constant COUPON_SUPPLY_CHANGE_LIMIT = 6e16; // 6%
// uint256 private constant COUPON_EXPIRATION = 120; // 30 days
// uint256 private constant DEBT_RATIO_CAP = 35e16; // 35%
// uint256 private constant ADVANCE_INCENTIVE = 1e17; // 0.1 ESB
// uint256 private constant GOVERNANCE_PERIOD = 9; // 9 epochs
// uint256 private constant GOVERNANCE_EXPIRATION = 2; // 2 + 1 epochs
// uint256 private constant GOVERNANCE_EMERGENCY_DELAY = 6; // 6 epochs
// uint256 private constant EPOCH_PERIOD = 21600; // 6 hours
*/
// File: contracts/dao/Curve.sol
pragma solidity ^0.5.17;
contract Curve {
using SafeMath for uint256;
using Decimal for Decimal.D256;
function calculateCouponPremium(
uint256 totalSupply,
uint256 totalDebt,
uint256 amount
) internal pure returns (uint256) {
return effectivePremium(totalSupply, totalDebt, amount).mul(amount).asUint256();
}
function effectivePremium(
uint256 totalSupply,
uint256 totalDebt,
uint256 amount
) private pure returns (Decimal.D256 memory) {
Decimal.D256 memory debtRatio = Decimal.ratio(totalDebt, totalSupply);
Decimal.D256 memory debtRatioUpperBound = Constants.getDebtRatioCap();
uint256 totalSupplyEnd = totalSupply.sub(amount);
uint256 totalDebtEnd = totalDebt.sub(amount);
Decimal.D256 memory debtRatioEnd = Decimal.ratio(totalDebtEnd, totalSupplyEnd);
if (debtRatio.greaterThan(debtRatioUpperBound)) {
if (debtRatioEnd.greaterThan(debtRatioUpperBound)) {
return curve(debtRatioUpperBound);
}
Decimal.D256 memory premiumCurve = curveMean(debtRatioEnd, debtRatioUpperBound);
Decimal.D256 memory premiumCurveDelta = debtRatioUpperBound.sub(debtRatioEnd);
Decimal.D256 memory premiumFlat = curve(debtRatioUpperBound);
Decimal.D256 memory premiumFlatDelta = debtRatio.sub(debtRatioUpperBound);
return (premiumCurve.mul(premiumCurveDelta)).add(premiumFlat.mul(premiumFlatDelta))
.div(premiumCurveDelta.add(premiumFlatDelta));
}
return curveMean(debtRatioEnd, debtRatio);
}
// 1/(3(1-R)^2)-1/3
function curve(Decimal.D256 memory debtRatio) private pure returns (Decimal.D256 memory) {
return Decimal.one().div(
Decimal.from(3).mul((Decimal.one().sub(debtRatio)).pow(2))
).sub(Decimal.ratio(1, 3));
}
// 1/(3(1-R)(1-R'))-1/3
function curveMean(
Decimal.D256 memory lower,
Decimal.D256 memory upper
) private pure returns (Decimal.D256 memory) {
if (lower.equals(upper)) {
return curve(lower);
}
return Decimal.one().div(
Decimal.from(3).mul(Decimal.one().sub(upper)).mul(Decimal.one().sub(lower))
).sub(Decimal.ratio(1, 3));
}
}
// File: @uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol
pragma solidity >=0.5.0;
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: contracts/token/IBitcoin.sol
pragma solidity ^0.5.17;
contract IBitcoin is IERC20 {
function burn(uint256 amount) public;
function burnFrom(address account, uint256 amount) public;
function mint(address account, uint256 amount) public returns (bool);
}
// File: contracts/oracle/IOracle.sol
contract IOracle {
function setup() public;
function capture() public returns (Decimal.D256 memory, bool);
function pair() external view returns (address);
}
// File: contracts/dao/State.sol
contract Account {
enum Status {Frozen, Fluid, Locked}
struct State {
uint256 staged;
uint256 balance;
mapping(uint256 => uint256) coupons;
mapping(address => uint256) couponAllowances;
uint256 fluidUntil;
uint256 lockedUntil;
}
}
contract Epoch {
struct Global {
uint256 start;
uint256 period;
uint256 current;
}
struct Coupons {
uint256 outstanding;
uint256 expiration;
uint256[] expiring;
}
struct State {
uint256 bonded;
Coupons coupons;
}
}
contract Candidate {
enum Vote {UNDECIDED, APPROVE, REJECT}
struct State {
uint256 start;
uint256 period;
uint256 approve;
uint256 reject;
mapping(address => Vote) votes;
bool initialized;
}
}
contract Storage {
struct Provider {
IBitcoin bitcoin;
IOracle oracle;
address pool;
}
struct Balance {
uint256 supply;
uint256 bonded;
uint256 staged;
uint256 redeemable;
uint256 debt;
uint256 coupons;
}
struct State {
Epoch.Global epoch;
Balance balance;
Provider provider;
mapping(address => Account.State) accounts;
mapping(uint256 => Epoch.State) epochs;
mapping(address => Candidate.State) candidates;
}
}
contract State {
Storage.State _state;
}
// File: contracts/dao/Getters.sol
contract Getters is State {
using SafeMath for uint256;
using Decimal for Decimal.D256;
bytes32 private constant IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* ERC20 Interface
*/
function name() public view returns (string memory) {
return "Empty Set Bitcoin Stake";
}
function symbol() public view returns (string memory) {
return "ESBS";
}
function decimals() public view returns (uint8) {
return 18;
}
function balanceOf(address account) public view returns (uint256) {
return _state.accounts[account].balance;
}
function totalSupply() public view returns (uint256) {
return _state.balance.supply;
}
function allowance(address owner, address spender)
external
view
returns (uint256)
{
return 0;
}
/**
* Global
*/
function bitcoin() public view returns (IBitcoin) {
return _state.provider.bitcoin;
}
function oracle() public view returns (IOracle) {
return _state.provider.oracle;
}
function pool() public view returns (address) {
return _state.provider.pool;
}
function totalBonded() public view returns (uint256) {
return _state.balance.bonded;
}
function totalStaged() public view returns (uint256) {
return _state.balance.staged;
}
function totalDebt() public view returns (uint256) {
return _state.balance.debt;
}
function totalRedeemable() public view returns (uint256) {
return _state.balance.redeemable;
}
function totalCoupons() public view returns (uint256) {
return _state.balance.coupons;
}
function totalNet() public view returns (uint256) {
return bitcoin().totalSupply().sub(totalDebt());
}
/**
* Account
*/
function balanceOfStaged(address account) public view returns (uint256) {
return _state.accounts[account].staged;
}
function balanceOfBonded(address account) public view returns (uint256) {
uint256 totalSupply = totalSupply();
if (totalSupply == 0) {
return 0;
}
return totalBonded().mul(balanceOf(account)).div(totalSupply);
}
function balanceOfCoupons(address account, uint256 epoch)
public
view
returns (uint256)
{
if (outstandingCoupons(epoch) == 0) {
return 0;
}
return _state.accounts[account].coupons[epoch];
}
function statusOf(address account) public view returns (Account.Status) {
if (_state.accounts[account].lockedUntil > epoch()) {
return Account.Status.Locked;
}
return
epoch() >= _state.accounts[account].fluidUntil
? Account.Status.Frozen
: Account.Status.Fluid;
}
function fluidUntil(address account) public view returns (uint256) {
return _state.accounts[account].fluidUntil;
}
function lockedUntil(address account) public view returns (uint256) {
return _state.accounts[account].lockedUntil;
}
function allowanceCoupons(address owner, address spender)
public
view
returns (uint256)
{
return _state.accounts[owner].couponAllowances[spender];
}
/**
* Epoch
*/
function epoch() public view returns (uint256) {
return _state.epoch.current;
}
function epochTime() public view returns (uint256) {
Constants.EpochStrategy memory strategy =
Constants.getCurrentEpochStrategy();
return
blockTimestamp().sub(strategy.start).div(strategy.period).add(
strategy.offset
);
}
// Overridable for testing
function blockTimestamp() internal view returns (uint256) {
return block.timestamp;
}
function outstandingCoupons(uint256 epoch) public view returns (uint256) {
return _state.epochs[epoch].coupons.outstanding;
}
function couponsExpiration(uint256 epoch) public view returns (uint256) {
return _state.epochs[epoch].coupons.expiration;
}
function expiringCoupons(uint256 epoch) public view returns (uint256) {
return _state.epochs[epoch].coupons.expiring.length;
}
function expiringCouponsAtIndex(uint256 epoch, uint256 i)
public
view
returns (uint256)
{
return _state.epochs[epoch].coupons.expiring[i];
}
function totalBondedAt(uint256 epoch) public view returns (uint256) {
return _state.epochs[epoch].bonded;
}
function bootstrappingAt(uint256 epoch) public view returns (bool) {
return epoch <= Constants.getBootstrappingPeriod();
}
/**
* Governance
*/
function recordedVote(address account, address candidate)
public
view
returns (Candidate.Vote)
{
return _state.candidates[candidate].votes[account];
}
function startFor(address candidate) public view returns (uint256) {
return _state.candidates[candidate].start;
}
function periodFor(address candidate) public view returns (uint256) {
return _state.candidates[candidate].period;
}
function approveFor(address candidate) public view returns (uint256) {
return _state.candidates[candidate].approve;
}
function rejectFor(address candidate) public view returns (uint256) {
return _state.candidates[candidate].reject;
}
function votesFor(address candidate) public view returns (uint256) {
return approveFor(candidate).add(rejectFor(candidate));
}
function isNominated(address candidate) public view returns (bool) {
return _state.candidates[candidate].start > 0;
}
function isInitialized(address candidate) public view returns (bool) {
return _state.candidates[candidate].initialized;
}
function implementation() public view returns (address impl) {
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
impl := sload(slot)
}
}
}
// File: contracts/dao/Setters.sol
contract Setters is State, Getters {
using SafeMath for uint256;
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* ERC20 Interface
*/
function transfer(address recipient, uint256 amount) external returns (bool) {
return false;
}
function approve(address spender, uint256 amount) external returns (bool) {
return false;
}
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
return false;
}
/**
* Global
*/
function incrementTotalBonded(uint256 amount) internal {
_state.balance.bonded = _state.balance.bonded.add(amount);
}
function decrementTotalBonded(uint256 amount, string memory reason) internal {
_state.balance.bonded = _state.balance.bonded.sub(amount, reason);
}
function incrementTotalDebt(uint256 amount) internal {
_state.balance.debt = _state.balance.debt.add(amount);
}
function decrementTotalDebt(uint256 amount, string memory reason) internal {
_state.balance.debt = _state.balance.debt.sub(amount, reason);
}
function incrementTotalRedeemable(uint256 amount) internal {
_state.balance.redeemable = _state.balance.redeemable.add(amount);
}
function decrementTotalRedeemable(uint256 amount, string memory reason) internal {
_state.balance.redeemable = _state.balance.redeemable.sub(amount, reason);
}
/**
* Account
*/
function incrementBalanceOf(address account, uint256 amount) internal {
_state.accounts[account].balance = _state.accounts[account].balance.add(amount);
_state.balance.supply = _state.balance.supply.add(amount);
emit Transfer(address(0), account, amount);
}
function decrementBalanceOf(address account, uint256 amount, string memory reason) internal {
_state.accounts[account].balance = _state.accounts[account].balance.sub(amount, reason);
_state.balance.supply = _state.balance.supply.sub(amount, reason);
emit Transfer(account, address(0), amount);
}
function incrementBalanceOfStaged(address account, uint256 amount) internal {
_state.accounts[account].staged = _state.accounts[account].staged.add(amount);
_state.balance.staged = _state.balance.staged.add(amount);
}
function decrementBalanceOfStaged(address account, uint256 amount, string memory reason) internal {
_state.accounts[account].staged = _state.accounts[account].staged.sub(amount, reason);
_state.balance.staged = _state.balance.staged.sub(amount, reason);
}
function incrementBalanceOfCoupons(address account, uint256 epoch, uint256 amount) internal {
_state.accounts[account].coupons[epoch] = _state.accounts[account].coupons[epoch].add(amount);
_state.epochs[epoch].coupons.outstanding = _state.epochs[epoch].coupons.outstanding.add(amount);
_state.balance.coupons = _state.balance.coupons.add(amount);
}
function decrementBalanceOfCoupons(address account, uint256 epoch, uint256 amount, string memory reason) internal {
_state.accounts[account].coupons[epoch] = _state.accounts[account].coupons[epoch].sub(amount, reason);
_state.epochs[epoch].coupons.outstanding = _state.epochs[epoch].coupons.outstanding.sub(amount, reason);
_state.balance.coupons = _state.balance.coupons.sub(amount, reason);
}
function unfreeze(address account) internal {
_state.accounts[account].fluidUntil = epoch().add(Constants.getDAOExitLockupEpochs());
}
function updateAllowanceCoupons(address owner, address spender, uint256 amount) internal {
_state.accounts[owner].couponAllowances[spender] = amount;
}
function decrementAllowanceCoupons(address owner, address spender, uint256 amount, string memory reason) internal {
_state.accounts[owner].couponAllowances[spender] =
_state.accounts[owner].couponAllowances[spender].sub(amount, reason);
}
/**
* Epoch
*/
function incrementEpoch() internal {
_state.epoch.current = _state.epoch.current.add(1);
}
function snapshotTotalBonded() internal {
_state.epochs[epoch()].bonded = totalSupply();
}
function initializeCouponsExpiration(uint256 epoch, uint256 expiration) internal {
_state.epochs[epoch].coupons.expiration = expiration;
_state.epochs[expiration].coupons.expiring.push(epoch);
}
function eliminateOutstandingCoupons(uint256 epoch) internal {
uint256 outstandingCouponsForEpoch = outstandingCoupons(epoch);
if(outstandingCouponsForEpoch == 0) {
return;
}
_state.balance.coupons = _state.balance.coupons.sub(outstandingCouponsForEpoch);
_state.epochs[epoch].coupons.outstanding = 0;
}
/**
* Governance
*/
function createCandidate(address candidate, uint256 period) internal {
_state.candidates[candidate].start = epoch();
_state.candidates[candidate].period = period;
}
function recordVote(address account, address candidate, Candidate.Vote vote) internal {
_state.candidates[candidate].votes[account] = vote;
}
function incrementApproveFor(address candidate, uint256 amount) internal {
_state.candidates[candidate].approve = _state.candidates[candidate].approve.add(amount);
}
function decrementApproveFor(address candidate, uint256 amount, string memory reason) internal {
_state.candidates[candidate].approve = _state.candidates[candidate].approve.sub(amount, reason);
}
function incrementRejectFor(address candidate, uint256 amount) internal {
_state.candidates[candidate].reject = _state.candidates[candidate].reject.add(amount);
}
function decrementRejectFor(address candidate, uint256 amount, string memory reason) internal {
_state.candidates[candidate].reject = _state.candidates[candidate].reject.sub(amount, reason);
}
function placeLock(address account, address candidate) internal {
uint256 currentLock = _state.accounts[account].lockedUntil;
uint256 newLock = startFor(candidate).add(periodFor(candidate));
if (newLock > currentLock) {
_state.accounts[account].lockedUntil = newLock;
}
}
function initialized(address candidate) internal {
_state.candidates[candidate].initialized = true;
}
}
// File: contracts/external/Require.sol
pragma solidity ^0.5.7;
/**
* @title Require
* @author dYdX
*
* Stringifies parameters to pretty-print revert messages. Costs more gas than regular require()
*/
library Require {
// ============ Constants ============
uint256 constant ASCII_ZERO = 48; // '0'
uint256 constant ASCII_RELATIVE_ZERO = 87; // 'a' - 10
uint256 constant ASCII_LOWER_EX = 120; // 'x'
bytes2 constant COLON = 0x3a20; // ': '
bytes2 constant COMMA = 0x2c20; // ', '
bytes2 constant LPAREN = 0x203c; // ' <'
byte constant RPAREN = 0x3e; // '>'
uint256 constant FOUR_BIT_MASK = 0xf;
// ============ Library Functions ============
function that(
bool must,
bytes32 file,
bytes32 reason
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason)
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
uint256 payloadA
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
RPAREN
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
uint256 payloadA,
uint256 payloadB
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
COMMA,
stringify(payloadB),
RPAREN
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
address payloadA
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
RPAREN
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
address payloadA,
uint256 payloadB
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
COMMA,
stringify(payloadB),
RPAREN
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
address payloadA,
uint256 payloadB,
uint256 payloadC
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
COMMA,
stringify(payloadB),
COMMA,
stringify(payloadC),
RPAREN
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
bytes32 payloadA
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
RPAREN
)
)
);
}
}
function that(
bool must,
bytes32 file,
bytes32 reason,
bytes32 payloadA,
uint256 payloadB,
uint256 payloadC
)
internal
pure
{
if (!must) {
revert(
string(
abi.encodePacked(
stringifyTruncated(file),
COLON,
stringifyTruncated(reason),
LPAREN,
stringify(payloadA),
COMMA,
stringify(payloadB),
COMMA,
stringify(payloadC),
RPAREN
)
)
);
}
}
// ============ Private Functions ============
function stringifyTruncated(
bytes32 input
)
private
pure
returns (bytes memory)
{
// put the input bytes into the result
bytes memory result = abi.encodePacked(input);
// determine the length of the input by finding the location of the last non-zero byte
for (uint256 i = 32; i > 0; ) {
// reverse-for-loops with unsigned integer
/* solium-disable-next-line security/no-modify-for-iter-var */
i--;
// find the last non-zero byte in order to determine the length
if (result[i] != 0) {
uint256 length = i + 1;
/* solium-disable-next-line security/no-inline-assembly */
assembly {
mstore(result, length) // r.length = length;
}
return result;
}
}
// all bytes are zero
return new bytes(0);
}
function stringify(
uint256 input
)
private
pure
returns (bytes memory)
{
if (input == 0) {
return "0";
}
// get the final string length
uint256 j = input;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
// allocate the string
bytes memory bstr = new bytes(length);
// populate the string starting with the least-significant character
j = input;
for (uint256 i = length; i > 0; ) {
// reverse-for-loops with unsigned integer
/* solium-disable-next-line security/no-modify-for-iter-var */
i--;
// take last decimal digit
bstr[i] = byte(uint8(ASCII_ZERO + (j % 10)));
// remove the last decimal digit
j /= 10;
}
return bstr;
}
function stringify(
address input
)
private
pure
returns (bytes memory)
{
uint256 z = uint256(input);
// addresses are "0x" followed by 20 bytes of data which take up 2 characters each
bytes memory result = new bytes(42);
// populate the result with "0x"
result[0] = byte(uint8(ASCII_ZERO));
result[1] = byte(uint8(ASCII_LOWER_EX));
// for each byte (starting from the lowest byte), populate the result with two characters
for (uint256 i = 0; i < 20; i++) {
// each byte takes two characters
uint256 shift = i * 2;
// populate the least-significant character
result[41 - shift] = char(z & FOUR_BIT_MASK);
z = z >> 4;
// populate the most-significant character
result[40 - shift] = char(z & FOUR_BIT_MASK);
z = z >> 4;
}
return result;
}
function stringify(
bytes32 input
)
private
pure
returns (bytes memory)
{
uint256 z = uint256(input);
// bytes32 are "0x" followed by 32 bytes of data which take up 2 characters each
bytes memory result = new bytes(66);
// populate the result with "0x"
result[0] = byte(uint8(ASCII_ZERO));
result[1] = byte(uint8(ASCII_LOWER_EX));
// for each byte (starting from the lowest byte), populate the result with two characters
for (uint256 i = 0; i < 32; i++) {
// each byte takes two characters
uint256 shift = i * 2;
// populate the least-significant character
result[65 - shift] = char(z & FOUR_BIT_MASK);
z = z >> 4;
// populate the most-significant character
result[64 - shift] = char(z & FOUR_BIT_MASK);
z = z >> 4;
}
return result;
}
function char(
uint256 input
)
private
pure
returns (byte)
{
// return ASCII digit (0-9)
if (input < 10) {
return byte(uint8(input + ASCII_ZERO));
}
// return ASCII letter (a-f)
return byte(uint8(input + ASCII_RELATIVE_ZERO));
}
}
// File: contracts/dao/Comptroller.sol
contract Comptroller is Setters {
using SafeMath for uint256;
bytes32 private constant FILE = "Comptroller";
function mintToAccount(address account, uint256 amount) internal {
bitcoin().mint(account, amount);
if (!bootstrappingAt(epoch())) {
increaseDebt(amount);
}
balanceCheck();
}
function burnFromAccount(address account, uint256 amount) internal {
bitcoin().transferFrom(account, address(this), amount);
bitcoin().burn(amount);
decrementTotalDebt(amount, "Comptroller: not enough outstanding debt");
balanceCheck();
}
function redeemToAccount(address account, uint256 amount) internal {
bitcoin().transfer(account, amount);
decrementTotalRedeemable(
amount,
"Comptroller: not enough redeemable balance"
);
balanceCheck();
}
function burnRedeemable(uint256 amount) internal {
bitcoin().burn(amount);
decrementTotalRedeemable(
amount,
"Comptroller: not enough redeemable balance"
);
balanceCheck();
}
function increaseDebt(uint256 amount) internal returns (uint256) {
incrementTotalDebt(amount);
uint256 lessDebt = resetDebt(Constants.getDebtRatioCap());
balanceCheck();
return lessDebt > amount ? 0 : amount.sub(lessDebt);
}
function decreaseDebt(uint256 amount) internal {
decrementTotalDebt(amount, "Comptroller: not enough debt");
balanceCheck();
}
function increaseSupply(uint256 newSupply)
internal
returns (uint256, uint256)
{
uint256 rewards;
if (
Constants.getTreasuryAddress() ==
address(0x0000000000000000000000000000000000000000)
) {
// Pay out to Pool, with treasury reward in addition
uint256 poolAllocation =
newSupply.mul(Constants.getOraclePoolRatio()).div(100);
uint256 treasuryAllocation =
newSupply.mul(Constants.getTreasuryRatio()).div(10000);
rewards = poolAllocation.add(treasuryAllocation);
mintToPool(rewards);
} else {
// 0-a. Pay out to Pool
uint256 poolReward =
newSupply.mul(Constants.getOraclePoolRatio()).div(100);
mintToPool(poolReward);
// 0-b. Pay out to Treasury
uint256 treasuryReward =
newSupply.mul(Constants.getTreasuryRatio()).div(10000);
mintToTreasury(treasuryReward);
rewards = poolReward.add(treasuryReward);
}
newSupply = newSupply > rewards ? newSupply.sub(rewards) : 0;
// 1. True up redeemable pool
uint256 newRedeemable = 0;
uint256 totalRedeemable = totalRedeemable();
uint256 totalCoupons = totalCoupons();
if (totalRedeemable < totalCoupons) {
newRedeemable = totalCoupons.sub(totalRedeemable);
newRedeemable = newRedeemable > newSupply
? newSupply
: newRedeemable;
mintToRedeemable(newRedeemable);
newSupply = newSupply.sub(newRedeemable);
}
// 2. Payout to DAO
if (totalBonded() == 0) {
newSupply = 0;
}
if (newSupply > 0) {
mintToDAO(newSupply);
}
balanceCheck();
return (newRedeemable, newSupply.add(rewards));
}
function resetDebt(Decimal.D256 memory targetDebtRatio)
internal
returns (uint256)
{
uint256 targetDebt =
targetDebtRatio.mul(bitcoin().totalSupply()).asUint256();
uint256 currentDebt = totalDebt();
if (currentDebt > targetDebt) {
uint256 lessDebt = currentDebt.sub(targetDebt);
decreaseDebt(lessDebt);
return lessDebt;
}
return 0;
}
function balanceCheck() private {
Require.that(
bitcoin().balanceOf(address(this)) >=
totalBonded().add(totalStaged()).add(totalRedeemable()),
FILE,
"Inconsistent balances"
);
}
function mintToDAO(uint256 amount) private {
if (amount > 0) {
bitcoin().mint(address(this), amount);
incrementTotalBonded(amount);
}
}
function mintToPool(uint256 amount) private {
if (amount > 0) {
bitcoin().mint(pool(), amount);
}
}
function mintToTreasury(uint256 amount) private {
if (amount > 0) {
bitcoin().mint(Constants.getTreasuryAddress(), amount);
}
}
function mintToRedeemable(uint256 amount) private {
bitcoin().mint(address(this), amount);
incrementTotalRedeemable(amount);
balanceCheck();
}
}
// File: contracts/dao/Market.sol
contract Market is Comptroller, Curve {
using SafeMath for uint256;
bytes32 private constant FILE = "Market";
event CouponExpiration(
uint256 indexed epoch,
uint256 couponsExpired,
uint256 lessRedeemable,
uint256 lessDebt,
uint256 newBonded
);
event CouponPurchase(
address indexed account,
uint256 indexed epoch,
uint256 bitcoinAmount,
uint256 couponAmount
);
event CouponRedemption(
address indexed account,
uint256 indexed epoch,
uint256 couponAmount
);
event CouponTransfer(
address indexed from,
address indexed to,
uint256 indexed epoch,
uint256 value
);
event CouponApproval(
address indexed owner,
address indexed spender,
uint256 value
);
function step() internal {
// Expire prior coupons
for (uint256 i = 0; i < expiringCoupons(epoch()); i++) {
expireCouponsForEpoch(expiringCouponsAtIndex(epoch(), i));
}
// Record expiry for current epoch's coupons
uint256 expirationEpoch = epoch().add(Constants.getCouponExpiration());
initializeCouponsExpiration(epoch(), expirationEpoch);
}
function expireCouponsForEpoch(uint256 epoch) private {
uint256 couponsForEpoch = outstandingCoupons(epoch);
(uint256 lessRedeemable, uint256 newBonded) = (0, 0);
eliminateOutstandingCoupons(epoch);
uint256 totalRedeemable = totalRedeemable();
uint256 totalCoupons = totalCoupons();
if (totalRedeemable > totalCoupons) {
lessRedeemable = totalRedeemable.sub(totalCoupons);
burnRedeemable(lessRedeemable);
(, newBonded) = increaseSupply(lessRedeemable);
}
emit CouponExpiration(
epoch,
couponsForEpoch,
lessRedeemable,
0,
newBonded
);
}
function couponPremium(uint256 amount) public view returns (uint256) {
return
calculateCouponPremium(
bitcoin().totalSupply(),
totalDebt(),
amount
);
}
function purchaseCoupons(uint256 bitcoinAmount) external returns (uint256) {
Require.that(bitcoinAmount > 0, FILE, "Must purchase non-zero amount");
Require.that(totalDebt() >= bitcoinAmount, FILE, "Not enough debt");
uint256 epoch = epoch();
uint256 couponAmount = bitcoinAmount.add(couponPremium(bitcoinAmount));
burnFromAccount(msg.sender, bitcoinAmount);
incrementBalanceOfCoupons(msg.sender, epoch, couponAmount);
emit CouponPurchase(msg.sender, epoch, bitcoinAmount, couponAmount);
return couponAmount;
}
function redeemCoupons(uint256 couponEpoch, uint256 couponAmount) external {
require(epoch().sub(couponEpoch) >= 2, "Market: Too early to redeem");
decrementBalanceOfCoupons(
msg.sender,
couponEpoch,
couponAmount,
"Market: Insufficient coupon balance"
);
redeemToAccount(msg.sender, couponAmount);
emit CouponRedemption(msg.sender, couponEpoch, couponAmount);
}
function approveCoupons(address spender, uint256 amount) external {
require(
spender != address(0),
"Market: Coupon approve to the zero address"
);
updateAllowanceCoupons(msg.sender, spender, amount);
emit CouponApproval(msg.sender, spender, amount);
}
function transferCoupons(
address sender,
address recipient,
uint256 epoch,
uint256 amount
) external {
require(
sender != address(0),
"Market: Coupon transfer from the zero address"
);
require(
recipient != address(0),
"Market: Coupon transfer to the zero address"
);
decrementBalanceOfCoupons(
sender,
epoch,
amount,
"Market: Insufficient coupon balance"
);
incrementBalanceOfCoupons(recipient, epoch, amount);
if (
msg.sender != sender &&
allowanceCoupons(sender, msg.sender) != uint256(-1)
) {
decrementAllowanceCoupons(
sender,
msg.sender,
amount,
"Market: Insufficient coupon approval"
);
}
emit CouponTransfer(sender, recipient, epoch, amount);
}
}
// File: contracts/dao/Regulator.sol
contract Regulator is Comptroller {
using SafeMath for uint256;
using Decimal for Decimal.D256;
event SupplyIncrease(uint256 indexed epoch, uint256 price, uint256 newRedeemable, uint256 lessDebt, uint256 newBonded);
event SupplyDecrease(uint256 indexed epoch, uint256 price, uint256 newDebt);
event SupplyNeutral(uint256 indexed epoch);
function step() internal {
Decimal.D256 memory price = oracleCapture();
if (price.greaterThan(Decimal.one())) {
growSupply(price);
return;
}
if (price.lessThan(Decimal.one())) {
shrinkSupply(price);
return;
}
emit SupplyNeutral(epoch());
}
function shrinkSupply(Decimal.D256 memory price) private {
Decimal.D256 memory delta = limit(Decimal.one().sub(price), price);
uint256 newDebt = delta.mul(totalNet()).asUint256();
uint256 cappedNewDebt = increaseDebt(newDebt);
emit SupplyDecrease(epoch(), price.value, cappedNewDebt);
return;
}
function growSupply(Decimal.D256 memory price) private {
uint256 lessDebt = resetDebt(Decimal.zero());
Decimal.D256 memory delta = limit(price.sub(Decimal.one()), price);
uint256 newSupply = delta.mul(totalNet()).asUint256();
(uint256 newRedeemable, uint256 newBonded) = increaseSupply(newSupply);
emit SupplyIncrease(epoch(), price.value, newRedeemable, lessDebt, newBonded);
}
function limit(Decimal.D256 memory delta, Decimal.D256 memory price) private view returns (Decimal.D256 memory) {
Decimal.D256 memory supplyChangeLimit = Constants.getSupplyChangeLimit();
uint256 totalRedeemable = totalRedeemable();
uint256 totalCoupons = totalCoupons();
if (price.greaterThan(Decimal.one()) && (totalRedeemable < totalCoupons)) {
supplyChangeLimit = Constants.getCouponSupplyChangeLimit();
}
return delta.greaterThan(supplyChangeLimit) ? supplyChangeLimit : delta;
}
function oracleCapture() private returns (Decimal.D256 memory) {
(Decimal.D256 memory price, bool valid) = oracle().capture();
if (bootstrappingAt(epoch().sub(1))) {
return Constants.getBootstrappingPrice();
}
if (!valid) {
return Decimal.one();
}
return price;
}
}
// File: contracts/dao/Permission.sol
contract Permission is Setters {
bytes32 private constant FILE = "Permission";
// Can modify account state
modifier onlyFrozenOrFluid(address account) {
Require.that(
statusOf(account) != Account.Status.Locked,
FILE,
"Not frozen or fluid"
);
_;
}
// Can participate in balance-dependant activities
modifier onlyFrozenOrLocked(address account) {
Require.that(
statusOf(account) != Account.Status.Fluid,
FILE,
"Not frozen or locked"
);
_;
}
modifier initializer() {
Require.that(
!isInitialized(implementation()),
FILE,
"Already initialized"
);
initialized(implementation());
_;
}
}
// File: contracts/dao/Bonding.sol
contract Bonding is Setters, Permission {
using SafeMath for uint256;
bytes32 private constant FILE = "Bonding";
event Deposit(address indexed account, uint256 value);
event Withdraw(address indexed account, uint256 value);
event Bond(
address indexed account,
uint256 start,
uint256 value,
uint256 valueUnderlying
);
event Unbond(
address indexed account,
uint256 start,
uint256 value,
uint256 valueUnderlying
);
function step() internal {
Require.that(epochTime() > epoch(), FILE, "Still current epoch");
snapshotTotalBonded();
incrementEpoch();
}
function deposit(uint256 value) external onlyFrozenOrLocked(msg.sender) {
bitcoin().transferFrom(msg.sender, address(this), value);
incrementBalanceOfStaged(msg.sender, value);
emit Deposit(msg.sender, value);
}
function withdraw(uint256 value) external onlyFrozenOrLocked(msg.sender) {
bitcoin().transfer(msg.sender, value);
decrementBalanceOfStaged(
msg.sender,
value,
"Bonding: insufficient staged balance"
);
emit Withdraw(msg.sender, value);
}
function bond(uint256 value) external onlyFrozenOrFluid(msg.sender) {
unfreeze(msg.sender);
uint256 balance =
totalBonded() == 0
? value.mul(Constants.getInitialStakeMultiple())
: value.mul(totalSupply()).div(totalBonded());
incrementBalanceOf(msg.sender, balance);
incrementTotalBonded(value);
decrementBalanceOfStaged(
msg.sender,
value,
"Bonding: insufficient staged balance"
);
emit Bond(msg.sender, epoch().add(1), balance, value);
}
function unbond(uint256 value) external onlyFrozenOrFluid(msg.sender) {
unfreeze(msg.sender);
uint256 staged =
value.mul(balanceOfBonded(msg.sender)).div(balanceOf(msg.sender));
incrementBalanceOfStaged(msg.sender, staged);
decrementTotalBonded(staged, "Bonding: insufficient total bonded");
decrementBalanceOf(msg.sender, value, "Bonding: insufficient balance");
emit Unbond(msg.sender, epoch().add(1), value, staged);
}
function unbondUnderlying(uint256 value)
external
onlyFrozenOrFluid(msg.sender)
{
unfreeze(msg.sender);
uint256 balance = value.mul(totalSupply()).div(totalBonded());
incrementBalanceOfStaged(msg.sender, value);
decrementTotalBonded(value, "Bonding: insufficient total bonded");
decrementBalanceOf(
msg.sender,
balance,
"Bonding: insufficient balance"
);
emit Unbond(msg.sender, epoch().add(1), balance, value);
}
}
// File: @openzeppelin/upgrades/contracts/utils/Address.sol
pragma solidity ^0.5.0;
/**
* Utility library of inline functions on addresses
*
* Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/utils/Address.sol
* This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
* when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
* build/artifacts folder) as well as the vanilla Address implementation from an openzeppelin version.
*/
library OpenZeppelinUpgradesAddress {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param account address of the account to check
* @return whether the target address is a contract
*/
function isContract(address account) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
// File: contracts/dao/Upgradeable.sol
/**
* Based off of, and designed to interface with, openzeppelin/upgrades package
*/
contract Upgradeable is State {
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 private constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev Emitted when the implementation is upgraded.
* @param implementation Address of the new implementation.
*/
event Upgraded(address indexed implementation);
function initialize() public;
/**
* @dev Upgrades the proxy to a new implementation.
* @param newImplementation Address of the new implementation.
*/
function upgradeTo(address newImplementation) internal {
setImplementation(newImplementation);
(bool success, bytes memory reason) = newImplementation.delegatecall(abi.encodeWithSignature("initialize()"));
require(success, string(reason));
emit Upgraded(newImplementation);
}
/**
* @dev Sets the implementation address of the proxy.
* @param newImplementation Address of the new implementation.
*/
function setImplementation(address newImplementation) private {
require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
sstore(slot, newImplementation)
}
}
}
// File: contracts/dao/Govern.sol
contract Govern is Setters, Permission, Upgradeable {
using SafeMath for uint256;
using Decimal for Decimal.D256;
bytes32 private constant FILE = "Govern";
event Proposal(address indexed candidate, address indexed account, uint256 indexed start, uint256 period);
event Vote(address indexed account, address indexed candidate, Candidate.Vote vote, uint256 bonded);
event Commit(address indexed account, address indexed candidate);
function vote(address candidate, Candidate.Vote vote) external onlyFrozenOrLocked(msg.sender) {
Require.that(
balanceOf(msg.sender) > 0,
FILE,
"Must have stake"
);
if (!isNominated(candidate)) {
Require.that(
canPropose(msg.sender),
FILE,
"Not enough stake to propose"
);
createCandidate(candidate, Constants.getGovernancePeriod());
emit Proposal(candidate, msg.sender, epoch(), Constants.getGovernancePeriod());
}
Require.that(
epoch() < startFor(candidate).add(periodFor(candidate)),
FILE,
"Ended"
);
uint256 bonded = balanceOf(msg.sender);
Candidate.Vote recordedVote = recordedVote(msg.sender, candidate);
if (vote == recordedVote) {
return;
}
if (recordedVote == Candidate.Vote.REJECT) {
decrementRejectFor(candidate, bonded, "Govern: Insufficient reject");
}
if (recordedVote == Candidate.Vote.APPROVE) {
decrementApproveFor(candidate, bonded, "Govern: Insufficient approve");
}
if (vote == Candidate.Vote.REJECT) {
incrementRejectFor(candidate, bonded);
}
if (vote == Candidate.Vote.APPROVE) {
incrementApproveFor(candidate, bonded);
}
recordVote(msg.sender, candidate, vote);
placeLock(msg.sender, candidate);
emit Vote(msg.sender, candidate, vote, bonded);
}
function commit(address candidate) external {
Require.that(
isNominated(candidate),
FILE,
"Not nominated"
);
uint256 endsAfter = startFor(candidate).add(periodFor(candidate)).sub(1);
Require.that(
epoch() > endsAfter,
FILE,
"Not ended"
);
Require.that(
epoch() <= endsAfter.add(1).add(Constants.getGovernanceExpiration()),
FILE,
"Expired"
);
Require.that(
Decimal.ratio(votesFor(candidate), totalBondedAt(endsAfter)).greaterThan(Constants.getGovernanceQuorum()),
FILE,
"Must have quorom"
);
Require.that(
approveFor(candidate) > rejectFor(candidate),
FILE,
"Not approved"
);
upgradeTo(candidate);
emit Commit(msg.sender, candidate);
}
function emergencyCommit(address candidate) external {
Require.that(
isNominated(candidate),
FILE,
"Not nominated"
);
Require.that(
epochTime() > epoch().add(Constants.getGovernanceEmergencyDelay()),
FILE,
"Epoch synced"
);
Require.that(
Decimal.ratio(approveFor(candidate), totalSupply()).greaterThan(Constants.getGovernanceSuperMajority()),
FILE,
"Must have super majority"
);
Require.that(
approveFor(candidate) > rejectFor(candidate),
FILE,
"Not approved"
);
upgradeTo(candidate);
emit Commit(msg.sender, candidate);
}
function canPropose(address account) private view returns (bool) {
if (totalBonded() == 0) {
return false;
}
Decimal.D256 memory stake = Decimal.ratio(balanceOf(account), totalSupply());
return stake.greaterThan(Constants.getGovernanceProposalThreshold());
}
}
// File: contracts/dao/Implementation.sol
contract Implementation is State, Bonding, Market, Regulator, Govern {
using SafeMath for uint256;
event Advance(uint256 indexed epoch, uint256 block, uint256 timestamp);
event Incentivization(address indexed account, uint256 amount);
function initialize() public initializer {
// Dev rewards
incentivize(msg.sender, 5e16); // 0.05 ESB
}
function advance() external {
incentivize(msg.sender, Constants.getAdvanceIncentive());
Bonding.step();
Regulator.step();
Market.step();
emit Advance(epoch(), block.number, block.timestamp);
}
function incentivize(address account, uint256 amount) private {
mintToAccount(account, amount);
emit Incentivization(account, amount);
}
}
{
"compilationTarget": {
"Implementation.sol": "Implementation"
},
"evmVersion": "istanbul",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"block","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Advance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"start","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"valueUnderlying","type":"uint256"}],"name":"Bond","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"candidate","type":"address"}],"name":"Commit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"CouponApproval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"couponsExpired","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lessRedeemable","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lessDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBonded","type":"uint256"}],"name":"CouponExpiration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bitcoinAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"couponAmount","type":"uint256"}],"name":"CouponPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"couponAmount","type":"uint256"}],"name":"CouponRedemption","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"CouponTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Incentivization","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"candidate","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"start","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"period","type":"uint256"}],"name":"Proposal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDebt","type":"uint256"}],"name":"SupplyDecrease","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newRedeemable","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lessDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBonded","type":"uint256"}],"name":"SupplyIncrease","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"SupplyNeutral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"start","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"valueUnderlying","type":"uint256"}],"name":"Unbond","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"candidate","type":"address"},{"indexed":false,"internalType":"enum Candidate.Vote","name":"vote","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"bonded","type":"uint256"}],"name":"Vote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Withdraw","type":"event"},{"constant":false,"inputs":[],"name":"advance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowanceCoupons","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveCoupons","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"candidate","type":"address"}],"name":"approveFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOfBonded","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"balanceOfCoupons","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOfStaged","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bitcoin","outputs":[{"internalType":"contract IBitcoin","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"bond","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"bootstrappingAt","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"candidate","type":"address"}],"name":"commit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"couponPremium","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"couponsExpiration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"deposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"candidate","type":"address"}],"name":"emergencyCommit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"epochTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"expiringCoupons","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"i","type":"uint256"}],"name":"expiringCouponsAtIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"fluidUntil","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"impl","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"candidate","type":"address"}],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"candidate","type":"address"}],"name":"isNominated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"lockedUntil","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IOracle","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"outstandingCoupons","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"candidate","type":"address"}],"name":"periodFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pool","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"bitcoinAmount","type":"uint256"}],"name":"purchaseCoupons","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"candidate","type":"address"}],"name":"recordedVote","outputs":[{"internalType":"enum Candidate.Vote","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"couponEpoch","type":"uint256"},{"internalType":"uint256","name":"couponAmount","type":"uint256"}],"name":"redeemCoupons","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"candidate","type":"address"}],"name":"rejectFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"candidate","type":"address"}],"name":"startFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"statusOf","outputs":[{"internalType":"enum Account.Status","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalBonded","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"totalBondedAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalCoupons","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalNet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalRedeemable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalStaged","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferCoupons","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"unbond","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"unbondUnderlying","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"candidate","type":"address"},{"internalType":"enum Candidate.Vote","name":"vote","type":"uint8"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"candidate","type":"address"}],"name":"votesFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]