Skip to content

Instantly share code, notes, and snippets.

@jambestwick
Created January 30, 2022 13:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jambestwick/f28856b0eab703ee2a629b68c3e51da2 to your computer and use it in GitHub Desktop.
Save jambestwick/f28856b0eab703ee2a629b68c3e51da2 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.7+commit.e28d00a7.js&optimize=false&runs=200&gist=
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;
library TestsAccounts {
function getAccount(uint index) pure public returns (address) {
address[15] memory accounts;
accounts[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
accounts[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2;
accounts[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db;
accounts[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB;
accounts[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2;
accounts[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372;
accounts[6] = 0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678;
accounts[7] = 0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7;
accounts[8] = 0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C;
accounts[9] = 0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC;
accounts[10] = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;
accounts[11] = 0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C;
accounts[12] = 0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB;
accounts[13] = 0x583031D1113aD414F02576BD6afaBfb302140225;
accounts[14] = 0xdD870fA1b7C4700F2BD7f44238821C26f7392148;
return accounts[index];
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;
library Assert {
event AssertionEvent(
bool passed,
string message,
string methodName
);
event AssertionEventUint(
bool passed,
string message,
string methodName,
uint256 returned,
uint256 expected
);
event AssertionEventInt(
bool passed,
string message,
string methodName,
int256 returned,
int256 expected
);
event AssertionEventBool(
bool passed,
string message,
string methodName,
bool returned,
bool expected
);
event AssertionEventAddress(
bool passed,
string message,
string methodName,
address returned,
address expected
);
event AssertionEventBytes32(
bool passed,
string message,
string methodName,
bytes32 returned,
bytes32 expected
);
event AssertionEventString(
bool passed,
string message,
string methodName,
string returned,
string expected
);
event AssertionEventUintInt(
bool passed,
string message,
string methodName,
uint256 returned,
int256 expected
);
event AssertionEventIntUint(
bool passed,
string message,
string methodName,
int256 returned,
uint256 expected
);
function ok(bool a, string memory message) public returns (bool result) {
result = a;
emit AssertionEvent(result, message, "ok");
}
function equal(uint256 a, uint256 b, string memory message) public returns (bool result) {
result = (a == b);
emit AssertionEventUint(result, message, "equal", a, b);
}
function equal(int256 a, int256 b, string memory message) public returns (bool result) {
result = (a == b);
emit AssertionEventInt(result, message, "equal", a, b);
}
function equal(bool a, bool b, string memory message) public returns (bool result) {
result = (a == b);
emit AssertionEventBool(result, message, "equal", a, b);
}
// TODO: only for certain versions of solc
//function equal(fixed a, fixed b, string message) public returns (bool result) {
// result = (a == b);
// emit AssertionEvent(result, message);
//}
// TODO: only for certain versions of solc
//function equal(ufixed a, ufixed b, string message) public returns (bool result) {
// result = (a == b);
// emit AssertionEvent(result, message);
//}
function equal(address a, address b, string memory message) public returns (bool result) {
result = (a == b);
emit AssertionEventAddress(result, message, "equal", a, b);
}
function equal(bytes32 a, bytes32 b, string memory message) public returns (bool result) {
result = (a == b);
emit AssertionEventBytes32(result, message, "equal", a, b);
}
function equal(string memory a, string memory b, string memory message) public returns (bool result) {
result = (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b)));
emit AssertionEventString(result, message, "equal", a, b);
}
function notEqual(uint256 a, uint256 b, string memory message) public returns (bool result) {
result = (a != b);
emit AssertionEventUint(result, message, "notEqual", a, b);
}
function notEqual(int256 a, int256 b, string memory message) public returns (bool result) {
result = (a != b);
emit AssertionEventInt(result, message, "notEqual", a, b);
}
function notEqual(bool a, bool b, string memory message) public returns (bool result) {
result = (a != b);
emit AssertionEventBool(result, message, "notEqual", a, b);
}
// TODO: only for certain versions of solc
//function notEqual(fixed a, fixed b, string message) public returns (bool result) {
// result = (a != b);
// emit AssertionEvent(result, message);
//}
// TODO: only for certain versions of solc
//function notEqual(ufixed a, ufixed b, string message) public returns (bool result) {
// result = (a != b);
// emit AssertionEvent(result, message);
//}
function notEqual(address a, address b, string memory message) public returns (bool result) {
result = (a != b);
emit AssertionEventAddress(result, message, "notEqual", a, b);
}
function notEqual(bytes32 a, bytes32 b, string memory message) public returns (bool result) {
result = (a != b);
emit AssertionEventBytes32(result, message, "notEqual", a, b);
}
function notEqual(string memory a, string memory b, string memory message) public returns (bool result) {
result = (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b)));
emit AssertionEventString(result, message, "notEqual", a, b);
}
/*----------------- Greater than --------------------*/
function greaterThan(uint256 a, uint256 b, string memory message) public returns (bool result) {
result = (a > b);
emit AssertionEventUint(result, message, "greaterThan", a, b);
}
function greaterThan(int256 a, int256 b, string memory message) public returns (bool result) {
result = (a > b);
emit AssertionEventInt(result, message, "greaterThan", a, b);
}
// TODO: safely compare between uint and int
function greaterThan(uint256 a, int256 b, string memory message) public returns (bool result) {
if(b < int(0)) {
// int is negative uint "a" always greater
result = true;
} else {
result = (a > uint(b));
}
emit AssertionEventUintInt(result, message, "greaterThan", a, b);
}
function greaterThan(int256 a, uint256 b, string memory message) public returns (bool result) {
if(a < int(0)) {
// int is negative uint "b" always greater
result = false;
} else {
result = (uint(a) > b);
}
emit AssertionEventIntUint(result, message, "greaterThan", a, b);
}
/*----------------- Lesser than --------------------*/
function lesserThan(uint256 a, uint256 b, string memory message) public returns (bool result) {
result = (a < b);
emit AssertionEventUint(result, message, "lesserThan", a, b);
}
function lesserThan(int256 a, int256 b, string memory message) public returns (bool result) {
result = (a < b);
emit AssertionEventInt(result, message, "lesserThan", a, b);
}
// TODO: safely compare between uint and int
function lesserThan(uint256 a, int256 b, string memory message) public returns (bool result) {
if(b < int(0)) {
// int is negative int "b" always lesser
result = false;
} else {
result = (a < uint(b));
}
emit AssertionEventUintInt(result, message, "lesserThan", a, b);
}
function lesserThan(int256 a, uint256 b, string memory message) public returns (bool result) {
if(a < int(0)) {
// int is negative int "a" always lesser
result = true;
} else {
result = (uint(a) < b);
}
emit AssertionEventIntUint(result, message, "lesserThan", a, b);
}
}
REMIX EXAMPLE PROJECT
Remix example project is present when Remix loads very first time or there are no files existing in the File Explorer.
It contains 3 directories:
1. 'contracts': Holds three contracts with different complexity level, denoted with number prefix in file name.
2. 'scripts': Holds two scripts to deploy a contract. It is explained below.
3. 'tests': Contains one test file for 'Ballot' contract with unit tests in Solidity.
SCRIPTS
The 'scripts' folder contains example async/await scripts for deploying the 'Storage' contract.
For the deployment of any other contract, 'contractName' and 'constructorArgs' should be updated (along with other code if required).
Scripts have full access to the web3.js and ethers.js libraries.
To run a script, right click on file name in the file explorer and click 'Run'. Remember, Solidity file must already be compiled.
Output from script will appear in remix terminal.
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.7.5;
pragma abicoder v2;
interface IOwnable {
function policy() external view returns (address);
function renounceManagement() external;
function pushManagement( address newOwner_ ) external;
function pullManagement() external;
}
contract OwnableData {
address public owner;
address public pendingOwner;
}
contract Ownable is OwnableData {
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @notice `owner` defaults to msg.sender on construction.
constructor() {
owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.
/// Can only be invoked by the current `owner`.
/// @param newOwner Address of the new owner.
/// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.
/// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.
function transferOwnership(
address newOwner,
bool direct,
bool renounce
) public onlyOwner {
if (direct) {
// Checks
require(newOwner != address(0) || renounce, "Ownable: zero address");
// Effects
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
pendingOwner = address(0);
} else {
// Effects
pendingOwner = newOwner;
}
}
/// @notice Needs to be called by `pendingOwner` to claim ownership.
function claimOwnership() public {
address _pendingOwner = pendingOwner;
// Checks
require(msg.sender == _pendingOwner, "Ownable: caller != pending owner");
// Effects
emit OwnershipTransferred(owner, _pendingOwner);
owner = _pendingOwner;
pendingOwner = address(0);
}
/// @notice Only allows the `owner` to execute the function.
modifier onlyOwner() {
require(msg.sender == owner, "Ownable: caller is not the owner");
_;
}
}
library LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
function add32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
function sub32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
}
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);
}
library SafeERC20 {
using LowGasSafeMath 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);
_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 ITreasury {
function deposit( uint _amount, address _token, uint _profit ) external returns ( bool );
function valueOf( address _token, uint _amount ) external view returns ( uint value_ );
}
interface IBondCalculator {
function valuation( address _LP, uint _amount ) external view returns ( uint );
function markdown( address _LP ) external view returns ( uint );
}
interface IStaking {
function stake( uint _amount, address _recipient ) external returns ( bool );
}
interface IStakingHelper {
function stake( uint _amount, address _recipient ) external;
}
contract TimeBondDepository is Ownable {
using FixedPoint for *;
using SafeERC20 for IERC20;
using LowGasSafeMath for uint;
using LowGasSafeMath 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 );
event InitTerms( Terms terms);
event LogSetTerms(PARAMETER param, uint value);
event LogSetAdjustment( Adjust adjust);
event LogSetStaking( address indexed stakingContract, bool isHelper);
event LogRecoverLostToken( address indexed tokenToRecover, uint amount);
/* ======== STATE VARIABLES ======== */
IERC20 public immutable Time; // token given as payment for bond
IERC20 public immutable principle; // token used to create bond
ITreasury public immutable treasury; // mints Time when receives principle
address public immutable DAO; // receives profit share from bond
bool public immutable isLiquidityBond; // LP and Reserve bonds are treated slightly different
IBondCalculator public immutable bondCalculator; // calculates value of LP tokens
IStaking public staking; // to auto-stake payout
IStakingHelper 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 time for debt decay
mapping (address => bool) public allowedZappers;
/* ======== STRUCTS ======== */
// Info for creating new bonds
struct Terms {
uint controlVariable; // scaling variable for price
uint minimumPrice; // vs principle value
uint maxPayout; // in thousandths of a %. i.e. 500 = 0.5%
uint fee; // as % of bond payout, in hundreths. ( 500 = 5% = 0.05 for every 1 paid)
uint maxDebt; // 9 decimal debt ratio, max % total supply created as debt
uint32 vestingTerm; // in seconds
}
// Info for bond holder
struct Bond {
uint payout; // Time remaining to be paid
uint 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
uint rate; // increment
uint target; // BCV when adjustment finished
uint32 buffer; // minimum length (in seconds) between adjustments
uint32 lastTime; // time when last adjustment made
}
/* ======== INITIALIZATION ======== */
constructor (
address _Time,
address _principle,
address _treasury,
address _DAO,
address _bondCalculator
) {
require( _Time != address(0) );
Time = IERC20(_Time);
require( _principle != address(0) );
principle = IERC20(_principle);
require( _treasury != address(0) );
treasury = ITreasury(_treasury);
require( _DAO != address(0) );
DAO = _DAO;
// bondCalculator should be address(0) if not LP bond
bondCalculator = IBondCalculator(_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
*/
function initializeBondTerms(
uint _controlVariable,
uint _minimumPrice,
uint _maxPayout,
uint _fee,
uint _maxDebt,
uint32 _vestingTerm
) external onlyOwner() {
require( terms.controlVariable == 0, "Bonds must be initialized from 0" );
require( _controlVariable >= 40, "Can lock adjustment" );
require( _maxPayout <= 1000, "Payout cannot be above 1 percent" );
require( _vestingTerm >= 129600, "Vesting must be longer than 36 hours" );
require( _fee <= 10000, "DAO fee cannot exceed payout" );
terms = Terms ({
controlVariable: _controlVariable,
minimumPrice: _minimumPrice,
maxPayout: _maxPayout,
fee: _fee,
maxDebt: _maxDebt,
vestingTerm: _vestingTerm
});
lastDecay = uint32(block.timestamp);
emit InitTerms(terms);
}
/* ======== 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, uint _input ) external onlyOwner() {
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;
}
emit LogSetTerms(_parameter, _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 onlyOwner() {
require( _increment <= terms.controlVariable.mul( 25 ) / 1000 , "Increment too large" );
require(_target >= 40, "Next Adjustment could be locked");
adjustment = Adjust({
add: _addition,
rate: _increment,
target: _target,
buffer: _buffer,
lastTime: uint32(block.timestamp)
});
emit LogSetAdjustment(adjustment);
}
/**
* @notice set contract for auto stake
* @param _staking address
* @param _helper bool
*/
function setStaking( address _staking, bool _helper ) external onlyOwner() {
require( _staking != address(0), "IA" );
if ( _helper ) {
useHelper = true;
stakingHelper = IStakingHelper(_staking);
} else {
useHelper = false;
staking = IStaking(_staking);
}
emit LogSetStaking(_staking, _helper);
}
function allowZapper(address zapper) external onlyOwner {
require(zapper != address(0), "ZNA");
allowedZappers[zapper] = true;
}
function removeZapper(address zapper) external onlyOwner {
allowedZappers[zapper] = false;
}
/* ======== 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" );
require(msg.sender == _depositor || allowedZappers[msg.sender], "LFNA");
decayDebt();
uint priceInUSD = bondPriceInUSD(); // Stored in bond info
uint nativePrice = _bondPrice();
require( _maxPrice >= nativePrice, "Slippage limit: more than max price" ); // slippage protection
uint value = treasury.valueOf( address(principle), _amount );
uint payout = payoutFor( value ); // payout to bonder is computed
require( totalDebt.add(value) <= terms.maxDebt, "Max capacity reached" );
require( payout >= 10000000, "Bond too small" ); // must be > 0.01 Time ( underflow protection )
require( payout <= maxPayout(), "Bond too large"); // size protection because there is no slippage
// profits are calculated
uint fee = payout.mul( terms.fee )/ 10000 ;
uint profit = value.sub( payout ).sub( fee );
uint balanceBefore = Time.balanceOf(address(this));
/**
principle is transferred in
approved and
deposited into the treasury, returning (_amount - profit) Time
*/
principle.safeTransferFrom( msg.sender, address(this), _amount );
principle.approve( address( treasury ), _amount );
treasury.deposit( _amount, address(principle), profit );
if ( fee != 0 ) { // fee is transferred to dao
Time.safeTransfer( DAO, fee );
}
require(balanceBefore.add(profit) == Time.balanceOf(address(this)), "Not enough Time to cover profit");
// 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
return payout;
}
/**
* @notice redeem bond for user
* @param _recipient address
* @param _stake bool
* @return uint
*/
function redeem( address _recipient, bool _stake ) external returns ( uint ) {
require(msg.sender == _recipient, "NA");
Bond memory info = bondInfo[ _recipient ];
// (seconds since last interaction / vesting term remaining)
uint 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
uint payout = info.payout.mul( percentVested ) / 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
Time.transfer( _recipient, _amount ); // send payout
} else { // if user wants to stake
if ( useHelper ) { // use if staking warmup is 0
Time.approve( address(stakingHelper), _amount );
stakingHelper.stake( _amount, _recipient );
} else {
Time.approve( address(staking), _amount );
staking.stake( _amount, _recipient );
}
}
return _amount;
}
/**
* @notice makes incremental adjustment to control variable
*/
function adjust() internal {
uint timeCanAdjust = adjustment.lastTime.add32( adjustment.buffer );
if( adjustment.rate != 0 && block.timestamp >= timeCanAdjust ) {
uint initial = terms.controlVariable;
uint bcv = initial;
if ( adjustment.add ) {
bcv = bcv.add(adjustment.rate);
if ( bcv >= adjustment.target ) {
adjustment.rate = 0;
bcv = adjustment.target;
}
} else {
bcv = bcv.sub(adjustment.rate);
if ( bcv <= adjustment.target ) {
adjustment.rate = 0;
bcv = adjustment.target;
}
}
terms.controlVariable = bcv;
adjustment.lastTime = uint32(block.timestamp);
emit ControlVariableAdjustment( initial, bcv, 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 Time.totalSupply().mul( terms.maxPayout ) / 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() / 1e16 ;
}
/**
* @notice calculate current bond premium
* @return price_ uint
*/
function bondPrice() public view returns ( uint price_ ) {
price_ = terms.controlVariable.mul( debtRatio() ).add( 1000000000 ) / 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 ( uint price_ ) {
price_ = terms.controlVariable.mul( debtRatio() ).add( 1000000000 ) / 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 ( uint price_ ) {
if( isLiquidityBond ) {
price_ = bondPrice().mul( bondCalculator.markdown( address(principle) ) ) / 100 ;
} else {
price_ = bondPrice().mul( 10 ** principle.decimals() ) / 100;
}
}
/**
* @notice calculate current ratio of debt to Time supply
* @return debtRatio_ uint
*/
function debtRatio() public view returns ( uint debtRatio_ ) {
uint supply = Time.totalSupply();
debtRatio_ = FixedPoint.fraction(
currentDebt().mul( 1e9 ),
supply
).decode112with18() / 1e18;
}
/**
* @notice debt ratio in same terms for reserve or liquidity bonds
* @return uint
*/
function standardizedDebtRatio() external view returns ( uint ) {
if ( isLiquidityBond ) {
return debtRatio().mul( bondCalculator.markdown( address(principle) ) ) / 1e9;
} else {
return debtRatio();
}
}
/**
* @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 ) / 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).sub32( bond.lastTime );
uint vesting = bond.vesting;
if ( vesting > 0 ) {
percentVested_ = secondsSinceLast.mul( 10000 ) / vesting;
} else {
percentVested_ = 0;
}
}
/**
* @notice calculate amount of Time 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 ) / 10000;
}
}
/* ======= AUXILLIARY ======= */
/**
* @notice allow anyone to send lost tokens (excluding principle or Time) to the DAO
* @return bool
*/
function recoverLostToken(IERC20 _token ) external returns ( bool ) {
require( _token != Time, "NAT" );
require( _token != principle, "NAP" );
uint balance = _token.balanceOf( address(this));
_token.safeTransfer( DAO, balance );
emit LogRecoverLostToken(address(_token), balance);
return true;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Storage
* @dev Store & retrieve value in a variable
*/
contract Storage {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Owner
* @dev Set & change owner
*/
contract Owner {
address private owner;
// event for EVM logging
event OwnerSet(address indexed oldOwner, address indexed newOwner);
// modifier to check if caller is owner
modifier isOwner() {
// If the first argument of 'require' evaluates to 'false', execution terminates and all
// changes to the state and to Ether balances are reverted.
// This used to consume all gas in old EVM versions, but not anymore.
// It is often a good idea to use 'require' to check if functions are called correctly.
// As a second argument, you can also provide an explanation about what went wrong.
require(msg.sender == owner, "Caller is not owner");
_;
}
/**
* @dev Set contract deployer as owner
*/
constructor() {
owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor
emit OwnerSet(address(0), owner);
}
/**
* @dev Change owner
* @param newOwner address of new owner
*/
function changeOwner(address newOwner) public isOwner {
emit OwnerSet(owner, newOwner);
owner = newOwner;
}
/**
* @dev Return owner address
* @return address of owner
*/
function getOwner() external view returns (address) {
return owner;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Ballot
* @dev Implements voting process along with vote delegation
*/
contract Ballot {
struct Voter {
uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
struct Proposal {
// If you can limit the length to a certain number of bytes,
// always use one of bytes1 to bytes32 because they are much cheaper
bytes32 name; // short name (up to 32 bytes)
uint voteCount; // number of accumulated votes
}
address public chairperson;
mapping(address => Voter) public voters;
Proposal[] public proposals;
/**
* @dev Create a new ballot to choose one of 'proposalNames'.
* @param proposalNames names of proposals
*/
constructor(bytes32[] memory proposalNames) {
chairperson = msg.sender;
voters[chairperson].weight = 1;
for (uint i = 0; i < proposalNames.length; i++) {
// 'Proposal({...})' creates a temporary
// Proposal object and 'proposals.push(...)'
// appends it to the end of 'proposals'.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
/**
* @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'.
* @param voter address of voter
*/
function giveRightToVote(address voter) public {
require(
msg.sender == chairperson,
"Only chairperson can give right to vote."
);
require(
!voters[voter].voted,
"The voter already voted."
);
require(voters[voter].weight == 0);
voters[voter].weight = 1;
}
/**
* @dev Delegate your vote to the voter 'to'.
* @param to address to which vote is delegated
*/
function delegate(address to) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted, "You already voted.");
require(to != msg.sender, "Self-delegation is disallowed.");
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
// We found a loop in the delegation, not allowed.
require(to != msg.sender, "Found loop in delegation.");
}
sender.voted = true;
sender.delegate = to;
Voter storage delegate_ = voters[to];
if (delegate_.voted) {
// If the delegate already voted,
// directly add to the number of votes
proposals[delegate_.vote].voteCount += sender.weight;
} else {
// If the delegate did not vote yet,
// add to her weight.
delegate_.weight += sender.weight;
}
}
/**
* @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'.
* @param proposal index of proposal in the proposals array
*/
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
require(sender.weight != 0, "Has no right to vote");
require(!sender.voted, "Already voted.");
sender.voted = true;
sender.vote = proposal;
// If 'proposal' is out of the range of the array,
// this will throw automatically and revert all
// changes.
proposals[proposal].voteCount += sender.weight;
}
/**
* @dev Computes the winning proposal taking all previous votes into account.
* @return winningProposal_ index of winning proposal in the proposals array
*/
function winningProposal() public view
returns (uint winningProposal_)
{
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal_ = p;
}
}
}
/**
* @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then
* @return winnerName_ the name of the winner
*/
function winnerName() public view
returns (bytes32 winnerName_)
{
winnerName_ = proposals[winningProposal()].name;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
// WARNING: There is a known vuln contained within this contract related to vote delegation,
// it's NOT recommmended to use this in production.
// SushiToken with Governance.
contract SushiToken is ERC20("SushiToken", "SUSHI"), Ownable {
/// @notice Creates `_amount` token to `_to`. Must only be called by the owner (MasterChef).
function mint(address _to, uint256 _amount) public onlyOwner {
_mint(_to, _amount);
_moveDelegates(address(0), _delegates[_to], _amount);
}
// Copied and modified from YAM code:
// https://github.com/yam-finance/yam-protocol/blob/master/contracts/token/YAMGovernanceStorage.sol
// https://github.com/yam-finance/yam-protocol/blob/master/contracts/token/YAMGovernance.sol
// Which is copied and modified from COMPOUND:
// https://github.com/compound-finance/compound-protocol/blob/master/contracts/Governance/Comp.sol
/// @notice A record of each accounts delegate
mapping (address => address) internal _delegates;
/// @notice A checkpoint for marking number of votes from a given block
struct Checkpoint {
uint32 fromBlock;
uint256 votes;
}
/// @notice A record of votes checkpoints for each account, by index
mapping (address => mapping (uint32 => Checkpoint)) public checkpoints;
/// @notice The number of checkpoints for each account
mapping (address => uint32) public numCheckpoints;
/// @notice The EIP-712 typehash for the contract's domain
bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
/// @notice The EIP-712 typehash for the delegation struct used by the contract
bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
/// @notice A record of states for signing / validating signatures
mapping (address => uint) public nonces;
/// @notice An event thats emitted when an account changes its delegate
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
/// @notice An event thats emitted when a delegate account's vote balance changes
event DelegateVotesChanged(address indexed delegate, uint previousBalance, uint newBalance);
/**
* @notice Delegate votes from `msg.sender` to `delegatee`
* @param delegator The address to get delegatee for
*/
function delegates(address delegator)
external
view
returns (address)
{
return _delegates[delegator];
}
/**
* @notice Delegate votes from `msg.sender` to `delegatee`
* @param delegatee The address to delegate votes to
*/
function delegate(address delegatee) external {
return _delegate(msg.sender, delegatee);
}
/**
* @notice Delegates votes from signatory to `delegatee`
* @param delegatee The address to delegate votes to
* @param nonce The contract state required to match the signature
* @param expiry The time at which to expire the signature
* @param v The recovery byte of the signature
* @param r Half of the ECDSA signature pair
* @param s Half of the ECDSA signature pair
*/
function delegateBySig(
address delegatee,
uint nonce,
uint expiry,
uint8 v,
bytes32 r,
bytes32 s
)
external
{
bytes32 domainSeparator = keccak256(
abi.encode(
DOMAIN_TYPEHASH,
keccak256(bytes(name())),
getChainId(),
address(this)
)
);
bytes32 structHash = keccak256(
abi.encode(
DELEGATION_TYPEHASH,
delegatee,
nonce,
expiry
)
);
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
domainSeparator,
structHash
)
);
address signatory = ecrecover(digest, v, r, s);
require(signatory != address(0), "SUSHI::delegateBySig: invalid signature");
require(nonce == nonces[signatory]++, "SUSHI::delegateBySig: invalid nonce");
require(now <= expiry, "SUSHI::delegateBySig: signature expired");
return _delegate(signatory, delegatee);
}
/**
* @notice Gets the current votes balance for `account`
* @param account The address to get votes balance
* @return The number of current votes for `account`
*/
function getCurrentVotes(address account)
external
view
returns (uint256)
{
uint32 nCheckpoints = numCheckpoints[account];
return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
}
/**
* @notice Determine the prior number of votes for an account as of a block number
* @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
* @param account The address of the account to check
* @param blockNumber The block number to get the vote balance at
* @return The number of votes the account had as of the given block
*/
function getPriorVotes(address account, uint blockNumber)
external
view
returns (uint256)
{
require(blockNumber < block.number, "SUSHI::getPriorVotes: not yet determined");
uint32 nCheckpoints = numCheckpoints[account];
if (nCheckpoints == 0) {
return 0;
}
// First check most recent balance
if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
return checkpoints[account][nCheckpoints - 1].votes;
}
// Next check implicit zero balance
if (checkpoints[account][0].fromBlock > blockNumber) {
return 0;
}
uint32 lower = 0;
uint32 upper = nCheckpoints - 1;
while (upper > lower) {
uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
Checkpoint memory cp = checkpoints[account][center];
if (cp.fromBlock == blockNumber) {
return cp.votes;
} else if (cp.fromBlock < blockNumber) {
lower = center;
} else {
upper = center - 1;
}
}
return checkpoints[account][lower].votes;
}
function _delegate(address delegator, address delegatee)
internal
{
address currentDelegate = _delegates[delegator];
uint256 delegatorBalance = balanceOf(delegator); // balance of underlying SUSHIs (not scaled);
_delegates[delegator] = delegatee;
emit DelegateChanged(delegator, currentDelegate, delegatee);
_moveDelegates(currentDelegate, delegatee, delegatorBalance);
}
function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal {
if (srcRep != dstRep && amount > 0) {
if (srcRep != address(0)) {
// decrease old representative
uint32 srcRepNum = numCheckpoints[srcRep];
uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
uint256 srcRepNew = srcRepOld.sub(amount);
_writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
}
if (dstRep != address(0)) {
// increase new representative
uint32 dstRepNum = numCheckpoints[dstRep];
uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
uint256 dstRepNew = dstRepOld.add(amount);
_writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
}
}
}
function _writeCheckpoint(
address delegatee,
uint32 nCheckpoints,
uint256 oldVotes,
uint256 newVotes
)
internal
{
uint32 blockNumber = safe32(block.number, "SUSHI::_writeCheckpoint: block number exceeds 32 bits");
if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
} else {
checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
numCheckpoints[delegatee] = nCheckpoints + 1;
}
emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
}
function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
require(n < 2**32, errorMessage);
return uint32(n);
}
function getChainId() internal pure returns (uint) {
uint256 chainId;
assembly { chainId := chainid() }
return chainId;
}
}
// this line is added to create a gist. Empty file is not allowed.
// this line is added to create a gist. Empty file is not allowed.
// this line is added to create a gist. Empty file is not allowed.
pragma solidity ^0.5.2;
import "../Roles.sol";
/**
* @title WhitelistAdminRole
* @dev WhitelistAdmins are responsible for assigning and removing Whitelisted accounts.
*/
contract WhitelistAdminRole {
using Roles for Roles.Role;
event WhitelistAdminAdded(address indexed account);
event WhitelistAdminRemoved(address indexed account);
Roles.Role private _whitelistAdmins;
constructor () internal {
_addWhitelistAdmin(msg.sender);
}
modifier onlyWhitelistAdmin() {
require(isWhitelistAdmin(msg.sender));
_;
}
function isWhitelistAdmin(address account) public view returns (bool) {
return _whitelistAdmins.has(account);
}
function addWhitelistAdmin(address account) public onlyWhitelistAdmin {
_addWhitelistAdmin(account);
}
function renounceWhitelistAdmin() public {
_removeWhitelistAdmin(msg.sender);
}
function _addWhitelistAdmin(address account) internal {
_whitelistAdmins.add(account);
emit WhitelistAdminAdded(account);
}
function _removeWhitelistAdmin(address account) internal {
_whitelistAdmins.remove(account);
emit WhitelistAdminRemoved(account);
}
}
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.7.5;
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 LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
function add32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
function sub32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
}
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);
}
library SafeERC20 {
using LowGasSafeMath 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);
_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 ITreasury {
function deposit( uint _amount, address _token, uint _profit ) external returns ( bool );
function valueOf( address _token, uint _amount ) external view returns ( uint value_ );
function mintRewards( address _recipient, uint _amount ) external;
}
interface IStaking {
function stake( uint _amount, address _recipient ) external returns ( bool );
}
interface IStakingHelper {
function stake( uint _amount, address _recipient ) external;
}
interface IWAVAX9 is IERC20 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;
}
contract TimeBondDepository is Ownable {
using FixedPoint for *;
using SafeERC20 for IERC20;
using SafeERC20 for IWAVAX9;
using LowGasSafeMath for uint;
using LowGasSafeMath 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 ======== */
IERC20 public immutable Time; // token given as payment for bond
IWAVAX9 public immutable principle; // token used to create bond
ITreasury public immutable treasury; // mints Time when receives principle
address public immutable DAO; // receives profit share from bond
AggregatorV3Interface public priceFeed;
IStaking public staking; // to auto-stake payout
IStakingHelper 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 time for debt decay
mapping (address => bool) public allowedZappers;
/* ======== 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; // Time 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; // time when last adjustment made
}
/* ======== INITIALIZATION ======== */
constructor (
address _Time,
address _principle,
address _treasury,
address _DAO,
address _feed
) {
require( _Time != address(0) );
Time = IERC20(_Time);
require( _principle != address(0) );
principle = IWAVAX9(_principle);
require( _treasury != address(0) );
treasury = ITreasury(_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
*/
function initializeBondTerms(
uint _controlVariable,
uint _minimumPrice,
uint _maxPayout,
uint _maxDebt,
uint32 _vestingTerm
) external onlyPolicy() {
require( currentDebt() == 0, "Debt must be 0 for initialization" );
require( _controlVariable >= 40, "Can lock adjustment" );
require( _maxPayout <= 1000, "Payout cannot be above 1 percent" );
require( _vestingTerm >= 129600, "Vesting must be longer than 36 hours" );
terms = Terms ({
controlVariable: _controlVariable,
vestingTerm: _vestingTerm,
minimumPrice: _minimumPrice,
maxPayout: _maxPayout,
maxDebt: _maxDebt
});
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() {
require( _increment <= terms.controlVariable.mul( 25 )/ 1000, "Increment too large" );
require(_target >= 40, "Next Adjustment could be locked");
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) , "IA");
if ( _helper ) {
useHelper = true;
stakingHelper = IStakingHelper(_staking);
} else {
useHelper = false;
staking = IStaking(_staking);
}
}
function allowZapper(address zapper) external onlyPolicy {
require(zapper != address(0), "ZNA");
allowedZappers[zapper] = true;
}
function removeZapper(address zapper) external onlyPolicy {
allowedZappers[zapper] = false;
}
/* ======== 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" );
require(msg.sender == _depositor || allowedZappers[msg.sender], "LFNA");
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 = treasury.valueOf( address(principle), _amount );
uint payout = payoutFor( value ); // payout to bonder is computed
require( payout >= 10000000, "Bond too small" ); // must be > 0.01 Time ( 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
require(msg.value == _amount, "UA");
principle.deposit{value: _amount}(); // wrap only what is needed to pay
principle.transfer(address(treasury), _amount);
} else {
principle.safeTransferFrom( msg.sender, address(treasury), _amount );
}
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
return payout;
}
/**
* @notice redeem bond for user
* @param _recipient address
* @param _stake bool
* @return uint
*/
function redeem( address _recipient, bool _stake ) external returns ( uint ) {
require(msg.sender == _recipient, "NA");
Bond memory info = bondInfo[ _recipient ];
uint percentVested = percentVestedFor( _recipient ); // (seconds 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 )/ 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
Time.transfer( _recipient, _amount ); // send payout
} else { // if user wants to stake
if ( useHelper ) { // use if staking warmup is 0
Time.approve( address(stakingHelper), _amount );
stakingHelper.stake( _amount, _recipient );
} else {
Time.approve( address(staking), _amount );
staking.stake( _amount, _recipient );
}
}
return _amount;
}
/**
* @notice makes incremental adjustment to control variable
*/
function adjust() internal {
uint timeCanAdjust = adjustment.lastTime.add32( 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 Time.totalSupply().mul( terms.maxPayout )/ 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()/ 1e14;
}
/**
* @notice calculate current bond premium
* @return price_ uint
*/
function bondPrice() public view returns ( uint price_ ) {
price_ = terms.controlVariable.mul( debtRatio() )/ 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() ).add( 1000000000 ) / 1e7;
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 Time supply
* @return debtRatio_ uint
*/
function debtRatio() public view returns ( uint debtRatio_ ) {
uint supply = Time.totalSupply();
debtRatio_ = FixedPoint.fraction(
currentDebt().mul( 1e9 ),
supply
).decode112with18()/ 1e18;
}
/**
* @notice debt ratio in same terms as reserve bonds
* @return uint
*/
function standardizedDebtRatio() external view returns ( uint ) {
return debtRatio().mul( uint( assetPrice() ) )/ 10**priceFeed.decimals(); // 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 )/ 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).sub32( bond.lastTime );
uint vesting = bond.vesting;
if ( vesting > 0 ) {
percentVested_ = secondsSinceLast.mul( 10000 )/vesting;
} else {
percentVested_ = 0;
}
}
/**
* @notice calculate amount of Time 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 )/ 10000;
}
}
/* ======= AUXILLIARY ======= */
/**
* @notice allow anyone to send lost tokens (excluding principle or Time) to the DAO
* @return bool
*/
function recoverLostToken( IERC20 _token ) external returns ( bool ) {
require( _token != Time, "NAT" );
require( _token != principle, "NAP" );
_token.safeTransfer( DAO, _token.balanceOf( address(this) ) );
return true;
}
function recoverLostETH() 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;
library LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
function add32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
function sub32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
function div(uint256 x, uint256 y) internal pure returns(uint256 z){
require(y > 0);
z=x/y;
}
}
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);
}
}
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 LowGasSafeMath 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;
/**
* @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.
*/
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 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`.
*/
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));
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));
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);
_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 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( 0 ), 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.
*/
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);
_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.
*/
/**
* @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 { }
}
library Counters {
using LowGasSafeMath 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);
}
}
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 immutable 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, "ERC20Permit: 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 OwnableData {
address public owner;
address public pendingOwner;
}
contract Ownable is OwnableData {
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @notice `owner` defaults to msg.sender on construction.
constructor() {
owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.
/// Can only be invoked by the current `owner`.
/// @param newOwner Address of the new owner.
/// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.
/// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.
function transferOwnership(
address newOwner,
bool direct,
bool renounce
) public onlyOwner {
if (direct) {
// Checks
require(newOwner != address(0) || renounce, "Ownable: zero address");
// Effects
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
pendingOwner = address(0);
} else {
// Effects
pendingOwner = newOwner;
}
}
/// @notice Needs to be called by `pendingOwner` to claim ownership.
function claimOwnership() public {
address _pendingOwner = pendingOwner;
// Checks
require(msg.sender == _pendingOwner, "Ownable: caller != pending owner");
// Effects
emit OwnershipTransferred(owner, _pendingOwner);
owner = _pendingOwner;
pendingOwner = address(0);
}
/// @notice Only allows the `owner` to execute the function.
modifier onlyOwner() {
require(msg.sender == owner, "Ownable: caller is not the owner");
_;
}
}
contract MEMOries is ERC20Permit, Ownable {
using LowGasSafeMath for uint256;
modifier onlyStakingContract() {
require( msg.sender == stakingContract, "OSC" );
_;
}
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 );
event LogSetIndex(uint256 indexed index );
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;
// 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("MEMOries", "MEMO", 9) ERC20Permit() {
initializer = msg.sender;
_totalSupply = INITIAL_FRAGMENTS_SUPPLY;
_gonsPerFragment = TOTAL_GONS.div(_totalSupply);
}
function initialize( address stakingContract_ ) external returns ( bool ) {
require( msg.sender == initializer, "NA" );
require( stakingContract_ != address(0), "IA" );
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 onlyOwner() {
require( INDEX == 0, "INZ");
INDEX = gonsForBalance( _INDEX );
emit LogSetIndex(INDEX);
}
/**
@notice increases MEMOries 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_ );
} 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 MEMOries
function circulatingSupply() public view returns ( uint ) {
return _totalSupply.sub( balanceOf( stakingContract ) );
}
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;
}
}
// Right click on the script name and hit "Run" to execute
(async () => {
try {
console.log('Running deployWithEthers script...')
const contractName = 'Storage' // Change this for other contract
const constructorArgs = [] // Put constructor args (if any) here for your contract
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
// 'web3Provider' is a remix global variable object
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner()
let factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer);
let contract = await factory.deploy(...constructorArgs);
console.log('Contract Address: ', contract.address);
// The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed()
console.log('Deployment successful.')
} catch (e) {
console.log(e.message)
}
})()
// Right click on the script name and hit "Run" to execute
(async () => {
try {
console.log('Running deployWithWeb3 script...')
const contractName = 'Storage' // Change this for other contract
const constructorArgs = [] // Put constructor args (if any) here for your contract
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
const accounts = await web3.eth.getAccounts()
let contract = new web3.eth.Contract(metadata.abi)
contract = contract.deploy({
data: metadata.data.bytecode.object,
arguments: constructorArgs
})
const newContractInstance = await contract.send({
from: accounts[0],
gas: 1500000,
gasPrice: '30000000000'
})
console.log('Contract deployed at address: ', newContractInstance.options.address)
} catch (e) {
console.log(e.message)
}
})()
// this line is added to create a gist. Empty file is not allowed.
// this line is added to create a gist. Empty file is not allowed.
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.7.5;
library LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
function add32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
function sub32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
}
interface IERC20 {
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);
}
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);
}
}
library SafeERC20 {
using LowGasSafeMath 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);
_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 OwnableData {
address public owner;
address public pendingOwner;
}
contract Ownable is OwnableData {
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @notice `owner` defaults to msg.sender on construction.
constructor() {
owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.
/// Can only be invoked by the current `owner`.
/// @param newOwner Address of the new owner.
/// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.
/// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.
function transferOwnership(
address newOwner,
bool direct,
bool renounce
) public onlyOwner {
if (direct) {
// Checks
require(newOwner != address(0) || renounce, "Ownable: zero address");
// Effects
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
pendingOwner = address(0);
} else {
// Effects
pendingOwner = newOwner;
}
}
/// @notice Needs to be called by `pendingOwner` to claim ownership.
function claimOwnership() public {
address _pendingOwner = pendingOwner;
// Checks
require(msg.sender == _pendingOwner, "Ownable: caller != pending owner");
// Effects
emit OwnershipTransferred(owner, _pendingOwner);
owner = _pendingOwner;
pendingOwner = address(0);
}
/// @notice Only allows the `owner` to execute the function.
modifier onlyOwner() {
require(msg.sender == owner, "Ownable: caller is not the owner");
_;
}
}
interface IMemo is IERC20 {
function rebase( uint256 ohmProfit_, uint epoch_) external returns (uint256);
function circulatingSupply() external view returns (uint256);
function balanceOf(address who) external view override returns (uint256);
function gonsForBalance( uint amount ) external view returns ( uint );
function balanceForGons( uint gons ) external view returns ( uint );
function index() external view returns ( uint );
}
interface IWarmup {
function retrieve( address staker_, uint amount_ ) external;
}
interface IDistributor {
function distribute() external returns ( bool );
}
contract TimeStaking is Ownable {
using LowGasSafeMath for uint256;
using LowGasSafeMath for uint32;
using SafeERC20 for IERC20;
using SafeERC20 for IMemo;
IERC20 public immutable Time;
IMemo public immutable Memories;
struct Epoch {
uint number;
uint distribute;
uint32 length;
uint32 endTime;
}
Epoch public epoch;
IDistributor public distributor;
uint public totalBonus;
IWarmup public warmupContract;
uint public warmupPeriod;
event LogStake(address indexed recipient, uint256 amount);
event LogClaim(address indexed recipient, uint256 amount);
event LogForfeit(address indexed recipient, uint256 memoAmount, uint256 timeAmount);
event LogDepositLock(address indexed user, bool locked);
event LogUnstake(address indexed recipient, uint256 amount);
event LogRebase(uint256 distribute);
event LogSetContract(CONTRACTS contractType, address indexed _contract);
event LogWarmupPeriod(uint period);
constructor (
address _Time,
address _Memories,
uint32 _epochLength,
uint _firstEpochNumber,
uint32 _firstEpochTime
) {
require( _Time != address(0) );
Time = IERC20(_Time);
require( _Memories != address(0) );
Memories = IMemo(_Memories);
epoch = Epoch({
length: _epochLength,
number: _firstEpochNumber,
endTime: _firstEpochTime,
distribute: 0
});
}
struct Claim {
uint deposit;
uint gons;
uint expiry;
bool lock; // prevents malicious delays
}
mapping( address => Claim ) public warmupInfo;
/**
@notice stake Time to enter warmup
@param _amount uint
@return bool
*/
function stake( uint _amount, address _recipient ) external returns ( bool ) {
rebase();
Time.safeTransferFrom( msg.sender, address(this), _amount );
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( Memories.gonsForBalance( _amount ) ),
expiry: epoch.number.add( warmupPeriod ),
lock: false
});
Memories.safeTransfer( address(warmupContract), _amount );
emit LogStake(_recipient, _amount);
return true;
}
/**
@notice retrieve MEMO from warmup
@param _recipient address
*/
function claim ( address _recipient ) external {
Claim memory info = warmupInfo[ _recipient ];
if ( epoch.number >= info.expiry && info.expiry != 0 ) {
delete warmupInfo[ _recipient ];
uint256 amount = Memories.balanceForGons( info.gons );
warmupContract.retrieve( _recipient, amount);
emit LogClaim(_recipient, amount);
}
}
/**
@notice forfeit MEMO in warmup and retrieve Time
*/
function forfeit() external {
Claim memory info = warmupInfo[ msg.sender ];
delete warmupInfo[ msg.sender ];
uint memoBalance = Memories.balanceForGons( info.gons );
warmupContract.retrieve( address(this), memoBalance);
Time.safeTransfer( msg.sender, info.deposit);
emit LogForfeit(msg.sender, memoBalance, info.deposit);
}
/**
@notice prevent new deposits to address (protection from malicious activity)
*/
function toggleDepositLock() external {
warmupInfo[ msg.sender ].lock = !warmupInfo[ msg.sender ].lock;
emit LogDepositLock(msg.sender, warmupInfo[ msg.sender ].lock);
}
/**
@notice redeem MEMO for Time
@param _amount uint
@param _trigger bool
*/
function unstake( uint _amount, bool _trigger ) external {
if ( _trigger ) {
rebase();
}
Memories.safeTransferFrom( msg.sender, address(this), _amount );
Time.safeTransfer( msg.sender, _amount );
emit LogUnstake(msg.sender, _amount);
}
/**
@notice returns the MEMO index, which tracks rebase growth
@return uint
*/
function index() external view returns ( uint ) {
return Memories.index();
}
/**
@notice trigger rebase if epoch over
*/
function rebase() public {
if( epoch.endTime <= uint32(block.timestamp) ) {
Memories.rebase( epoch.distribute, epoch.number );
epoch.endTime = epoch.endTime.add32( epoch.length );
epoch.number++;
if ( address(distributor) != address(0) ) {
distributor.distribute();
}
uint balance = contractBalance();
uint staked = Memories.circulatingSupply();
if( balance <= staked ) {
epoch.distribute = 0;
} else {
epoch.distribute = balance.sub( staked );
}
emit LogRebase(epoch.distribute);
}
}
/**
@notice returns contract Time holdings, including bonuses provided
@return uint
*/
function contractBalance() public view returns ( uint ) {
return Time.balanceOf( address(this) ).add( totalBonus );
}
enum CONTRACTS { DISTRIBUTOR, WARMUP }
/**
@notice sets the contract address for LP staking
@param _contract address
*/
function setContract( CONTRACTS _contract, address _address ) external onlyOwner {
if( _contract == CONTRACTS.DISTRIBUTOR ) { // 0
distributor = IDistributor(_address);
} else if ( _contract == CONTRACTS.WARMUP ) { // 1
require( address(warmupContract) == address( 0 ), "Warmup cannot be set more than once" );
warmupContract = IWarmup(_address);
}
emit LogSetContract(_contract, _address);
}
/**
* @notice set warmup period in epoch's numbers for new stakers
* @param _warmupPeriod uint
*/
function setWarmup( uint _warmupPeriod ) external onlyOwner {
warmupPeriod = _warmupPeriod;
emit LogWarmupPeriod(_warmupPeriod);
}
}
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.7.5;
library LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
function add32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
function sub32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
function div(uint256 x, uint256 y) internal pure returns(uint256 z){
require(y > 0);
z=x/y;
}
}
interface IERC20 {
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);
}
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 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");
(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);
}
}
contract OwnableData {
address public owner;
address public pendingOwner;
}
contract Ownable is OwnableData {
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @notice `owner` defaults to msg.sender on construction.
constructor() {
owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.
/// Can only be invoked by the current `owner`.
/// @param newOwner Address of the new owner.
/// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.
/// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.
function transferOwnership(
address newOwner,
bool direct,
bool renounce
) public onlyOwner {
if (direct) {
// Checks
require(newOwner != address(0) || renounce, "Ownable: zero address");
// Effects
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
pendingOwner = address(0);
} else {
// Effects
pendingOwner = newOwner;
}
}
/// @notice Needs to be called by `pendingOwner` to claim ownership.
function claimOwnership() public {
address _pendingOwner = pendingOwner;
// Checks
require(msg.sender == _pendingOwner, "Ownable: caller != pending owner");
// Effects
emit OwnershipTransferred(owner, _pendingOwner);
owner = _pendingOwner;
pendingOwner = address(0);
}
/// @notice Only allows the `owner` to execute the function.
modifier onlyOwner() {
require(msg.sender == owner, "Ownable: caller is not the owner");
_;
}
}
interface ITreasury {
function mintRewards( address _recipient, uint _amount ) external;
}
contract Distributor is Ownable {
using LowGasSafeMath for uint;
using LowGasSafeMath for uint32;
/* ====== VARIABLES ====== */
IERC20 public immutable TIME;
ITreasury public immutable treasury;
uint32 public immutable epochLength;
uint32 public nextEpochTime;
mapping( uint => Adjust ) public adjustments;
event LogDistribute(address indexed recipient, uint amount);
event LogAdjust(uint initialRate, uint currentRate, uint targetRate);
event LogAddRecipient(address indexed recipient, uint rate);
event LogRemoveRecipient(address indexed recipient);
/* ====== STRUCTS ====== */
struct Info {
uint rate; // in ten-thousandths ( 5000 = 0.5% )
address recipient;
}
Info[] public info;
struct Adjust {
bool add;
uint rate;
uint target;
}
/* ====== CONSTRUCTOR ====== */
constructor( address _treasury, address _time, uint32 _epochLength, uint32 _nextEpochTime ) {
require( _treasury != address(0) );
treasury = ITreasury(_treasury);
require( _time != address(0) );
TIME = IERC20(_time);
epochLength = _epochLength;
nextEpochTime = _nextEpochTime;
}
/* ====== PUBLIC FUNCTIONS ====== */
/**
@notice send epoch reward to staking contract
*/
function distribute() external returns ( bool ) {
if ( nextEpochTime <= uint32(block.timestamp) ) {
nextEpochTime = nextEpochTime.add32( epochLength ); // set next epoch time
// distribute rewards to each recipient
for ( uint i = 0; i < info.length; i++ ) {
if ( info[ i ].rate > 0 ) {
treasury.mintRewards( // mint and send from treasury
info[ i ].recipient,
nextRewardAt( info[ i ].rate )
);
adjust( i ); // check for adjustment
}
emit LogDistribute(info[ i ].recipient, nextRewardAt( info[ i ].rate ));
}
return true;
} else {
return false;
}
}
/* ====== INTERNAL FUNCTIONS ====== */
/**
@notice increment reward rate for collector
*/
function adjust( uint _index ) internal {
Adjust memory adjustment = adjustments[ _index ];
if ( adjustment.rate != 0 ) {
uint initial = info[ _index ].rate;
uint rate = initial;
if ( adjustment.add ) { // if rate should increase
rate = rate.add( adjustment.rate ); // raise rate
if ( rate >= adjustment.target ) { // if target met
rate = adjustment.target;
delete adjustments[ _index ];
}
} else { // if rate should decrease
rate = rate.sub( adjustment.rate ); // lower rate
if ( rate <= adjustment.target ) { // if target met
rate = adjustment.target;
delete adjustments[ _index ];
}
}
info[ _index ].rate = rate;
emit LogAdjust(initial, rate, adjustment.target);
}
}
/* ====== VIEW FUNCTIONS ====== */
/**
@notice view function for next reward at given rate
@param _rate uint
@return uint
*/
function nextRewardAt( uint _rate ) public view returns ( uint ) {
return TIME.totalSupply().mul( _rate ).div( 1000000 );
}
/**
@notice view function for next reward for specified address
@param _recipient address
@return uint
*/
function nextRewardFor( address _recipient ) external view returns ( uint ) {
uint reward;
for ( uint i = 0; i < info.length; i++ ) {
if ( info[ i ].recipient == _recipient ) {
reward = nextRewardAt( info[ i ].rate );
}
}
return reward;
}
/* ====== POLICY FUNCTIONS ====== */
/**
@notice adds recipient for distributions
@param _recipient address
@param _rewardRate uint
*/
function addRecipient( address _recipient, uint _rewardRate ) external onlyOwner {
require( _recipient != address(0), "IA" );
require(_rewardRate <= 5000, "Too high reward rate");
require(info.length <= 4, "limit recipients max to 5");
info.push( Info({
recipient: _recipient,
rate: _rewardRate
}));
emit LogAddRecipient(_recipient, _rewardRate);
}
/**
@notice removes recipient for distributions
@param _index uint
@param _recipient address
*/
function removeRecipient( uint _index, address _recipient ) external onlyOwner {
require( _recipient == info[ _index ].recipient, "NA" );
info[_index] = info[info.length-1];
adjustments[_index] = adjustments[ info.length-1 ];
info.pop();
delete adjustments[ info.length-1 ];
emit LogRemoveRecipient(_recipient);
}
/**
@notice set adjustment info for a collector's reward rate
@param _index uint
@param _add bool
@param _rate uint
@param _target uint
*/
function setAdjustment( uint _index, bool _add, uint _rate, uint _target ) external onlyOwner {
require(_target <= 5000, "Too high reward rate");
adjustments[ _index ] = Adjust({
add: _add,
rate: _rate,
target: _target
});
}
}
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.7.5;
interface IERC20 {
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);
}
interface IStaking {
function stake( uint _amount, address _recipient ) external returns ( bool );
function claim( address _recipient ) external;
}
contract StakingHelper {
event LogStake(address indexed recipient, uint amount);
IStaking public immutable staking;
IERC20 public immutable Time;
constructor ( address _staking, address _Time ) {
require( _staking != address(0) );
staking = IStaking(_staking);
require( _Time != address(0) );
Time = IERC20(_Time);
}
function stake( uint _amount, address recipient ) external {
Time.transferFrom( msg.sender, address(this), _amount );
Time.approve( address(staking), _amount );
staking.stake( _amount, recipient );
staking.claim( recipient );
emit LogStake(recipient, _amount);
}
}
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.7.5;
interface IERC20 {
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);
}
contract StakingWarmup {
address public immutable staking;
IERC20 public immutable MEMOries;
constructor ( address _staking, address _MEMOries ) {
require( _staking != address(0) );
staking = _staking;
require( _MEMOries != address(0) );
MEMOries = IERC20(_MEMOries);
}
function retrieve( address _staker, uint _amount ) external {
require( msg.sender == staking, "NA" );
MEMOries.transfer( _staker, _amount );
}
}
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.7.5;
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 (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));
}
}
// 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 LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
function add32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
function sub32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
function div(uint256 x, uint256 y) internal pure returns(uint256 z){
require(y > 0);
z=x/y;
}
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;
}
}
}
interface IERC20 {
function decimals() external view returns (uint8);
}
interface IUniswapV2ERC20 {
function totalSupply() external view returns (uint);
}
interface IUniswapV2Pair is IUniswapV2ERC20 {
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function token0() external view returns ( address );
function token1() external view returns ( address );
}
interface IBondingCalculator {
function valuation( address pair_, uint amount_ ) external view returns ( uint _value );
}
contract TimeBondingCalculator is IBondingCalculator {
using FixedPoint for *;
using LowGasSafeMath for uint;
using LowGasSafeMath for uint112;
IERC20 public immutable Time;
constructor( address _Time ) {
require( _Time != address(0) );
Time = IERC20(_Time);
}
function getKValue( address _pair ) public view returns( uint k_ ) {
uint token0 = IERC20( IUniswapV2Pair( _pair ).token0() ).decimals();
uint token1 = IERC20( IUniswapV2Pair( _pair ).token1() ).decimals();
uint pairDecimals = IERC20( _pair ).decimals();
(uint reserve0, uint reserve1, ) = IUniswapV2Pair( _pair ).getReserves();
if (token0.add(token1) < pairDecimals)
{
uint decimals = pairDecimals.sub(token0.add(token1));
k_ = reserve0.mul(reserve1).mul( 10 ** decimals );
}
else {
uint decimals = token0.add(token1).sub(pairDecimals);
k_ = reserve0.mul(reserve1).div( 10 ** decimals );
}
}
function getTotalValue( address _pair ) public view returns ( uint _value ) {
_value = getKValue( _pair ).sqrrt().mul(2);
}
function valuation( address _pair, uint amount_ ) external view override returns ( uint _value ) {
uint totalValue = getTotalValue( _pair );
uint totalSupply = IUniswapV2Pair( _pair ).totalSupply();
_value = totalValue.mul( FixedPoint.fraction( amount_, totalSupply ).decode112with18() ).div( 1e18 );
}
function markdown( address _pair ) external view returns ( uint ) {
( uint reserve0, uint reserve1, ) = IUniswapV2Pair( _pair ).getReserves();
uint reserve;
if ( IUniswapV2Pair( _pair ).token0() == address(Time) ) {
reserve = reserve1;
} else {
require(IUniswapV2Pair( _pair ).token1() == address(Time), "not a Time lp pair");
reserve = reserve0;
}
return reserve.mul( 2 * ( 10 ** Time.decimals() ) ).div( getTotalValue( _pair ) );
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "../contracts/3_Ballot.sol";
contract BallotTest {
bytes32[] proposalNames;
Ballot ballotToTest;
function beforeAll () public {
proposalNames.push(bytes32("candidate1"));
ballotToTest = new Ballot(proposalNames);
}
function checkWinningProposal () public {
ballotToTest.vote(0);
Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal");
Assert.equal(ballotToTest.winnerName(), bytes32("candidate1"), "candidate1 should be the winner name");
}
function checkWinninProposalWithReturnValue () public view returns (bool) {
return ballotToTest.winningProposal() == 0;
}
}
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.7.5;
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 LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
}
abstract contract ERC20 is IERC20 {
using LowGasSafeMath for uint256;
// 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));
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));
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);
_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(0), 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);
_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 { }
}
library Counters {
using LowGasSafeMath 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);
}
}
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, "ERC20Permit: 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;
event VaultTransferred(address indexed newVault);
function setVault( address vault_ ) external onlyOwner() {
require(vault_ != address(0), "IA0");
_vault = vault_;
emit VaultTransferred( _vault );
}
function vault() public view returns (address) {
return _vault;
}
modifier onlyVault() {
require( _vault == msg.sender, "VaultOwned: caller is not the Vault" );
_;
}
}
contract TimeERC20Token is ERC20Permit, VaultOwned {
using LowGasSafeMath for uint256;
constructor() ERC20("Time", "TIME", 9) {
}
function mint(address account_, uint256 amount_) external onlyVault() {
_mint(account_, amount_);
}
function burn(uint256 amount) external virtual {
_burn(msg.sender, amount);
}
function burnFrom(address account_, uint256 amount_) external virtual {
_burnFrom(account_, amount_);
}
function _burnFrom(address account_, uint256 amount_) internal virtual {
uint256 decreasedAllowance_ =
allowance(account_, msg.sender).sub(amount_);
_approve(account_, msg.sender, decreasedAllowance_);
_burn(account_, amount_);
}
}
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.7.5;
library LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
function add32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
function sub32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
function mul32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
function div(uint256 x, uint256 y) internal pure returns(uint256 z){
require(y > 0);
z=x/y;
}
}
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);
}
}
}
}
contract OwnableData {
address public owner;
address public pendingOwner;
}
contract Ownable is OwnableData {
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @notice `owner` defaults to msg.sender on construction.
constructor() {
owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.
/// Can only be invoked by the current `owner`.
/// @param newOwner Address of the new owner.
/// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.
/// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.
function transferOwnership(
address newOwner,
bool direct,
bool renounce
) public onlyOwner {
if (direct) {
// Checks
require(newOwner != address(0) || renounce, "Ownable: zero address");
// Effects
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
pendingOwner = address(0);
} else {
// Effects
pendingOwner = newOwner;
}
}
/// @notice Needs to be called by `pendingOwner` to claim ownership.
function claimOwnership() public {
address _pendingOwner = pendingOwner;
// Checks
require(msg.sender == _pendingOwner, "Ownable: caller != pending owner");
// Effects
emit OwnershipTransferred(owner, _pendingOwner);
owner = _pendingOwner;
pendingOwner = address(0);
}
/// @notice Only allows the `owner` to execute the function.
modifier onlyOwner() {
require(msg.sender == owner, "Ownable: caller is not the owner");
_;
}
}
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 LowGasSafeMath 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 IERC20Mintable {
function mint( uint256 amount_ ) external;
function mint( address account_, uint256 ammount_ ) external;
}
interface ITIMEERC20 is IERC20Mintable, IERC20 {
function burnFrom(address account_, uint256 amount_) external;
}
interface IBondCalculator {
function valuation( address pair_, uint amount_ ) external view returns ( uint _value );
}
contract TimeTreasury is Ownable {
using LowGasSafeMath for uint;
using LowGasSafeMath for uint32;
using SafeERC20 for IERC20;
event Deposit( address indexed token, uint amount, uint value );
event Withdrawal( address indexed token, uint amount, uint value );
event CreateDebt( address indexed debtor, address indexed token, uint amount, uint value );
event RepayDebt( address indexed debtor, address indexed token, uint amount, uint value );
event ReservesManaged( address indexed token, uint amount );
event ReservesUpdated( uint indexed totalReserves );
event ReservesAudited( uint indexed totalReserves );
event RewardsMinted( address indexed caller, address indexed recipient, uint amount );
event ChangeQueued( MANAGING indexed managing, address queued );
event ChangeActivated( MANAGING indexed managing, address activated, bool result );
event ChangeLimitAmount( uint256 amount );
enum MANAGING {
RESERVEDEPOSITOR,
RESERVESPENDER,
RESERVETOKEN,
RESERVEMANAGER,
LIQUIDITYDEPOSITOR,
LIQUIDITYTOKEN,
LIQUIDITYMANAGER,
DEBTOR,
REWARDMANAGER,
SOHM
}
ITIMEERC20 public immutable Time;
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 => uint ) 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.
mapping( address => uint256 ) public hourlyLimitAmounts; // tracks amounts
mapping( address => uint32 ) public hourlyLimitQueue; // Delays changes to mapping.
uint256 public limitAmount;
IERC20 public MEMOries;
uint public sOHMQueue; // Delays change to sOHM address
uint public totalReserves; // Risk-free value of all assets
uint public totalDebt;
constructor (
address _Time,
address _MIM,
uint32 _secondsNeededForQueue,
uint256 _limitAmount
) {
require( _Time != address(0) );
Time = ITIMEERC20(_Time);
isReserveToken[ _MIM ] = true;
reserveTokens.push( _MIM );
// isLiquidityToken[ _OHMDAI ] = true;
// liquidityTokens.push( _OHMDAI );
secondsNeededForQueue = _secondsNeededForQueue;
limitAmount = _limitAmount;
}
function setLimitAmount(uint amount) external onlyOwner {
limitAmount = amount;
emit ChangeLimitAmount(limitAmount);
}
/**
@notice allow approved address to deposit an asset for Time
@param _amount uint
@param _token address
@param _profit uint
@return send_ uint
*/
function deposit( uint _amount, address _token, uint _profit ) external returns ( uint 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" );
}
uint value = valueOf(_token, _amount);
// mint Time needed and store amount of rewards for distribution
send_ = value.sub( _profit );
limitRequirements(msg.sender, send_);
Time.mint( msg.sender, send_ );
totalReserves = totalReserves.add( value );
emit ReservesUpdated( totalReserves );
emit Deposit( _token, _amount, value );
}
/**
@notice allow approved address to burn Time for reserves
@param _amount uint
@param _token address
*/
function withdraw( uint _amount, address _token ) external {
require( isReserveToken[ _token ], "Not accepted" ); // Only reserves can be used for redemptions
require( isReserveSpender[ msg.sender ], "Not approved" );
uint value = valueOf( _token, _amount );
Time.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( uint _amount, address _token ) external {
require( isDebtor[ msg.sender ], "Not approved" );
require( isReserveToken[ _token ], "Not accepted" );
uint value = valueOf( _token, _amount );
uint maximumDebt = MEMOries.balanceOf( msg.sender ); // Can only borrow against sOHM held
uint balance = debtorBalance[ msg.sender ];
uint availableDebt = maximumDebt.sub( balance );
require( value <= availableDebt, "Exceeds debt limit" );
limitRequirements(msg.sender, value);
debtorBalance[ msg.sender ] = balance.add( value );
totalDebt = totalDebt.add( value );
totalReserves = totalReserves.sub( value );
emit ReservesUpdated( totalReserves );
IERC20( _token ).safeTransfer( 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( uint _amount, address _token ) external {
require( isDebtor[ msg.sender ], "Not approved" );
require( isReserveToken[ _token ], "Not accepted" );
IERC20( _token ).safeTransferFrom( msg.sender, address(this), _amount );
uint value = valueOf( _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 Time
@param _amount uint
*/
function repayDebtWithTime( uint _amount ) external {
require( isDebtor[ msg.sender ], "Not approved as debtor" );
require( isReserveSpender[ msg.sender ], "Not approved as spender" );
Time.burnFrom( msg.sender, _amount );
debtorBalance[ msg.sender ] = debtorBalance[ msg.sender ].sub( _amount );
totalDebt = totalDebt.sub( _amount );
emit RepayDebt( msg.sender, address(Time), _amount, _amount );
}
/**
@notice allow approved address to withdraw assets
@param _token address
@param _amount uint
*/
function manage( address _token, uint _amount ) external {
uint value = valueOf(_token, _amount);
if( isLiquidityToken[ _token ] ) {
require( isLiquidityManager[ msg.sender ], "Not approved" );
require(value <= excessReserves());
} else {
if (isReserveToken[ _token ]) require(value <= excessReserves());
require( isReserveManager[ msg.sender ], "Not approved" );
}
limitRequirements(msg.sender, value);
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, uint _amount ) external {
require( isRewardManager[ msg.sender ], "Not approved" );
require( _amount <= excessReserves(), "Insufficient reserves" );
limitRequirements(msg.sender, _amount);
Time.mint( _recipient, _amount );
emit RewardsMinted( msg.sender, _recipient, _amount );
}
/**
@notice returns excess reserves not backing tokens
@return uint
*/
function excessReserves() public view returns ( uint ) {
return totalReserves.sub( Time.totalSupply().sub( totalDebt ) );
}
/**
@notice takes inventory of all tracked assets
@notice always consolidate to recognized reserves before audit
*/
function auditReserves() external onlyOwner {
uint reserves;
for( uint i = 0; i < reserveTokens.length; i++ ) {
reserves = reserves.add (
valueOf( reserveTokens[ i ], IERC20( reserveTokens[ i ] ).balanceOf( address(this) ) )
);
}
for( uint i = 0; i < liquidityTokens.length; i++ ) {
reserves = reserves.add (
valueOf( liquidityTokens[ i ], IERC20( liquidityTokens[ i ] ).balanceOf( address(this) ) )
);
}
totalReserves = reserves;
emit ReservesUpdated( reserves );
emit ReservesAudited( reserves );
}
/**
@notice returns Time valuation of asset
@param _token address
@param _amount uint
@return value_ uint
*/
function valueOf( address _token, uint _amount ) public view returns ( uint value_ ) {
if ( isReserveToken[ _token ] ) {
// convert amount to match Time decimals
value_ = _amount.mul( 10 ** Time.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 onlyOwner returns ( bool ) {
require( _address != address(0), "IA" );
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.SOHM ) { // 9
sOHMQueue = 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 onlyOwner returns ( bool ) {
require( _address != address(0), "IA" );
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 ) && !listContains( liquidityTokens, _address ) ) {
reserveTokens.push( _address );
}
}
result = !isReserveToken[ _address ];
require(!result || !isLiquidityToken[_address], "Do not add to both types of token");
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 ) && !listContains( reserveTokens, _address ) ) {
liquidityTokens.push( _address );
}
}
result = !isLiquidityToken[ _address ];
require(!result || !isReserveToken[_address], "Do not add to both types of token");
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.SOHM ) { // 9
sOHMQueue = 0;
MEMOries = IERC20(_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 LimitRequirements
@param _address address
@param _address value
*/
function limitRequirements(
address _address,
uint256 value
) internal {
if (block.timestamp.sub(hourlyLimitQueue[_address]) >= 1 hours)
{
hourlyLimitAmounts[_address] = limitAmount;
hourlyLimitQueue[_address] = uint32(block.timestamp);
}
hourlyLimitAmounts[_address] = hourlyLimitAmounts[_address].sub(value);
}
/**
@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( uint i = 0; i < _list.length; i++ ) {
if( _list[ i ] == _token ) {
return true;
}
}
return false;
}
}
/**
*Submitted for verification at Etherscan.io on 2021-06-12
*/
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;
/**
* @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);
}
library LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
function add32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
function sub32(uint32 x, uint32 y) internal pure returns (uint32 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
function div(uint256 x, uint256 y) internal pure returns(uint256 z){
require(y > 0);
z=x/y;
}
}
/**
* @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 LowGasSafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 public immutable 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 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));
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));
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);
_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);
_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 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 LowGasSafeMath 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);
_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");
}
}
}
interface IMEMO is IERC20 {
function index() external view returns ( uint );
}
contract wMEMO is ERC20 {
using SafeERC20 for IMEMO;
using LowGasSafeMath for uint;
IMEMO public immutable MEMO;
event Wrap(address indexed recipient, uint256 amountMemo, uint256 amountWmemo);
event UnWrap(address indexed recipient,uint256 amountWmemo, uint256 amountMemo);
constructor( address _MEMO ) ERC20( 'Wrapped MEMO', 'wMEMO' ) {
require( _MEMO != address(0) );
MEMO = IMEMO(_MEMO);
}
/**
@notice wrap MEMO
@param _amount uint
@return uint
*/
function wrap( uint _amount ) external returns ( uint ) {
MEMO.safeTransferFrom( msg.sender, address(this), _amount );
uint value = MEMOTowMEMO( _amount );
_mint( msg.sender, value );
emit Wrap(msg.sender, _amount, value);
return value;
}
/**
@notice unwrap MEMO
@param _amount uint
@return uint
*/
function unwrap( uint _amount ) external returns ( uint ) {
_burn( msg.sender, _amount );
uint value = wMEMOToMEMO( _amount );
MEMO.safeTransfer( msg.sender, value );
emit UnWrap(msg.sender, _amount, value);
return value;
}
/**
@notice converts wMEMO amount to MEMO
@param _amount uint
@return uint
*/
function wMEMOToMEMO( uint _amount ) public view returns ( uint ) {
return _amount.mul( MEMO.index() ).div( 10 ** decimals );
}
/**
@notice converts MEMO amount to wMEMO
@param _amount uint
@return uint
*/
function MEMOTowMEMO( uint _amount ) public view returns ( uint ) {
return _amount.mul( 10 ** decimals ).div( MEMO.index() );
}
}
// ███████╗░█████╗░██████╗░██████╗░███████╗██████╗░░░░███████╗██╗
// ╚════██║██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗░░░██╔════╝██║
// ░░███╔═╝███████║██████╔╝██████╔╝█████╗░░██████╔╝░░░█████╗░░██║
// ██╔══╝░░██╔══██║██╔═══╝░██╔═══╝░██╔══╝░░██╔══██╗░░░██╔══╝░░██║
// ███████╗██║░░██║██║░░░░░██║░░░░░███████╗██║░░██║██╗██║░░░░░██║
// ╚══════╝╚═╝░░╚═╝╚═╝░░░░░╚═╝░░░░░╚══════╝╚═╝░░╚═╝╚═╝╚═╝░░░░░╚═╝
// Copyright (C) 2021 zapper
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
///@author Zapper
///@notice This contract adds liquidity to TraderJoe pools on Avalanche using AVAX or any ERC20 Tokens.
// SPDX-License-Identifier: GPL-2.0 AND MIT
// File contracts/oz/0.8.0/utils/Context.sol
pragma solidity ^0.8.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
// File contracts/oz/0.8.0/access/Ownable.sol
pragma solidity ^0.8.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(
newOwner != address(0),
"Ownable: new owner is the zero address"
);
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// File contracts/oz/0.8.0/token/ERC20/IERC20.sol
pragma solidity ^0.8.0;
/**
* @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
);
}
// File contracts/oz/0.8.0/utils/Address.sol
pragma solidity ^0.8.0;
/**
* @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 on 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"
);
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);
}
/**
* @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.4._
*/
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.4._
*/
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);
}
}
}
}
// File contracts/oz/0.8.0/token/ERC20/utils/SafeERC20.sol
pragma solidity ^0.8.0;
/**
* @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 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) + value;
_callOptionalReturn(
token,
abi.encodeWithSelector(
token.approve.selector,
spender,
newAllowance
)
);
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(
oldAllowance >= value,
"SafeERC20: decreased allowance below zero"
);
uint256 newAllowance = oldAllowance - value;
_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"
);
}
}
}
// File contracts/oz/0.8.0/token/ERC20/extensions/IERC20Metadata.sol
pragma solidity ^0.8.0;
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// File contracts/_base/ZapBaseV2_1.sol
// ███████╗░█████╗░██████╗░██████╗░███████╗██████╗░░░░███████╗██╗
// ╚════██║██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗░░░██╔════╝██║
// ░░███╔═╝███████║██████╔╝██████╔╝█████╗░░██████╔╝░░░█████╗░░██║
// ██╔══╝░░██╔══██║██╔═══╝░██╔═══╝░██╔══╝░░██╔══██╗░░░██╔══╝░░██║
// ███████╗██║░░██║██║░░░░░██║░░░░░███████╗██║░░██║██╗██║░░░░░██║
// ╚══════╝╚═╝░░╚═╝╚═╝░░░░░╚═╝░░░░░╚══════╝╚═╝░░╚═╝╚═╝╚═╝░░░░░╚═╝
// Copyright (C) 2021 zapper
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
/// @author Zapper
/// @notice This abstract contract, which is inherited by Zaps,
/// provides utility functions for moving tokens, checking allowances
/// and balances, and accounting
/// for fees.
pragma solidity ^0.8.0;
abstract contract ZapBaseV2_1 is Ownable {
using SafeERC20 for IERC20;
bool public stopped = false;
event ApprovedTargets(address indexed target, bool isApproved);
// swapTarget => approval status
mapping(address => bool) public approvedTargets;
// circuit breaker modifiers
modifier stopInEmergency {
if (stopped) {
revert("Paused");
} else {
_;
}
}
function _approveToken(address token, address spender) internal {
IERC20 _token = IERC20(token);
if (_token.allowance(address(this), spender) > 0) return;
else {
_token.safeApprove(spender, type(uint256).max);
}
}
function _approveToken(
address token,
address spender,
uint256 amount
) internal {
IERC20(token).safeApprove(spender, 0);
IERC20(token).safeApprove(spender, amount);
}
// - to Pause the contract
function toggleContractActive() public onlyOwner {
stopped = !stopped;
}
function setApprovedTargets(
address[] calldata targets,
bool[] calldata isApproved
) external onlyOwner {
require(targets.length == isApproved.length, "Invalid Input length");
for (uint256 i = 0; i < targets.length; i++) {
approvedTargets[targets[i]] = isApproved[i];
emit ApprovedTargets(targets[i], isApproved[i]);
}
}
receive() external payable {
require(msg.sender != tx.origin, "Do not send ETH directly");
}
}
// File contracts/_base/ZapInBaseV3_1.sol
// ███████╗░█████╗░██████╗░██████╗░███████╗██████╗░░░░███████╗██╗
// ╚════██║██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗░░░██╔════╝██║
// ░░███╔═╝███████║██████╔╝██████╔╝█████╗░░██████╔╝░░░█████╗░░██║
// ██╔══╝░░██╔══██║██╔═══╝░██╔═══╝░██╔══╝░░██╔══██╗░░░██╔══╝░░██║
// ███████╗██║░░██║██║░░░░░██║░░░░░███████╗██║░░██║██╗██║░░░░░██║
// ╚══════╝╚═╝░░╚═╝╚═╝░░░░░╚═╝░░░░░╚══════╝╚═╝░░╚═╝╚═╝╚═╝░░░░░╚═╝
// Copyright (C) 2021 zapper
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
/// @author Zapper
/// @notice This abstract contract provides utility functions for moving tokens.
pragma solidity ^0.8.0;
abstract contract ZapInBaseV3_1 is ZapBaseV2_1 {
using SafeERC20 for IERC20;
/**
@dev Transfer tokens (including ETH) from msg.sender to this contract
@param token The ERC20 token to transfer to this contract (0 address if ETH)
@return Quantity of tokens transferred to this contract
*/
function _pullTokens(
address token,
uint256 amount
) internal returns (uint256) {
if (token == address(0)) {
require(msg.value > 0, "No eth sent");
return msg.value;
}
require(amount > 0, "Invalid token amount");
require(msg.value == 0, "Eth sent with token");
//transfer token
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
return amount;
}
}
// File contracts/Avalanche/TraderJoe/TraderJoe_ZapIn_V1.sol
pragma solidity ^0.8.0;
// import "@uniswap/lib/contracts/libraries/Babylonian.sol";
library Babylonian {
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
// else z = 0
}
}
interface IWETH {
function deposit() external payable;
}
interface IUniswapV2Factory {
function getPair(address tokenA, address tokenB)
external
view
returns (address);
}
interface IUniswapV2Router02 {
function addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
)
external
returns (
uint256 amountA,
uint256 amountB,
uint256 liquidity
);
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
}
interface IUniswapV2Pair {
function token0() external pure returns (address);
function token1() external pure returns (address);
function getReserves()
external
view
returns (
uint112 _reserve0,
uint112 _reserve1,
uint32 _blockTimestampLast
);
}
interface ITimeBondDepository {
function deposit( uint _amount, uint _maxPrice, address _depositor) external returns ( uint );
}
contract Wonderland_ZapIn_V1 is ZapInBaseV3_1 {
using SafeERC20 for IERC20;
mapping (ITimeBondDepository => address) public allowedPairs;
mapping (ITimeBondDepository => address) public allowedReserves;
IUniswapV2Factory private constant joeFactory =
IUniswapV2Factory(0x9Ad6C38BE94206cA50bb0d90783181662f0Cfa10);
IUniswapV2Router02 private constant joeRouter =
IUniswapV2Router02(0x60aE616a2155Ee3d9A68541Ba4544862310933d4);
address private constant wavaxTokenAddress =
address(0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7);
uint256 private constant deadline =
0xf000000000000000000000000000000000000000000000000000000000000000;
address immutable DAO;
struct Cache {
uint256 initialBalance;
uint256 balanceAfter;
uint256 toInvest;
}
event AddPairAddress(address indexed bondDepository, address pair);
event RemovePairAddress(address indexed bondDepository);
event AddReserveAddress(address indexed bondDepository, address reserve);
event RemoveReserveAddress(address indexed bondDepository);
constructor(address _DAO)
{
DAO = _DAO;
// 0x exchange
approvedTargets[0xDef1C0ded9bec7F1a1670819833240f027b25EfF] = true;
//allowedPairs
allowedPairs[ITimeBondDepository(0xc26850686ce755FFb8690EA156E5A6cf03DcBDE1)] = 0xf64e1c5B6E17031f5504481Ac8145F4c3eab4917;
allowedPairs[ITimeBondDepository(0xA184AE1A71EcAD20E822cB965b99c287590c4FFe)] = 0x113f413371fC4CC4C9d6416cf1DE9dFd7BF747Df;
//allowedReserves
allowedReserves[ITimeBondDepository(0x694738E0A438d90487b4a549b201142c1a97B556)] = 0x130966628846BFd36ff31a822705796e8cb8C18D;
allowedReserves[ITimeBondDepository(0xE02B1AA2c4BE73093BE79d763fdFFC0E3cf67318)] = 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7;
}
event zapIn(
address sender,
address pool,
uint256 tokensRec
);
function addPairAddress(address bondDepository, address pair) external onlyOwner {
require(bondDepository != address(0), "BNA");
require(pair != address(0), "BNA");
allowedPairs[ITimeBondDepository(bondDepository)] = pair;
emit AddPairAddress(bondDepository, pair);
}
function removePairAddress(address bondDepository) external onlyOwner {
require(bondDepository != address(0), "BNA");
allowedPairs[ITimeBondDepository(bondDepository)] = address(0);
emit RemovePairAddress(bondDepository);
}
function addReserveAddress(address bondDepository, address reserve) external onlyOwner {
require(bondDepository != address(0), "BNA");
require(reserve != address(0), "BNA");
allowedReserves[ITimeBondDepository(bondDepository)] = reserve;
emit AddReserveAddress(bondDepository, reserve);
}
function removeReserveAddress(address bondDepository) external onlyOwner {
require(bondDepository != address(0), "BNA");
allowedReserves[ITimeBondDepository(bondDepository)] = address(0);
emit RemoveReserveAddress(bondDepository);
}
/**
@notice Add liquidity to TraderJoe pools with ETH/ERC20 Tokens and buys Time Bond
@param _FromTokenContractAddress The ERC20 token used (address(0x00) if ether)
@param _amount The amount of fromToken to invest
@param _minPoolTokens Minimum quantity of pool tokens to receive. Reverts otherwise
@param _swapTarget Excecution target for the first swap
@param swapData DEX quote data
@param _bondMaxPrice max price that bond can be bought
@return Amount of LP bought
*/
function ZapInLp(
address _FromTokenContractAddress,
ITimeBondDepository _bondDepository,
uint256 _amount,
uint256 _minPoolTokens,
address _swapTarget,
bytes calldata swapData,
uint _bondMaxPrice,
address _to
) external payable stopInEmergency returns (uint256) {
require(msg.sender == _to, "NA");
require (allowedPairs[_bondDepository] != address(0), "BNA");
Cache memory cache;
cache.toInvest =
_pullTokens(_FromTokenContractAddress, _amount);
cache.initialBalance = _FromTokenContractAddress == address(0)
? address(this).balance
: IERC20(_FromTokenContractAddress).balanceOf(address(this));
uint256 LPBought =
_performZapInLp(
_FromTokenContractAddress,
allowedPairs[_bondDepository],
cache.toInvest,
_swapTarget,
swapData,
true
);
cache.balanceAfter = _FromTokenContractAddress == address(0)
? IERC20(wavaxTokenAddress).balanceOf(address(this))
: IERC20(_FromTokenContractAddress).balanceOf(address(this));
require((cache.initialBalance - cache.toInvest) == cache.balanceAfter, "Not all tokens used");
require(LPBought >= _minPoolTokens, "High Slippage");
_approveToken(allowedPairs[_bondDepository], address(_bondDepository), LPBought);
_bondDepository.deposit(LPBought, _bondMaxPrice, _to);
emit zapIn(msg.sender, allowedPairs[_bondDepository], LPBought);
return LPBought;
}
function ZapIn(
address _FromTokenContractAddress,
ITimeBondDepository _bondDepository,
uint256 _amount,
uint256 _minReturnTokens,
address _swapTarget,
bytes calldata swapData,
uint _bondMaxPrice,
address _to
) external payable stopInEmergency returns (uint256) {
require(msg.sender == _to, "NA");
require (allowedReserves[_bondDepository] != address(0), "BNA");
Cache memory cache;
cache.toInvest =
_pullTokens(_FromTokenContractAddress, _amount);
cache.initialBalance = _FromTokenContractAddress == address(0)
? address(this).balance
: IERC20(_FromTokenContractAddress).balanceOf(address(this));
uint256 TokenBought =
_fillQuote(
_FromTokenContractAddress,
allowedReserves[_bondDepository],
cache.toInvest,
_swapTarget,
swapData
);
cache.balanceAfter = _FromTokenContractAddress == address(0)
? IERC20(wavaxTokenAddress).balanceOf(address(this))
: IERC20(_FromTokenContractAddress).balanceOf(address(this));
require((cache.initialBalance - cache.toInvest) == cache.balanceAfter, "Not all tokens used");
require(TokenBought >= _minReturnTokens, "High Slippage");
_approveToken(allowedReserves[_bondDepository], address(_bondDepository), TokenBought);
_bondDepository.deposit(TokenBought, _bondMaxPrice, _to);
emit zapIn(msg.sender, allowedReserves[_bondDepository], TokenBought);
return TokenBought;
}
function _getPairTokens(address _pairAddress)
internal
pure
returns (address token0, address token1)
{
IUniswapV2Pair uniPair = IUniswapV2Pair(_pairAddress);
token0 = uniPair.token0();
token1 = uniPair.token1();
}
function _performZapInLp(
address _FromTokenContractAddress,
address _pairAddress,
uint256 _amount,
address _swapTarget,
bytes calldata swapData,
bool transferResidual
) internal returns (uint256) {
uint256 intermediateAmt;
address intermediateToken;
(address _ToUniswapToken0, address _ToUniswapToken1) =
_getPairTokens(_pairAddress);
if (
_FromTokenContractAddress != _ToUniswapToken0 &&
_FromTokenContractAddress != _ToUniswapToken1
) {
// swap to intermediate
(intermediateAmt, intermediateToken) = _fillQuoteLp(
_FromTokenContractAddress,
_pairAddress,
_amount,
_swapTarget,
swapData
);
} else {
intermediateToken = _FromTokenContractAddress;
intermediateAmt = _amount;
}
// divide intermediate into appropriate amount to add liquidity
(uint256 token0Bought, uint256 token1Bought) =
_swapIntermediate(
intermediateToken,
_ToUniswapToken0,
_ToUniswapToken1,
intermediateAmt
);
return
_uniDeposit(
_ToUniswapToken0,
_ToUniswapToken1,
token0Bought,
token1Bought,
transferResidual
);
}
function _uniDeposit(
address _ToUnipoolToken0,
address _ToUnipoolToken1,
uint256 token0Bought,
uint256 token1Bought,
bool transferResidual
) internal returns (uint256) {
_approveToken(_ToUnipoolToken0, address(joeRouter), token0Bought);
_approveToken(_ToUnipoolToken1, address(joeRouter), token1Bought);
(uint256 amountA, uint256 amountB, uint256 LP) =
joeRouter.addLiquidity(
_ToUnipoolToken0,
_ToUnipoolToken1,
token0Bought,
token1Bought,
1,
1,
address(this),
deadline
);
if (transferResidual) {
//Returning Residue in token0, if any.
if (token0Bought - amountA > 0) {
IERC20(_ToUnipoolToken0).safeTransfer(
msg.sender,
token0Bought - amountA
);
}
//Returning Residue in token1, if any
if (token1Bought - amountB > 0) {
IERC20(_ToUnipoolToken1).safeTransfer(
msg.sender,
token1Bought - amountB
);
}
}
return LP;
}
function _fillQuote(
address _fromTokenAddress,
address _toTokenAddress,
uint256 _amount,
address _swapTarget,
bytes calldata swapData
) internal returns (uint256 amountBought) {
if (_swapTarget == wavaxTokenAddress) {
IWETH(wavaxTokenAddress).deposit{ value: _amount }();
return _amount;
}
uint256 valueToSend;
if (_fromTokenAddress == address(0)) {
valueToSend = _amount;
} else {
_approveToken(_fromTokenAddress, _swapTarget, _amount);
}
IERC20 token = IERC20(_toTokenAddress);
uint256 initialBalance = token.balanceOf(address(this));
require(approvedTargets[_swapTarget], "Target not Authorized");
(bool success, ) = _swapTarget.call{ value: valueToSend }(swapData);
require(success, "Error Swapping Tokens 1");
amountBought =
token.balanceOf(address(this)) - initialBalance;
require(amountBought > 0, "Error Swapping Tokens 2");
}
function _fillQuoteLp(
address _fromTokenAddress,
address _pairAddress,
uint256 _amount,
address _swapTarget,
bytes calldata swapData
) internal returns (uint256 amountBought, address intermediateToken) {
if (_swapTarget == wavaxTokenAddress) {
IWETH(wavaxTokenAddress).deposit{ value: _amount }();
return (_amount, wavaxTokenAddress);
}
uint256 valueToSend;
if (_fromTokenAddress == address(0)) {
valueToSend = _amount;
} else {
_approveToken(_fromTokenAddress, _swapTarget, _amount);
}
(address _token0, address _token1) = _getPairTokens(_pairAddress);
IERC20 token0 = IERC20(_token0);
IERC20 token1 = IERC20(_token1);
uint256 initialBalance0 = token0.balanceOf(address(this));
uint256 initialBalance1 = token1.balanceOf(address(this));
require(approvedTargets[_swapTarget], "Target not Authorized");
(bool success, ) = _swapTarget.call{ value: valueToSend }(swapData);
require(success, "Error Swapping Tokens 1");
uint256 finalBalance0 =
token0.balanceOf(address(this)) - initialBalance0;
uint256 finalBalance1 =
token1.balanceOf(address(this)) - initialBalance1;
if (finalBalance0 > finalBalance1) {
amountBought = finalBalance0;
intermediateToken = _token0;
} else {
amountBought = finalBalance1;
intermediateToken = _token1;
}
require(amountBought > 0, "Swapped to Invalid Intermediate");
}
function _swapIntermediate(
address _toContractAddress,
address _ToUnipoolToken0,
address _ToUnipoolToken1,
uint256 _amount
) internal returns (uint256 token0Bought, uint256 token1Bought) {
IUniswapV2Pair pair =
IUniswapV2Pair(
joeFactory.getPair(_ToUnipoolToken0, _ToUnipoolToken1)
);
(uint256 res0, uint256 res1, ) = pair.getReserves();
if (_toContractAddress == _ToUnipoolToken0) {
uint256 amountToSwap = calculateSwapInAmount(res0, _amount);
//if no reserve or a new pair is created
if (amountToSwap <= 0) amountToSwap = _amount / 2;
token1Bought = _token2Token(
_toContractAddress,
_ToUnipoolToken1,
amountToSwap
);
token0Bought = _amount - amountToSwap;
} else {
uint256 amountToSwap = calculateSwapInAmount(res1, _amount);
//if no reserve or a new pair is created
if (amountToSwap <= 0) amountToSwap = _amount / 2;
token0Bought = _token2Token(
_toContractAddress,
_ToUnipoolToken0,
amountToSwap
);
token1Bought = _amount - amountToSwap;
}
}
function calculateSwapInAmount(uint256 reserveIn, uint256 userIn)
internal
pure
returns (uint256)
{
return
(Babylonian.sqrt(
reserveIn * ((userIn * 3988000) + (reserveIn * 3988009))
) - (reserveIn * 1997)) / 1994;
}
/**
@notice This function is used to swap ERC20 <> ERC20
@param _FromTokenContractAddress The token address to swap from.
@param _ToTokenContractAddress The token address to swap to.
@param tokens2Trade The amount of tokens to swap
@return tokenBought The quantity of tokens bought
*/
function _token2Token(
address _FromTokenContractAddress,
address _ToTokenContractAddress,
uint256 tokens2Trade
) internal returns (uint256 tokenBought) {
if (_FromTokenContractAddress == _ToTokenContractAddress) {
return tokens2Trade;
}
_approveToken(
_FromTokenContractAddress,
address(joeRouter),
tokens2Trade
);
address pair =
joeFactory.getPair(
_FromTokenContractAddress,
_ToTokenContractAddress
);
require(pair != address(0), "No Swap Available");
address[] memory path = new address[](2);
path[0] = _FromTokenContractAddress;
path[1] = _ToTokenContractAddress;
tokenBought = joeRouter.swapExactTokensForTokens(
tokens2Trade,
1,
path,
address(this),
deadline
)[path.length - 1];
require(tokenBought > 0, "Error Swapping Tokens 2");
}
/**
* @notice allow owner to send lost tokens to the DAO
* @return bool
*/
function recoverLostToken( address _token ) external onlyOwner returns ( bool ) {
IERC20(_token).safeTransfer(DAO, IERC20( _token ).balanceOf( address(this)));
return true;
}
function refundETH() external onlyOwner {
if (address(this).balance > 0)
{
(bool success, ) = DAO.call{value: address(this).balance}(new bytes(0));
require(success, 'STE');
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment