ERC-20
Overview
Max Total Supply
90,000,000,000,680.284320276 BASH
Holders
66
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 9 Decimals)
Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Contract Name:
BASHERC20Token
Compiler Version
v0.7.5+commit.eb77ed08
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import {Counters} from "./libraries/Counters.sol"; library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping (bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. bytes32 lastvalue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastvalue; // Update the index for the moved value set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { require(set._values.length > index, "EnumerableSet: index out of bounds"); return set._values[index]; } function _getValues( Set storage set_ ) private view returns ( bytes32[] storage ) { return set_._values; } // TODO needs insert function that maintains order. // TODO needs NatSpec documentation comment. /** * Inserts new value by moving existing value at provided index to end * of array and setting provided value at provided index */ function _insert(Set storage set_, uint256 index_, bytes32 valueToInsert_ ) private returns ( bool ) { require( set_._values.length > index_ ); require( !_contains( set_, valueToInsert_ ), "Remove value you wish to insert if you wish to reorder array." ); bytes32 existingValue_ = _at( set_, index_ ); set_._values[index_] = valueToInsert_; return _add( set_, existingValue_); } struct Bytes4Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes4Set storage set, bytes4 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes4Set storage set, bytes4 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes4Set storage set, bytes4 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values on the set. O(1). */ function length(Bytes4Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes4Set storage set, uint256 index) internal view returns ( bytes4 ) { return bytes4( _at( set._inner, index ) ); } function getValues( Bytes4Set storage set_ ) internal view returns ( bytes4[] memory ) { bytes4[] memory bytes4Array_; for( uint256 iteration_ = 0; _length( set_._inner ) > iteration_; iteration_++ ) { bytes4Array_[iteration_] = bytes4( _at( set_._inner, iteration_ ) ); } return bytes4Array_; } function insert( Bytes4Set storage set_, uint256 index_, bytes4 valueToInsert_ ) internal returns ( bool ) { return _insert( set_._inner, index_, valueToInsert_ ); } struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values on the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns ( bytes32 ) { return _at(set._inner, index); } function getValues( Bytes32Set storage set_ ) internal view returns ( bytes4[] memory ) { bytes4[] memory bytes4Array_; for( uint256 iteration_ = 0; _length( set_._inner ) >= iteration_; iteration_++ ){ bytes4Array_[iteration_] = bytes4( at( set_, iteration_ ) ); } return bytes4Array_; } function insert( Bytes32Set storage set_, uint256 index_, bytes32 valueToInsert_ ) internal returns ( bool ) { return _insert( set_._inner, index_, valueToInsert_ ); } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(value))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(value))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(value))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint256(_at(set._inner, index))); } /** * TODO Might require explicit conversion of bytes32[] to address[]. * Might require iteration. */ function getValues( AddressSet storage set_ ) internal view returns ( address[] memory ) { address[] memory addressArray; for( uint256 iteration_ = 0; _length(set_._inner) >= iteration_; iteration_++ ){ addressArray[iteration_] = at( set_, iteration_ ); } return addressArray; } function insert(AddressSet storage set_, uint256 index_, address valueToInsert_ ) internal returns ( bool ) { return _insert( set_._inner, index_, bytes32(uint256(valueToInsert_)) ); } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } struct UInt256Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UInt256Set storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UInt256Set storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UInt256Set storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UInt256Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UInt256Set storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } } 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); } library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 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; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } function sqrrt(uint256 a) internal pure returns (uint c) { if (a > 3) { c = a; uint b = add( div( a, 2), 1 ); while (b < c) { c = b; b = div( add( div( a, b ), b), 2 ); } } else if (a != 0) { c = 1; } } function percentageAmount( uint256 total_, uint8 percentage_ ) internal pure returns ( uint256 percentAmount_ ) { return div( mul( total_, percentage_ ), 1000 ); } function substractPercentage( uint256 total_, uint8 percentageToSub_ ) internal pure returns ( uint256 result_ ) { return sub( total_, div( mul( total_, percentageToSub_ ), 1000 ) ); } function percentageOfTotal( uint256 part_, uint256 total_ ) internal pure returns ( uint256 percent_ ) { return div( mul(part_, 100) , total_ ); } function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); } function quadraticPricing( uint256 payment_, uint256 multiplier_ ) internal pure returns (uint256) { return sqrrt( mul( multiplier_, payment_ ) ); } function bondingCurve( uint256 supply_, uint256 multiplier_ ) internal pure returns (uint256) { return mul( multiplier_, supply_ ); } } abstract contract ERC20 is IERC20 { using SafeMath for uint256; // TODO comment actual hash value. bytes32 constant private ERC20TOKEN_ERC1820_INTERFACE_ID = keccak256( "ERC20Token" ); // Present in ERC777 mapping (address => uint256) internal _balances; // Present in ERC777 mapping (address => mapping (address => uint256)) internal _allowances; // Present in ERC777 uint256 internal _totalSupply; // Present in ERC777 string internal _name; // Present in ERC777 string internal _symbol; // Present in ERC777 uint8 internal _decimals; constructor (string memory name_, string memory symbol_, uint8 decimals_) { _name = name_; _symbol = symbol_; _decimals = decimals_; } function name() public view returns (string memory) { return _name; } function symbol() public view returns (string memory) { return _symbol; } function decimals() public view returns (uint8) { return _decimals; } function totalSupply() public view override returns (uint256) { return _totalSupply; } function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(msg.sender, recipient, amount); return true; } function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(msg.sender, spender, amount); return true; } function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, msg.sender, _allowances[sender][msg.sender] .sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); return true; } function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][spender] .sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } function _mint(address account_, uint256 amount_) internal virtual { require(account_ != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address( this ), account_, amount_); _totalSupply = _totalSupply.add(amount_); _balances[account_] = _balances[account_].add(amount_); emit Transfer(address( this ), account_, amount_); } function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal virtual { } } interface IERC2612Permit { function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function nonces(address owner) external view returns (uint256); } abstract contract ERC20Permit is ERC20, IERC2612Permit { using Counters for Counters.Counter; mapping(address => Counters.Counter) private _nonces; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; bytes32 public DOMAIN_SEPARATOR; constructor() { uint256 chainID; assembly { chainID := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name())), keccak256(bytes("1")), // Version chainID, address(this) ) ); } function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual override { require(block.timestamp <= deadline, "Permit: expired deadline"); bytes32 hashStruct = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, _nonces[owner].current(), deadline)); bytes32 _hash = keccak256(abi.encodePacked(uint16(0x1901), DOMAIN_SEPARATOR, hashStruct)); address signer = ecrecover(_hash, v, r, s); require(signer != address(0) && signer == owner, "ZeroSwapPermit: Invalid signature"); _nonces[owner].increment(); _approve(owner, spender, amount); } function nonces(address owner) public view override returns (uint256) { return _nonces[owner].current(); } } interface IOwnable { function owner() external view returns (address); function renounceOwnership() external; function transferOwnership( address newOwner_ ) external; } contract Ownable is IOwnable { address internal _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); constructor () { _owner = msg.sender; emit OwnershipTransferred( address(0), _owner ); } function owner() public view override returns (address) { return _owner; } modifier onlyOwner() { require( _owner == msg.sender, "Ownable: caller is not the owner" ); _; } function renounceOwnership() public virtual override onlyOwner() { emit OwnershipTransferred( _owner, address(0) ); _owner = address(0); } function transferOwnership( address newOwner_ ) public virtual override onlyOwner() { require( newOwner_ != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred( _owner, newOwner_ ); _owner = newOwner_; } } contract VaultOwned is Ownable { address internal _vault; function setVault( address vault_ ) external onlyOwner() returns ( bool ) { _vault = vault_; return true; } function vault() public view returns (address) { return _vault; } modifier onlyVault() { require( _vault == msg.sender, "VaultOwned: caller is not the Vault" ); _; } } contract BASHERC20Token is ERC20Permit, VaultOwned { using SafeMath for uint256; constructor() ERC20("ATBASH", "BASH", 9) { //_mint(msg.sender, 100000000000); } function mint(address account_, uint256 amount_) external onlyVault() { _mint(account_, amount_); } function burn(uint256 amount) public virtual { _burn(msg.sender, amount); } function burnFrom(address account_, uint256 amount_) public virtual { _burnFrom(account_, amount_); } function _burnFrom(address account_, uint256 amount_) public virtual { uint256 decreasedAllowance_ = allowance(account_, msg.sender).sub( amount_, "ERC20: burn amount exceeds allowance" ); _approve(account_, msg.sender, decreasedAllowance_); _burn(account_, amount_); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.7.5; import "./SafeMath.sol"; library Counters { using SafeMath for uint256; struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { // The {SafeMath} overflow check can be skipped here, see the comment at the top counter._value += 1; } function decrement(Counter storage counter) internal { counter._value = counter._value.sub(1); } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; 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; } function add32(uint32 a, uint32 b) internal pure returns (uint32) { uint32 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. */ 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. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { 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; } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import {IOwnable} from "./interfaces/IOwnable.sol"; import {SafeMath} from "./libraries/SafeMath.sol"; import {Address} from "./libraries/Address.sol"; import {Counters} from "./libraries/Counters.sol"; 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); } abstract contract ERC20 is IERC20 { using SafeMath for uint256; // TODO comment actual hash value. bytes32 constant private ERC20TOKEN_ERC1820_INTERFACE_ID = keccak256( "ERC20Token" ); // Present in ERC777 mapping (address => uint256) internal _balances; // Present in ERC777 mapping (address => mapping (address => uint256)) internal _allowances; // Present in ERC777 uint256 internal _totalSupply; // Present in ERC777 string internal _name; // Present in ERC777 string internal _symbol; // Present in ERC777 uint8 internal _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name_, string memory symbol_, uint8 decimals_) { _name = name_; _symbol = symbol_; _decimals = decimals_; } /** * @dev Returns the name of the token. */ // Present in ERC777 function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ // Present in ERC777 function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ // Present in ERC777 function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ // Present in ERC777 function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ // Present in ERC777 function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ // Overrideen in ERC777 // Confirm that this behavior changes function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(msg.sender, recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ // Present in ERC777 function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ // Present in ERC777 function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(msg.sender, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ // Present in ERC777 function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, msg.sender, _allowances[sender][msg.sender] .sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][spender] .sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ // Present in ERC777 function _mint(address account_, uint256 ammount_) internal virtual { require(account_ != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address( this ), account_, ammount_); _totalSupply = _totalSupply.add(ammount_); _balances[account_] = _balances[account_].add(ammount_); emit Transfer(address( this ), account_, ammount_); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ // Present in ERC777 function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ // Present in ERC777 function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ // Present in ERC777 function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal virtual { } } interface IERC2612Permit { /** * @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens, * given `owner`'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current ERC2612 nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); } abstract contract ERC20Permit is ERC20, IERC2612Permit { using Counters for Counters.Counter; mapping(address => Counters.Counter) private _nonces; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; bytes32 public DOMAIN_SEPARATOR; constructor() { uint256 chainID; assembly { chainID := chainid() } DOMAIN_SEPARATOR = keccak256(abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name())), keccak256(bytes("1")), // Version chainID, address(this) )); } /** * @dev See {IERC2612Permit-permit}. * */ function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual override { require(block.timestamp <= deadline, "Permit: expired deadline"); bytes32 hashStruct = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, _nonces[owner].current(), deadline)); bytes32 _hash = keccak256(abi.encodePacked(uint16(0x1901), DOMAIN_SEPARATOR, hashStruct)); address signer = ecrecover(_hash, v, r, s); require(signer != address(0) && signer == owner, "ZeroSwapPermit: Invalid signature"); _nonces[owner].increment(); _approve(owner, spender, amount); } /** * @dev See {IERC2612Permit-nonces}. */ function nonces(address owner) public view override returns (uint256) { return _nonces[owner].current(); } } contract Ownable is IOwnable { address internal _owner; address internal _newOwner; event OwnershipPushed(address indexed previousOwner, address indexed newOwner); event OwnershipPulled(address indexed previousOwner, address indexed newOwner); constructor () { _owner = msg.sender; emit OwnershipPushed( address(0), _owner ); } function manager() public view override returns (address) { return _owner; } modifier onlyManager() { require( _owner == msg.sender, "Ownable: caller is not the owner" ); _; } function renounceManagement() public virtual override onlyManager() { emit OwnershipPushed( _owner, address(0) ); _owner = address(0); } function pushManagement( address newOwner_ ) public virtual override onlyManager() { require( newOwner_ != address(0), "Ownable: new owner is the zero address"); emit OwnershipPushed( _owner, newOwner_ ); _newOwner = newOwner_; } function pullManagement() public virtual override { require( msg.sender == _newOwner, "Ownable: must be new owner to pull"); emit OwnershipPulled( _owner, _newOwner ); _owner = _newOwner; } } contract sBASH is ERC20Permit, Ownable { using SafeMath for uint256; modifier onlyStakingContract() { require( msg.sender == stakingContract ); _; } address public stakingContract; address public initializer; event LogSupply(uint256 indexed epoch, uint256 timestamp, uint256 totalSupply ); event LogRebase( uint256 indexed epoch, uint256 rebase, uint256 index ); event LogStakingContractUpdated( address stakingContract ); struct Rebase { uint epoch; uint rebase; // 18 decimals uint totalStakedBefore; uint totalStakedAfter; uint amountRebased; uint index; uint32 timeOccured; } Rebase[] public rebases; uint public INDEX; uint256 private constant MAX_UINT256 = ~uint256(0); uint256 private constant INITIAL_FRAGMENTS_SUPPLY = 5000000 * 10**9; // 9 decimal sbash // TOTAL_GONS is a multiple of INITIAL_FRAGMENTS_SUPPLY so that _gonsPerFragment is an integer. // Use the highest value that fits in a uint256 for max granularity. uint256 private constant TOTAL_GONS = MAX_UINT256 - (MAX_UINT256 % INITIAL_FRAGMENTS_SUPPLY); // MAX_SUPPLY = maximum integer < (sqrt(4*TOTAL_GONS + 1) - 1) / 2 uint256 private constant MAX_SUPPLY = ~uint128(0); // (2^128) - 1 uint256 private _gonsPerFragment; mapping(address => uint256) private _gonBalances; mapping ( address => mapping ( address => uint256 ) ) private _allowedValue; constructor() ERC20("Staked ATBASH", "sBASH", 9) ERC20Permit() { initializer = msg.sender; _totalSupply = INITIAL_FRAGMENTS_SUPPLY; _gonsPerFragment = TOTAL_GONS.div(_totalSupply); //_mint(msg.sender, 100000000000); } function initialize( address stakingContract_ ) external returns ( bool ) { require( msg.sender == initializer ); require( stakingContract_ != address(0) ); stakingContract = stakingContract_; _gonBalances[ stakingContract ] = TOTAL_GONS; emit Transfer( address(0x0), stakingContract, _totalSupply ); emit LogStakingContractUpdated( stakingContract_ ); initializer = address(0); return true; } function setIndex( uint _INDEX ) external onlyManager() returns ( bool ) { require( INDEX == 0 ); INDEX = gonsForBalance( _INDEX ); return true; } /** @notice increases sbash supply to increase staking balances relative to profit_ @param profit_ uint256 @return uint256 */ function rebase( uint256 profit_, uint epoch_ ) public onlyStakingContract() returns ( uint256 ) { uint256 rebaseAmount; uint256 circulatingSupply_ = circulatingSupply(); if ( profit_ == 0 ) { emit LogSupply( epoch_, block.timestamp, _totalSupply ); emit LogRebase( epoch_, 0, index() ); return _totalSupply; } else if ( circulatingSupply_ > 0 ){ rebaseAmount = profit_.mul( _totalSupply ).div( circulatingSupply_ ); // totalSupply / (totalySupply - stakingContractSupplyBalance) } else { rebaseAmount = profit_; } _totalSupply = _totalSupply.add( rebaseAmount ); if ( _totalSupply > MAX_SUPPLY ) { _totalSupply = MAX_SUPPLY; } _gonsPerFragment = TOTAL_GONS.div( _totalSupply ); _storeRebase( circulatingSupply_, profit_, epoch_ ); return _totalSupply; } /** @notice emits event with data about rebase @param previousCirculating_ uint @param profit_ uint @param epoch_ uint @return bool */ function _storeRebase( uint previousCirculating_, uint profit_, uint epoch_ ) internal returns ( bool ) { uint rebasePercent = profit_.mul( 1e18 ).div( previousCirculating_ ); rebases.push( Rebase ( { epoch: epoch_, rebase: rebasePercent, // 18 decimals totalStakedBefore: previousCirculating_, totalStakedAfter: circulatingSupply(), amountRebased: profit_, index: index(), timeOccured: uint32(block.timestamp) })); emit LogSupply( epoch_, block.timestamp, _totalSupply ); emit LogRebase( epoch_, rebasePercent, index() ); return true; } function balanceOf( address who ) public view override returns ( uint256 ) { return _gonBalances[ who ].div( _gonsPerFragment ); } function gonsForBalance( uint amount ) public view returns ( uint ) { return amount.mul( _gonsPerFragment ); } function balanceForGons( uint gons ) public view returns ( uint ) { return gons.div( _gonsPerFragment ); } // Staking contract holds excess sBASH // todo: in ohmv2 adds supply in warmup function circulatingSupply() public view returns ( uint ) { return _totalSupply.sub( balanceOf( stakingContract ) ); // sBASH - stakingContract sBASH } function index() public view returns ( uint ) { return balanceForGons( INDEX ); } function transfer( address to, uint256 value ) public override returns (bool) { uint256 gonValue = value.mul( _gonsPerFragment ); _gonBalances[ msg.sender ] = _gonBalances[ msg.sender ].sub( gonValue ); _gonBalances[ to ] = _gonBalances[ to ].add( gonValue ); emit Transfer( msg.sender, to, value ); return true; } function allowance( address owner_, address spender ) public view override returns ( uint256 ) { return _allowedValue[ owner_ ][ spender ]; } function transferFrom( address from, address to, uint256 value ) public override returns ( bool ) { _allowedValue[ from ][ msg.sender ] = _allowedValue[ from ][ msg.sender ].sub( value ); emit Approval( from, msg.sender, _allowedValue[ from ][ msg.sender ] ); uint256 gonValue = gonsForBalance( value ); _gonBalances[ from ] = _gonBalances[from].sub( gonValue ); _gonBalances[ to ] = _gonBalances[to].add( gonValue ); emit Transfer( from, to, value ); return true; } function approve( address spender, uint256 value ) public override returns (bool) { _allowedValue[ msg.sender ][ spender ] = value; emit Approval( msg.sender, spender, value ); return true; } // What gets called in a permit function _approve( address owner, address spender, uint256 value ) internal override virtual { _allowedValue[owner][spender] = value; emit Approval( owner, spender, value ); } function increaseAllowance( address spender, uint256 addedValue ) public override returns (bool) { _allowedValue[ msg.sender ][ spender ] = _allowedValue[ msg.sender ][ spender ].add( addedValue ); emit Approval( msg.sender, spender, _allowedValue[ msg.sender ][ spender ] ); return true; } function decreaseAllowance( address spender, uint256 subtractedValue ) public override returns (bool) { uint256 oldValue = _allowedValue[ msg.sender ][ spender ]; if (subtractedValue >= oldValue) { _allowedValue[ msg.sender ][ spender ] = 0; } else { _allowedValue[ msg.sender ][ spender ] = oldValue.sub( subtractedValue ); } emit Approval( msg.sender, spender, _allowedValue[ msg.sender ][ spender ] ); return true; } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; interface IOwnable { function manager() external view returns (address); function renounceManagement() external; function pushManagement(address newOwner_) external; function pullManagement() external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.7.5; library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} */ function sendValue(address payable recipient, uint256 amount) internal { require( address(this).balance >= amount, "Address: insufficient balance" ); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{value: amount}(""); require( success, "Address: unable to send value, recipient may have reverted" ); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue( target, data, value, "Address: low-level call with value failed" ); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require( address(this).balance >= value, "Address: insufficient balance for call" ); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{value: value}( data ); return _verifyCallResult(success, returndata, errorMessage); } function _functionCallWithValue( address target, bytes memory data, uint256 weiValue, string memory errorMessage ) private returns (bytes memory) { require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{value: weiValue}( data ); if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall( target, data, "Address: low-level static call failed" ); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.3._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall( target, data, "Address: low-level delegate call failed" ); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.3._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) private pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } function addressToString(address _address) internal pure returns (string memory) { bytes32 _bytes = bytes32(uint256(_address)); bytes memory HEX = "0123456789abcdef"; bytes memory _addr = new bytes(42); _addr[0] = "0"; _addr[1] = "x"; for (uint256 i = 0; i < 20; i++) { _addr[2 + i * 2] = HEX[uint8(_bytes[i + 12] >> 4)]; _addr[3 + i * 2] = HEX[uint8(_bytes[i + 12] & 0x0f)]; } return string(_addr); } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import {IERC20} from "./interfaces/IERC20.sol"; import {IsBash} from "./interfaces/IsBash.sol"; import {IWarmup} from "./interfaces/IWarmup.sol"; import {SafeERC20} from "./libraries/SafeERC20.sol"; import {SafeMath} from "./libraries/SafeMath.sol"; import {Address} from "./libraries/Address.sol"; import {Ownable} from "./types/Ownable.sol"; import {IDistributor} from "./interfaces/IDistributor.sol"; contract ATBASHStaking is Ownable { using SafeMath for uint256; using SafeMath for uint32; using SafeERC20 for IERC20; address public immutable Bash; address public immutable sBash; struct Epoch { uint256 number; uint256 distribute; uint32 length; uint32 endTime; } Epoch public epoch; address public distributor; address public locker; uint256 public totalBonus; address public warmupContract; uint256 public warmupPeriod; constructor( address _Bash, address _sBash, uint32 _epochLength, uint256 _firstEpochNumber, uint32 _firstEpochTime ) { require(_Bash != address(0)); Bash = _Bash; require(_sBash != address(0)); sBash = _sBash; epoch = Epoch({ length: _epochLength, number: _firstEpochNumber, endTime: _firstEpochTime, distribute: 0 }); } struct Claim { uint256 deposit; uint256 gons; uint256 expiry; bool lock; // prevents malicious delays } mapping(address => Claim) public warmupInfo; /** @notice stake bash to enter warmup @param _amount uint @return bool */ function stake(uint256 _amount, address _recipient) external returns (bool) { rebase(); IERC20(Bash).safeTransferFrom(msg.sender, address(this), _amount); // Time = Ohm = Bash Claim memory info = warmupInfo[_recipient]; require(!info.lock, "Deposits for account are locked"); warmupInfo[_recipient] = Claim({ deposit: info.deposit.add(_amount), gons: info.gons.add(IsBash(sBash).gonsForBalance(_amount)), expiry: epoch.number.add(warmupPeriod), lock: false }); IERC20(sBash).safeTransfer(warmupContract, _amount); return true; } /** @notice retrieve sbash from warmup @param _recipient address */ function claim(address _recipient) public { Claim memory info = warmupInfo[_recipient]; if (epoch.number >= info.expiry && info.expiry != 0) { delete warmupInfo[_recipient]; IWarmup(warmupContract).retrieve( _recipient, IsBash(sBash).balanceForGons(info.gons) ); } } /** @notice forfeit sbash in warmup and retrieve bash */ function forfeit() external { Claim memory info = warmupInfo[msg.sender]; delete warmupInfo[msg.sender]; IWarmup(warmupContract).retrieve( address(this), IsBash(sBash).balanceForGons(info.gons) ); IERC20(Bash).safeTransfer(msg.sender, info.deposit); } /** @notice prevent new deposits to address (protection from malicious activity) */ function toggleDepositLock() external { warmupInfo[msg.sender].lock = !warmupInfo[msg.sender].lock; } /** @notice redeem sbash for bash @param _amount uint @param _trigger bool */ function unstake(uint256 _amount, bool _trigger) external { if (_trigger) { rebase(); } IERC20(sBash).safeTransferFrom(msg.sender, address(this), _amount); IERC20(Bash).safeTransfer(msg.sender, _amount); } /** @notice returns the sbash index, which tracks rebase growth @return uint */ function index() public view returns (uint256) { return IsBash(sBash).index(); } /** @notice trigger rebase if epoch over */ function rebase() public { if (epoch.endTime <= uint32(block.timestamp)) { IsBash(sBash).rebase(epoch.distribute, epoch.number); epoch.endTime = epoch.endTime.add32(epoch.length); epoch.number++; if (distributor != address(0)) { IDistributor(distributor).distribute(); } uint256 balance = contractBalance(); // amount of BASH in contract uint256 staked = IsBash(sBash).circulatingSupply(); // amount of unstaked sbash if (balance <= staked) { epoch.distribute = 0; } else { epoch.distribute = balance.sub(staked); } } } /** @notice returns contract bash holdings, including bonuses provided @return uint */ function contractBalance() public view returns (uint256) { return IERC20(Bash).balanceOf(address(this)).add(totalBonus); } /** @notice provide bonus to locked staking contract @param _amount uint */ function giveLockBonus(uint256 _amount) external { require(msg.sender == locker); totalBonus = totalBonus.add(_amount); IERC20(sBash).safeTransfer(locker, _amount); } /** @notice reclaim bonus from locked staking contract @param _amount uint */ function returnLockBonus(uint256 _amount) external { require(msg.sender == locker); totalBonus = totalBonus.sub(_amount); IERC20(sBash).safeTransferFrom(locker, address(this), _amount); } enum CONTRACTS { DISTRIBUTOR, WARMUP, LOCKER } /** @notice sets the contract address for LP staking @param _contract address */ function setContract(CONTRACTS _contract, address _address) external onlyManager { if (_contract == CONTRACTS.DISTRIBUTOR) { // 0 distributor = _address; } else if (_contract == CONTRACTS.WARMUP) { // 1 require( warmupContract == address(0), "Warmup cannot be set more than once" ); warmupContract = _address; } else if (_contract == CONTRACTS.LOCKER) { // 2 require( locker == address(0), "Locker cannot be set more than once" ); locker = _address; } } /** * @notice set warmup period in epoch's numbers for new stakers * @param _warmupPeriod uint */ function setWarmup(uint256 _warmupPeriod) external onlyManager { warmupPeriod = _warmupPeriod; } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; interface IERC20 { /** * @dev Returns the decimals of the token. */ function decimals() external view returns (uint8); /** * @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 ); }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity >=0.7.5; import "./IERC20.sol"; // sBash interface IsBash is IERC20 { function rebase( uint256 profit_, uint epoch_) external returns (uint256); function circulatingSupply() external view returns (uint256); // function balanceOf(address who) external view returns (uint256); function gonsForBalance( uint amount ) external view returns ( uint ); function balanceForGons( uint gons ) external view returns ( uint ); function index() external view returns ( uint ); }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import "./IERC20.sol"; interface IWarmup is IERC20 { function retrieve( address staker_, uint amount_ ) external; }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import {Address} from "../libraries/Address.sol"; import {SafeMath} from "../libraries/SafeMath.sol"; import {IERC20} from "../interfaces/IERC20.sol"; library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transfer.selector, to, value) ); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, value) ); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).add( value ); _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).sub( value, "SafeERC20: decreased allowance below zero" ); _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall( data, "SafeERC20: low-level call failed" ); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require( abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed" ); } } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import "../interfaces/IOwnable.sol"; contract Ownable is IOwnable { address internal _owner; address internal _newOwner; event OwnershipPushed( address indexed previousOwner, address indexed newOwner ); event OwnershipPulled( address indexed previousOwner, address indexed newOwner ); constructor() { _owner = msg.sender; emit OwnershipPushed(address(0), _owner); } function manager() public view override returns (address) { return _owner; } modifier onlyManager() { require(_owner == msg.sender, "Ownable: caller is not the owner"); _; } function renounceManagement() public virtual override onlyManager { emit OwnershipPushed(_owner, address(0)); _owner = address(0); } function pushManagement(address newOwner_) public virtual override onlyManager { require( newOwner_ != address(0), "Ownable: new owner is the zero address" ); emit OwnershipPushed(_owner, newOwner_); _newOwner = newOwner_; } function pullManagement() public virtual override { require(msg.sender == _newOwner, "Ownable: must be new owner to pull"); emit OwnershipPulled(_owner, _newOwner); _owner = _newOwner; } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; interface IDistributor { function distribute() external returns (bool); }
/** *Submitted for verification at snowtrace.io on 2021-11-05 */ // SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import {IERC20} from "./interfaces/IERC20.sol"; import {ITreasury} from "./interfaces/ITreasury.sol"; import {IERC20Burnable, IERC20Mintable} from "./interfaces/IBash.sol"; library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function add32(uint32 a, uint32 b) internal pure returns (uint32) { uint32 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function mul32(uint32 a, uint32 b) internal pure returns (uint32) { if (a == 0) { return 0; } uint32 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; return c; } } library Address { function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, errorMessage); } function _functionCallWithValue( address target, bytes memory data, uint256 weiValue, string memory errorMessage ) private returns (bytes memory) { require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{value: weiValue}( data ); if (success) { return returndata; } else { if (returndata.length > 0) { // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } function _verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) private pure returns (bytes memory) { if (success) { return returndata; } else { if (returndata.length > 0) { // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } interface IOwnable { function manager() external view returns (address); function renounceManagement() external; function pushManagement(address newOwner_) external; function pullManagement() external; } contract Ownable is IOwnable { address internal _owner; address internal _newOwner; event OwnershipPushed( address indexed previousOwner, address indexed newOwner ); event OwnershipPulled( address indexed previousOwner, address indexed newOwner ); constructor() { _owner = msg.sender; emit OwnershipPushed(address(0), _owner); } function manager() public view override returns (address) { return _owner; } modifier onlyManager() { require(_owner == msg.sender, "Ownable: caller is not the owner"); _; } function renounceManagement() public virtual override onlyManager { emit OwnershipPushed(_owner, address(0)); _owner = address(0); } function pushManagement(address newOwner_) public virtual override onlyManager { require( newOwner_ != address(0), "Ownable: new owner is the zero address" ); emit OwnershipPushed(_owner, newOwner_); _newOwner = newOwner_; } function pullManagement() public virtual override { require(msg.sender == _newOwner, "Ownable: must be new owner to pull"); emit OwnershipPulled(_owner, _newOwner); _owner = _newOwner; } } // interface IERC20 { // function decimals() external view returns (uint8); // function balanceOf(address account) external view returns (uint256); // function transfer(address recipient, uint256 amount) // external // returns (bool); // function approve(address spender, uint256 amount) external returns (bool); // function totalSupply() external view returns (uint256); // function transferFrom( // address sender, // address recipient, // uint256 amount // ) external returns (bool); // event Transfer(address indexed from, address indexed to, uint256 value); // event Approval( // address indexed owner, // address indexed spender, // uint256 value // ); // } library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transfer.selector, to, value) ); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } function _callOptionalReturn(IERC20 token, bytes memory data) private { bytes memory returndata = address(token).functionCall( data, "SafeERC20: low-level call failed" ); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require( abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed" ); } } } interface IBondCalculator { function valuation(address pair_, uint256 amount_) external view returns (uint256 _value); } contract BashTreasury is Ownable, ITreasury { using SafeMath for uint256; using SafeMath for uint32; using SafeERC20 for IERC20; event Deposit(address indexed token, uint256 amount, uint256 value); event Withdrawal(address indexed token, uint256 amount, uint256 value); event CreateDebt( address indexed debtor, address indexed token, uint256 amount, uint256 value ); event RepayDebt( address indexed debtor, address indexed token, uint256 amount, uint256 value ); event ReservesManaged(address indexed token, uint256 amount); event ReservesUpdated(uint256 indexed totalReserves); event ReservesAudited(uint256 indexed totalReserves); event RewardsMinted( address indexed caller, address indexed recipient, uint256 amount ); event ChangeQueued(MANAGING indexed managing, address queued); event ChangeActivated( MANAGING indexed managing, address activated, bool result ); enum MANAGING { RESERVEDEPOSITOR, RESERVESPENDER, RESERVETOKEN, RESERVEMANAGER, LIQUIDITYDEPOSITOR, LIQUIDITYTOKEN, LIQUIDITYMANAGER, DEBTOR, REWARDMANAGER, SBASH } address public immutable Bash; uint32 public immutable secondsNeededForQueue; address[] public reserveTokens; // Push only, beware false-positives. mapping(address => bool) public isReserveToken; mapping(address => uint32) public reserveTokenQueue; // Delays changes to mapping. address[] public reserveDepositors; // Push only, beware false-positives. Only for viewing. mapping(address => bool) public isReserveDepositor; mapping(address => uint32) public reserveDepositorQueue; // Delays changes to mapping. address[] public reserveSpenders; // Push only, beware false-positives. Only for viewing. mapping(address => bool) public isReserveSpender; mapping(address => uint32) public reserveSpenderQueue; // Delays changes to mapping. address[] public liquidityTokens; // Push only, beware false-positives. mapping(address => bool) public isLiquidityToken; mapping(address => uint32) public LiquidityTokenQueue; // Delays changes to mapping. address[] public liquidityDepositors; // Push only, beware false-positives. Only for viewing. mapping(address => bool) public isLiquidityDepositor; mapping(address => uint32) public LiquidityDepositorQueue; // Delays changes to mapping. mapping(address => address) public bondCalculator; // bond calculator for liquidity token address[] public reserveManagers; // Push only, beware false-positives. Only for viewing. mapping(address => bool) public isReserveManager; mapping(address => uint32) public ReserveManagerQueue; // Delays changes to mapping. address[] public liquidityManagers; // Push only, beware false-positives. Only for viewing. mapping(address => bool) public isLiquidityManager; mapping(address => uint32) public LiquidityManagerQueue; // Delays changes to mapping. address[] public debtors; // Push only, beware false-positives. Only for viewing. mapping(address => bool) public isDebtor; mapping(address => uint32) public debtorQueue; // Delays changes to mapping. mapping(address => uint256) public debtorBalance; address[] public rewardManagers; // Push only, beware false-positives. Only for viewing. mapping(address => bool) public isRewardManager; mapping(address => uint32) public rewardManagerQueue; // Delays changes to mapping. address public sBash; uint256 public sBashQueue; // Delays change to sbash address uint256 public totalReserves; // Risk-free value of all assets uint256 public totalDebt; constructor( address _Bash, address _DAI, // stable uint32 _secondsNeededForQueue ) { require(_Bash != address(0)); Bash = _Bash; isReserveToken[_DAI] = true; reserveTokens.push(_DAI); secondsNeededForQueue = _secondsNeededForQueue; } /** @notice allow approved address to deposit an asset for bash @param _amount uint @param _token address @param _profit uint @return send_ uint */ function deposit( uint256 _amount, address _token, uint256 _profit ) external override returns (uint256 send_) { require( isReserveToken[_token] || isLiquidityToken[_token], "Not accepted" ); IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount); if (isReserveToken[_token]) { require(isReserveDepositor[msg.sender], "Not approved"); } else { require(isLiquidityDepositor[msg.sender], "Not approved"); } uint256 value = tokenValue(_token, _amount); // value of token in BASH // mint BASH needed and store amount of rewards for distribution send_ = value.sub(_profit); IERC20Mintable(Bash).mint(msg.sender, send_); totalReserves = totalReserves.add(value); emit ReservesUpdated(totalReserves); emit Deposit(_token, _amount, value); } /** @notice allow approved address to burn bash for reserves @param _amount uint @param _token address */ function withdraw(uint256 _amount, address _token) external { require(isReserveToken[_token], "Not accepted"); // Only reserves can be used for redemptions require(isReserveSpender[msg.sender] == true, "Not approved"); uint256 value = tokenValue(_token, _amount); IERC20Burnable(Bash).burnFrom(msg.sender, value); totalReserves = totalReserves.sub(value); emit ReservesUpdated(totalReserves); IERC20(_token).safeTransfer(msg.sender, _amount); emit Withdrawal(_token, _amount, value); } /** @notice allow approved address to borrow reserves @param _amount uint @param _token address */ function incurDebt(uint256 _amount, address _token) external { require(isDebtor[msg.sender], "Not approved"); require(isReserveToken[_token], "Not accepted"); uint256 value = tokenValue(_token, _amount); uint256 maximumDebt = IERC20(sBash).balanceOf(msg.sender); // Can only borrow against sbash held uint256 availableDebt = maximumDebt.sub(debtorBalance[msg.sender]); require(value <= availableDebt, "Exceeds debt limit"); debtorBalance[msg.sender] = debtorBalance[msg.sender].add(value); totalDebt = totalDebt.add(value); totalReserves = totalReserves.sub(value); emit ReservesUpdated(totalReserves); IERC20(_token).transfer(msg.sender, _amount); emit CreateDebt(msg.sender, _token, _amount, value); } /** @notice allow approved address to repay borrowed reserves with reserves @param _amount uint @param _token address */ function repayDebtWithReserve(uint256 _amount, address _token) external { require(isDebtor[msg.sender], "Not approved"); require(isReserveToken[_token], "Not accepted"); IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount); uint256 value = tokenValue(_token, _amount); debtorBalance[msg.sender] = debtorBalance[msg.sender].sub(value); totalDebt = totalDebt.sub(value); totalReserves = totalReserves.add(value); emit ReservesUpdated(totalReserves); emit RepayDebt(msg.sender, _token, _amount, value); } /** @notice allow approved address to repay borrowed reserves with bash @param _amount uint */ function repayDebtWithBash(uint256 _amount) external { require(isDebtor[msg.sender], "Not approved"); IERC20Burnable(Bash).burnFrom(msg.sender, _amount); debtorBalance[msg.sender] = debtorBalance[msg.sender].sub(_amount); totalDebt = totalDebt.sub(_amount); emit RepayDebt(msg.sender, Bash, _amount, _amount); } /** @notice allow approved address to withdraw assets @param _token address @param _amount uint */ function manage(address _token, uint256 _amount) external { if (isLiquidityToken[_token]) { require(isLiquidityManager[msg.sender], "Not approved"); } else { require(isReserveManager[msg.sender], "Not approved"); } uint256 value = tokenValue(_token, _amount); require(value <= excessReserves(), "Insufficient reserves"); totalReserves = totalReserves.sub(value); emit ReservesUpdated(totalReserves); IERC20(_token).safeTransfer(msg.sender, _amount); emit ReservesManaged(_token, _amount); } /** @notice send epoch reward to staking contract */ function mintRewards(address _recipient, uint256 _amount) override external { require(isRewardManager[msg.sender], "Not approved"); require(_amount <= excessReserves(), "Insufficient reserves"); IERC20Mintable(Bash).mint(_recipient, _amount); emit RewardsMinted(msg.sender, _recipient, _amount); } /** @notice returns excess reserves not backing tokens @return uint */ function excessReserves() public view returns (uint256) { return totalReserves.sub(IERC20(Bash).totalSupply().sub(totalDebt)); } /** @notice takes inventory of all tracked assets @notice always consolidate to recognized reserves before audit */ function auditReserves() external onlyManager { uint256 reserves; for (uint256 i = 0; i < reserveTokens.length; i++) { reserves = reserves.add( tokenValue( reserveTokens[i], IERC20(reserveTokens[i]).balanceOf(address(this)) ) ); } for (uint256 i = 0; i < liquidityTokens.length; i++) { reserves = reserves.add( tokenValue( liquidityTokens[i], IERC20(liquidityTokens[i]).balanceOf(address(this)) ) ); } totalReserves = reserves; emit ReservesUpdated(reserves); emit ReservesAudited(reserves); } /** @notice returns BASH valuation of asset @param _token address @param _amount uint @return value_ uint */ function tokenValue(address _token, uint256 _amount) public view override returns (uint256 value_) { if (isReserveToken[_token]) { // convert amount to match Bash decimals value_ = _amount.mul(10**IERC20(Bash).decimals()).div( 10**IERC20(_token).decimals() ); } else if (isLiquidityToken[_token]) { value_ = IBondCalculator(bondCalculator[_token]).valuation( _token, _amount ); } } // function tokenValue(address _token, uint256 _amount) public view override returns (uint256 value_) // { // if (isReserveToken[_token]) { // // convert amount to match Bash decimals // value_ = _amount.mul(10**IERC20(Bash).decimals()).div( // 10**IERC20(_token).decimals() // ); // } else if (isLiquidityToken[_token]) { // value_ = IBondCalculator(bondCalculator[_token]).valuation( // _token, // _amount // ); // } // } /** @notice queue address to change boolean in mapping @param _managing MANAGING @param _address address @return bool */ function queue(MANAGING _managing, address _address) external onlyManager returns (bool) { require(_address != address(0)); if (_managing == MANAGING.RESERVEDEPOSITOR) { // 0 reserveDepositorQueue[_address] = uint32(block.timestamp).add32( secondsNeededForQueue ); } else if (_managing == MANAGING.RESERVESPENDER) { // 1 reserveSpenderQueue[_address] = uint32(block.timestamp).add32( secondsNeededForQueue ); } else if (_managing == MANAGING.RESERVETOKEN) { // 2 reserveTokenQueue[_address] = uint32(block.timestamp).add32( secondsNeededForQueue ); } else if (_managing == MANAGING.RESERVEMANAGER) { // 3 ReserveManagerQueue[_address] = uint32(block.timestamp).add32( secondsNeededForQueue.mul32(2) ); } else if (_managing == MANAGING.LIQUIDITYDEPOSITOR) { // 4 LiquidityDepositorQueue[_address] = uint32(block.timestamp).add32( secondsNeededForQueue ); } else if (_managing == MANAGING.LIQUIDITYTOKEN) { // 5 LiquidityTokenQueue[_address] = uint32(block.timestamp).add32( secondsNeededForQueue ); } else if (_managing == MANAGING.LIQUIDITYMANAGER) { // 6 LiquidityManagerQueue[_address] = uint32(block.timestamp).add32( secondsNeededForQueue.mul32(2) ); } else if (_managing == MANAGING.DEBTOR) { // 7 debtorQueue[_address] = uint32(block.timestamp).add32( secondsNeededForQueue ); } else if (_managing == MANAGING.REWARDMANAGER) { // 8 rewardManagerQueue[_address] = uint32(block.timestamp).add32( secondsNeededForQueue ); } else if (_managing == MANAGING.SBASH) { // 9 sBashQueue = uint32(block.timestamp).add32(secondsNeededForQueue); } else return false; emit ChangeQueued(_managing, _address); return true; } /** @notice verify queue then set boolean in mapping @param _managing MANAGING @param _address address @param _calculator address @return bool */ function toggle( MANAGING _managing, address _address, address _calculator ) external onlyManager returns (bool) { require(_address != address(0)); bool result; if (_managing == MANAGING.RESERVEDEPOSITOR) { // 0 if ( requirements( reserveDepositorQueue, isReserveDepositor, _address ) ) { reserveDepositorQueue[_address] = 0; if (!listContains(reserveDepositors, _address)) { reserveDepositors.push(_address); } } result = !isReserveDepositor[_address]; isReserveDepositor[_address] = result; } else if (_managing == MANAGING.RESERVESPENDER) { // 1 if (requirements(reserveSpenderQueue, isReserveSpender, _address)) { reserveSpenderQueue[_address] = 0; if (!listContains(reserveSpenders, _address)) { reserveSpenders.push(_address); } } result = !isReserveSpender[_address]; isReserveSpender[_address] = result; } else if (_managing == MANAGING.RESERVETOKEN) { // 2 if (requirements(reserveTokenQueue, isReserveToken, _address)) { reserveTokenQueue[_address] = 0; if (!listContains(reserveTokens, _address)) { reserveTokens.push(_address); } } result = !isReserveToken[_address]; isReserveToken[_address] = result; } else if (_managing == MANAGING.RESERVEMANAGER) { // 3 if (requirements(ReserveManagerQueue, isReserveManager, _address)) { reserveManagers.push(_address); ReserveManagerQueue[_address] = 0; if (!listContains(reserveManagers, _address)) { reserveManagers.push(_address); } } result = !isReserveManager[_address]; isReserveManager[_address] = result; } else if (_managing == MANAGING.LIQUIDITYDEPOSITOR) { // 4 if ( requirements( LiquidityDepositorQueue, isLiquidityDepositor, _address ) ) { liquidityDepositors.push(_address); LiquidityDepositorQueue[_address] = 0; if (!listContains(liquidityDepositors, _address)) { liquidityDepositors.push(_address); } } result = !isLiquidityDepositor[_address]; isLiquidityDepositor[_address] = result; } else if (_managing == MANAGING.LIQUIDITYTOKEN) { // 5 if (requirements(LiquidityTokenQueue, isLiquidityToken, _address)) { LiquidityTokenQueue[_address] = 0; if (!listContains(liquidityTokens, _address)) { liquidityTokens.push(_address); } } result = !isLiquidityToken[_address]; isLiquidityToken[_address] = result; bondCalculator[_address] = _calculator; } else if (_managing == MANAGING.LIQUIDITYMANAGER) { // 6 if ( requirements( LiquidityManagerQueue, isLiquidityManager, _address ) ) { LiquidityManagerQueue[_address] = 0; if (!listContains(liquidityManagers, _address)) { liquidityManagers.push(_address); } } result = !isLiquidityManager[_address]; isLiquidityManager[_address] = result; } else if (_managing == MANAGING.DEBTOR) { // 7 if (requirements(debtorQueue, isDebtor, _address)) { debtorQueue[_address] = 0; if (!listContains(debtors, _address)) { debtors.push(_address); } } result = !isDebtor[_address]; isDebtor[_address] = result; } else if (_managing == MANAGING.REWARDMANAGER) { // 8 if (requirements(rewardManagerQueue, isRewardManager, _address)) { rewardManagerQueue[_address] = 0; if (!listContains(rewardManagers, _address)) { rewardManagers.push(_address); } } result = !isRewardManager[_address]; isRewardManager[_address] = result; } else if (_managing == MANAGING.SBASH) { // 9 sBashQueue = 0; sBash = _address; result = true; } else return false; emit ChangeActivated(_managing, _address, result); return true; } /** @notice checks requirements and returns altered structs @param queue_ mapping( address => uint ) @param status_ mapping( address => bool ) @param _address address @return bool */ function requirements( mapping(address => uint32) storage queue_, mapping(address => bool) storage status_, address _address ) internal view returns (bool) { if (!status_[_address]) { require(queue_[_address] != 0, "Must queue"); require( queue_[_address] <= uint32(block.timestamp), "Queue not expired" ); return true; } return false; } /** @notice checks array to ensure against duplicate @param _list address[] @param _token address @return bool */ function listContains(address[] storage _list, address _token) internal view returns (bool) { for (uint256 i = 0; i < _list.length; i++) { if (_list[i] == _token) { return true; } } return false; } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity <=0.8.10; interface ITreasury { function tokenValue(address _token, uint256 _amount) external view returns (uint256); function deposit( uint256 _amount, address _token, uint256 _profit ) external returns (uint256); function mintRewards(address _recipient, uint256 _amount) external; }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import {IERC20} from "./IERC20.sol"; interface IERC20Mintable { function mint(uint256 amount_) external; function mint(address account_, uint256 ammount_) external; } interface IERC20Burnable { function burnFrom(address account_, uint256 amount_) external; } interface IBash is IERC20, IERC20Mintable { }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import {ITreasury} from "./interfaces/ITreasury.sol"; interface IOwnable { function policy() external view returns (address); function renounceManagement() external; function pushManagement( address newOwner_ ) external; function pullManagement() external; } contract Ownable is IOwnable { address internal _owner; address internal _newOwner; event OwnershipPushed(address indexed previousOwner, address indexed newOwner); event OwnershipPulled(address indexed previousOwner, address indexed newOwner); constructor () { _owner = msg.sender; emit OwnershipPushed( address(0), _owner ); } function policy() public view override returns (address) { return _owner; } modifier onlyPolicy() { require( _owner == msg.sender, "Ownable: caller is not the owner" ); _; } function renounceManagement() public virtual override onlyPolicy() { emit OwnershipPushed( _owner, address(0) ); _owner = address(0); } function pushManagement( address newOwner_ ) public virtual override onlyPolicy() { require( newOwner_ != address(0), "Ownable: new owner is the zero address"); emit OwnershipPushed( _owner, newOwner_ ); _newOwner = newOwner_; } function pullManagement() public virtual override { require( msg.sender == _newOwner, "Ownable: must be new owner to pull"); emit OwnershipPulled( _owner, _newOwner ); _owner = _newOwner; } } library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; return c; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } function sqrrt(uint256 a) internal pure returns (uint c) { if (a > 3) { c = a; uint b = add( div( a, 2), 1 ); while (b < c) { c = b; b = div( add( div( a, b ), b), 2 ); } } else if (a != 0) { c = 1; } } } library Address { function isContract(address account) internal view returns (bool) { uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, errorMessage); } function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { if (returndata.length > 0) { assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } function addressToString(address _address) internal pure returns(string memory) { bytes32 _bytes = bytes32(uint256(_address)); bytes memory HEX = "0123456789abcdef"; bytes memory _addr = new bytes(42); _addr[0] = '0'; _addr[1] = 'x'; for(uint256 i = 0; i < 20; i++) { _addr[2+i*2] = HEX[uint8(_bytes[i + 12] >> 4)]; _addr[3+i*2] = HEX[uint8(_bytes[i + 12] & 0x0f)]; } return string(_addr); } } interface IERC20 { function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } abstract contract ERC20 is IERC20 { using SafeMath for uint256; // TODO comment actual hash value. bytes32 constant private ERC20TOKEN_ERC1820_INTERFACE_ID = keccak256( "ERC20Token" ); mapping (address => uint256) internal _balances; mapping (address => mapping (address => uint256)) internal _allowances; uint256 internal _totalSupply; string internal _name; string internal _symbol; uint8 internal _decimals; constructor (string memory name_, string memory symbol_, uint8 decimals_) { _name = name_; _symbol = symbol_; _decimals = decimals_; } function name() public view returns (string memory) { return _name; } function symbol() public view returns (string memory) { return _symbol; } function decimals() public view override returns (uint8) { return _decimals; } function totalSupply() public view override returns (uint256) { return _totalSupply; } function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(msg.sender, recipient, amount); return true; } function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(msg.sender, spender, amount); return true; } function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); return true; } function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } function _mint(address account_, uint256 ammount_) internal virtual { require(account_ != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address( this ), account_, ammount_); _totalSupply = _totalSupply.add(ammount_); _balances[account_] = _balances[account_].add(ammount_); emit Transfer(address( this ), account_, ammount_); } function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal virtual { } } interface IERC2612Permit { function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function nonces(address owner) external view returns (uint256); } library Counters { using SafeMath for uint256; struct Counter { uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { counter._value += 1; } function decrement(Counter storage counter) internal { counter._value = counter._value.sub(1); } } abstract contract ERC20Permit is ERC20, IERC2612Permit { using Counters for Counters.Counter; mapping(address => Counters.Counter) private _nonces; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; bytes32 public DOMAIN_SEPARATOR; constructor() { uint256 chainID; assembly { chainID := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name())), keccak256(bytes("1")), // Version chainID, address(this) ) ); } function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual override { require(block.timestamp <= deadline, "Permit: expired deadline"); bytes32 hashStruct = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, _nonces[owner].current(), deadline)); bytes32 _hash = keccak256(abi.encodePacked(uint16(0x1901), DOMAIN_SEPARATOR, hashStruct)); address signer = ecrecover(_hash, v, r, s); require(signer != address(0) && signer == owner, "ZeroSwapPermit: Invalid signature"); _nonces[owner].increment(); _approve(owner, spender, amount); } function nonces(address owner) public view override returns (uint256) { return _nonces[owner].current(); } } library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeApprove(IERC20 token, address spender, uint256 value) internal { require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function _callOptionalReturn(IERC20 token, bytes memory data) private { bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } library FullMath { function fullMul(uint256 x, uint256 y) private pure returns (uint256 l, uint256 h) { uint256 mm = mulmod(x, y, uint256(-1)); l = x * y; h = mm - l; if (mm < l) h -= 1; } function fullDiv( uint256 l, uint256 h, uint256 d ) private pure returns (uint256) { uint256 pow2 = d & -d; d /= pow2; l /= pow2; l += h * ((-pow2) / pow2 + 1); uint256 r = 1; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; return l * r; } function mulDiv( uint256 x, uint256 y, uint256 d ) internal pure returns (uint256) { (uint256 l, uint256 h) = fullMul(x, y); uint256 mm = mulmod(x, y, d); if (mm > l) h -= 1; l -= mm; require(h < d, 'FullMath::mulDiv: overflow'); return fullDiv(l, h, d); } } library FixedPoint { struct uq112x112 { uint224 _x; } struct uq144x112 { uint256 _x; } uint8 private constant RESOLUTION = 112; uint256 private constant Q112 = 0x10000000000000000000000000000; uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000; uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits) function decode(uq112x112 memory self) internal pure returns (uint112) { return uint112(self._x >> RESOLUTION); } function decode112with18(uq112x112 memory self) internal pure returns (uint) { return uint(self._x) / 5192296858534827; } function fraction(uint256 numerator, uint256 denominator) internal pure returns (uq112x112 memory) { require(denominator > 0, 'FixedPoint::fraction: division by zero'); if (numerator == 0) return FixedPoint.uq112x112(0); if (numerator <= uint144(-1)) { uint256 result = (numerator << RESOLUTION) / denominator; require(result <= uint224(-1), 'FixedPoint::fraction: overflow'); return uq112x112(uint224(result)); } else { uint256 result = FullMath.mulDiv(numerator, Q112, denominator); require(result <= uint224(-1), 'FixedPoint::fraction: overflow'); return uq112x112(uint224(result)); } } } interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); } interface IStaking { function stake( uint _amount, address _recipient ) external returns ( bool ); } interface IStakingHelper { function stake( uint _amount, address _recipient ) external; } contract OlympusBondDepository is Ownable { using FixedPoint for *; using SafeERC20 for IERC20; using SafeMath for uint; /* ======== EVENTS ======== */ event BondCreated( uint deposit, uint indexed payout, uint indexed expires, uint indexed priceInUSD ); event BondRedeemed( address indexed recipient, uint payout, uint remaining ); event BondPriceChanged( uint indexed priceInUSD, uint indexed internalPrice, uint indexed debtRatio ); event ControlVariableAdjustment( uint initialBCV, uint newBCV, uint adjustment, bool addition ); /* ======== STATE VARIABLES ======== */ address public immutable bash; // token given as payment for bond address public immutable principle; // token used to create bond address public immutable treasury; // mints bash when receives principle address public immutable DAO; // receives profit share from bond AggregatorV3Interface internal priceFeed; address public staking; // to auto-stake payout address public stakingHelper; // to stake and claim if no staking warmup bool public useHelper; Terms public terms; // stores terms for new bonds Adjust public adjustment; // stores adjustment to BCV data mapping( address => Bond ) public bondInfo; // stores bond information for depositors uint public totalDebt; // total value of outstanding bonds; used for pricing uint public lastDecay; // reference block for debt decay /* ======== STRUCTS ======== */ // Info for creating new bonds struct Terms { uint controlVariable; // scaling variable for price uint vestingTerm; // in blocks uint minimumPrice; // vs principle value. 4 decimals (1500 = 0.15) uint maxPayout; // in thousandths of a %. i.e. 500 = 0.5% uint maxDebt; // 9 decimal debt ratio, max % total supply created as debt } // Info for bond holder struct Bond { uint payout; // bash remaining to be paid uint vesting; // Blocks left to vest uint lastBlock; // Last interaction uint pricePaid; // In DAI, for front end viewing } // Info for incremental adjustments to control variable struct Adjust { bool add; // addition or subtraction uint rate; // increment uint target; // BCV when adjustment finished uint buffer; // minimum length (in blocks) between adjustments uint lastBlock; // block when last adjustment made } /* ======== INITIALIZATION ======== */ constructor ( address _bash, address _principle, address _treasury, address _DAO, address _feed ) { require( _bash != address(0) ); bash = _bash; require( _principle != address(0) ); principle = _principle; require( _treasury != address(0) ); treasury = _treasury; require( _DAO != address(0) ); DAO = _DAO; require( _feed != address(0) ); priceFeed = AggregatorV3Interface( _feed ); } /** * @notice initializes bond parameters * @param _controlVariable uint * @param _vestingTerm uint * @param _minimumPrice uint * @param _maxPayout uint * @param _maxDebt uint * @param _initialDebt uint */ function initializeBondTerms( uint _controlVariable, uint _vestingTerm, uint _minimumPrice, uint _maxPayout, uint _maxDebt, uint _initialDebt ) external onlyPolicy() { require( currentDebt() == 0, "Debt must be 0 for initialization" ); terms = Terms ({ controlVariable: _controlVariable, vestingTerm: _vestingTerm, minimumPrice: _minimumPrice, maxPayout: _maxPayout, maxDebt: _maxDebt }); totalDebt = _initialDebt; lastDecay = block.number; } /* ======== POLICY FUNCTIONS ======== */ enum PARAMETER { VESTING, PAYOUT, DEBT } /** * @notice set parameters for new bonds * @param _parameter PARAMETER * @param _input uint */ function setBondTerms ( PARAMETER _parameter, uint _input ) external onlyPolicy() { if ( _parameter == PARAMETER.VESTING ) { // 0 require( _input >= 10000, "Vesting must be longer than 36 hours" ); terms.vestingTerm = _input; } else if ( _parameter == PARAMETER.PAYOUT ) { // 1 require( _input <= 1000, "Payout cannot be above 1 percent" ); terms.maxPayout = _input; } else if ( _parameter == PARAMETER.DEBT ) { // 3 terms.maxDebt = _input; } } /** * @notice set control variable adjustment * @param _addition bool * @param _increment uint * @param _target uint * @param _buffer uint */ function setAdjustment ( bool _addition, uint _increment, uint _target, uint _buffer ) external onlyPolicy() { require( _increment <= terms.controlVariable.mul( 25 ).div( 1000 ), "Increment too large" ); adjustment = Adjust({ add: _addition, rate: _increment, target: _target, buffer: _buffer, lastBlock: block.number }); } /** * @notice set contract for auto stake * @param _staking address * @param _helper bool */ function setStaking( address _staking, bool _helper ) external onlyPolicy() { require( _staking != address(0) ); if ( _helper ) { useHelper = true; stakingHelper = _staking; } else { useHelper = false; staking = _staking; } } /* ======== USER FUNCTIONS ======== */ /** * @notice deposit bond * @param _amount uint * @param _maxPrice uint * @param _depositor address * @return uint */ function deposit( uint _amount, uint _maxPrice, address _depositor ) external returns ( uint ) { require( _depositor != address(0), "Invalid address" ); decayDebt(); require( totalDebt <= terms.maxDebt, "Max capacity reached" ); uint priceInUSD = bondPriceInUSD(); // Stored in bond info uint nativePrice = _bondPrice(); require( _maxPrice >= nativePrice, "Slippage limit: more than max price" ); // slippage protection uint value = ITreasury( treasury ).tokenValue( principle, _amount ); uint payout = payoutFor( value ); // payout to bonder is computed require( payout >= 10000000, "Bond too small" ); // must be > 0.01 bash ( underflow protection ) require( payout <= maxPayout(), "Bond too large"); // size protection because there is no slippage /** asset carries risk and is not minted against asset transfered to treasury and rewards minted as payout */ IERC20( principle ).safeTransferFrom( msg.sender, treasury, _amount ); ITreasury( treasury ).mintRewards( address(this), payout ); // total debt is increased totalDebt = totalDebt.add( value ); // depositor info is stored bondInfo[ _depositor ] = Bond({ payout: bondInfo[ _depositor ].payout.add( payout ), vesting: terms.vestingTerm, lastBlock: block.number, pricePaid: priceInUSD }); // indexed events are emitted emit BondCreated( _amount, payout, block.number.add( terms.vestingTerm ), priceInUSD ); emit BondPriceChanged( bondPriceInUSD(), _bondPrice(), debtRatio() ); adjust(); // control variable is adjusted return payout; } /** * @notice redeem bond for user * @param _recipient address * @param _stake bool * @return uint */ function redeem( address _recipient, bool _stake ) external returns ( uint ) { Bond memory info = bondInfo[ _recipient ]; uint percentVested = percentVestedFor( _recipient ); // (blocks since last interaction / vesting term remaining) if ( percentVested >= 10000 ) { // if fully vested delete bondInfo[ _recipient ]; // delete user info emit BondRedeemed( _recipient, info.payout, 0 ); // emit bond data return stakeOrSend( _recipient, _stake, info.payout ); // pay user everything due } else { // if unfinished // calculate payout vested uint payout = info.payout.mul( percentVested ).div( 10000 ); // store updated deposit info bondInfo[ _recipient ] = Bond({ payout: info.payout.sub( payout ), vesting: info.vesting.sub( block.number.sub( info.lastBlock ) ), lastBlock: block.number, pricePaid: info.pricePaid }); emit BondRedeemed( _recipient, payout, bondInfo[ _recipient ].payout ); return stakeOrSend( _recipient, _stake, payout ); } } /* ======== INTERNAL HELPER FUNCTIONS ======== */ /** * @notice allow user to stake payout automatically * @param _stake bool * @param _amount uint * @return uint */ function stakeOrSend( address _recipient, bool _stake, uint _amount ) internal returns ( uint ) { if ( !_stake ) { // if user does not want to stake IERC20( bash ).transfer( _recipient, _amount ); // send payout } else { // if user wants to stake if ( useHelper ) { // use if staking warmup is 0 IERC20( bash ).approve( stakingHelper, _amount ); IStakingHelper( stakingHelper ).stake( _amount, _recipient ); } else { IERC20( bash ).approve( staking, _amount ); IStaking( staking ).stake( _amount, _recipient ); } } return _amount; } /** * @notice makes incremental adjustment to control variable */ function adjust() internal { uint blockCanAdjust = adjustment.lastBlock.add( adjustment.buffer ); if( adjustment.rate != 0 && block.number >= blockCanAdjust ) { uint initial = terms.controlVariable; if ( adjustment.add ) { terms.controlVariable = terms.controlVariable.add( adjustment.rate ); if ( terms.controlVariable >= adjustment.target ) { adjustment.rate = 0; } } else { terms.controlVariable = terms.controlVariable.sub( adjustment.rate ); if ( terms.controlVariable <= adjustment.target ) { adjustment.rate = 0; } } adjustment.lastBlock = block.number; emit ControlVariableAdjustment( initial, terms.controlVariable, adjustment.rate, adjustment.add ); } } /** * @notice reduce total debt */ function decayDebt() internal { totalDebt = totalDebt.sub( debtDecay() ); lastDecay = block.number; } /* ======== VIEW FUNCTIONS ======== */ /** * @notice determine maximum bond size * @return uint */ function maxPayout() public view returns ( uint ) { return IERC20( bash ).totalSupply().mul( terms.maxPayout ).div( 100000 ); } /** * @notice calculate interest due for new bond * @param _value uint * @return uint */ function payoutFor( uint _value ) public view returns ( uint ) { return FixedPoint.fraction( _value, bondPrice() ).decode112with18().div( 1e14 ); } /** * @notice calculate current bond premium * @return price_ uint */ function bondPrice() public view returns ( uint price_ ) { price_ = terms.controlVariable.mul( debtRatio() ).div( 1e5 ); if ( price_ < terms.minimumPrice ) { price_ = terms.minimumPrice; } } /** * @notice calculate current bond price and remove floor if above * @return price_ uint */ function _bondPrice() internal returns ( uint price_ ) { price_ = terms.controlVariable.mul( debtRatio() ).div( 1e5 ); if ( price_ < terms.minimumPrice ) { price_ = terms.minimumPrice; } else if ( terms.minimumPrice != 0 ) { terms.minimumPrice = 0; } } /** * @notice get asset price from chainlink */ function assetPrice() public view returns (int) { ( , int price, , , ) = priceFeed.latestRoundData(); return price; } /** * @notice converts bond price to DAI value * @return price_ uint */ function bondPriceInUSD() public view returns ( uint price_ ) { price_ = bondPrice().mul( uint( assetPrice() ) ).mul( 1e6 ); } /** * @notice calculate current ratio of debt to bash supply * @return debtRatio_ uint */ function debtRatio() public view returns ( uint debtRatio_ ) { uint supply = IERC20( bash ).totalSupply(); debtRatio_ = FixedPoint.fraction( currentDebt().mul( 1e9 ), supply ).decode112with18().div( 1e18 ); } /** * @notice debt ratio in same terms as reserve bonds * @return uint */ function standardizedDebtRatio() external view returns ( uint ) { return debtRatio().mul( uint( assetPrice() ) ).div( 1e8 ); // ETH feed is 8 decimals } /** * @notice calculate debt factoring in decay * @return uint */ function currentDebt() public view returns ( uint ) { return totalDebt.sub( debtDecay() ); } /** * @notice amount to decay total debt by * @return decay_ uint */ function debtDecay() public view returns ( uint decay_ ) { uint blocksSinceLast = block.number.sub( lastDecay ); decay_ = totalDebt.mul( blocksSinceLast ).div( terms.vestingTerm ); if ( decay_ > totalDebt ) { decay_ = totalDebt; } } /** * @notice calculate how far into vesting a depositor is * @param _depositor address * @return percentVested_ uint */ function percentVestedFor( address _depositor ) public view returns ( uint percentVested_ ) { Bond memory bond = bondInfo[ _depositor ]; uint blocksSinceLast = block.number.sub( bond.lastBlock ); uint vesting = bond.vesting; if ( vesting > 0 ) { percentVested_ = blocksSinceLast.mul( 10000 ).div( vesting ); } else { percentVested_ = 0; } } /** * @notice calculate amount of bash available for claim by depositor * @param _depositor address * @return pendingPayout_ uint */ function pendingPayoutFor( address _depositor ) external view returns ( uint pendingPayout_ ) { uint percentVested = percentVestedFor( _depositor ); uint payout = bondInfo[ _depositor ].payout; if ( percentVested >= 10000 ) { pendingPayout_ = payout; } else { pendingPayout_ = payout.mul( percentVested ).div( 10000 ); } } /* ======= AUXILLIARY ======= */ /** * @notice allow anyone to send lost tokens (excluding principle or bash) to the DAO * @return bool */ function recoverLostToken( address _token ) external returns ( bool ) { require( _token != bash ); require( _token != principle ); IERC20( _token ).safeTransfer( DAO, IERC20( _token ).balanceOf( address(this) ) ); return true; } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import {ITreasury} from "./interfaces/ITreasury.sol"; interface IOwnable { function policy() external view returns (address); function renounceManagement() external; function pushManagement( address newOwner_ ) external; function pullManagement() external; } contract Ownable is IOwnable { address internal _owner; address internal _newOwner; event OwnershipPushed(address indexed previousOwner, address indexed newOwner); event OwnershipPulled(address indexed previousOwner, address indexed newOwner); constructor () { _owner = msg.sender; emit OwnershipPushed( address(0), _owner ); } function policy() public view override returns (address) { return _owner; } modifier onlyPolicy() { require( _owner == msg.sender, "Ownable: caller is not the owner" ); _; } function renounceManagement() public virtual override onlyPolicy() { emit OwnershipPushed( _owner, address(0) ); _owner = address(0); } function pushManagement( address newOwner_ ) public virtual override onlyPolicy() { require( newOwner_ != address(0), "Ownable: new owner is the zero address"); emit OwnershipPushed( _owner, newOwner_ ); _newOwner = newOwner_; } function pullManagement() public virtual override { require( msg.sender == _newOwner, "Ownable: must be new owner to pull"); emit OwnershipPulled( _owner, _newOwner ); _owner = _newOwner; } } library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function sub32(uint32 a, uint32 b) internal pure returns (uint32) { return sub32(a, b, "SafeMath: subtraction overflow"); } function sub32(uint32 a, uint32 b, string memory errorMessage) internal pure returns (uint32) { require(b <= a, errorMessage); uint32 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; return c; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } function sqrrt(uint256 a) internal pure returns (uint c) { if (a > 3) { c = a; uint b = add( div( a, 2), 1 ); while (b < c) { c = b; b = div( add( div( a, b ), b), 2 ); } } else if (a != 0) { c = 1; } } } library Address { function isContract(address account) internal view returns (bool) { uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, errorMessage); } function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } function _functionCallWithValue( address target, bytes memory data, uint256 weiValue, string memory errorMessage ) private returns (bytes memory) { require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) private pure returns(bytes memory) { if (success) { return returndata; } else { if (returndata.length > 0) { assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } function addressToString(address _address) internal pure returns(string memory) { bytes32 _bytes = bytes32(uint256(_address)); bytes memory HEX = "0123456789abcdef"; bytes memory _addr = new bytes(42); _addr[0] = '0'; _addr[1] = 'x'; for(uint256 i = 0; i < 20; i++) { _addr[2+i*2] = HEX[uint8(_bytes[i + 12] >> 4)]; _addr[3+i*2] = HEX[uint8(_bytes[i + 12] & 0x0f)]; } return string(_addr); } } interface IERC20 { function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } abstract contract ERC20 is IERC20 { using SafeMath for uint256; // TODO comment actual hash value. bytes32 constant private ERC20TOKEN_ERC1820_INTERFACE_ID = keccak256( "ERC20Token" ); mapping (address => uint256) internal _balances; mapping (address => mapping (address => uint256)) internal _allowances; uint256 internal _totalSupply; string internal _name; string internal _symbol; uint8 internal _decimals; constructor (string memory name_, string memory symbol_, uint8 decimals_) { _name = name_; _symbol = symbol_; _decimals = decimals_; } function name() public view returns (string memory) { return _name; } function symbol() public view returns (string memory) { return _symbol; } function decimals() public view override returns (uint8) { return _decimals; } function totalSupply() public view override returns (uint256) { return _totalSupply; } function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(msg.sender, recipient, amount); return true; } function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(msg.sender, spender, amount); return true; } function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, msg.sender, _allowances[sender][msg.sender] .sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); return true; } function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][spender] .sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } function _mint(address account_, uint256 ammount_) internal virtual { require(account_ != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address( this ), account_, ammount_); _totalSupply = _totalSupply.add(ammount_); _balances[account_] = _balances[account_].add(ammount_); emit Transfer(address( this ), account_, ammount_); } function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal virtual { } } interface IERC2612Permit { function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function nonces(address owner) external view returns (uint256); } library Counters { using SafeMath for uint256; struct Counter { uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { counter._value += 1; } function decrement(Counter storage counter) internal { counter._value = counter._value.sub(1); } } abstract contract ERC20Permit is ERC20, IERC2612Permit { using Counters for Counters.Counter; mapping(address => Counters.Counter) private _nonces; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; bytes32 public DOMAIN_SEPARATOR; constructor() { uint256 chainID; assembly { chainID := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name())), keccak256(bytes("1")), // Version chainID, address(this) ) ); } function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual override { require(block.timestamp <= deadline, "Permit: expired deadline"); bytes32 hashStruct = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, _nonces[owner].current(), deadline)); bytes32 _hash = keccak256(abi.encodePacked(uint16(0x1901), DOMAIN_SEPARATOR, hashStruct)); address signer = ecrecover(_hash, v, r, s); require(signer != address(0) && signer == owner, "ZeroSwapPermit: Invalid signature"); _nonces[owner].increment(); _approve(owner, spender, amount); } function nonces(address owner) public view override returns (uint256) { return _nonces[owner].current(); } } library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeApprove(IERC20 token, address spender, uint256 value) internal { require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender) .sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function _callOptionalReturn(IERC20 token, bytes memory data) private { bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } library FullMath { function fullMul(uint256 x, uint256 y) private pure returns (uint256 l, uint256 h) { uint256 mm = mulmod(x, y, uint256(-1)); l = x * y; h = mm - l; if (mm < l) h -= 1; } function fullDiv( uint256 l, uint256 h, uint256 d ) private pure returns (uint256) { uint256 pow2 = d & -d; d /= pow2; l /= pow2; l += h * ((-pow2) / pow2 + 1); uint256 r = 1; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; return l * r; } function mulDiv( uint256 x, uint256 y, uint256 d ) internal pure returns (uint256) { (uint256 l, uint256 h) = fullMul(x, y); uint256 mm = mulmod(x, y, d); if (mm > l) h -= 1; l -= mm; require(h < d, 'FullMath::mulDiv: overflow'); return fullDiv(l, h, d); } } library FixedPoint { struct uq112x112 { uint224 _x; } struct uq144x112 { uint256 _x; } uint8 private constant RESOLUTION = 112; uint256 private constant Q112 = 0x10000000000000000000000000000; uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000; uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits) function decode(uq112x112 memory self) internal pure returns (uint112) { return uint112(self._x >> RESOLUTION); } function decode112with18(uq112x112 memory self) internal pure returns (uint) { return uint(self._x) / 5192296858534827; } function fraction(uint256 numerator, uint256 denominator) internal pure returns (uq112x112 memory) { require(denominator > 0, 'FixedPoint::fraction: division by zero'); if (numerator == 0) return FixedPoint.uq112x112(0); if (numerator <= uint144(-1)) { uint256 result = (numerator << RESOLUTION) / denominator; require(result <= uint224(-1), 'FixedPoint::fraction: overflow'); return uq112x112(uint224(result)); } else { uint256 result = FullMath.mulDiv(numerator, Q112, denominator); require(result <= uint224(-1), 'FixedPoint::fraction: overflow'); return uq112x112(uint224(result)); } } } interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); } interface IStaking { function stake( uint _amount, address _recipient ) external returns ( bool ); } interface IStakingHelper { function stake( uint _amount, address _recipient ) external; } interface IWETH9 is IERC20 { /// @notice Deposit ether to get wrapped ether function deposit() external payable; } contract SnowbankPriceFeedBondDepository is Ownable { using FixedPoint for *; using SafeERC20 for IERC20; using SafeMath for uint; using SafeMath for uint32; /* ======== EVENTS ======== */ event BondCreated( uint deposit, uint indexed payout, uint indexed expires, uint indexed priceInUSD ); event BondRedeemed( address indexed recipient, uint payout, uint remaining ); event BondPriceChanged( uint indexed priceInUSD, uint indexed internalPrice, uint indexed debtRatio ); event ControlVariableAdjustment( uint initialBCV, uint newBCV, uint adjustment, bool addition ); /* ======== STATE VARIABLES ======== */ address public immutable OHM; // token given as payment for bond address public immutable principle; // token used to create bond address public immutable treasury; // mints OHM when receives principle address public immutable DAO; // receives profit share from bond AggregatorV3Interface internal priceFeed; address public staking; // to auto-stake payout address public stakingHelper; // to stake and claim if no staking warmup bool public useHelper; Terms public terms; // stores terms for new bonds Adjust public adjustment; // stores adjustment to BCV data mapping( address => Bond ) public bondInfo; // stores bond information for depositors uint public totalDebt; // total value of outstanding bonds; used for pricing uint32 public lastDecay; // reference block for debt decay /* ======== STRUCTS ======== */ // Info for creating new bonds struct Terms { uint controlVariable; // scaling variable for price uint minimumPrice; // vs principle value. 4 decimals (1500 = 0.15) uint maxPayout; // in thousandths of a %. i.e. 500 = 0.5% uint maxDebt; // 9 decimal debt ratio, max % total supply created as debt uint32 vestingTerm; // in seconds } // Info for bond holder struct Bond { uint payout; // OHM remaining to be paid uint pricePaid; // In DAI, for front end viewing uint32 vesting; // Seconds left to vest uint32 lastTime; // Last interaction } // Info for incremental adjustments to control variable struct Adjust { bool add; // addition or subtraction uint rate; // increment uint target; // BCV when adjustment finished uint32 buffer; // minimum length (in seconds) between adjustments uint32 lastTime; // block when last adjustment made } /* ======== INITIALIZATION ======== */ constructor ( address _OHM, address _principle, address _treasury, address _DAO, address _feed ) { require( _OHM != address(0) ); OHM = _OHM; require( _principle != address(0) ); principle = _principle; require( _treasury != address(0) ); treasury = _treasury; require( _DAO != address(0) ); DAO = _DAO; require( _feed != address(0) ); priceFeed = AggregatorV3Interface( _feed ); } /** * @notice initializes bond parameters * @param _controlVariable uint * @param _vestingTerm uint * @param _minimumPrice uint * @param _maxPayout uint * @param _maxDebt uint * @param _initialDebt uint */ function initializeBondTerms( uint _controlVariable, uint _minimumPrice, uint _maxPayout, uint _maxDebt, uint _initialDebt, uint32 _vestingTerm ) external onlyPolicy() { require( currentDebt() == 0, "Debt must be 0 for initialization" ); terms = Terms ({ controlVariable: _controlVariable, vestingTerm: _vestingTerm, minimumPrice: _minimumPrice, maxPayout: _maxPayout, maxDebt: _maxDebt }); totalDebt = _initialDebt; lastDecay = uint32(block.timestamp); } /* ======== POLICY FUNCTIONS ======== */ enum PARAMETER { VESTING, PAYOUT, DEBT, MINPRICE } /** * @notice set parameters for new bonds * @param _parameter PARAMETER * @param _input uint */ function setBondTerms ( PARAMETER _parameter, uint _input ) external onlyPolicy() { if ( _parameter == PARAMETER.VESTING ) { // 0 require( _input >= 129600, "Vesting must be longer than 36 hours" ); terms.vestingTerm = uint32(_input); } else if ( _parameter == PARAMETER.PAYOUT ) { // 1 require( _input <= 1000, "Payout cannot be above 1 percent" ); terms.maxPayout = _input; } else if ( _parameter == PARAMETER.DEBT ) { // 2 terms.maxDebt = _input; } else if ( _parameter == PARAMETER.MINPRICE ) { // 3 terms.minimumPrice = _input; } } /** * @notice set control variable adjustment * @param _addition bool * @param _increment uint * @param _target uint * @param _buffer uint */ function setAdjustment ( bool _addition, uint _increment, uint _target, uint32 _buffer ) external onlyPolicy() { adjustment = Adjust({ add: _addition, rate: _increment, target: _target, buffer: _buffer, lastTime: uint32(block.timestamp) }); } /** * @notice set contract for auto stake * @param _staking address * @param _helper bool */ function setStaking( address _staking, bool _helper ) external onlyPolicy() { require( _staking != address(0) ); if ( _helper ) { useHelper = true; stakingHelper = _staking; } else { useHelper = false; staking = _staking; } } /* ======== USER FUNCTIONS ======== */ /** * @notice deposit bond * @param _amount uint * @param _maxPrice uint * @param _depositor address * @return uint */ function deposit( uint _amount, uint _maxPrice, address _depositor ) external payable returns ( uint ) { require( _depositor != address(0), "Invalid address" ); decayDebt(); require( totalDebt <= terms.maxDebt, "Max capacity reached" ); uint priceInUSD = bondPriceInUSD(); // Stored in bond info uint nativePrice = _bondPrice(); require( _maxPrice >= nativePrice, "Slippage limit: more than max price" ); // slippage protection uint value = ITreasury( treasury ).tokenValue( principle, _amount ); uint payout = payoutFor( value ); // payout to bonder is computed require( payout >= 10000000, "Bond too small" ); // must be > 0.01 OHM ( underflow protection ) require( payout <= maxPayout(), "Bond too large"); // size protection because there is no slippage /** asset carries risk and is not minted against asset transfered to treasury and rewards minted as payout */ if (address(this).balance >= _amount) { // pay with WETH9 IWETH9(principle).deposit{value: _amount}(); // wrap only what is needed to pay IWETH9(principle).transfer(treasury, _amount); } else { IERC20( principle ).safeTransferFrom( msg.sender, treasury, _amount ); } ITreasury( treasury ).mintRewards( address(this), payout ); // total debt is increased totalDebt = totalDebt.add( value ); // depositor info is stored bondInfo[ _depositor ] = Bond({ payout: bondInfo[ _depositor ].payout.add( payout ), vesting: terms.vestingTerm, lastTime: uint32(block.timestamp), pricePaid: priceInUSD }); // indexed events are emitted emit BondCreated( _amount, payout, block.timestamp.add( terms.vestingTerm ), priceInUSD ); emit BondPriceChanged( bondPriceInUSD(), _bondPrice(), debtRatio() ); adjust(); // control variable is adjusted refundETH(); //refund user if needed return payout; } /** * @notice redeem bond for user * @param _recipient address * @param _stake bool * @return uint */ function redeem( address _recipient, bool _stake ) external returns ( uint ) { Bond memory info = bondInfo[ _recipient ]; uint percentVested = percentVestedFor( _recipient ); // (blocks since last interaction / vesting term remaining) if ( percentVested >= 10000 ) { // if fully vested delete bondInfo[ _recipient ]; // delete user info emit BondRedeemed( _recipient, info.payout, 0 ); // emit bond data return stakeOrSend( _recipient, _stake, info.payout ); // pay user everything due } else { // if unfinished // calculate payout vested uint payout = info.payout.mul( percentVested ).div( 10000 ); // store updated deposit info bondInfo[ _recipient ] = Bond({ payout: info.payout.sub( payout ), vesting: info.vesting.sub32( uint32( block.timestamp ).sub32( info.lastTime ) ), lastTime: uint32( block.timestamp ), pricePaid: info.pricePaid }); emit BondRedeemed( _recipient, payout, bondInfo[ _recipient ].payout ); return stakeOrSend( _recipient, _stake, payout ); } } /* ======== INTERNAL HELPER FUNCTIONS ======== */ /** * @notice allow user to stake payout automatically * @param _stake bool * @param _amount uint * @return uint */ function stakeOrSend( address _recipient, bool _stake, uint _amount ) internal returns ( uint ) { if ( !_stake ) { // if user does not want to stake IERC20( OHM ).transfer( _recipient, _amount ); // send payout } else { // if user wants to stake if ( useHelper ) { // use if staking warmup is 0 IERC20( OHM ).approve( stakingHelper, _amount ); IStakingHelper( stakingHelper ).stake( _amount, _recipient ); } else { IERC20( OHM ).approve( staking, _amount ); IStaking( staking ).stake( _amount, _recipient ); } } return _amount; } /** * @notice makes incremental adjustment to control variable */ function adjust() internal { uint timeCanAdjust = adjustment.lastTime.add( adjustment.buffer ); if( adjustment.rate != 0 && block.timestamp >= timeCanAdjust ) { uint initial = terms.controlVariable; if ( adjustment.add ) { terms.controlVariable = terms.controlVariable.add( adjustment.rate ); if ( terms.controlVariable >= adjustment.target ) { adjustment.rate = 0; } } else { terms.controlVariable = terms.controlVariable.sub( adjustment.rate ); if ( terms.controlVariable <= adjustment.target ) { adjustment.rate = 0; } } adjustment.lastTime = uint32(block.timestamp); emit ControlVariableAdjustment( initial, terms.controlVariable, adjustment.rate, adjustment.add ); } } /** * @notice reduce total debt */ function decayDebt() internal { totalDebt = totalDebt.sub( debtDecay() ); lastDecay = uint32(block.timestamp); } /* ======== VIEW FUNCTIONS ======== */ /** * @notice determine maximum bond size * @return uint */ function maxPayout() public view returns ( uint ) { return IERC20( OHM ).totalSupply().mul( terms.maxPayout ).div( 100000 ); } /** * @notice calculate interest due for new bond * @param _value uint * @return uint */ function payoutFor( uint _value ) public view returns ( uint ) { return FixedPoint.fraction( _value, bondPrice() ).decode112with18().div( 1e14 ); } /** * @notice calculate current bond premium * @return price_ uint */ function bondPrice() public view returns ( uint price_ ) { price_ = terms.controlVariable.mul( debtRatio() ).div( 1e5 ); if ( price_ < terms.minimumPrice ) { price_ = terms.minimumPrice; } } /** * @notice calculate current bond price and remove floor if above * @return price_ uint */ function _bondPrice() internal returns ( uint price_ ) { price_ = terms.controlVariable.mul( debtRatio() ).div( 1e5 ); if ( price_ < terms.minimumPrice ) { price_ = terms.minimumPrice; } else if ( terms.minimumPrice != 0 ) { terms.minimumPrice = 0; } } /** * @notice get asset price from chainlink */ function assetPrice() public view returns (int) { ( , int price, , , ) = priceFeed.latestRoundData(); return price; } /** * @notice converts bond price to DAI value * @return price_ uint */ function bondPriceInUSD() public view returns ( uint price_ ) { price_ = bondPrice().mul( uint( assetPrice() ) ).mul( 1e6 ); } /** * @notice calculate current ratio of debt to OHM supply * @return debtRatio_ uint */ function debtRatio() public view returns ( uint debtRatio_ ) { uint supply = IERC20( OHM ).totalSupply(); debtRatio_ = FixedPoint.fraction( currentDebt().mul( 1e9 ), supply ).decode112with18().div( 1e18 ); } /** * @notice debt ratio in same terms as reserve bonds * @return uint */ function standardizedDebtRatio() external view returns ( uint ) { return debtRatio().mul( uint( assetPrice() ) ).div( 1e8 ); // ETH feed is 8 decimals } /** * @notice calculate debt factoring in decay * @return uint */ function currentDebt() public view returns ( uint ) { return totalDebt.sub( debtDecay() ); } /** * @notice amount to decay total debt by * @return decay_ uint */ function debtDecay() public view returns ( uint decay_ ) { uint32 timeSinceLast = uint32(block.timestamp).sub32( lastDecay ); decay_ = totalDebt.mul( timeSinceLast ).div( terms.vestingTerm ); if ( decay_ > totalDebt ) { decay_ = totalDebt; } } /** * @notice calculate how far into vesting a depositor is * @param _depositor address * @return percentVested_ uint */ function percentVestedFor( address _depositor ) public view returns ( uint percentVested_ ) { Bond memory bond = bondInfo[ _depositor ]; uint secondsSinceLast = uint32(block.timestamp).sub( bond.lastTime ); uint vesting = bond.vesting; if ( vesting > 0 ) { percentVested_ = secondsSinceLast.mul( 10000 ).div( vesting ); } else { percentVested_ = 0; } } /** * @notice calculate amount of OHM available for claim by depositor * @param _depositor address * @return pendingPayout_ uint */ function pendingPayoutFor( address _depositor ) external view returns ( uint pendingPayout_ ) { uint percentVested = percentVestedFor( _depositor ); uint payout = bondInfo[ _depositor ].payout; if ( percentVested >= 10000 ) { pendingPayout_ = payout; } else { pendingPayout_ = payout.mul( percentVested ).div( 10000 ); } } /* ======= AUXILLIARY ======= */ /** * @notice allow anyone to send lost tokens (excluding principle or OHM) to the DAO * @return bool */ function recoverLostToken( address _token ) external returns ( bool ) { require( _token != OHM ); require( _token != principle ); IERC20( _token ).safeTransfer( DAO, IERC20( _token ).balanceOf( address(this) ) ); return true; } function refundETH() internal { if (address(this).balance > 0) safeTransferETH(DAO, address(this).balance); } /// @notice Transfers ETH to the recipient address /// @dev Fails with `STE` /// @param to The destination of the transfer /// @param value The value to be transferred function safeTransferETH(address to, uint256 value) internal { (bool success, ) = to.call{value: value}(new bytes(0)); require(success, 'STE'); } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import {ITreasury} from "./interfaces/ITreasury.sol"; interface IOwnable { function policy() external view returns (address); function renounceManagement() external; function pushManagement(address newOwner_) external; function pullManagement() external; } contract Ownable is IOwnable { address internal _owner; address internal _newOwner; event OwnershipPushed( address indexed previousOwner, address indexed newOwner ); event OwnershipPulled( address indexed previousOwner, address indexed newOwner ); constructor() { _owner = msg.sender; emit OwnershipPushed(address(0), _owner); } function policy() public view override returns (address) { return _owner; } modifier onlyPolicy() { require(_owner == msg.sender, "Ownable: caller is not the owner"); _; } function renounceManagement() public virtual override onlyPolicy { emit OwnershipPushed(_owner, address(0)); _owner = address(0); } function pushManagement(address newOwner_) public virtual override onlyPolicy { require(newOwner_ != address(0), "Ownable: new owner is the zero address"); emit OwnershipPushed(_owner, newOwner_); _newOwner = newOwner_; } function pullManagement() public virtual override { require(msg.sender == _newOwner, "Ownable: must be new owner to pull"); emit OwnershipPulled(_owner, _newOwner); _owner = _newOwner; } } library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; return c; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } function sqrrt(uint256 a) internal pure returns (uint256 c) { if (a > 3) { c = a; uint256 b = add(div(a, 2), 1); while (b < c) { c = b; b = div(add(div(a, b), b), 2); } } else if (a != 0) { c = 1; } } } library Address { function isContract(address account) internal view returns (bool) { uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{value: amount}(""); require( success, "Address: unable to send value, recipient may have reverted" ); } function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, errorMessage); } function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue( target, data, value, "Address: low-level call with value failed" ); } function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require( address(this).balance >= value, "Address: insufficient balance for call" ); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{value: value}(data); return _verifyCallResult(success, returndata, errorMessage); } function _functionCallWithValue( address target, bytes memory data, uint256 weiValue, string memory errorMessage ) private returns (bytes memory) { require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{value: weiValue}( data ); if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall( target, data, "Address: low-level delegate call failed" ); } function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) private pure returns (bytes memory) { if (success) { return returndata; } else { if (returndata.length > 0) { assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } function addressToString(address _address) internal pure returns (string memory) { bytes32 _bytes = bytes32(uint256(_address)); bytes memory HEX = "0123456789abcdef"; bytes memory _addr = new bytes(42); _addr[0] = "0"; _addr[1] = "x"; for (uint256 i = 0; i < 20; i++) { _addr[2 + i * 2] = HEX[uint8(_bytes[i + 12] >> 4)]; _addr[3 + i * 2] = HEX[uint8(_bytes[i + 12] & 0x0f)]; } return string(_addr); } } interface IERC20 { function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } abstract contract ERC20 is IERC20 { using SafeMath for uint256; // TODO comment actual hash value. bytes32 private constant ERC20TOKEN_ERC1820_INTERFACE_ID = keccak256("ERC20Token"); mapping(address => uint256) internal _balances; mapping(address => mapping(address => uint256)) internal _allowances; uint256 internal _totalSupply; string internal _name; string internal _symbol; uint8 internal _decimals; constructor( string memory name_, string memory symbol_, uint8 decimals_ ) { _name = name_; _symbol = symbol_; _decimals = decimals_; } function name() public view returns (string memory) { return _name; } function symbol() public view returns (string memory) { return _symbol; } function decimals() public view override returns (uint8) { return _decimals; } function totalSupply() public view override returns (uint256) { return _totalSupply; } function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(msg.sender, recipient, amount); return true; } function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(msg.sender, spender, amount); return true; } function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve( sender, msg.sender, _allowances[sender][msg.sender].sub( amount, "ERC20: transfer amount exceeds allowance" ) ); return true; } function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve( msg.sender, spender, _allowances[msg.sender][spender].add(addedValue) ); return true; } function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve( msg.sender, spender, _allowances[msg.sender][spender].sub( subtractedValue, "ERC20: decreased allowance below zero" ) ); return true; } function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub( amount, "ERC20: transfer amount exceeds balance" ); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } function _mint(address account_, uint256 ammount_) internal virtual { require(account_ != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(this), account_, ammount_); _totalSupply = _totalSupply.add(ammount_); _balances[account_] = _balances[account_].add(ammount_); emit Transfer(address(this), account_, ammount_); } function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub( amount, "ERC20: burn amount exceeds balance" ); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal virtual {} } interface IERC2612Permit { function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function nonces(address owner) external view returns (uint256); } library Counters { using SafeMath for uint256; struct Counter { uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { counter._value += 1; } function decrement(Counter storage counter) internal { counter._value = counter._value.sub(1); } } abstract contract ERC20Permit is ERC20, IERC2612Permit { using Counters for Counters.Counter; mapping(address => Counters.Counter) private _nonces; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; bytes32 public DOMAIN_SEPARATOR; constructor() { uint256 chainID; assembly { chainID := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ), keccak256(bytes(name())), keccak256(bytes("1")), // Version chainID, address(this) ) ); } function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual override { require(block.timestamp <= deadline, "Permit: expired deadline"); bytes32 hashStruct = keccak256( abi.encode( PERMIT_TYPEHASH, owner, spender, amount, _nonces[owner].current(), deadline ) ); bytes32 _hash = keccak256( abi.encodePacked(uint16(0x1901), DOMAIN_SEPARATOR, hashStruct) ); address signer = ecrecover(_hash, v, r, s); require( signer != address(0) && signer == owner, "ZeroSwapPermit: Invalid signature" ); _nonces[owner].increment(); _approve(owner, spender, amount); } function nonces(address owner) public view override returns (uint256) { return _nonces[owner].current(); } } library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transfer.selector, to, value) ); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } function safeApprove( IERC20 token, address spender, uint256 value ) internal { require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, value) ); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance) ); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).sub( value, "SafeERC20: decreased allowance below zero" ); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance) ); } function _callOptionalReturn(IERC20 token, bytes memory data) private { bytes memory returndata = address(token).functionCall( data, "SafeERC20: low-level call failed" ); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require( abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed" ); } } } library FullMath { function fullMul(uint256 x, uint256 y) private pure returns (uint256 l, uint256 h) { uint256 mm = mulmod(x, y, uint256(-1)); l = x * y; h = mm - l; if (mm < l) h -= 1; } function fullDiv( uint256 l, uint256 h, uint256 d ) private pure returns (uint256) { uint256 pow2 = d & -d; d /= pow2; l /= pow2; l += h * ((-pow2) / pow2 + 1); uint256 r = 1; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; return l * r; } function mulDiv( uint256 x, uint256 y, uint256 d ) internal pure returns (uint256) { (uint256 l, uint256 h) = fullMul(x, y); uint256 mm = mulmod(x, y, d); if (mm > l) h -= 1; l -= mm; require(h < d, "FullMath::mulDiv: overflow"); return fullDiv(l, h, d); } } library FixedPoint { struct uq112x112 { uint224 _x; } struct uq144x112 { uint256 _x; } uint8 private constant RESOLUTION = 112; uint256 private constant Q112 = 0x10000000000000000000000000000; uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000; uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits) function decode(uq112x112 memory self) internal pure returns (uint112) { return uint112(self._x >> RESOLUTION); } function decode112with18(uq112x112 memory self) internal pure returns (uint256) { return uint256(self._x) / 5192296858534827; } function fraction(uint256 numerator, uint256 denominator) internal pure returns (uq112x112 memory) { require(denominator > 0, "FixedPoint::fraction: division by zero"); if (numerator == 0) return FixedPoint.uq112x112(0); if (numerator <= uint144(-1)) { uint256 result = (numerator << RESOLUTION) / denominator; require(result <= uint224(-1), "FixedPoint::fraction: overflow"); return uq112x112(uint224(result)); } else { uint256 result = FullMath.mulDiv(numerator, Q112, denominator); require(result <= uint224(-1), "FixedPoint::fraction: overflow"); return uq112x112(uint224(result)); } } } interface IBondCalculator { function valuation(address _LP, uint256 _amount) external view returns (uint256); function markdown(address _LP) external view returns (uint256); } interface IStaking { function stake(uint256 _amount, address _recipient) external returns (bool); } interface IStakingHelper { function stake(uint256 _amount, address _recipient) external; } contract MockOlympusBondDepository is Ownable { using FixedPoint for *; using SafeERC20 for IERC20; using SafeMath for uint256; /* ======== EVENTS ======== */ event BondCreated( uint256 deposit, uint256 indexed payout, uint256 indexed expires, uint256 indexed priceInUSD ); event BondRedeemed( address indexed recipient, uint256 payout, uint256 remaining ); event BondPriceChanged( uint256 indexed priceInUSD, uint256 indexed internalPrice, uint256 indexed debtRatio ); event ControlVariableAdjustment( uint256 initialBCV, uint256 newBCV, uint256 adjustment, bool addition ); /* ======== STATE VARIABLES ======== */ address public immutable OHM; // token given as payment for bond address public immutable principle; // token used to create bond address public immutable treasury; // mints OHM when receives principle address public immutable DAO; // receives profit share from bond bool public immutable isLiquidityBond; // LP and Reserve bonds are treated slightly different address public immutable bondCalculator; // calculates value of LP tokens address public staking; // to auto-stake payout address public stakingHelper; // to stake and claim if no staking warmup bool public useHelper; Terms public terms; // stores terms for new bonds Adjust public adjustment; // stores adjustment to BCV data mapping(address => Bond) public bondInfo; // stores bond information for depositors uint256 public totalDebt; // total value of outstanding bonds; used for pricing uint256 public lastDecay; // reference block for debt decay /* ======== STRUCTS ======== */ // Info for creating new bonds struct Terms { uint256 controlVariable; // scaling variable for price uint256 vestingTerm; // in blocks uint256 minimumPrice; // vs principle value uint256 maxPayout; // in thousandths of a %. i.e. 500 = 0.5% uint256 fee; // as % of bond payout, in hundreths. ( 500 = 5% = 0.05 for every 1 paid) uint256 maxDebt; // 9 decimal debt ratio, max % total supply created as debt } // Info for bond holder struct Bond { uint256 payout; // OHM remaining to be paid uint256 vesting; // Blocks left to vest uint256 lastBlock; // Last interaction uint256 pricePaid; // In DAI, for front end viewing } // Info for incremental adjustments to control variable struct Adjust { bool add; // addition or subtraction uint256 rate; // increment uint256 target; // BCV when adjustment finished uint256 buffer; // minimum length (in blocks) between adjustments uint256 lastBlock; // block when last adjustment made } /* ======== INITIALIZATION ======== */ constructor( address _OHM, address _principle, address _treasury, address _DAO, address _bondCalculator ) { require(_OHM != address(0)); OHM = _OHM; require(_principle != address(0)); principle = _principle; require(_treasury != address(0)); treasury = _treasury; require(_DAO != address(0)); DAO = _DAO; // bondCalculator should be address(0) if not LP bond bondCalculator = _bondCalculator; isLiquidityBond = (_bondCalculator != address(0)); } /** * @notice initializes bond parameters * @param _controlVariable uint * @param _vestingTerm uint * @param _minimumPrice uint * @param _maxPayout uint * @param _fee uint * @param _maxDebt uint * @param _initialDebt uint */ function initializeBondTerms( uint256 _controlVariable, uint256 _vestingTerm, uint256 _minimumPrice, uint256 _maxPayout, uint256 _fee, uint256 _maxDebt, uint256 _initialDebt ) external onlyPolicy { require(terms.controlVariable == 0, "Bonds must be initialized from 0"); terms = Terms({ controlVariable: _controlVariable, vestingTerm: _vestingTerm, minimumPrice: _minimumPrice, maxPayout: _maxPayout, fee: _fee, maxDebt: _maxDebt }); totalDebt = _initialDebt; lastDecay = block.number; } /* ======== POLICY FUNCTIONS ======== */ enum PARAMETER { VESTING, PAYOUT, FEE, DEBT } /** * @notice set parameters for new bonds * @param _parameter PARAMETER * @param _input uint */ function setBondTerms(PARAMETER _parameter, uint256 _input) external onlyPolicy { if (_parameter == PARAMETER.VESTING) { // 0 require(_input >= 10000, "Vesting must be longer than 36 hours"); terms.vestingTerm = _input; } else if (_parameter == PARAMETER.PAYOUT) { // 1 require(_input <= 1000, "Payout cannot be above 1 percent"); terms.maxPayout = _input; } else if (_parameter == PARAMETER.FEE) { // 2 require(_input <= 10000, "DAO fee cannot exceed payout"); terms.fee = _input; } else if (_parameter == PARAMETER.DEBT) { // 3 terms.maxDebt = _input; } } /** * @notice set control variable adjustment * @param _addition bool * @param _increment uint * @param _target uint * @param _buffer uint */ function setAdjustment( bool _addition, uint256 _increment, uint256 _target, uint256 _buffer ) external onlyPolicy { require( _increment <= terms.controlVariable.mul(25).div(1000), "Increment too large" ); adjustment = Adjust({ add: _addition, rate: _increment, target: _target, buffer: _buffer, lastBlock: block.number }); } /** * @notice set contract for auto stake * @param _staking address * @param _helper bool */ function setStaking(address _staking, bool _helper) external onlyPolicy { require(_staking != address(0)); if (_helper) { useHelper = true; stakingHelper = _staking; } else { useHelper = false; staking = _staking; } } /* ======== USER FUNCTIONS ======== */ /** * @notice deposit bond * @param _amount uint * @param _maxPrice uint * @param _depositor address * @return uint */ function deposit( uint256 _amount, uint256 _maxPrice, address _depositor ) external returns (uint256) { require(_depositor != address(0), "Invalid address"); decayDebt(); require(totalDebt <= terms.maxDebt, "Max capacity reached"); uint256 priceInUSD = bondPriceInUSD(); // Stored in bond info uint256 nativePrice = _bondPrice(); require(_maxPrice >= nativePrice, "Slippage limit: more than max price"); // slippage protection uint256 value = ITreasury(treasury).tokenValue(principle, _amount); uint256 payout = payoutFor(value); // payout to bonder is computed require(payout >= 10000000, "Bond too small"); // must be > 0.01 OHM ( underflow protection ) require(payout <= maxPayout(), "Bond too large"); // size protection because there is no slippage // profits are calculated uint256 fee = payout.mul(terms.fee).div(10000); uint256 profit = value.sub(payout).sub(fee); /** principle is transferred in approved and deposited into the treasury, returning (_amount - profit) OHM */ IERC20(principle).safeTransferFrom(msg.sender, address(this), _amount); IERC20(principle).approve(address(treasury), _amount); ITreasury(treasury).deposit(_amount, principle, profit); if (fee != 0) { // fee is transferred to dao IERC20(OHM).safeTransfer(DAO, fee); } // total debt is increased totalDebt = totalDebt.add(value); // depositor info is stored bondInfo[_depositor] = Bond({ payout: bondInfo[_depositor].payout.add(payout), vesting: terms.vestingTerm, lastBlock: block.number, pricePaid: priceInUSD }); // indexed events are emitted emit BondCreated( _amount, payout, block.number.add(terms.vestingTerm), priceInUSD ); emit BondPriceChanged(bondPriceInUSD(), _bondPrice(), debtRatio()); adjust(); // control variable is adjusted return payout; } /** * @notice redeem bond for user * @param _recipient address * @param _stake bool * @return uint */ function redeem(address _recipient, bool _stake) external returns (uint256) { Bond memory info = bondInfo[_recipient]; uint256 percentVested = percentVestedFor(_recipient); // (blocks since last interaction / vesting term remaining) if (percentVested >= 10000) { // if fully vested delete bondInfo[_recipient]; // delete user info emit BondRedeemed(_recipient, info.payout, 0); // emit bond data return stakeOrSend(_recipient, _stake, info.payout); // pay user everything due } else { // if unfinished // calculate payout vested uint256 payout = info.payout.mul(percentVested).div(10000); // store updated deposit info bondInfo[_recipient] = Bond({ payout: info.payout.sub(payout), vesting: info.vesting.sub(block.number.sub(info.lastBlock)), lastBlock: block.number, pricePaid: info.pricePaid }); emit BondRedeemed(_recipient, payout, bondInfo[_recipient].payout); return stakeOrSend(_recipient, _stake, payout); } } /* ======== INTERNAL HELPER FUNCTIONS ======== */ /** * @notice allow user to stake payout automatically * @param _stake bool * @param _amount uint * @return uint */ function stakeOrSend( address _recipient, bool _stake, uint256 _amount ) internal returns (uint256) { if (!_stake) { // if user does not want to stake IERC20(OHM).transfer(_recipient, _amount); // send payout } else { // if user wants to stake if (useHelper) { // use if staking warmup is 0 IERC20(OHM).approve(stakingHelper, _amount); IStakingHelper(stakingHelper).stake(_amount, _recipient); } else { IERC20(OHM).approve(staking, _amount); IStaking(staking).stake(_amount, _recipient); } } return _amount; } /** * @notice makes incremental adjustment to control variable */ function adjust() internal { uint256 blockCanAdjust = adjustment.lastBlock.add(adjustment.buffer); if (adjustment.rate != 0 && block.number >= blockCanAdjust) { uint256 initial = terms.controlVariable; if (adjustment.add) { terms.controlVariable = terms.controlVariable.add(adjustment.rate); if (terms.controlVariable >= adjustment.target) { adjustment.rate = 0; } } else { terms.controlVariable = terms.controlVariable.sub(adjustment.rate); if (terms.controlVariable <= adjustment.target) { adjustment.rate = 0; } } adjustment.lastBlock = block.number; emit ControlVariableAdjustment( initial, terms.controlVariable, adjustment.rate, adjustment.add ); } } /** * @notice reduce total debt */ function decayDebt() internal { totalDebt = totalDebt.sub(debtDecay()); lastDecay = block.number; } /* ======== VIEW FUNCTIONS ======== */ /** * @notice determine maximum bond size * @return uint */ function maxPayout() public view returns (uint256) { return IERC20(OHM).totalSupply().mul(terms.maxPayout).div(100000); } /** * @notice calculate interest due for new bond * @param _value uint * @return uint */ function payoutFor(uint256 _value) public view returns (uint256) { return FixedPoint.fraction(_value, bondPrice()).decode112with18().div(1e16); } /** * @notice calculate current bond premium * @return price_ uint */ function bondPrice() public view returns (uint256 price_) { price_ = terms.controlVariable.mul(debtRatio()).add(1000000000).div(1e7); if (price_ < terms.minimumPrice) { price_ = terms.minimumPrice; } } /** * @notice calculate current bond price and remove floor if above * @return price_ uint */ function _bondPrice() internal returns (uint256 price_) { price_ = terms.controlVariable.mul(debtRatio()).add(1000000000).div(1e7); if (price_ < terms.minimumPrice) { price_ = terms.minimumPrice; } else if (terms.minimumPrice != 0) { terms.minimumPrice = 0; } } /** * @notice converts bond price to DAI value * @return price_ uint */ function bondPriceInUSD() public view returns (uint256 price_) { if (isLiquidityBond) { price_ = bondPrice() .mul(IBondCalculator(bondCalculator).markdown(principle)) .div(100); } else { price_ = bondPrice().mul(10**IERC20(principle).decimals()).div(100); } } /** * @notice calculate current ratio of debt to OHM supply * @return debtRatio_ uint */ function debtRatio() public view returns (uint256 debtRatio_) { uint256 supply = IERC20(OHM).totalSupply(); debtRatio_ = FixedPoint .fraction(currentDebt().mul(1e9), supply) .decode112with18() .div(1e18); } /** * @notice debt ratio in same terms for reserve or liquidity bonds * @return uint */ function standardizedDebtRatio() external view returns (uint256) { if (isLiquidityBond) { return debtRatio() .mul(IBondCalculator(bondCalculator).markdown(principle)) .div(1e9); } else { return debtRatio(); } } /** * @notice calculate debt factoring in decay * @return uint */ function currentDebt() public view returns (uint256) { return totalDebt.sub(debtDecay()); } /** * @notice amount to decay total debt by * @return decay_ uint */ function debtDecay() public view returns (uint256 decay_) { uint256 blocksSinceLast = block.number.sub(lastDecay); decay_ = totalDebt.mul(blocksSinceLast).div(terms.vestingTerm); if (decay_ > totalDebt) { decay_ = totalDebt; } } /** * @notice calculate how far into vesting a depositor is * @param _depositor address * @return percentVested_ uint */ function percentVestedFor(address _depositor) public view returns (uint256 percentVested_) { Bond memory bond = bondInfo[_depositor]; uint256 blocksSinceLast = block.number.sub(bond.lastBlock); uint256 vesting = bond.vesting; if (vesting > 0) { percentVested_ = blocksSinceLast.mul(10000).div(vesting); } else { percentVested_ = 0; } } /** * @notice calculate amount of OHM available for claim by depositor * @param _depositor address * @return pendingPayout_ uint */ function pendingPayoutFor(address _depositor) external view returns (uint256 pendingPayout_) { uint256 percentVested = percentVestedFor(_depositor); uint256 payout = bondInfo[_depositor].payout; if (percentVested >= 10000) { pendingPayout_ = payout; } else { pendingPayout_ = payout.mul(percentVested).div(10000); } } /* ======= AUXILLIARY ======= */ /** * @notice allow anyone to send lost tokens (excluding principle or OHM) to the DAO * @return bool */ function recoverLostToken(address _token) external returns (bool) { require(_token != OHM); require(_token != principle); IERC20(_token).safeTransfer(DAO, IERC20(_token).balanceOf(address(this))); return true; } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import { ITreasury } from "./interfaces/ITreasury.sol"; interface IOwnable { function policy() external view returns (address); function renounceManagement() external; function pushManagement(address newOwner_) external; function pullManagement() external; } contract Ownable is IOwnable { address internal _owner; address internal _newOwner; event OwnershipPushed( address indexed previousOwner, address indexed newOwner ); event OwnershipPulled( address indexed previousOwner, address indexed newOwner ); constructor() { _owner = msg.sender; emit OwnershipPushed(address(0), _owner); } function policy() public view override returns (address) { return _owner; } modifier onlyPolicy() { require(_owner == msg.sender, "Ownable: caller is not the owner"); _; } function renounceManagement() public virtual override onlyPolicy { emit OwnershipPushed(_owner, address(0)); _owner = address(0); } function pushManagement(address newOwner_) public virtual override onlyPolicy { require( newOwner_ != address(0), "Ownable: new owner is the zero address" ); emit OwnershipPushed(_owner, newOwner_); _newOwner = newOwner_; } function pullManagement() public virtual override { require(msg.sender == _newOwner, "Ownable: must be new owner to pull"); emit OwnershipPulled(_owner, _newOwner); _owner = _newOwner; } } library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function sub32(uint32 a, uint32 b) internal pure returns (uint32) { return sub32(a, b, "SafeMath: subtraction overflow"); } function sub32( uint32 a, uint32 b, string memory errorMessage ) internal pure returns (uint32) { require(b <= a, errorMessage); uint32 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; return c; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } function sqrrt(uint256 a) internal pure returns (uint256 c) { if (a > 3) { c = a; uint256 b = add(div(a, 2), 1); while (b < c) { c = b; b = div(add(div(a, b), b), 2); } } else if (a != 0) { c = 1; } } } library Address { function isContract(address account) internal view returns (bool) { uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } function sendValue(address payable recipient, uint256 amount) internal { require( address(this).balance >= amount, "Address: insufficient balance" ); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{value: amount}(""); require( success, "Address: unable to send value, recipient may have reverted" ); } function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, errorMessage); } function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue( target, data, value, "Address: low-level call with value failed" ); } function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require( address(this).balance >= value, "Address: insufficient balance for call" ); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{value: value}( data ); return _verifyCallResult(success, returndata, errorMessage); } function _functionCallWithValue( address target, bytes memory data, uint256 weiValue, string memory errorMessage ) private returns (bytes memory) { require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{value: weiValue}( data ); if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall( target, data, "Address: low-level static call failed" ); } function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall( target, data, "Address: low-level delegate call failed" ); } function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) private pure returns (bytes memory) { if (success) { return returndata; } else { if (returndata.length > 0) { assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } function addressToString(address _address) internal pure returns (string memory) { bytes32 _bytes = bytes32(uint256(_address)); bytes memory HEX = "0123456789abcdef"; bytes memory _addr = new bytes(42); _addr[0] = "0"; _addr[1] = "x"; for (uint256 i = 0; i < 20; i++) { _addr[2 + i * 2] = HEX[uint8(_bytes[i + 12] >> 4)]; _addr[3 + i * 2] = HEX[uint8(_bytes[i + 12] & 0x0f)]; } return string(_addr); } } interface IERC20 { function decimals() external view returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval( address indexed owner, address indexed spender, uint256 value ); } abstract contract ERC20 is IERC20 { using SafeMath for uint256; // TODO comment actual hash value. bytes32 private constant ERC20TOKEN_ERC1820_INTERFACE_ID = keccak256("ERC20Token"); mapping(address => uint256) internal _balances; mapping(address => mapping(address => uint256)) internal _allowances; uint256 internal _totalSupply; string internal _name; string internal _symbol; uint8 internal _decimals; constructor( string memory name_, string memory symbol_, uint8 decimals_ ) { _name = name_; _symbol = symbol_; _decimals = decimals_; } function name() public view returns (string memory) { return _name; } function symbol() public view returns (string memory) { return _symbol; } function decimals() public view override returns (uint8) { return _decimals; } function totalSupply() public view override returns (uint256) { return _totalSupply; } function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(msg.sender, recipient, amount); return true; } function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(msg.sender, spender, amount); return true; } function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve( sender, msg.sender, _allowances[sender][msg.sender].sub( amount, "ERC20: transfer amount exceeds allowance" ) ); return true; } function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve( msg.sender, spender, _allowances[msg.sender][spender].add(addedValue) ); return true; } function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve( msg.sender, spender, _allowances[msg.sender][spender].sub( subtractedValue, "ERC20: decreased allowance below zero" ) ); return true; } function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub( amount, "ERC20: transfer amount exceeds balance" ); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } function _mint(address account_, uint256 ammount_) internal virtual { require(account_ != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(this), account_, ammount_); _totalSupply = _totalSupply.add(ammount_); _balances[account_] = _balances[account_].add(ammount_); emit Transfer(address(this), account_, ammount_); } function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub( amount, "ERC20: burn amount exceeds balance" ); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal virtual {} } interface IERC2612Permit { function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; function nonces(address owner) external view returns (uint256); } library Counters { using SafeMath for uint256; struct Counter { uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { counter._value += 1; } function decrement(Counter storage counter) internal { counter._value = counter._value.sub(1); } } abstract contract ERC20Permit is ERC20, IERC2612Permit { using Counters for Counters.Counter; mapping(address => Counters.Counter) private _nonces; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; bytes32 public DOMAIN_SEPARATOR; constructor() { uint256 chainID; assembly { chainID := chainid() } DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ), keccak256(bytes(name())), keccak256(bytes("1")), // Version chainID, address(this) ) ); } function permit( address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual override { require(block.timestamp <= deadline, "Permit: expired deadline"); bytes32 hashStruct = keccak256( abi.encode( PERMIT_TYPEHASH, owner, spender, amount, _nonces[owner].current(), deadline ) ); bytes32 _hash = keccak256( abi.encodePacked(uint16(0x1901), DOMAIN_SEPARATOR, hashStruct) ); address signer = ecrecover(_hash, v, r, s); require( signer != address(0) && signer == owner, "ZeroSwapPermit: Invalid signature" ); _nonces[owner].increment(); _approve(owner, spender, amount); } function nonces(address owner) public view override returns (uint256) { return _nonces[owner].current(); } } library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transfer.selector, to, value) ); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn( token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value) ); } function safeApprove( IERC20 token, address spender, uint256 value ) internal { require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn( token, abi.encodeWithSelector(token.approve.selector, spender, value) ); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).add( value ); _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).sub( value, "SafeERC20: decreased allowance below zero" ); _callOptionalReturn( token, abi.encodeWithSelector( token.approve.selector, spender, newAllowance ) ); } function _callOptionalReturn(IERC20 token, bytes memory data) private { bytes memory returndata = address(token).functionCall( data, "SafeERC20: low-level call failed" ); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require( abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed" ); } } } library FullMath { function fullMul(uint256 x, uint256 y) private pure returns (uint256 l, uint256 h) { uint256 mm = mulmod(x, y, uint256(-1)); l = x * y; h = mm - l; if (mm < l) h -= 1; } function fullDiv( uint256 l, uint256 h, uint256 d ) private pure returns (uint256) { uint256 pow2 = d & -d; d /= pow2; l /= pow2; l += h * ((-pow2) / pow2 + 1); uint256 r = 1; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; return l * r; } function mulDiv( uint256 x, uint256 y, uint256 d ) internal pure returns (uint256) { (uint256 l, uint256 h) = fullMul(x, y); uint256 mm = mulmod(x, y, d); if (mm > l) h -= 1; l -= mm; require(h < d, "FullMath::mulDiv: overflow"); return fullDiv(l, h, d); } } library FixedPoint { struct uq112x112 { uint224 _x; } struct uq144x112 { uint256 _x; } uint8 private constant RESOLUTION = 112; uint256 private constant Q112 = 0x10000000000000000000000000000; uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000; uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits) function decode(uq112x112 memory self) internal pure returns (uint112) { return uint112(self._x >> RESOLUTION); } function decode112with18(uq112x112 memory self) internal pure returns (uint256) { return uint256(self._x) / 5192296858534827; } function fraction(uint256 numerator, uint256 denominator) internal pure returns (uq112x112 memory) { require(denominator > 0, "FixedPoint::fraction: division by zero"); if (numerator == 0) return FixedPoint.uq112x112(0); if (numerator <= uint144(-1)) { uint256 result = (numerator << RESOLUTION) / denominator; require(result <= uint224(-1), "FixedPoint::fraction: overflow"); return uq112x112(uint224(result)); } else { uint256 result = FullMath.mulDiv(numerator, Q112, denominator); require(result <= uint224(-1), "FixedPoint::fraction: overflow"); return uq112x112(uint224(result)); } } } interface IBondCalculator { function valuation(address _LP, uint256 _amount) external view returns (uint256); function markdown(address _LP) external view returns (uint256); } interface IStaking { function stake(uint256 _amount, address _recipient) external returns (bool); } interface IStakingHelper { function stake(uint256 _amount, address _recipient) external; } contract atbashBondDepository is Ownable { using FixedPoint for *; using SafeERC20 for IERC20; using SafeMath for uint256; using SafeMath for uint32; /* ======== EVENTS ======== */ event BondCreated( uint256 deposit, uint256 indexed payout, uint256 indexed expires, uint256 indexed priceInUSD ); event BondRedeemed( address indexed recipient, uint256 payout, uint256 remaining ); event BondPriceChanged( uint256 indexed priceInUSD, uint256 indexed internalPrice, uint256 indexed debtRatio ); event ControlVariableAdjustment( uint256 initialBCV, uint256 newBCV, uint256 adjustment, bool addition ); /* ======== STATE VARIABLES ======== */ address public immutable Bash; // token given as payment for bond address public immutable principle; // token used to create bond address public immutable treasury; // mints bash when receives principle address public immutable DAO; // receives profit share from bond bool public immutable isLiquidityBond; // LP and Reserve bonds are treated slightly different address public immutable bondCalculator; // calculates value of LP tokens address public staking; // to auto-stake payout address public stakingHelper; // to stake and claim if no staking warmup bool public useHelper; Terms public terms; // stores terms for new bonds Adjust public adjustment; // stores adjustment to BCV data mapping(address => Bond) public bondInfo; // stores bond information for depositors uint256 public totalDebt; // total value of outstanding bonds; used for pricing uint32 public lastDecay; // reference time for debt decay /* ======== STRUCTS ======== */ // Info for creating new bonds struct Terms { uint256 controlVariable; // scaling variable for price uint256 minimumPrice; // vs principle value uint256 maxPayout; // in thousandths of a %. i.e. 500 = 0.5% uint256 fee; // as % of bond payout, in hundreths. ( 500 = 5% = 0.05 for every 1 paid) uint256 maxDebt; // 9 decimal debt ratio, max % total supply created as debt uint32 vestingTerm; // in seconds } // Info for bond holder struct Bond { uint256 payout; // bash remaining to be paid uint256 pricePaid; // In DAI, for front end viewing uint32 lastTime; // Last interaction uint32 vesting; // Seconds left to vest } // Info for incremental adjustments to control variable struct Adjust { bool add; // addition or subtraction uint256 rate; // increment uint256 target; // BCV when adjustment finished uint32 buffer; // minimum length (in seconds) between adjustments uint32 lastTime; // time when last adjustment made } /* ======== INITIALIZATION ======== */ constructor( address _bash, address _principle, // dai, bashdai address _treasury, address _DAO, address _bondCalculator ) { require(_bash != address(0)); Bash = _bash; require(_principle != address(0)); principle = _principle; require(_treasury != address(0)); treasury = _treasury; require(_DAO != address(0)); DAO = _DAO; // bondCalculator should be address(0) if not LP bond bondCalculator = _bondCalculator; isLiquidityBond = (_bondCalculator != address(0)); } /** * @notice initializes bond parameters * @param _controlVariable uint * @param _vestingTerm uint32 * @param _minimumPrice uint * @param _maxPayout uint * @param _fee uint * @param _maxDebt uint * @param _initialDebt uint */ function initializeBondTerms( uint256 _controlVariable, uint256 _minimumPrice, uint256 _maxPayout, uint256 _fee, uint256 _maxDebt, uint256 _initialDebt, uint32 _vestingTerm ) external onlyPolicy { require(terms.controlVariable == 0, "Bonds must be initialized from 0"); terms = Terms({ controlVariable: _controlVariable, minimumPrice: _minimumPrice, maxPayout: _maxPayout, fee: _fee, maxDebt: _maxDebt, vestingTerm: _vestingTerm }); totalDebt = _initialDebt; lastDecay = uint32(block.timestamp); } /* ======== POLICY FUNCTIONS ======== */ enum PARAMETER { VESTING, PAYOUT, FEE, DEBT, MINPRICE } /** * @notice set parameters for new bonds * @param _parameter PARAMETER * @param _input uint */ function setBondTerms(PARAMETER _parameter, uint256 _input) external onlyPolicy { if (_parameter == PARAMETER.VESTING) { // 0 require(_input >= 129600, "Vesting must be longer than 36 hours"); terms.vestingTerm = uint32(_input); } else if (_parameter == PARAMETER.PAYOUT) { // 1 require(_input <= 1000, "Payout cannot be above 1 percent"); terms.maxPayout = _input; } else if (_parameter == PARAMETER.FEE) { // 2 require(_input <= 10000, "DAO fee cannot exceed payout"); terms.fee = _input; } else if (_parameter == PARAMETER.DEBT) { // 3 terms.maxDebt = _input; } else if (_parameter == PARAMETER.MINPRICE) { // 4 terms.minimumPrice = _input; } } /** * @notice set control variable adjustment * @param _addition bool * @param _increment uint * @param _target uint * @param _buffer uint */ function setAdjustment( bool _addition, uint256 _increment, uint256 _target, uint32 _buffer ) external onlyPolicy { adjustment = Adjust({ add: _addition, rate: _increment, target: _target, buffer: _buffer, lastTime: uint32(block.timestamp) }); } /** * @notice set contract for auto stake * @param _staking address * @param _helper bool */ function setStaking(address _staking, bool _helper) external onlyPolicy { require(_staking != address(0)); if (_helper) { useHelper = true; stakingHelper = _staking; } else { useHelper = false; staking = _staking; } } /* ======== USER FUNCTIONS ======== */ /** * @notice deposit bond * @param _amount uint * @param _maxPrice uint * @param _depositor address * @return uint */ function deposit( uint256 _amount, uint256 _maxPrice, address _depositor ) external returns (uint256) { require(_depositor != address(0), "Invalid address"); decayDebt(); require(totalDebt <= terms.maxDebt, "Max capacity reached"); uint256 priceInUSD = bondPriceInUSD(); // Stored in bond info uint256 nativePrice = _bondPrice(); require( _maxPrice >= nativePrice, "Slippage limit: more than max price" ); // slippage protection uint256 value = ITreasury(treasury).tokenValue(principle, _amount); // valuation of asset in BASH uint256 payout = payoutFor(value); // payout to bonder is computed require(payout >= 10000000, "Bond too small"); // must be > 0.01 BASH ( underflow protection ) require(payout <= maxPayout(), "Bond too large"); // size protection because there is no slippage // profits are calculated uint256 fee = payout.mul(terms.fee).div(10000); uint256 profit = value.sub(payout).sub(fee); /** principle is transferred in approved and deposited into the treasury, returning (_amount - profit) BASH */ IERC20(principle).safeTransferFrom(msg.sender, address(this), _amount); IERC20(principle).approve(address(treasury), _amount); ITreasury(treasury).deposit(_amount, principle, profit); if (fee != 0) { // fee is transferred to dao IERC20(Bash).safeTransfer(DAO, fee); } // total debt is increased totalDebt = totalDebt.add(value); // debt in BASH owed to bonders not yet vested (vested bash is no longer debt) // depositor info is stored bondInfo[_depositor] = Bond({ payout: bondInfo[_depositor].payout.add(payout), vesting: terms.vestingTerm, lastTime: uint32(block.timestamp), pricePaid: priceInUSD }); // indexed events are emitted emit BondCreated( _amount, payout, block.timestamp.add(terms.vestingTerm), priceInUSD ); emit BondPriceChanged(bondPriceInUSD(), _bondPrice(), debtRatio()); adjust(); // control variable is adjusted return payout; } /** * @notice redeem bond for user * @param _recipient address * @param _stake bool * @return uint */ function redeem(address _recipient, bool _stake) external returns (uint256) { Bond memory info = bondInfo[_recipient]; // (seconds since last interaction / vesting term remaining) uint256 percentVested = percentVestedFor(_recipient); if (percentVested >= 10000) { // if fully vested delete bondInfo[_recipient]; // delete user info emit BondRedeemed(_recipient, info.payout, 0); // emit bond data return stakeOrSend(_recipient, _stake, info.payout); // pay user everything due } else { // if unfinished // calculate payout vested uint256 payout = info.payout.mul(percentVested).div(10000); // store updated deposit info bondInfo[_recipient] = Bond({ payout: info.payout.sub(payout), vesting: info.vesting.sub32( uint32(block.timestamp).sub32(info.lastTime) ), lastTime: uint32(block.timestamp), pricePaid: info.pricePaid }); emit BondRedeemed(_recipient, payout, bondInfo[_recipient].payout); return stakeOrSend(_recipient, _stake, payout); } } /* ======== INTERNAL HELPER FUNCTIONS ======== */ /** * @notice allow user to stake payout automatically * @param _stake bool * @param _amount uint * @return uint */ function stakeOrSend( address _recipient, bool _stake, uint256 _amount ) internal returns (uint256) { if (!_stake) { // if user does not want to stake IERC20(Bash).transfer(_recipient, _amount); // send payout } else { // if user wants to stake if (useHelper) { // use if staking warmup is 0 IERC20(Bash).approve(stakingHelper, _amount); IStakingHelper(stakingHelper).stake(_amount, _recipient); } else { IERC20(Bash).approve(staking, _amount); IStaking(staking).stake(_amount, _recipient); } } return _amount; } /** * @notice makes incremental adjustment to control variable */ function adjust() internal { uint256 timeCanAdjust = adjustment.lastTime.add(adjustment.buffer); if (adjustment.rate != 0 && block.timestamp >= timeCanAdjust) { uint256 initial = terms.controlVariable; if (adjustment.add) { terms.controlVariable = terms.controlVariable.add( adjustment.rate ); if (terms.controlVariable >= adjustment.target) { adjustment.rate = 0; } } else { terms.controlVariable = terms.controlVariable.sub( adjustment.rate ); if (terms.controlVariable <= adjustment.target) { adjustment.rate = 0; } } adjustment.lastTime = uint32(block.timestamp); emit ControlVariableAdjustment( initial, terms.controlVariable, adjustment.rate, adjustment.add ); } } /** * @notice reduce total debt */ function decayDebt() internal { totalDebt = totalDebt.sub(debtDecay()); lastDecay = uint32(block.timestamp); } /* ======== VIEW FUNCTIONS ======== */ /** * @notice determine maximum bond size * @return uint */ function maxPayout() public view returns (uint256) { return IERC20(Bash).totalSupply().mul(terms.maxPayout).div(100000); } /** * @notice calculate interest due for new bond * @param _value uint * @return uint */ function payoutFor(uint256 _value) public view returns (uint256) { // The payout amount in BASH is the deposited asset’s BASH value divided by the bond price return FixedPoint.fraction(_value, bondPrice()).decode112with18().div( 1e16 ); } /** * @notice calculate current bond premium * @return price_ uint */ function bondPrice() public view returns (uint256 price_) { price_ = terms.controlVariable.mul(debtRatio()).add(1000000000).div( 1e7 ); if (price_ < terms.minimumPrice) { price_ = terms.minimumPrice; } } /** * @notice calculate current bond price and remove floor if above * @return price_ uint */ function _bondPrice() internal returns (uint256 price_) { price_ = terms.controlVariable.mul(debtRatio()).add(1000000000).div( 1e7 ); if (price_ < terms.minimumPrice) { price_ = terms.minimumPrice; } else if (terms.minimumPrice != 0) { terms.minimumPrice = 0; } } /** * @notice converts bond price to DAI value * @return price_ uint */ function bondPriceInUSD() public view returns (uint256 price_) { if (isLiquidityBond) { price_ = bondPrice() .mul(IBondCalculator(bondCalculator).markdown(principle)) .div(100); } else { price_ = bondPrice().mul(10**IERC20(principle).decimals()).div(100); } } /** * @notice calculate current ratio of debt to BASH supply * @return debtRatio_ uint */ function debtRatio() public view returns (uint256 debtRatio_) { uint256 supply = IERC20(Bash).totalSupply(); debtRatio_ = FixedPoint .fraction(currentDebt().mul(1e9), supply) .decode112with18() .div(1e18); } /** * @notice debt ratio in same terms for reserve or liquidity bonds * @return uint */ function standardizedDebtRatio() external view returns (uint256) { if (isLiquidityBond) { return debtRatio() .mul(IBondCalculator(bondCalculator).markdown(principle)) .div(1e9); } else { return debtRatio(); } } /** * @notice calculate debt factoring in decay * @return uint */ function currentDebt() public view returns (uint256) { return totalDebt.sub(debtDecay()); } /** * @notice amount to decay total debt by * @return decay_ uint */ function debtDecay() public view returns (uint256 decay_) { uint32 timeSinceLast = uint32(block.timestamp).sub32(lastDecay); decay_ = totalDebt.mul(timeSinceLast).div(terms.vestingTerm); if (decay_ > totalDebt) { decay_ = totalDebt; } } /** * @notice calculate how far into vesting a depositor is * @param _depositor address * @return percentVested_ uint */ function percentVestedFor(address _depositor) public view returns (uint256 percentVested_) { Bond memory bond = bondInfo[_depositor]; uint256 secondsSinceLast = uint32(block.timestamp).sub(bond.lastTime); uint256 vesting = bond.vesting; if (vesting > 0) { percentVested_ = secondsSinceLast.mul(10000).div(vesting); } else { percentVested_ = 0; } } /** * @notice calculate amount of bash available for claim by depositor * @param _depositor address * @return pendingPayout_ uint */ function pendingPayoutFor(address _depositor) external view returns (uint256 pendingPayout_) { uint256 percentVested = percentVestedFor(_depositor); uint256 payout = bondInfo[_depositor].payout; if (percentVested >= 10000) { pendingPayout_ = payout; } else { pendingPayout_ = payout.mul(percentVested).div(10000); } } /* ======= AUXILLIARY ======= */ /** * @notice allow anyone to send lost tokens (excluding principle or bash) to the DAO * @return bool */ function recoverLostToken(address _token) external returns (bool) { require(_token != Bash); require(_token != principle); IERC20(_token).safeTransfer( DAO, IERC20(_token).balanceOf(address(this)) ); return true; } }
/** *Submitted for verification at Etherscan.io on 2021-06-12 */ // SPDX-License-Identifier: MIT pragma solidity 0.7.5; import {IsBash} from "./interfaces/IsBash.sol"; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ 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); } /** * @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. */ 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. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 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. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); return _functionCallWithValue(target, data, value, errorMessage); } function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is IERC20 { using SafeMath for uint256; using Address for address; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(msg.sender, recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(msg.sender, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}; * * Requirements: * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } contract wsBASH is ERC20 { using SafeERC20 for ERC20; using Address for address; using SafeMath for uint; address public immutable sBash; constructor( address _sBash ) ERC20( 'Wrapped sBASH', 'wsBASH' ) { require( _sBash != address(0) ); sBash = _sBash; } /** @notice wrap sBash @param _amount uint @return uint */ function wrap( uint _amount ) external returns ( uint ) { IERC20( sBash ).transferFrom( msg.sender, address(this), _amount ); uint value = sBashTowsBash( _amount ); _mint( msg.sender, value ); return value; } /** @notice unwrap sBash @param _amount uint @return uint */ function unwrap( uint _amount ) external returns ( uint ) { _burn( msg.sender, _amount ); uint value = wsBashTosBash( _amount ); IERC20( sBash ).transfer( msg.sender, value ); return value; } /** @notice converts wsBash amount to sBash @param _amount uint @return uint */ function wsBashTosBash( uint _amount ) public view returns ( uint ) { return _amount.mul( IsBash( sBash ).index() ).div( 10 ** decimals() ); } /** @notice converts sBash amount to wsBash @param _amount uint @return uint */ function sBashTowsBash( uint _amount ) public view returns ( uint ) { return _amount.mul( 10 ** decimals() ).div( IsBash( sBash ).index() ); } }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import {IERC20} from "./IERC20.sol"; interface IDai is IERC20 { }
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; import {IERC20} from "./interfaces/IERC20.sol"; import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; library FullMath { function fullMul(uint256 x, uint256 y) private pure returns (uint256 l, uint256 h) { uint256 mm = mulmod(x, y, uint256(-1)); l = x * y; h = mm - l; if (mm < l) h -= 1; } function fullDiv( uint256 l, uint256 h, uint256 d ) private pure returns (uint256) { uint256 pow2 = d & -d; d /= pow2; l /= pow2; l += h * ((-pow2) / pow2 + 1); uint256 r = 1; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; r *= 2 - d * r; return l * r; } function mulDiv( uint256 x, uint256 y, uint256 d ) internal pure returns (uint256) { (uint256 l, uint256 h) = fullMul(x, y); uint256 mm = mulmod(x, y, d); if (mm > l) h -= 1; l -= mm; require(h < d, "FullMath::mulDiv: overflow"); return fullDiv(l, h, d); } } library Babylonian { function sqrt(uint256 x) internal pure returns (uint256) { if (x == 0) return 0; uint256 xx = x; uint256 r = 1; if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; } if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; } if (xx >= 0x100000000) { xx >>= 32; r <<= 16; } if (xx >= 0x10000) { xx >>= 16; r <<= 8; } if (xx >= 0x100) { xx >>= 8; r <<= 4; } if (xx >= 0x10) { xx >>= 4; r <<= 2; } if (xx >= 0x8) { r <<= 1; } r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; r = (r + x / r) >> 1; // Seven iterations should be enough uint256 r1 = x / r; return (r < r1 ? r : r1); } } library BitMath { function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { require(x > 0, "BitMath::mostSignificantBit: zero"); if (x >= 0x100000000000000000000000000000000) { x >>= 128; r += 128; } if (x >= 0x10000000000000000) { x >>= 64; r += 64; } if (x >= 0x100000000) { x >>= 32; r += 32; } if (x >= 0x10000) { x >>= 16; r += 16; } if (x >= 0x100) { x >>= 8; r += 8; } if (x >= 0x10) { x >>= 4; r += 4; } if (x >= 0x4) { x >>= 2; r += 2; } if (x >= 0x2) r += 1; } } library FixedPoint { // range: [0, 2**112 - 1] // resolution: 1 / 2**112 struct uq112x112 { uint224 _x; } // range: [0, 2**144 - 1] // resolution: 1 / 2**112 struct uq144x112 { uint256 _x; } uint8 private constant RESOLUTION = 112; uint256 private constant Q112 = 0x10000000000000000000000000000; uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000; uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits) // decode a UQ112x112 into a uint112 by truncating after the radix point function decode(uq112x112 memory self) internal pure returns (uint112) { return uint112(self._x >> RESOLUTION); } // decode a uq112x112 into a uint with 18 decimals of precision function decode112with18(uq112x112 memory self) internal pure returns (uint256) { return uint256(self._x) / 5192296858534827; } function fraction(uint256 numerator, uint256 denominator) internal pure returns (uq112x112 memory) { require(denominator > 0, "FixedPoint::fraction: division by zero"); if (numerator == 0) return FixedPoint.uq112x112(0); if (numerator <= uint144(-1)) { uint256 result = (numerator << RESOLUTION) / denominator; require(result <= uint224(-1), "FixedPoint::fraction: overflow"); return uq112x112(uint224(result)); } else { uint256 result = FullMath.mulDiv(numerator, Q112, denominator); require(result <= uint224(-1), "FixedPoint::fraction: overflow"); return uq112x112(uint224(result)); } } // square root of a UQ112x112 // lossy between 0/1 and 40 bits function sqrt(uq112x112 memory self) internal pure returns (uq112x112 memory) { if (self._x <= uint144(-1)) { return uq112x112(uint224(Babylonian.sqrt(uint256(self._x) << 112))); } uint8 safeShiftBits = 255 - BitMath.mostSignificantBit(self._x); safeShiftBits -= safeShiftBits % 2; return uq112x112( uint224( Babylonian.sqrt(uint256(self._x) << safeShiftBits) << ((112 - safeShiftBits) / 2) ) ); } } library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { 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; } function sqrrt(uint256 a) internal pure returns (uint256 c) { if (a > 3) { c = a; uint256 b = add(div(a, 2), 1); while (b < c) { c = b; b = div(add(div(a, b), b), 2); } } else if (a != 0) { c = 1; } } } interface IUniswapV2ERC20 { function totalSupply() external view returns (uint256); } interface IBondingCalculator { function valuation(address pair_, uint256 amount_) external view returns (uint256 _value); } contract ATBASHBondingCalculator is IBondingCalculator { using FixedPoint for *; using SafeMath for uint256; using SafeMath for uint112; address public immutable Bash; constructor(address _Bash) { require(_Bash != address(0)); Bash = _Bash; } function getKValue(address _pair) public view returns (uint256 k_) { uint256 token0 = IERC20(IUniswapV2Pair(_pair).token0()).decimals(); uint256 token1 = IERC20(IUniswapV2Pair(_pair).token1()).decimals(); uint256 decimals = token0.add(token1).sub(IERC20(_pair).decimals()); (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(_pair) .getReserves(); k_ = reserve0.mul(reserve1).div(10**decimals); } function getTotalValue(address _pair) public view returns (uint256 _value) { _value = getKValue(_pair).sqrrt().mul(2); } function valuation(address _pair, uint256 amount_) external view override returns (uint256 _value) { uint256 totalValue = getTotalValue(_pair); uint256 totalSupply = IUniswapV2Pair(_pair).totalSupply(); _value = totalValue .mul(FixedPoint.fraction(amount_, totalSupply).decode112with18()) .div(1e18); } function markdown(address _pair) external view returns (uint256) { (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(_pair) .getReserves(); require( IUniswapV2Pair(_pair).token0() == Bash || IUniswapV2Pair(_pair).token1() == Bash, "Pair missing Bash" ); uint256 reserve; if (IUniswapV2Pair(_pair).token0() == Bash) { reserve = reserve1; } else { reserve = reserve0; } return reserve.mul(2 * (10**IERC20(Bash).decimals())).div(getTotalValue(_pair)); } }
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; }
{ "optimizer": { "enabled": true, "runs": 800 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "metadata": { "useLiteralContent": true } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"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":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","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"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"_burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault_","type":"address"}],"name":"setVault","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"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"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner_","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b506040805180820182526006815265082a88482a6960d31b60208083019182528351808501909452600484526308482a6960e31b90840152815191929160099162000060916003919062000221565b5081516200007690600490602085019062000221565b506005805460ff191660ff92909216919091179055504690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f620000ba62000187565b805160209182012060408051808201825260018152603160f81b90840152805180840194909452838101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6606084015260808301939093523060a0808401919091528351808403909101815260c0909201928390528151910120600755600880546001600160a01b0319163317908190556001600160a01b0316906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3620002cd565b60038054604080516020601f6002600019610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015620002175780601f10620001eb5761010080835404028352916020019162000217565b820191906000526020600020905b815481529060010190602001808311620001f957829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620002595760008555620002a4565b82601f106200027457805160ff1916838001178555620002a4565b82800160010185558215620002a4579182015b82811115620002a457825182559160200191906001019062000287565b50620002b2929150620002b6565b5090565b5b80821115620002b25760008155600101620002b7565b6114de80620002dd6000396000f3fe608060405234801561001057600080fd5b50600436106101985760003560e01c8063715018a6116100e3578063a457c2d71161008c578063dd62ed3e11610066578063dd62ed3e146104f6578063f2fde38b14610524578063fbfa77cf1461054a57610198565b8063a457c2d71461044d578063a9059cbb14610479578063d505accf146104a557610198565b80638da5cb5b116100bd5780638da5cb5b146103f557806395d89b4114610419578063a22b35ce1461042157610198565b8063715018a61461039b57806379cc6790146103a35780637ecebe00146103cf57610198565b80633644e5151161014557806342966c681161011f57806342966c68146103325780636817031b1461034f57806370a082311461037557610198565b80633644e515146102d057806339509351146102d857806340c10f191461030457610198565b806323b872dd1161017657806323b872dd1461027457806330adf81f146102aa578063313ce567146102b257610198565b806306fdde031461019d578063095ea7b31461021a57806318160ddd1461025a575b600080fd5b6101a5610552565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101df5781810151838201526020016101c7565b50505050905090810190601f16801561020c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102466004803603604081101561023057600080fd5b506001600160a01b0381351690602001356105e8565b604080519115158252519081900360200190f35b6102626105fe565b60408051918252519081900360200190f35b6102466004803603606081101561028a57600080fd5b506001600160a01b03813581169160208101359091169060400135610604565b61026261066d565b6102ba610691565b6040805160ff9092168252519081900360200190f35b61026261069a565b610246600480360360408110156102ee57600080fd5b506001600160a01b0381351690602001356106a0565b6103306004803603604081101561031a57600080fd5b506001600160a01b0381351690602001356106d6565b005b6103306004803603602081101561034857600080fd5b503561072d565b6102466004803603602081101561036557600080fd5b50356001600160a01b031661073a565b6102626004803603602081101561038b57600080fd5b50356001600160a01b03166107ce565b6103306107e9565b610330600480360360408110156103b957600080fd5b506001600160a01b03813516906020013561089f565b610262600480360360208110156103e557600080fd5b50356001600160a01b03166108a9565b6103fd6108d0565b604080516001600160a01b039092168252519081900360200190f35b6101a56108df565b6103306004803603604081101561043757600080fd5b506001600160a01b038135169060200135610940565b6102466004803603604081101561046357600080fd5b506001600160a01b03813516906020013561098c565b6102466004803603604081101561048f57600080fd5b506001600160a01b0381351690602001356109db565b610330600480360360e08110156104bb57600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060ff6080820135169060a08101359060c001356109e8565b6102626004803603604081101561050c57600080fd5b506001600160a01b0381358116916020013516610c15565b6103306004803603602081101561053a57600080fd5b50356001600160a01b0316610c40565b6103fd610d4d565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156105de5780601f106105b3576101008083540402835291602001916105de565b820191906000526020600020905b8154815290600101906020018083116105c157829003601f168201915b5050505050905090565b60006105f5338484610d5c565b50600192915050565b60025490565b6000610611848484610e48565b610663843361065e856040518060600160405280602881526020016113ab602891396001600160a01b038a1660009081526001602090815260408083203384529091529020549190610fa3565b610d5c565b5060019392505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b60055460ff1690565b60075481565b3360008181526001602090815260408083206001600160a01b038716845290915281205490916105f591859061065e908661103a565b6009546001600160a01b0316331461071f5760405162461bcd60e51b81526004018080602001828103825260238152602001806113d36023913960400191505060405180910390fd5b610729828261109b565b5050565b610737338261118b565b50565b6008546000906001600160a01b0316331461079c576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b50600980546001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff199091161790556001919050565b6001600160a01b031660009081526020819052604090205490565b6008546001600160a01b03163314610848576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6008546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a36008805473ffffffffffffffffffffffffffffffffffffffff19169055565b6107298282610940565b6001600160a01b03811660009081526006602052604081206108ca90611287565b92915050565b6008546001600160a01b031690565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156105de5780601f106105b3576101008083540402835291602001916105de565b6000610970826040518060600160405280602481526020016113f6602491396109698633610c15565b9190610fa3565b905061097d833383610d5c565b610987838361118b565b505050565b60006105f5338461065e85604051806060016040528060258152602001611484602591393360009081526001602090815260408083206001600160a01b038d1684529091529020549190610fa3565b60006105f5338484610e48565b83421115610a3d576040805162461bcd60e51b815260206004820152601860248201527f5065726d69743a206578706972656420646561646c696e650000000000000000604482015290519081900360640190fd5b6001600160a01b03871660009081526006602052604081207f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c990899089908990610a8690611287565b604080516020808201979097526001600160a01b0395861681830152939094166060840152608083019190915260a082015260c08082018990528251808303909101815260e08201835280519084012060075461190160f01b610100840152610102830152610122808301829052835180840390910181526101428301808552815191860191909120600091829052610162840180865281905260ff8a166101828501526101a284018990526101c28401889052935191955092936001926101e280820193601f1981019281900390910190855afa158015610b6c573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615801590610ba25750896001600160a01b0316816001600160a01b0316145b610bdd5760405162461bcd60e51b815260040180806020018281038252602181526020018061138a6021913960400191505060405180910390fd5b6001600160a01b038a166000908152600660205260409020610bfe9061128b565b610c098a8a8a610d5c565b50505050505050505050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6008546001600160a01b03163314610c9f576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b038116610ce45760405162461bcd60e51b815260040180806020018281038252602681526020018061131c6026913960400191505060405180910390fd5b6008546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a36008805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6009546001600160a01b031690565b6001600160a01b038316610da15760405162461bcd60e51b81526004018080602001828103825260248152602001806114606024913960400191505060405180910390fd5b6001600160a01b038216610de65760405162461bcd60e51b81526004018080602001828103825260228152602001806113426022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b038316610e8d5760405162461bcd60e51b815260040180806020018281038252602581526020018061143b6025913960400191505060405180910390fd5b6001600160a01b038216610ed25760405162461bcd60e51b81526004018080602001828103825260238152602001806112d76023913960400191505060405180910390fd5b610edd838383610987565b610f1a81604051806060016040528060268152602001611364602691396001600160a01b0386166000908152602081905260409020549190610fa3565b6001600160a01b038085166000908152602081905260408082209390935590841681522054610f49908261103a565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156110325760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610ff7578181015183820152602001610fdf565b50505050905090810190601f1680156110245780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600082820183811015611094576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6001600160a01b0382166110f6576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b611101308383610987565b60025461110e908261103a565b6002556001600160a01b038216600090815260208190526040902054611134908261103a565b6001600160a01b038316600081815260208181526040918290209390935580518481529051919230927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6001600160a01b0382166111d05760405162461bcd60e51b815260040180806020018281038252602181526020018061141a6021913960400191505060405180910390fd5b6111dc82600083610987565b611219816040518060600160405280602281526020016112fa602291396001600160a01b0385166000908152602081905260409020549190610fa3565b6001600160a01b03831660009081526020819052604090205560025461123f9082611294565b6002556040805182815290516000916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b5490565b80546001019055565b600061109483836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250610fa356fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a206275726e20616d6f756e7420657863656564732062616c616e63654f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63655a65726f537761705065726d69743a20496e76616c6964207369676e617475726545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e63655661756c744f776e65643a2063616c6c6572206973206e6f7420746865205661756c7445524332303a206275726e20616d6f756e74206578636565647320616c6c6f77616e636545524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220ecc9527e802efd402f87e41c91a929e7534cdc8c7b8c0e3f6ab8d2b7f8c95caf64736f6c63430007050033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101985760003560e01c8063715018a6116100e3578063a457c2d71161008c578063dd62ed3e11610066578063dd62ed3e146104f6578063f2fde38b14610524578063fbfa77cf1461054a57610198565b8063a457c2d71461044d578063a9059cbb14610479578063d505accf146104a557610198565b80638da5cb5b116100bd5780638da5cb5b146103f557806395d89b4114610419578063a22b35ce1461042157610198565b8063715018a61461039b57806379cc6790146103a35780637ecebe00146103cf57610198565b80633644e5151161014557806342966c681161011f57806342966c68146103325780636817031b1461034f57806370a082311461037557610198565b80633644e515146102d057806339509351146102d857806340c10f191461030457610198565b806323b872dd1161017657806323b872dd1461027457806330adf81f146102aa578063313ce567146102b257610198565b806306fdde031461019d578063095ea7b31461021a57806318160ddd1461025a575b600080fd5b6101a5610552565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101df5781810151838201526020016101c7565b50505050905090810190601f16801561020c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102466004803603604081101561023057600080fd5b506001600160a01b0381351690602001356105e8565b604080519115158252519081900360200190f35b6102626105fe565b60408051918252519081900360200190f35b6102466004803603606081101561028a57600080fd5b506001600160a01b03813581169160208101359091169060400135610604565b61026261066d565b6102ba610691565b6040805160ff9092168252519081900360200190f35b61026261069a565b610246600480360360408110156102ee57600080fd5b506001600160a01b0381351690602001356106a0565b6103306004803603604081101561031a57600080fd5b506001600160a01b0381351690602001356106d6565b005b6103306004803603602081101561034857600080fd5b503561072d565b6102466004803603602081101561036557600080fd5b50356001600160a01b031661073a565b6102626004803603602081101561038b57600080fd5b50356001600160a01b03166107ce565b6103306107e9565b610330600480360360408110156103b957600080fd5b506001600160a01b03813516906020013561089f565b610262600480360360208110156103e557600080fd5b50356001600160a01b03166108a9565b6103fd6108d0565b604080516001600160a01b039092168252519081900360200190f35b6101a56108df565b6103306004803603604081101561043757600080fd5b506001600160a01b038135169060200135610940565b6102466004803603604081101561046357600080fd5b506001600160a01b03813516906020013561098c565b6102466004803603604081101561048f57600080fd5b506001600160a01b0381351690602001356109db565b610330600480360360e08110156104bb57600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060ff6080820135169060a08101359060c001356109e8565b6102626004803603604081101561050c57600080fd5b506001600160a01b0381358116916020013516610c15565b6103306004803603602081101561053a57600080fd5b50356001600160a01b0316610c40565b6103fd610d4d565b60038054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156105de5780601f106105b3576101008083540402835291602001916105de565b820191906000526020600020905b8154815290600101906020018083116105c157829003601f168201915b5050505050905090565b60006105f5338484610d5c565b50600192915050565b60025490565b6000610611848484610e48565b610663843361065e856040518060600160405280602881526020016113ab602891396001600160a01b038a1660009081526001602090815260408083203384529091529020549190610fa3565b610d5c565b5060019392505050565b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b60055460ff1690565b60075481565b3360008181526001602090815260408083206001600160a01b038716845290915281205490916105f591859061065e908661103a565b6009546001600160a01b0316331461071f5760405162461bcd60e51b81526004018080602001828103825260238152602001806113d36023913960400191505060405180910390fd5b610729828261109b565b5050565b610737338261118b565b50565b6008546000906001600160a01b0316331461079c576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b50600980546001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff199091161790556001919050565b6001600160a01b031660009081526020819052604090205490565b6008546001600160a01b03163314610848576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6008546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a36008805473ffffffffffffffffffffffffffffffffffffffff19169055565b6107298282610940565b6001600160a01b03811660009081526006602052604081206108ca90611287565b92915050565b6008546001600160a01b031690565b60048054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156105de5780601f106105b3576101008083540402835291602001916105de565b6000610970826040518060600160405280602481526020016113f6602491396109698633610c15565b9190610fa3565b905061097d833383610d5c565b610987838361118b565b505050565b60006105f5338461065e85604051806060016040528060258152602001611484602591393360009081526001602090815260408083206001600160a01b038d1684529091529020549190610fa3565b60006105f5338484610e48565b83421115610a3d576040805162461bcd60e51b815260206004820152601860248201527f5065726d69743a206578706972656420646561646c696e650000000000000000604482015290519081900360640190fd5b6001600160a01b03871660009081526006602052604081207f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c990899089908990610a8690611287565b604080516020808201979097526001600160a01b0395861681830152939094166060840152608083019190915260a082015260c08082018990528251808303909101815260e08201835280519084012060075461190160f01b610100840152610102830152610122808301829052835180840390910181526101428301808552815191860191909120600091829052610162840180865281905260ff8a166101828501526101a284018990526101c28401889052935191955092936001926101e280820193601f1981019281900390910190855afa158015610b6c573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615801590610ba25750896001600160a01b0316816001600160a01b0316145b610bdd5760405162461bcd60e51b815260040180806020018281038252602181526020018061138a6021913960400191505060405180910390fd5b6001600160a01b038a166000908152600660205260409020610bfe9061128b565b610c098a8a8a610d5c565b50505050505050505050565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6008546001600160a01b03163314610c9f576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b038116610ce45760405162461bcd60e51b815260040180806020018281038252602681526020018061131c6026913960400191505060405180910390fd5b6008546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a36008805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6009546001600160a01b031690565b6001600160a01b038316610da15760405162461bcd60e51b81526004018080602001828103825260248152602001806114606024913960400191505060405180910390fd5b6001600160a01b038216610de65760405162461bcd60e51b81526004018080602001828103825260228152602001806113426022913960400191505060405180910390fd5b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b6001600160a01b038316610e8d5760405162461bcd60e51b815260040180806020018281038252602581526020018061143b6025913960400191505060405180910390fd5b6001600160a01b038216610ed25760405162461bcd60e51b81526004018080602001828103825260238152602001806112d76023913960400191505060405180910390fd5b610edd838383610987565b610f1a81604051806060016040528060268152602001611364602691396001600160a01b0386166000908152602081905260409020549190610fa3565b6001600160a01b038085166000908152602081905260408082209390935590841681522054610f49908261103a565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156110325760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610ff7578181015183820152602001610fdf565b50505050905090810190601f1680156110245780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600082820183811015611094576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6001600160a01b0382166110f6576040805162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015290519081900360640190fd5b611101308383610987565b60025461110e908261103a565b6002556001600160a01b038216600090815260208190526040902054611134908261103a565b6001600160a01b038316600081815260208181526040918290209390935580518481529051919230927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b6001600160a01b0382166111d05760405162461bcd60e51b815260040180806020018281038252602181526020018061141a6021913960400191505060405180910390fd5b6111dc82600083610987565b611219816040518060600160405280602281526020016112fa602291396001600160a01b0385166000908152602081905260409020549190610fa3565b6001600160a01b03831660009081526020819052604090205560025461123f9082611294565b6002556040805182815290516000916001600160a01b038516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9181900360200190a35050565b5490565b80546001019055565b600061109483836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250610fa356fe45524332303a207472616e7366657220746f20746865207a65726f206164647265737345524332303a206275726e20616d6f756e7420657863656564732062616c616e63654f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737345524332303a20617070726f766520746f20746865207a65726f206164647265737345524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63655a65726f537761705065726d69743a20496e76616c6964207369676e617475726545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e63655661756c744f776e65643a2063616c6c6572206973206e6f7420746865205661756c7445524332303a206275726e20616d6f756e74206578636565647320616c6c6f77616e636545524332303a206275726e2066726f6d20746865207a65726f206164647265737345524332303a207472616e736665722066726f6d20746865207a65726f206164647265737345524332303a20617070726f76652066726f6d20746865207a65726f206164647265737345524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa2646970667358221220ecc9527e802efd402f87e41c91a929e7534cdc8c7b8c0e3f6ab8d2b7f8c95caf64736f6c63430007050033
Deployed Bytecode Sourcemap
26795:876:7:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19779:75;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;20560:164;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;20560:164:7;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;20020:92;;;:::i;:::-;;;;;;;;;;;;;;;;20730:324;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;20730:324:7;;;;;;;;;;;;;;;;;:::i;23891:108::-;;;:::i;19941:75::-;;;:::i;:::-;;;;;;;;;;;;;;;;;;;24006:31;;;:::i;21060:211::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;21060:211:7;;;;;;;;:::i;26981:111::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;26981:111:7;;;;;;;;:::i;:::-;;27098:87;;;;;;;;;;;;;;;;-1:-1:-1;27098:87:7;;:::i;26484:118::-;;;;;;;;;;;;;;;;-1:-1:-1;26484:118:7;-1:-1:-1;;;;;26484:118:7;;:::i;20116:119::-;;;;;;;;;;;;;;;;-1:-1:-1;20116:119:7;-1:-1:-1;;;;;20116:119:7;;:::i;26014:148::-;;;:::i;27196:113::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;27196:113:7;;;;;;;;:::i;25261:118::-;;;;;;;;;;;;;;;;-1:-1:-1;25261:118:7;-1:-1:-1;;;;;25261:118:7;;:::i;25820:80::-;;;:::i;:::-;;;;-1:-1:-1;;;;;25820:80:7;;;;;;;;;;;;;;19858:79;;;:::i;27315:354::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;27315:354:7;;;;;;;;:::i;21277:273::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;21277:273:7;;;;;;;;:::i;20239:160::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;20239:160:7;;;;;;;;:::i;24506:749::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;24506:749:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;20405:149::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;20405:149:7;;;;;;;;;;:::i;26166:246::-;;;;;;;;;;;;;;;;-1:-1:-1;26166:246:7;-1:-1:-1;;;;;26166:246:7;;:::i;26606:71::-;;;:::i;19779:75::-;19844:5;19837:12;;;;;;;;-1:-1:-1;;19837:12:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19816:13;;19837:12;;19844:5;;19837:12;;19844:5;19837:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19779:75;:::o;20560:164::-;20643:4;20659:37;20668:10;20680:7;20689:6;20659:8;:37::i;:::-;-1:-1:-1;20713:4:7;20560:164;;;;:::o;20020:92::-;20095:12;;20020:92;:::o;20730:324::-;20836:4;20852:36;20862:6;20870:9;20881:6;20852:9;:36::i;:::-;20898:128;20907:6;20915:10;20927:98;20974:6;20927:98;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;20927:19:7;;;;;;:11;:19;;;;;;;;20947:10;20927:31;;;;;;;;;:98;:46;:98::i;:::-;20898:8;:128::i;:::-;-1:-1:-1;21043:4:7;20730:324;;;;;:::o;23891:108::-;23933:66;23891:108;:::o;19941:75::-;20002:9;;;;19941:75;:::o;24006:31::-;;;;:::o;21060:211::-;21173:10;21148:4;21194:23;;;:11;:23;;;;;;;;-1:-1:-1;;;;;21194:32:7;;;;;;;;;;21148:4;;21164:79;;21185:7;;21194:48;;21231:10;21194:36;:48::i;26981:111::-;26717:6;;-1:-1:-1;;;;;26717:6:7;26727:10;26717:20;26708:70;;;;-1:-1:-1;;;26708:70:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27061:24:::1;27067:8;27077:7;27061:5;:24::i;:::-;26981:111:::0;;:::o;27098:87::-;27153:25;27159:10;27171:6;27153:5;:25::i;:::-;27098:87;:::o;26484:118::-;25940:6;;26551:4;;-1:-1:-1;;;;;25940:6:7;25950:10;25940:20;25931:67;;;;;-1:-1:-1;;;25931:67:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;26564:6:7::1;:15:::0;;-1:-1:-1;;;;;26564:15:7;::::1;-1:-1:-1::0;;26564:15:7;;::::1;;::::0;;;26484:118;;;:::o;20116:119::-;-1:-1:-1;;;;;20212:18:7;20190:7;20212:18;;;;;;;;;;;;20116:119::o;26014:148::-;25940:6;;-1:-1:-1;;;;;25940:6:7;25950:10;25940:20;25931:67;;;;;-1:-1:-1;;;25931:67:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26112:6:::1;::::0;26090:42:::1;::::0;26128:1:::1;::::0;-1:-1:-1;;;;;26112:6:7::1;::::0;26090:42:::1;::::0;26128:1;;26090:42:::1;26138:6;:19:::0;;-1:-1:-1;;26138:19:7::1;::::0;;26014:148::o;27196:113::-;27274:28;27284:8;27294:7;27274:9;:28::i;25261:118::-;-1:-1:-1;;;;;25348:14:7;;25322:7;25348:14;;;:7;:14;;;;;:24;;:22;:24::i;:::-;25341:31;25261:118;-1:-1:-1;;25261:118:7:o;25820:80::-;25889:6;;-1:-1:-1;;;;;25889:6:7;25820:80;:::o;19858:79::-;19925:7;19918:14;;;;;;;;-1:-1:-1;;19918:14:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;19897:13;;19918:14;;19925:7;;19918:14;;19925:7;19918:14;;;;;;;;;;;;;;;;;;;;;;;;27315:354;27394:27;27436:130;27489:7;27436:130;;;;;;;;;;;;;;;;;:31;27446:8;27456:10;27436:9;:31::i;:::-;:35;:130;:35;:130::i;:::-;27394:172;;27577:51;27586:8;27596:10;27608:19;27577:8;:51::i;:::-;27638:24;27644:8;27654:7;27638:5;:24::i;:::-;27315:354;;;:::o;21277:273::-;21370:4;21386:136;21395:10;21407:7;21416:105;21464:15;21416:105;;;;;;;;;;;;;;;;;21428:10;21416:23;;;;:11;:23;;;;;;;;-1:-1:-1;;;;;21416:32:7;;;;;;;;;;;:105;:47;:105::i;20239:160::-;20325:4;20337:40;20347:10;20359:9;20370:6;20337:9;:40::i;24506:749::-;24742:8;24723:15;:27;;24715:64;;;;;-1:-1:-1;;;24715:64:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;24885:14:7;;24790:18;24885:14;;;:7;:14;;;;;23933:66;;24861:5;;24868:7;;24877:6;;24885:24;;:22;:24::i;:::-;24833:87;;;;;;;;;;;-1:-1:-1;;;;;24833:87:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;24823:98;;;;;;24991:16;;-1:-1:-1;;;24958:62:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;24948:73;;;;;;;;;-1:-1:-1;25049:25:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;24823:98;;-1:-1:-1;24948:73:7;;25049:25;;;;;;;-1:-1:-1;;25049:25:7;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;25049:25:7;;-1:-1:-1;;25049:25:7;;;-1:-1:-1;;;;;;;25092:20:7;;;;;;:39;;;25126:5;-1:-1:-1;;;;;25116:15:7;:6;-1:-1:-1;;;;;25116:15:7;;25092:39;25084:85;;;;-1:-1:-1;;;25084:85:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;25180:14:7;;;;;;:7;:14;;;;;:26;;:24;:26::i;:::-;25216:32;25225:5;25232:7;25241:6;25216:8;:32::i;:::-;24506:749;;;;;;;;;;:::o;20405:149::-;-1:-1:-1;;;;;20520:18:7;;;20494:7;20520:18;;;:11;:18;;;;;;;;:27;;;;;;;;;;;;;20405:149::o;26166:246::-;25940:6;;-1:-1:-1;;;;;25940:6:7;25950:10;25940:20;25931:67;;;;;-1:-1:-1;;;25931:67:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;26265:23:7;::::1;26256:75;;;;-1:-1:-1::0;;;26256:75:7::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26364:6;::::0;26342:41:::1;::::0;-1:-1:-1;;;;;26342:41:7;;::::1;::::0;26364:6:::1;::::0;26342:41:::1;::::0;26364:6:::1;::::0;26342:41:::1;26389:6;:18:::0;;-1:-1:-1;;26389:18:7::1;-1:-1:-1::0;;;;;26389:18:7;;;::::1;::::0;;;::::1;::::0;;26166:246::o;26606:71::-;26666:6;;-1:-1:-1;;;;;26666:6:7;26606:71;:::o;22891:340::-;-1:-1:-1;;;;;22992:19:7;;22984:68;;;;-1:-1:-1;;;22984:68:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;23070:21:7;;23062:68;;;;-1:-1:-1;;;23062:68:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;23141:18:7;;;;;;;:11;:18;;;;;;;;:27;;;;;;;;;;;;;:36;;;23192:32;;;;;;;;;;;;;;;;;22891:340;;;:::o;21556:518::-;-1:-1:-1;;;;;21659:20:7;;21651:70;;;;-1:-1:-1;;;21651:70:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;21737:23:7;;21729:71;;;;-1:-1:-1;;;21729:71:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21809:47;21830:6;21838:9;21849:6;21809:20;:47::i;:::-;21885:71;21907:6;21885:71;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;21885:17:7;;:9;:17;;;;;;;;;;;;:71;:21;:71::i;:::-;-1:-1:-1;;;;;21865:17:7;;;:9;:17;;;;;;;;;;;:91;;;;21987:20;;;;;;;:32;;22012:6;21987:24;:32::i;:::-;-1:-1:-1;;;;;21964:20:7;;;:9;:20;;;;;;;;;;;;:55;;;;22032:35;;;;;;;21964:20;;22032:35;;;;;;;;;;;;;21556:518;;;:::o;16499:187::-;16585:7;16620:12;16612:6;;;;16604:29;;;;-1:-1:-1;;;16604:29:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;16655:5:7;;;16499:187::o;16177:176::-;16235:7;16266:5;;;16289:6;;;;16281:46;;;;;-1:-1:-1;;;16281:46:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;16345:1;16177:176;-1:-1:-1;;;16177:176:7:o;22080:389::-;-1:-1:-1;;;;;22165:22:7;;22157:66;;;;;-1:-1:-1;;;22157:66:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;22233:56;22263:4;22271:8;22281:7;22233:20;:56::i;:::-;22314:12;;:25;;22331:7;22314:16;:25::i;:::-;22299:12;:40;-1:-1:-1;;;;;22371:19:7;;:9;:19;;;;;;;;;;;:32;;22395:7;22371:23;:32::i;:::-;-1:-1:-1;;;;;22349:19:7;;:9;:19;;;;;;;;;;;;:54;;;;22418:44;;;;;;;22349:19;;22436:4;;22418:44;;;;;;;;;;22080:389;;:::o;22475:410::-;-1:-1:-1;;;;;22558:21:7;;22550:67;;;;-1:-1:-1;;;22550:67:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;22628:49;22649:7;22666:1;22670:6;22628:20;:49::i;:::-;22709:68;22732:6;22709:68;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;22709:18:7;;:9;:18;;;;;;;;;;;;:68;:22;:68::i;:::-;-1:-1:-1;;;;;22688:18:7;;:9;:18;;;;;;;;;;:89;22802:12;;:24;;22819:6;22802:16;:24::i;:::-;22787:12;:39;22841:37;;;;;;;;22867:1;;-1:-1:-1;;;;;22841:37:7;;;;;;;;;;;;22475:410;;:::o;524:112:17:-;615:14;;524:112::o;642:178::-;794:19;;812:1;794:19;;;642:178::o;16359:134:7:-;16417:7;16443:43;16447:1;16450;16443:43;;;;;;;;;;;;;;;;;:3;:43::i
Swarm Source
ipfs://ecc9527e802efd402f87e41c91a929e7534cdc8c7b8c0e3f6ab8d2b7f8c95caf
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.