// SPDX-License-Identifier: MIT // Sources flattened with hardhat v2.6.1 https://hardhat.org // File @openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol@v4.4.1 // OpenZeppelin Contracts v4.4.1 (utils/Address.sol) pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @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; 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" ); (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"); (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"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal 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 assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // File @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol@v4.4.1 // OpenZeppelin Contracts v4.4.1 (proxy/utils/Initializable.sol) pragma solidity ^0.8.0; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() initializer {} * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { // If the contract is initializing we ignore whether _initialized is set in order to support multiple // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the // contract may have been reentered. require( _initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized" ); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} modifier, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } function _isConstructor() private view returns (bool) { return !AddressUpgradeable.isContract(address(this)); } } // File @openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol@v4.4.1 // OpenZeppelin Contracts v4.4.1 (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 ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { __Context_init_unchained(); } function __Context_init_unchained() internal onlyInitializing {} function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } uint256[50] private __gap; } // File @openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol@v4.4.1 // OpenZeppelin Contracts v4.4.1 (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 OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal onlyInitializing { __Context_init_unchained(); __Ownable_init_unchained(); } function __Ownable_init_unchained() internal onlyInitializing { _transferOwnership(_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 { _transferOwnership(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" ); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } uint256[49] private __gap; } // File @openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol@v4.4.1 // OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol) pragma solidity ^0.8.0; // CAUTION // This version of SafeMath should only be used with Solidity 0.8 or later, // because it relies on the compiler's built in overflow checks. /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler * now has built in overflow checking. */ library SafeMathUpgradeable { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } } } // File contracts/utils/DSMath.sol /// math.sol -- mixin for inline numerical wizardry // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 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 General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . pragma solidity >0.4.13; contract DSMath { function WAD() internal pure returns (uint256) { return 10**18; } function RAY() internal pure returns (uint256) { return 10**27; } function add(uint256 x, uint256 y) public pure returns (uint256 z) { require((z = x + y) >= x, "ds-math-add-overflow"); } function sub(uint256 x, uint256 y) public pure returns (uint256 z) { require((z = x - y) <= x, "ds-math-sub-underflow"); } function mul(uint256 x, uint256 y) public pure returns (uint256 z) { require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow"); } function min(uint256 x, uint256 y) public pure returns (uint256 z) { return x <= y ? x : y; } function max(uint256 x, uint256 y) public pure returns (uint256 z) { return x >= y ? x : y; } function imin(int256 x, int256 y) public pure returns (int256 z) { return x <= y ? x : y; } function imax(int256 x, int256 y) public pure returns (int256 z) { return x >= y ? x : y; } function wmul(uint256 x, uint256 y) public pure returns (uint256 z) { z = add(mul(x, y), WAD() / 2) / WAD(); } function rmul(uint256 x, uint256 y) public pure returns (uint256 z) { z = add(mul(x, y), RAY() / 2) / RAY(); } function wdiv(uint256 x, uint256 y) public pure returns (uint256 z) { z = add(mul(x, WAD()), y / 2) / y; } function rdiv(uint256 x, uint256 y) public pure returns (uint256 z) { z = add(mul(x, RAY()), y / 2) / y; } // This famous algorithm is called "exponentiation by squaring" // and calculates x^n with x as fixed-point and n as regular unsigned. // // It's O(log n), instead of O(n) for naive repeated multiplication. // // These facts are why it works: // // If n is even, then x^n = (x^2)^(n/2). // If n is odd, then x^n = x * x^(n-1), // and applying the equation for even x gives // x^n = x * (x^2)^((n-1) / 2). // // Also, EVM division is flooring and // floor[(n-1) / 2] = floor[n / 2]. // function rpow(uint256 x, uint256 n) public pure returns (uint256 z) { z = n % 2 != 0 ? x : RAY(); for (n /= 2; n != 0; n /= 2) { x = rmul(x, x); if (n % 2 != 0) { z = rmul(z, x); } } } } // File contracts/Interfaces.sol pragma solidity >=0.6; pragma experimental ABIEncoderV2; interface cERC20 { function mint(uint256 mintAmount) external returns (uint256); function redeemUnderlying(uint256 mintAmount) external returns (uint256); function exchangeRateCurrent() external returns (uint256); function exchangeRateStored() external view returns (uint256); function balanceOf(address addr) external view returns (uint256); function transfer(address to, uint256 amount) external returns (bool); function approve(address spender, uint256 amount) external returns (bool); } interface GoodDollar is cERC20 { function getFees(uint256 value) external view returns (uint256, bool); function mint(address to, uint256 mintAmount) external returns (uint256); function isMinter(address account) external view returns (bool); } interface Staking { struct Staker { // The staked DAI amount uint256 stakedDAI; // The latest block number which the // staker has staked tokens uint256 lastStake; } function stakeDAI(uint256 amount) external; function withdrawStake() external; function stakers(address staker) external view returns (Staker memory); } interface Uniswap { function swapExactETHForTokens( uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external payable returns (uint256[] memory amounts); function swapExactTokensForETH( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function WETH() external pure returns (address); function factory() external pure returns (address); function quote( uint256 amountA, uint256 reserveA, uint256 reserveB ) external pure returns (uint256 amountB); function getAmountIn( uint256 amountOut, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountIn); function getAmountOut( uint256 amountI, uint256 reserveIn, uint256 reserveOut ) external pure returns (uint256 amountOut); } interface UniswapFactory { function getPair(address tokenA, address tokenB) external view returns (address); } interface UniswapPair { function getReserves() external view returns ( uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast ); function kLast() external view returns (uint256); } interface Reserve { function buy( address _buyWith, uint256 _tokenAmount, uint256 _minReturn ) external returns (uint256); } interface IIdentity { function isWhitelisted(address user) external view returns (bool); function addWhitelistedWithDID(address account, string memory did) external; function removeWhitelisted(address account) external; function addIdentityAdmin(address account) external returns (bool); function setAvatar(address _avatar) external; } interface UBIScheme { function currentDay() external view returns (uint256); } interface ProxyAdmin { function getProxyImplementation(address proxy) external view returns (address); function getProxyAdmin(address proxy) external view returns (address); function upgrade(address proxy, address implementation) external; function owner() external view returns (address); function transferOwnership(address newOwner) external; } // File contracts/staking/FuseStakingV3.sol pragma solidity >=0.6; pragma experimental ABIEncoderV2; // import "hardhat/console.sol"; interface IConsensus { /** * @dev delegate to a validator * @param _validator the address of the validator msg.sender is delegating to */ function delegate(address _validator) external payable; /** * @dev Function to be called when a delegator whishes to withdraw some of his staked funds for a validator * @param _validator the address of the validator msg.sender has delegating to * @param _amount the amount msg.sender wishes to withdraw from the contract */ function withdraw(address _validator, uint256 _amount) external; function delegatedAmount(address _address, address _validator) external view returns (uint256); function stakeAmount(address _address) external view returns (uint256); function delegators(address _validator) external view returns (address[] memory); } interface PegSwap { /** * @notice exchanges the source token for target token * @param sourceAmount count of tokens being swapped * @param source the token that is being given * @param target the token that is being taken */ function swap( uint256 sourceAmount, address source, address target ) external; } contract FuseStakingV3 is Initializable, OwnableUpgradeable, DSMath { using SafeMathUpgradeable for uint256; mapping(address => uint256) public stakers; address[] public validators; IConsensus public consensus; Uniswap public uniswap; GoodDollar public GD; UBIScheme public ubischeme; UniswapFactory public uniswapFactory; UniswapPair public uniswapPair; uint256 public lastDayCollected; //ubi day from ubischeme uint256 public stakeBackRatio; uint256 public maxSlippageRatio; //actually its max price impact ratio uint256 public keeperFeeRatio; uint256 public RATIO_BASE; uint256 public communityPoolRatio; //out of G$ bought how much should goto pool uint256 public communityPoolBalance; uint256 public pendingFuseEarnings; //earnings not used because of slippage address public USDC; address public fUSD; bool public paused; address public guardian; PegSwap pegSwap; event UBICollected( uint256 indexed currentDay, uint256 ubi, //G$ sent to ubischeme uint256 communityPool, //G$ added to pool uint256 gdBought, //actual G$ we got out of swapping stakingRewards + pendingFuseEarnings uint256 stakingRewards, //rewards earned since previous collection, uint256 pendingFuseEarnings, //new balance of fuse pending to be swapped for G$ address keeper, uint256 keeperGDFee ); /** * @dev initialize */ function initialize() public initializer { __Ownable_init_unchained(); consensus = IConsensus( address(0x3014ca10b91cb3D0AD85fEf7A3Cb95BCAc9c0f79) ); validators.push(address(0xcb876A393F05a6677a8a029f1C6D7603B416C0A6)); } modifier notPaused() { require(paused == false, "ubi collection is pauased"); _; } modifier onlyGuardian() { require(msg.sender == guardian, "not guardian"); _; } function upgrade0() external { if (RATIO_BASE == 0) { stakeBackRatio = 33333; //%33 communityPoolRatio = 33333; //%33 maxSlippageRatio = 3000; //3% keeperFeeRatio = 30; //0.03% RATIO_BASE = 100000; //100% } } function upgrade1( address _gd, address _ubischeme, address _uniswap ) external { if (address(uniswapPair) == address(0)) { uniswap = Uniswap( _uniswap == address(0) ? 0xFB76e9E7d88E308aB530330eD90e84a952570319 : _uniswap ); GD = GoodDollar(_gd); ubischeme = UBIScheme(_ubischeme); uniswapFactory = UniswapFactory(uniswap.factory()); uniswapPair = UniswapPair( uniswapFactory.getPair(uniswap.WETH(), _gd) ); } } function upgrade2() external { if (USDC == address(0)) { USDC = address(0x620fd5fa44BE6af63715Ef4E65DDFA0387aD13F5); fUSD = address(0x249BE57637D8B013Ad64785404b24aeBaE9B098B); } } function upgrade3() external { if (guardian == address(0)) { paused = true; guardian = address(0x5128E3C1f8846724cc1007Af9b4189713922E4BB); } } function upgrade4() external { if (address(pegSwap) == address(0)) { pegSwap = PegSwap(0xdfE016328E7BcD6FA06614fE3AF3877E931F7e0a); paused = false; } } function upgrade5() external { cERC20(fUSD).approve(address(pegSwap), type(uint256).max); cERC20(USDC).approve(address(uniswap), type(uint256).max); } function upgrade6() external { //switch to voltage uniswap = Uniswap(0xE3F85aAd0c8DD7337427B9dF5d0fB741d65EEEB5); uniswapFactory = UniswapFactory(uniswap.factory()); uniswapPair = UniswapPair( uniswapFactory.getPair(uniswap.WETH(), address(GD)) ); } function setContracts(address _gd, address _ubischeme) public onlyOwner { if (_gd != address(0)) { GD = GoodDollar(_gd); } if (_ubischeme != address(0)) { ubischeme = UBIScheme(_ubischeme); } } function stake() public payable returns (bool) { return stake(address(0)); } function stake(address _validator) public payable onlyGuardian returns (bool) { require(msg.value > 0, "stake must be > 0"); require(validators.length > 0, "no approved validators"); bool found; for ( uint256 i = 0; _validator != address(0) && i < validators.length; i++ ) { if (validators[i] != _validator) { found = true; break; } } require( _validator == address(0) || found, "validator not in approved list" ); bool staked = stakeNextValidator(msg.value, _validator); stakers[msg.sender] += msg.value; return staked; } function balanceOf(address _owner) public view returns (uint256) { return stakers[_owner]; } function withdrawAll() public onlyGuardian { for (uint256 i = 0; i < validators.length; i++) { uint256 cur = consensus.delegatedAmount( address(this), validators[i] ); if (cur == 0) continue; undelegateWithCatch(validators[i], cur); } uint256 effectiveBalance = balance(); //use only undelegated funds pendingFuseEarnings = 0; if (effectiveBalance > 0) { msg.sender.call{ value: effectiveBalance }(""); } } function withdraw(uint256 _value) public returns (uint256) { uint256 effectiveBalance = balance(); //use only undelegated funds uint256 toWithdraw = _value == 0 ? stakers[msg.sender] : _value; uint256 toCollect = toWithdraw; require( toWithdraw > 0 && toWithdraw <= stakers[msg.sender], "invalid withdraw amount" ); uint256 perValidator = _value.div(validators.length); for (uint256 i = 0; i < validators.length; i++) { uint256 cur = consensus.delegatedAmount( address(this), validators[i] ); if (cur == 0) continue; if (cur <= perValidator) { undelegateWithCatch(validators[i], cur); toCollect = toCollect.sub(cur); } else { undelegateWithCatch(validators[i], perValidator); toCollect = toCollect.sub(perValidator); } if (toCollect == 0) break; } effectiveBalance = balance().sub(effectiveBalance); //use only undelegated funds // in case some funds where not withdrawn if (toWithdraw > effectiveBalance) { toWithdraw = effectiveBalance; } stakers[msg.sender] = stakers[msg.sender].sub(toWithdraw); if (toWithdraw > 0) { msg.sender.call{ value: toWithdraw }(""); } return toWithdraw; } function stakeNextValidator(uint256 _value, address _validator) internal returns (bool) { if (validators.length == 0) return false; if (_validator != address(0)) { consensus.delegate{ value: _value }(_validator); return true; } uint256 perValidator = (totalDelegated() + _value) / validators.length; uint256 left = _value; for (uint256 i = 0; i < validators.length && left > 0; i++) { uint256 cur = consensus.delegatedAmount( address(this), validators[i] ); if (cur < perValidator) { uint256 toDelegate = perValidator.sub(cur); toDelegate = toDelegate < left ? toDelegate : left; consensus.delegate{ value: toDelegate }(validators[i]); left = left.sub(toDelegate); } } return true; } function addValidator(address _v) public onlyOwner { validators.push(_v); } function totalDelegated() public view returns (uint256) { uint256 total = 0; for (uint256 i = 0; i < validators.length; i++) { uint256 cur = consensus.delegatedAmount( address(this), validators[i] ); total += cur; } return total; } function removeValidator(address _validator) public onlyOwner { uint256 delegated = consensus.delegatedAmount( address(this), _validator ); if (delegated > 0) { uint256 prevBalance = balance(); undelegateWithCatch(_validator, delegated); // wasnt withdrawn because validator needs to be taken of active validators if (balance() == prevBalance) { // pendingValidators.push(_validator); return; } } for (uint256 i = 0; i < validators.length; i++) { if (validators[i] == _validator) { if (i < validators.length - 1) validators[i] = validators[validators.length - 1]; validators.pop(); break; } } } function collectUBIInterest() public notPaused { uint256 curDay = ubischeme.currentDay(); require( curDay != lastDayCollected, "can collect only once in a ubi cycle" ); uint256 earnings = balance() - pendingFuseEarnings; require(pendingFuseEarnings + earnings > 0, "no earnings to collect"); lastDayCollected = curDay; uint256 fuseUBI = earnings.mul(RATIO_BASE - stakeBackRatio).div( RATIO_BASE ); uint256 stakeBack = earnings - fuseUBI; uint256[] memory fuseswapResult = _buyGD(fuseUBI + pendingFuseEarnings); //buy GD with X% of earnings pendingFuseEarnings = fuseUBI + pendingFuseEarnings - fuseswapResult[0]; stakeNextValidator(stakeBack, address(0)); //stake back the rest of the earnings uint256 gdBought = fuseswapResult[fuseswapResult.length - 1]; uint256 keeperFee = gdBought.mul(keeperFeeRatio).div(RATIO_BASE); if (keeperFee > 0) GD.transfer(msg.sender, keeperFee); gdBought -= keeperFee; uint256 communityPoolContribution = gdBought .mul(communityPoolRatio) .div(RATIO_BASE); //subtract fee // * ommunityPoolRatio // = G$ after fee * communityPoolRatio% uint256 ubiAfterFeeAndPool = gdBought.sub(communityPoolContribution); GD.transfer(address(ubischeme), ubiAfterFeeAndPool); //transfer to ubischeme communityPoolBalance += communityPoolContribution; emit UBICollected( curDay, ubiAfterFeeAndPool, communityPoolContribution, gdBought, earnings, pendingFuseEarnings, msg.sender, keeperFee ); } /** * @dev internal method to buy GD from fuseswap * @param _value fuse to be sold * @return uniswap coversion results uint256[2] */ function _buyGD(uint256 _value) internal returns (uint256[] memory) { //buy from uniwasp require(_value > 0, "buy value should be > 0"); (uint256 maxFuse, uint256 fuseGDOut) = calcMaxFuseWithPriceImpact( _value ); ( uint256 maxFuseUSDC, uint256 usdcGDOut ) = calcMaxFuseUSDCWithPriceImpact(_value); address[] memory path; if (maxFuse >= maxFuseUSDC) { path = new address[](2); path[1] = address(GD); path[0] = uniswap.WETH(); return uniswap.swapExactETHForTokens{ value: maxFuse }( (fuseGDOut * 95) / 100, path, address(this), block.timestamp ); } else { (uint256 usdcAmount, uint256 usedFuse) = _buyUSDC(maxFuseUSDC); path = new address[](2); path[1] = address(GD); path[0] = USDC; uint256[] memory result = uniswap.swapExactTokensForTokens( usdcAmount, (usdcGDOut * 95) / 100, path, address(this), block.timestamp ); //buyGD should return how much fuse was used in [0] and how much G$ we got in [1] result[0] = usedFuse; return result; } } /** * @dev internal method to buy USDC via fuse->fusd * @param _fuseIn fuse to be sold * @return usdcAmount and usedFuse how much usdc we got and how much fuse was used */ function _buyUSDC(uint256 _fuseIn) internal returns (uint256 usdcAmount, uint256 usedFuse) { //buy from uniwasp require(_fuseIn > 0, "buy value should be > 0"); UniswapPair uniswapFUSEfUSDPair = UniswapPair( uniswapFactory.getPair(uniswap.WETH(), fUSD) ); //fusd is pegged 1:1 to usdc (uint256 r_fuse, uint256 r_fusd, ) = uniswapFUSEfUSDPair.getReserves(); (uint256 maxFuse, uint256 tokenOut) = calcMaxTokenWithPriceImpact( r_fuse, r_fusd, _fuseIn ); //expect r_token to be in 18 decimals address[] memory path = new address[](2); path[1] = fUSD; path[0] = uniswap.WETH(); uint256[] memory result = uniswap.swapExactETHForTokens{ value: maxFuse }((tokenOut * 95) / 100, path, address(this), block.timestamp); pegSwap.swap(result[1], fUSD, USDC); usedFuse = result[0]; usdcAmount = result[1] / 1e12; //convert fusd from 1e18 to usdc 1e6 } function calcMaxFuseWithPriceImpact(uint256 _value) public view returns (uint256 fuseAmount, uint256 tokenOut) { (uint256 r_fuse, uint256 r_gd, ) = UniswapPair( uniswapFactory.getPair(uniswap.WETH(), address(GD)) ).getReserves(); return calcMaxTokenWithPriceImpact(r_fuse, r_gd, _value); } function calcMaxFuseUSDCWithPriceImpact(uint256 _value) public view returns (uint256 maxFuse, uint256 gdOut) { UniswapPair uniswapFUSEfUSDPair = UniswapPair( uniswapFactory.getPair(uniswap.WETH(), fUSD) ); //fusd is pegged 1:1 to usdc UniswapPair uniswapGDUSDCPair = UniswapPair( uniswapFactory.getPair(address(GD), USDC) ); (uint256 rg_gd, uint256 rg_usdc, ) = uniswapGDUSDCPair.getReserves(); (uint256 r_fuse, uint256 r_fusd, ) = uniswapFUSEfUSDPair.getReserves(); //fusd is 1e18 so to keep in original 1e18 precision we first multiply by 1e18 uint256 fusdPriceInFuse = r_fuse.mul(1e18).div(r_fusd); // console.log( // "rgd: %s rusdc:%s usdcPriceInFuse: %s", // rg_gd, // rg_usdc, // fusdPriceInFuse // ); // console.log("rfuse: %s rusdc:%s", r_fuse, r_fusd); //how many fusd we can get for fuse //value and usdPriceInFuse are in 1e18, we mul by 1e18 to keep 18 decimals precision uint256 fuseValueInfUSD = _value.mul(1e18).div(fusdPriceInFuse); // console.log("fuse fusd value: %s", fuseValueInfUSD); (uint256 maxUSDC, uint256 tokenOut) = calcMaxTokenWithPriceImpact( rg_usdc * 1e12, rg_gd, fuseValueInfUSD ); //expect r_token to be in 18 decimals // console.log("max USDC: %s", maxUSDC); gdOut = tokenOut; maxFuse = maxUSDC.mul(fusdPriceInFuse).div(1e18); //both are in 1e18 precision, div by 1e18 to keep precision } /** * uniswap amountOut helper */ function getAmountOut( uint256 _amountIn, uint256 _reserveIn, uint256 _reserveOut ) internal pure returns (uint256 amountOut) { uint256 amountInWithFee = _amountIn * 997; uint256 numerator = amountInWithFee * _reserveOut; uint256 denominator = _reserveIn * 1000 + amountInWithFee; amountOut = numerator / denominator; } /** * @dev use binary search to find quantity that will result with price impact < maxPriceImpactRatio */ function calcMaxTokenWithPriceImpact( uint256 r_token, uint256 r_gd, uint256 _value ) public view returns (uint256 maxToken, uint256 tokenOut) { maxToken = (r_token * maxSlippageRatio) / RATIO_BASE; maxToken = maxToken < _value ? maxToken : _value; tokenOut = getAmountOut(maxToken, r_token, r_gd); // uint256 start = 0; // uint256 end = _value.div(1e18); //save iterations by moving precision to whole Fuse quantity // // uint256 curPriceWei = uint256(1e18).mul(r_gd) / r_token; //uniswap quote formula UniswapV2Library.sol // uint256 gdForQuantity = getAmountOut(1e18, r_token, r_gd); // uint256 priceForQuantityWei = rdiv(1e18, gdForQuantity.mul(1e16)).div( // 1e9 // ); // uint256 maxPriceWei = priceForQuantityWei // .mul(RATIO_BASE.add(maxSlippageRatio)) // .div(RATIO_BASE); // // console.log( // // "curPrice: %s, maxPrice %s", // // priceForQuantityWei, // // maxPriceWei // // ); // fuseAmount = _value; // tokenOut; // //Iterate while start not meets end // while (start <= end) { // // Find the mid index // uint256 midQuantityWei = start.add(end).mul(1e18).div(2); //restore quantity precision // if (midQuantityWei == 0) break; // gdForQuantity = getAmountOut(midQuantityWei, r_token, r_gd); // priceForQuantityWei = rdiv(midQuantityWei, gdForQuantity.mul(1e16)) // .div(1e9); // // console.log( // // "gdForQuantity: %s, priceForQuantity: %s, midQuantity: %s", // // gdForQuantity, // // priceForQuantityWei, // // midQuantityWei // // ); // if (priceForQuantityWei <= maxPriceWei) { // start = midQuantityWei.div(1e18) + 1; //reduce precision to whole quantity div 1e18 // fuseAmount = midQuantityWei; // tokenOut = gdForQuantity; // } else end = midQuantityWei.div(1e18) - 1; //reduce precision to whole quantity div 1e18 // } } function undelegateWithCatch(address _validator, uint256 _amount) internal returns (bool) { try consensus.withdraw(_validator, _amount) { return true; } catch Error( string memory /*reason*/ ) { // This is executed in case // revert was called inside getData // and a reason string was provided. return false; } catch ( bytes memory /*lowLevelData*/ ) { // This is executed in case revert() was used // or there was a failing assertion, division // by zero, etc. inside getData. return false; } } function balance() internal view returns (uint256) { return payable(address(this)).balance; } function setPaused(bool _paused) external onlyGuardian { paused = _paused; } function setGuardian(address _guardian) external onlyGuardian { guardian = _guardian; } function collectCommunityPool(address _to, uint256 amount) external onlyGuardian { communityPoolBalance -= amount; GD.transfer(_to, amount); } receive() external payable {} }