// 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: @openzeppelin/contracts/token/ERC20/IERC20.sol
/**
* @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/external/Require.sol
/**
* @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/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
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;
}
}
// File: contracts/token/IBitcoin.sol
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/IDAO.sol
contract IDAO {
function epoch() external view returns (uint256);
}
// File: contracts/oracle/PoolState.sol
contract PoolAccount {
enum Status {Frozen, Fluid, Locked}
struct State {
uint256 staged;
uint256 claimable;
uint256 bonded;
uint256 phantom;
uint256 fluidUntil;
}
}
contract PoolStorage {
struct Provider {
IDAO dao;
IBitcoin bitcoin;
IERC20 univ2;
}
struct Balance {
uint256 staged;
uint256 claimable;
uint256 bonded;
uint256 phantom;
}
struct State {
Balance balance;
Provider provider;
bool paused;
mapping(address => PoolAccount.State) accounts;
}
}
contract PoolState {
PoolStorage.State _state;
}
// File: contracts/oracle/PoolGetters.sol
contract PoolGetters is PoolState {
using SafeMath for uint256;
/**
* Global
*/
function WBTC() public view returns (address) {
return Constants.getWBTCAddress();
}
function dao() public view returns (IDAO) {
return _state.provider.dao;
}
function bitcoin() public view returns (IBitcoin) {
return _state.provider.bitcoin;
}
function univ2() public view returns (IERC20) {
return _state.provider.univ2;
}
function totalBonded() public view returns (uint256) {
return _state.balance.bonded;
}
function totalStaged() public view returns (uint256) {
return _state.balance.staged;
}
function totalClaimable() public view returns (uint256) {
return _state.balance.claimable;
}
function totalPhantom() public view returns (uint256) {
return _state.balance.phantom;
}
function totalRewarded(IBitcoin bitcoin) public view returns (uint256) {
return bitcoin.balanceOf(address(this)).sub(totalClaimable());
}
function paused() public view returns (bool) {
return _state.paused;
}
/**
* Account
*/
function balanceOfStaged(address account) public view returns (uint256) {
return _state.accounts[account].staged;
}
function balanceOfClaimable(address account) public view returns (uint256) {
return _state.accounts[account].claimable;
}
function balanceOfBonded(address account) public view returns (uint256) {
return _state.accounts[account].bonded;
}
function balanceOfPhantom(address account) public view returns (uint256) {
return _state.accounts[account].phantom;
}
function balanceOfRewarded(address account, IBitcoin bitcoin)
public
view
returns (uint256)
{
uint256 totalBonded = totalBonded();
if (totalBonded == 0) {
return 0;
}
uint256 totalRewardedWithPhantom =
totalRewarded(bitcoin).add(totalPhantom());
uint256 balanceOfRewardedWithPhantom =
totalRewardedWithPhantom.mul(balanceOfBonded(account)).div(
totalBonded
);
uint256 balanceOfPhantom = balanceOfPhantom(account);
if (balanceOfRewardedWithPhantom > balanceOfPhantom) {
return balanceOfRewardedWithPhantom.sub(balanceOfPhantom);
}
return 0;
}
function statusOf(address account, uint256 epoch)
public
view
returns (PoolAccount.Status)
{
return
epoch >= _state.accounts[account].fluidUntil
? PoolAccount.Status.Frozen
: PoolAccount.Status.Fluid;
}
}
// File: contracts/oracle/PoolSetters.sol
contract PoolSetters is PoolState, PoolGetters {
using SafeMath for uint256;
/**
* Global
*/
function pause() internal {
_state.paused = true;
}
/**
* Account
*/
function incrementBalanceOfBonded(address account, uint256 amount) internal {
_state.accounts[account].bonded = _state.accounts[account].bonded.add(amount);
_state.balance.bonded = _state.balance.bonded.add(amount);
}
function decrementBalanceOfBonded(address account, uint256 amount, string memory reason) internal {
_state.accounts[account].bonded = _state.accounts[account].bonded.sub(amount, reason);
_state.balance.bonded = _state.balance.bonded.sub(amount, reason);
}
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 incrementBalanceOfClaimable(address account, uint256 amount) internal {
_state.accounts[account].claimable = _state.accounts[account].claimable.add(amount);
_state.balance.claimable = _state.balance.claimable.add(amount);
}
function decrementBalanceOfClaimable(address account, uint256 amount, string memory reason) internal {
_state.accounts[account].claimable = _state.accounts[account].claimable.sub(amount, reason);
_state.balance.claimable = _state.balance.claimable.sub(amount, reason);
}
function incrementBalanceOfPhantom(address account, uint256 amount) internal {
_state.accounts[account].phantom = _state.accounts[account].phantom.add(amount);
_state.balance.phantom = _state.balance.phantom.add(amount);
}
function decrementBalanceOfPhantom(address account, uint256 amount, string memory reason) internal {
_state.accounts[account].phantom = _state.accounts[account].phantom.sub(amount, reason);
_state.balance.phantom = _state.balance.phantom.sub(amount, reason);
}
function unfreeze(address account, uint256 epoch) internal {
_state.accounts[account].fluidUntil = epoch.add(Constants.getPoolExitLockupEpochs());
}
}
// File: @uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol
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: contracts/external/UniswapV2Library.sol
library UniswapV2Library {
using SafeMath for uint;
// returns sorted token addresses, used to handle return values from pairs sorted in this order
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
}
// calculates the CREATE2 address for a pair without making any external calls
function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(uint(keccak256(abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
))));
}
// fetches and sorts the reserves for a pair
function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
(address token0,) = sortTokens(tokenA, tokenB);
(uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
(reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
// given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
amountB = amountA.mul(reserveB) / reserveA;
}
}
// File: contracts/oracle/Pool.sol
contract Pool is PoolSetters {
using SafeMath for uint256;
constructor(address bitcoin, address univ2) public {
_state.provider.dao = IDAO(msg.sender);
_state.provider.bitcoin = IBitcoin(bitcoin);
_state.provider.univ2 = IERC20(univ2);
}
address private constant UNISWAP_FACTORY =
address(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f);
function addLiquidity(uint256 bitcoinAmount)
internal
returns (uint256, uint256)
{
(address bitcoin, address WBTC) =
(address(_state.provider.bitcoin), WBTC());
(uint256 reserveA, uint256 reserveB) = getReserves(bitcoin, WBTC);
uint256 WBTCAmount =
(reserveA == 0 && reserveB == 0)
? bitcoinAmount
: UniswapV2Library.quote(bitcoinAmount, reserveA, reserveB);
address pair = address(_state.provider.univ2);
IERC20(bitcoin).transfer(pair, bitcoinAmount);
IERC20(WBTC).transferFrom(msg.sender, pair, WBTCAmount);
return (WBTCAmount, IUniswapV2Pair(pair).mint(address(this)));
}
// overridable for testing
function getReserves(address tokenA, address tokenB)
internal
view
returns (uint256 reserveA, uint256 reserveB)
{
(address token0, ) = UniswapV2Library.sortTokens(tokenA, tokenB);
(uint256 reserve0, uint256 reserve1, ) =
IUniswapV2Pair(
UniswapV2Library.pairFor(UNISWAP_FACTORY, tokenA, tokenB)
)
.getReserves();
(reserveA, reserveB) = tokenA == token0
? (reserve0, reserve1)
: (reserve1, reserve0);
}
bytes32 private constant FILE = "Pool";
event Deposit(address indexed account, uint256 value);
event Withdraw(address indexed account, uint256 value);
event Claim(address indexed account, uint256 value);
event Bond(address indexed account, uint256 start, uint256 value);
event Unbond(
address indexed account,
uint256 start,
uint256 value,
uint256 newClaimable
);
event Provide(
address indexed account,
uint256 value,
uint256 lessWBTC,
uint256 newUniv2
);
function deposit(uint256 value) external onlyFrozen(msg.sender) notPaused {
_state.provider.univ2.transferFrom(msg.sender, address(this), value);
incrementBalanceOfStaged(msg.sender, value);
balanceCheck();
emit Deposit(msg.sender, value);
}
function withdraw(uint256 value) external onlyFrozen(msg.sender) {
_state.provider.univ2.transfer(msg.sender, value);
decrementBalanceOfStaged(
msg.sender,
value,
"Pool: insufficient staged balance"
);
balanceCheck();
emit Withdraw(msg.sender, value);
}
function claim(uint256 value) external onlyFrozen(msg.sender) {
_state.provider.bitcoin.transfer(msg.sender, value);
decrementBalanceOfClaimable(
msg.sender,
value,
"Pool: insufficient claimable balance"
);
balanceCheck();
emit Claim(msg.sender, value);
}
function unfreeze(address account) internal {
super.unfreeze(account, _state.provider.dao.epoch());
}
function bond(uint256 value) external notPaused {
unfreeze(msg.sender);
uint256 totalRewardedWithPhantom =
totalRewarded(_state.provider.bitcoin).add(totalPhantom());
uint256 newPhantom =
totalBonded() == 0
? totalRewarded(_state.provider.bitcoin) == 0
? Constants.getInitialStakeMultiple().mul(value)
: 0
: totalRewardedWithPhantom.mul(value).div(totalBonded());
incrementBalanceOfBonded(msg.sender, value);
incrementBalanceOfPhantom(msg.sender, newPhantom);
decrementBalanceOfStaged(
msg.sender,
value,
"Pool: insufficient staged balance"
);
balanceCheck();
emit Bond(msg.sender, _state.provider.dao.epoch().add(1), value);
}
function unbond(uint256 value) external {
unfreeze(msg.sender);
uint256 balanceOfBonded = balanceOfBonded(msg.sender);
Require.that(balanceOfBonded > 0, FILE, "insufficient bonded balance");
uint256 newClaimable =
balanceOfRewarded(msg.sender, _state.provider.bitcoin)
.mul(value)
.div(balanceOfBonded);
uint256 lessPhantom =
balanceOfPhantom(msg.sender).mul(value).div(balanceOfBonded);
incrementBalanceOfStaged(msg.sender, value);
incrementBalanceOfClaimable(msg.sender, newClaimable);
decrementBalanceOfBonded(
msg.sender,
value,
"Pool: insufficient bonded balance"
);
decrementBalanceOfPhantom(
msg.sender,
lessPhantom,
"Pool: insufficient phantom balance"
);
balanceCheck();
emit Unbond(
msg.sender,
_state.provider.dao.epoch().add(1),
value,
newClaimable
);
}
function provide(uint256 value) external onlyFrozen(msg.sender) notPaused {
Require.that(totalBonded() > 0, FILE, "insufficient total bonded");
Require.that(
totalRewarded(_state.provider.bitcoin) > 0,
FILE,
"insufficient total rewarded"
);
Require.that(
balanceOfRewarded(msg.sender, _state.provider.bitcoin) >= value,
FILE,
"insufficient rewarded balance"
);
(uint256 lessWBTC, uint256 newUniv2) = addLiquidity(value);
uint256 totalRewardedWithPhantom =
totalRewarded(_state.provider.bitcoin).add(totalPhantom()).add(
value
);
uint256 newPhantomFromBonded =
totalRewardedWithPhantom.mul(newUniv2).div(totalBonded());
incrementBalanceOfBonded(msg.sender, newUniv2);
incrementBalanceOfPhantom(msg.sender, value.add(newPhantomFromBonded));
balanceCheck();
emit Provide(msg.sender, value, lessWBTC, newUniv2);
}
function emergencyWithdraw(address token, uint256 value) external onlyDao {
IERC20(token).transfer(address(_state.provider.dao), value);
}
function emergencyPause() external onlyDao {
pause();
}
function balanceCheck() private {
Require.that(
_state.provider.univ2.balanceOf(address(this)) >=
totalStaged().add(totalBonded()),
FILE,
"Inconsistent UNI-V2 balances"
);
}
modifier onlyFrozen(address account) {
Require.that(
statusOf(account, _state.provider.dao.epoch()) ==
PoolAccount.Status.Frozen,
FILE,
"Not frozen"
);
_;
}
modifier onlyDao() {
Require.that(
msg.sender == address(_state.provider.dao),
FILE,
"Not dao"
);
_;
}
modifier notPaused() {
Require.that(!paused(), FILE, "Paused");
_;
}
}
{
"compilationTarget": {
"Pool.sol": "Pool"
},
"evmVersion": "istanbul",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"bitcoin","type":"address"},{"internalType":"address","name":"univ2","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"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"}],"name":"Bond","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Claim","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":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lessWBTC","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newUniv2","type":"uint256"}],"name":"Provide","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":"newClaimable","type":"uint256"}],"name":"Unbond","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":true,"inputs":[],"name":"WBTC","outputs":[{"internalType":"address","name":"","type":"address"}],"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"}],"name":"balanceOfClaimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOfPhantom","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"contract IBitcoin","name":"bitcoin","type":"address"}],"name":"balanceOfRewarded","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":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"claim","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"dao","outputs":[{"internalType":"contract IDAO","name":"","type":"address"}],"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":[],"name":"emergencyPause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"emergencyWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"provide","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"statusOf","outputs":[{"internalType":"enum PoolAccount.Status","name":"","type":"uint8"}],"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":[],"name":"totalClaimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalPhantom","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IBitcoin","name":"bitcoin","type":"address"}],"name":"totalRewarded","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":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"unbond","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"univ2","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]