ETH Price: $1,902.15 (-0.48%)

Transaction Decoder

Block:
15760210 at Oct-16-2022 10:54:23 AM +UTC
Transaction Fee:
0.00070218881105883 ETH $1.34
Gas Used:
46,285 Gas / 15.170980038 Gwei

Emitted Events:

250 FEGstakeV2.Approval( src=[Sender] 0xc9050630aaaa8c1c676c98357e237c8188bf9e0a, dst=[Receiver] FEGstakeV2, amt=1000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
(Coinbase: MEV Builder)
0.198059977352385265 Eth0.198094228252385265 Eth0.0000342509
0x4a9D6b95...b20fa2358
0xC9050630...188Bf9e0A
0.017372738288653816 Eth
Nonce: 22
0.016670549477594986 Eth
Nonce: 23
0.00070218881105883

Execution Trace

FEGstakeV2.approve( dst=0x4a9D6b95459eb9532B7E4d82Ca214a3b20fa2358, amt=1000000000000000000000000000000000000000000 ) => ( True )
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.7.6;



abstract contract ReentrancyGuard {

    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor () {
        _status = _NOT_ENTERED;
    }

    modifier nonReentrant() {

        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        _status = _ENTERED;

        _;

        _status = _NOT_ENTERED;
    }
}

// ----------------------------------------------------------------------------
// SafeMath library
// ----------------------------------------------------------------------------


library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
    
    function ceil(uint a, uint m) internal pure returns (uint r) {
        return (a + m - 1) / m * m;
    }
}

// ----------------------------------------------------------------------------
// Owned contract
// ----------------------------------------------------------------------------
contract Owned {
    address public owner;

    event OwnershipTransferred(address indexed _from, address indexed _to);

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

    function transferOwnership(address payable _newOwner) public onlyOwner {
        require(_newOwner != address(0), "ERC20: sending to the zero address");
        owner = _newOwner;
        emit OwnershipTransferred(msg.sender, _newOwner);
    }
}

// ----------------------------------------------------------------------------
// ERC Token Standard #20 Interface
// ----------------------------------------------------------------------------
interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address tokenOwner) external view returns (uint256 balance);
    function allowance(address tokenOwner, address spender) external view returns (uint256 remaining);
    function transfer(address to, uint256 tokens) external returns (bool success);
    function approve(address spender, uint256 tokens) external returns (bool success);
    function transferFrom(address from, address to, uint256 tokens) external returns (bool success);
    function burnTokens(uint256 _amount) external;
    
    function calculateFeesBeforeSend(
        address sender,
        address recipient,
        uint256 amount
    ) external view returns (uint256, uint256);
    
    
    event Transfer(address indexed from, address indexed to, uint256 tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint256 tokens);
}

interface regreward {
    function distributeV2() external;
}

interface FEGex2 {
    function BUY(
        address to,
        uint minAmountOut
    ) 
        external payable
        returns (uint tokenAmountOut, uint spotPriceAfter);
}

// ----------------------------------------------------------------------------
// ERC20 Token, with the addition of symbol, name and decimals and assisted
// token transfers
// ----------------------------------------------------------------------------

library Roles {
    struct Role {
        mapping (address => bool) bearer;
    }

    function add(Role storage role, address account) internal {
        require(!has(role, account), "Roles: account already has role");
        role.bearer[account] = true;
    }

    function remove(Role storage role, address account) internal {
        require(has(role, account), "Roles: account does not have role");
        role.bearer[account] = false;
    }

    function has(Role storage role, address account) internal view returns (bool) {
        require(account != address(0), "Roles: account is the zero address");
        return role.bearer[account];
    }
}

contract WhitelistAdminRole is Owned  {
    using Roles for Roles.Role;

    event WhitelistAdminAdded(address indexed account);
    event WhitelistAdminRemoved(address indexed account);

    Roles.Role private _whitelistAdmins;

   constructor () {
        _addWhitelistAdmin(msg.sender);
    }
    
    modifier onlyWhitelistAdmin() {
        require(isWhitelistAdmin(msg.sender), "WhitelistAdminRole: caller does not have the WhitelistAdmin role");
        _;
    }

    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);
    }
}

contract FNum is ReentrancyGuard{

    uint public constant BASE              = 10**18;
    
    function badd(uint a, uint b)
        internal pure
        returns (uint)
    {
        uint c = a + b;
        require(c >= a, "ERR_ADD_OVERFLOW");
        return c;
    }

    function bsub(uint a, uint b)
        internal pure
        returns (uint)
    {
        (uint c, bool flag) = bsubSign(a, b);
        require(!flag, "ERR_SUB_UNDERFLOW");
        return c;
    }

    function bsubSign(uint a, uint b)
        internal pure
        returns (uint, bool)
    {
        if (a >= b) {
            return (a - b, false);
        } else {
            return (b - a, true);
        }
    }

    function bmul(uint a, uint b)
        internal pure
        returns (uint)
    {
        uint c0 = a * b;
        require(a == 0 || c0 / a == b, "ERR_MUL_OVERFLOW");
        uint c1 = c0 + (BASE / 2);
        require(c1 >= c0, "ERR_MUL_OVERFLOW");
        uint c2 = c1 / BASE;
        return c2;
    }

    function bdiv(uint a, uint b)
        internal pure
        returns (uint)
    {
        require(b != 0, "ERR_DIV_ZERO");
        uint c0 = a * BASE;
        require(a == 0 || c0 / a == BASE, "ERR_DIV_INTERNAL"); // bmul overflow
        uint c1 = c0 + (b / 2);
        require(c1 >= c0, "ERR_DIV_INTERNAL"); //  badd require
        uint c2 = c1 / b;
        return c2;
    }
    
function btoi(uint a)
        internal pure
        returns (uint)
    {
        return a / BASE;
    }

    function bfloor(uint a)
        internal pure
        returns (uint)
    {
        return btoi(a) * BASE;
    }
    
function bpowi(uint a, uint n)
        internal pure
        returns (uint)
    {
        uint z = n % 2 != 0 ? a : BASE;

        for (n /= 2; n != 0; n /= 2) {
            a = bmul(a, a);

            if (n % 2 != 0) {
                z = bmul(z, a);
            }
        }
        return z;
    }

    function bpow(uint base, uint exp)
        internal pure
        returns (uint)
    {

        uint whole  = bfloor(exp);
        uint remain = bsub(exp, whole);

        uint wholePow = bpowi(base, btoi(whole));

        if (remain == 0) {
            return wholePow;
        }
        uint BPOW_PRECISION = BASE / 10**10;
        uint partialResult = bpowApprox(base, remain, BPOW_PRECISION);
        return bmul(wholePow, partialResult);
    }

    function bpowApprox(uint base, uint exp, uint precision)
        internal pure
        returns (uint)
    {
        // term 0:
        uint a     = exp;
        (uint x, bool xneg)  = bsubSign(base, BASE);
        uint term = BASE;
        uint sum   = term;
        bool negative = false;


        for (uint i = 1; term >= precision; i++) {
            uint bigK = i * BASE;
            (uint c, bool cneg) = bsubSign(a, bsub(bigK, BASE));
            term = bmul(term, bmul(c, x));
            term = bdiv(term, bigK);
            if (term == 0) break;

            if (xneg) negative = !negative;
            if (cneg) negative = !negative;
            if (negative) {
                sum = bsub(sum, term);
            } else {
                sum = badd(sum, term);
            }
        }

        return sum;
    }


}

contract FTokenBase is FNum {

    mapping(address => uint)                   internal _balance;
    mapping(address => mapping(address=>uint)) internal _allowance;
    uint public _totalSupply;

    event Approval(address indexed src, address indexed dst, uint amt);
    event Transfer(address indexed src, address indexed dst, uint amt);

    function _mint(uint amt) internal {
        _balance[address(this)] = badd(_balance[address(this)], amt);
        _totalSupply = badd(_totalSupply, amt);
        emit Transfer(address(0), address(this), amt);
    }

    function _burn(uint amt) internal {
        require(_balance[address(this)] >= amt);
        _balance[address(this)] = bsub(_balance[address(this)], amt);
        _totalSupply = bsub(_totalSupply, amt);
        emit Transfer(address(this), address(0), amt);
    }
    
    function _move(address src, address dst, uint amt) internal {
        require(_balance[src] >= amt);
        _balance[src] = bsub(_balance[src], amt);
        _balance[dst] = badd(_balance[dst], amt);
        emit Transfer(src, dst, amt);
    }

    function _push(address to, uint amt) internal {
        _move(address(this), to, amt);
    }

    function _pull(address from, uint amt) internal {
        _move(from, address(this), amt);
    }
}

contract FToken is FTokenBase {

    string  private _name     = "FEG Stake Shares";
    string  private _symbol   = "FSS";
    uint8   private _decimals = 18;

    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 allowance(address src, address dst) external view returns (uint) {
        return _allowance[src][dst];
    }

    function balanceOf(address whom) external view returns (uint) {
        return _balance[whom];
    }

    function totalSupply() public view returns (uint) {
        return _totalSupply;
    }

    function approve(address dst, uint amt) external returns (bool) {
        _allowance[msg.sender][dst] = amt;
        emit Approval(msg.sender, dst, amt);
        return true;
    }

    function increaseApproval(address dst, uint amt) external returns (bool) {
        _allowance[msg.sender][dst] = badd(_allowance[msg.sender][dst], amt);
        emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
        return true;
    }

    function decreaseApproval(address dst, uint amt) external returns (bool) {
        uint oldValue = _allowance[msg.sender][dst];
        if (amt > oldValue) {
            _allowance[msg.sender][dst] = 0;
        } else {
            _allowance[msg.sender][dst] = bsub(oldValue, amt);
        }
        emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
        return true;
    }

    function transfer(address dst, uint amt) external returns (bool) {
        _move(msg.sender, dst, amt);
        return true;
    }

    function transferFrom(address src, address dst, uint amt) external returns (bool) {
        require(msg.sender == src || amt <= _allowance[src][msg.sender]);
     
        _move(src, dst, amt);
        if (msg.sender != src && _allowance[src][msg.sender] != uint256(-1)) {
            _allowance[src][msg.sender] = bsub(_allowance[src][msg.sender], amt);
            emit Approval(msg.sender, dst, _allowance[src][msg.sender]);
        }
        return true;
    }
}

contract FEGstakeV2 is Owned, ReentrancyGuard, WhitelistAdminRole, FNum, FTokenBase, FToken{
    using SafeMath for uint256;
    
    address public FEG   = 0x389999216860AB8E0175387A0c90E5c52522C945;
    address public fETH  = 0xf786c34106762Ab4Eeb45a51B42a62470E9D5332;
    address public USDT  = 0x979838c9C16FD365C9fE028B0bEa49B1750d86e9;
    address public TRY   = 0xc12eCeE46ed65D970EE5C899FCC7AE133AfF9b03;
    address public FETP  = 0xa40462266dC28dB1d570FC8F8a0F4B72B8618f7a;
    address public BTC   = 0xe3cDB92b094a3BeF3f16103b53bECfb17A3558ad;
    address public poolShares = address(this);
    address public regrewardContract; //Signs The Checks
    
    bool public live = false;
    bool public perform = false; //if true then distribution of rewards from the pool to stakers via the withdraw function is enabled
    bool public perform2 = true; //if true then distribution of TX rewards from unclaimed 1 and 2 wrap's will distribute to stakers
    bool public perform3 = true; //if true then distribution of TX rewards from unclaimed 3rd wrap's will distribute to stakers
    uint256 public scailment = 20; // FEG has TX fee, deduct this fee to not break maths
    
    uint256 public totalDividends = 0;
    uint256 public must = 3e15;
    uint256 public scaleatize = 99;
    uint256 private scaledRemainder = 0;
    uint256 private scaling = uint256(10) ** 12;
    uint public round = 1;
    uint256 public totalDividends1 = 0;
    uint256 private scaledRemainder1 = 0;
    uint256 private scaling1 = uint256(10) ** 12;
    uint public round1 = 1;
    uint256 public totalDividends2 = 0;
    uint256 private scaledRemainder2 = 0;
    uint256 private scaling2 = uint256(10) ** 12;
    uint public round2 = 1;
    
    mapping(address => uint) public farmTime; // When you staked
    
    struct USER{
        uint256 lastDividends;
        uint256 fromTotalDividend;
        uint round;
        uint256 remainder;
        uint256 lastDividends1;
        uint256 fromTotalDividend1;
        uint round1;
        uint256 remainder1;
        uint256 lastDividends2;
        uint256 fromTotalDividend2;
        uint round2;
        uint256 remainder2;
        bool initialized;
        bool activated;
    } 
    
    address[] internal stakeholders;
    uint public scalerize = 98;
    uint256 public scaletor = 1e17;
    uint256 public scaletor1 = 20e18;
    uint256 public scaletor2 = 1e15;
    uint256 public totalWrap; //  total unclaimed fETH rewards
    uint256 public totalWrap1; //  total unclaimed usdt rewards
    uint256 public totalWrap2; //  total unclaimed btc rewards
    uint256 public totalWrapRef  = bsub(IERC20(fETH).balanceOf(address(this)), totalWrap); //total fETH reflections unclaimed
    uint256 public totalWrapRef1 = bsub(IERC20(USDT).balanceOf(address(this)), totalWrap1); //total usdt reflections unclaimed
    uint256 public totalWrapRef2 = bsub(IERC20(BTC).balanceOf(address(this)), totalWrap2); //total BTC reflections unclaimed
    mapping(address => USER) stakers;
    mapping (uint => uint256) public payouts;                   // keeps record of each payout
    mapping (uint => uint256) public payouts1;                   // keeps record of each payout
    mapping (uint => uint256) public payouts2;                   // keeps record of each payout
    FEGex2 fegexpair;
    event STAKED(address staker, uint256 tokens);
    event ACTIVATED(address staker, uint256 cost);
    event START(address staker, uint256 tokens);
    event EARNED(address staker, uint256 tokens);
    event UNSTAKED(address staker, uint256 tokens);
    event PAYOUT(uint256 round, uint256 tokens, address sender);
    event PAYOUT1(uint256 round, uint256 tokens, address sender);
    event PAYOUT2(uint256 round, uint256 tokens, address sender);
    event CLAIMEDREWARD(address staker, uint256 reward);
    event CLAIMEDREWARD1(address staker, uint256 reward);
    event CLAIMEDREWARD2(address staker, uint256 reward);
    
    constructor(){
    fegexpair = FEGex2(FETP);
    }
    
    receive() external payable {
    }

    function changeFEGExPair(FEGex2 _fegexpair, address addy) external onlyOwner{ // Incase FEGex updates in future
        require(address(_fegexpair) != address(0), "setting 0 to contract");
        fegexpair = _fegexpair;
        FETP = addy;
    }
    
    function changeTRY(address _try) external onlyOwner{ // Incase TRY updates in future
        TRY = _try;
    }
    
    function changeScalerize(uint _sca) public onlyOwner{
        require(_sca != 0, "You cannot turn off");
        scalerize = _sca;
    }
    
    function changeScalatize(uint _scm) public onlyOwner {
        require(_scm != 0, "You cannot turn off");
        scaleatize = _scm;
    }
    
    function isStakeholder(address _address)
       public
       view
       returns(bool)
   {
       
       if(stakers[_address].initialized) return true;
       else return false;
   }
   
   function addStakeholder(address _stakeholder)
       internal
   {
       (bool _isStakeholder) = isStakeholder(_stakeholder);
       if(!_isStakeholder) {
           farmTime[msg.sender] =  block.timestamp;
           stakers[_stakeholder].initialized = true;
       }
   }
   
   // ------------------------------------------------------------------------
    // Token holders can stake their tokens using this function
    // @param tokens number of tokens to stake
    // ------------------------------------------------------------------------

    function calcPoolInGivenSingleOut(
        uint tokenBalanceOut,
        uint tokenWeightOut,
        uint poolSupply,
        uint totalWeight,
        uint tokenAmountOut,
        uint swapFee
    )
        public pure
        returns (uint poolAmountIn)
    {


        uint normalizedWeight = bdiv(tokenWeightOut, totalWeight);
        uint zar = bmul(bsub(BASE, normalizedWeight), swapFee);
        uint tokenAmountOutBeforeSwapFee = bdiv(tokenAmountOut, bsub(BASE, zar));

        uint newTokenBalanceOut = bsub(tokenBalanceOut, tokenAmountOutBeforeSwapFee);
        uint tokenOutRatio = bdiv(newTokenBalanceOut, tokenBalanceOut);


        uint poolRatio = bpow(tokenOutRatio, normalizedWeight);
        uint newPoolSupply = bmul(poolRatio, poolSupply);
        uint poolAmountInAfterExitFee = bsub(poolSupply, newPoolSupply);


        poolAmountIn = bdiv(poolAmountInAfterExitFee, bsub(BASE, 0));
        return (poolAmountIn);
    }
    
    function calcSingleOutGivenPoolIn(
        uint tokenBalanceOut,
        uint tokenWeightOut,
        uint poolSupply,
        uint totalWeight,
        uint poolAmountIn,
        uint swapFee
    )
        public pure
        returns (uint tokenAmountOut)
    {
        uint normalizedWeight = bdiv(tokenWeightOut, totalWeight);

        uint poolAmountInAfterExitFee = bmul(poolAmountIn, bsub(BASE, 0));
        uint newPoolSupply = bsub(poolSupply, poolAmountInAfterExitFee);
        uint poolRatio = bdiv(newPoolSupply, poolSupply);


        uint tokenOutRatio = bpow(poolRatio, bdiv(BASE, normalizedWeight));
        uint newTokenBalanceOut = bmul(tokenOutRatio, tokenBalanceOut);

        uint tokenAmountOutBeforeSwapFee = bsub(tokenBalanceOut, newTokenBalanceOut);
        uint zaz = bmul(bsub(BASE, normalizedWeight), swapFee);
        tokenAmountOut = bmul(tokenAmountOutBeforeSwapFee, bsub(BASE, zaz));
        return tokenAmountOut;
    }

    function calcPoolOutGivenSingleIn(
        uint tokenBalanceIn,
        uint tokenWeightIn,
        uint poolSupply,
        uint totalWeight,
        uint tokenAmountIn,
        uint swapFee
    )
        public pure
        returns (uint poolAmountOut)
    {

        uint normalizedWeight = bdiv(tokenWeightIn, totalWeight);
        uint zaz = bmul(bsub(BASE, normalizedWeight), swapFee);
        uint tokenAmountInAfterFee = bmul(tokenAmountIn, bsub(BASE, zaz));

        uint newTokenBalanceIn = badd(tokenBalanceIn, tokenAmountInAfterFee);
        uint tokenInRatio = bdiv(newTokenBalanceIn, tokenBalanceIn);

 
        uint poolRatio = bpow(tokenInRatio, normalizedWeight);
        uint newPoolSupply = bmul(poolRatio, poolSupply);
        poolAmountOut = bsub(newPoolSupply, poolSupply);
        return (poolAmountOut);
    }
     
    function calcOutGivenIn(
        uint tokenBalanceIn,
        uint tokenWeightIn,
        uint tokenBalanceOut,
        uint tokenWeightOut,
        uint tokenAmountIn,
        uint swapFee
    )
        public pure
        returns (uint tokenAmountOut, uint tokenInFee)
    {
        uint weightRatio = bdiv(tokenWeightIn, tokenWeightOut);
        uint adjustedIn = bsub(BASE, swapFee);
        adjustedIn = bmul(tokenAmountIn, adjustedIn);
        uint y = bdiv(tokenBalanceIn, badd(tokenBalanceIn, adjustedIn));
        uint foo = bpow(y, weightRatio);
        uint bar = bsub(BASE, foo);
        tokenAmountOut = bmul(tokenBalanceOut, bar);
        tokenInFee = bsub(tokenAmountIn, adjustedIn);
        return (tokenAmountOut, tokenInFee);
    }

    function calcInGivenOut(
        uint tokenBalanceIn,
        uint tokenWeightIn,
        uint tokenBalanceOut,
        uint tokenWeightOut,
        uint tokenAmountOut,
        uint swapFee
    )
        public pure
        returns (uint tokenAmountIn)
    {
        uint weightRatio = bdiv(tokenWeightOut, tokenWeightIn);
        uint diff = bsub(tokenBalanceOut, tokenAmountOut);
        uint y = bdiv(tokenBalanceOut, diff);
        uint foo = bpow(y, weightRatio);
        foo = bsub(foo, BASE);
        foo = bmul(tokenBalanceIn, foo);
        tokenAmountIn = bsub(BASE, swapFee);
        tokenAmountIn = bdiv(foo, tokenAmountIn);
        return (tokenAmountIn);
    }

    function activateUserStaking() public payable{ // Activation of FEGstake costs 0.02 fETH which is automatically refunded to your wallet in the form of TRY.
        require(msg.value == must, "You must deposit the right amount to activate");
      
        fegexpair.BUY{value: msg.value }(msg.sender, 100);
        stakers[msg.sender].activated = true;
        
        emit ACTIVATED(msg.sender, msg.value);
    }

    function isActivated(address staker) public view returns(bool){
        if(stakers[staker].activated) return true;
       else return false;
    }
    
    function Start(uint256 tokens) public onlyOwner returns(uint poolAmountOut){
        require(live == false, "Can only use once");
        require(IERC20(FEG).transferFrom(msg.sender, address(this), tokens), "Tokens cannot be transferred from user for locking");
        uint256 transferTxFee = (onePercent(tokens).mul(scailment)).div(10);
        uint256 tokensToStake = (tokens.sub(transferTxFee));
        addStakeholder(msg.sender);

        _mint(tokensToStake);
        live = true;
        IERC20(poolShares).transfer(msg.sender, tokensToStake);
        IERC20(address(fETH)).approve(address(FETP), 1000000000000000000000e18);        
    
        emit START(msg.sender, tokensToStake);    
        
        return poolAmountOut;
    }
    
    function STAKE(uint256 tokens) public returns(uint poolAmountOut){ 
        require(IERC20(FEG).balanceOf(msg.sender) >= tokens, "You do not have enough FEG");
        require(stakers[msg.sender].activated == true);
        require(live == true);
        uint256 transferTxFee = (onePercent(tokens).mul(scailment)).div(10);
        uint256 tokensToStake = (tokens.sub(transferTxFee));
        uint256 totalFEG = IERC20(FEG).balanceOf(address(this));
        addStakeholder(msg.sender);
        
        // add pending rewards to remainder to be claimed by user later, if there is any existing stake
            uint256 owing = pendingReward(msg.sender);
            stakers[msg.sender].remainder += owing;
            stakers[msg.sender].lastDividends = owing;
            stakers[msg.sender].fromTotalDividend = totalDividends;
            stakers[msg.sender].round =  round;
            
            uint256 owing1 = pendingReward1(msg.sender);
            stakers[msg.sender].remainder1 += owing1;
            stakers[msg.sender].lastDividends1 = owing1;
            stakers[msg.sender].fromTotalDividend1 = totalDividends1;
            stakers[msg.sender].round1 =  round1;
            
            uint256 owing2 = pendingReward2(msg.sender);
            stakers[msg.sender].remainder2 += owing2;
            stakers[msg.sender].lastDividends2 = owing2;
            stakers[msg.sender].fromTotalDividend2 = totalDividends2;
            stakers[msg.sender].round2 =  round2;
            
        poolAmountOut = calcPoolOutGivenSingleIn(
                            totalFEG,
                            bmul(BASE, 25),
                            _totalSupply,
                            bmul(BASE, 25),
                            tokensToStake,
                            0
                        );
                        
        require(IERC20(FEG).transferFrom(msg.sender, address(this), tokens), "Tokens cannot be transferred from user for locking");                
        _mint(poolAmountOut);
        IERC20(poolShares).transfer(msg.sender, poolAmountOut);
            
        emit STAKED(msg.sender, tokens); 
            
        return poolAmountOut;
        
    }

    
    // ------------------------------------------------------------------------
    // Owners can send the funds to be distributed to stakers using this function
    // @param tokens number of tokens to distribute
    // ------------------------------------------------------------------------

    function ADDFUNDS1(uint256 tokens) public onlyWhitelistAdmin{
        require(IERC20(fETH).transferFrom(msg.sender, address(this), tokens), "Tokens cannot be transferred from funder account");
        
        uint256 tokens_ = bmul(tokens, bdiv(99, 100));
        totalWrap = badd(totalWrap, tokens_);
        _addPayout(tokens_);
    }
    
    function ADDFUNDS2(uint256 tokens) public onlyWhitelistAdmin{
        require(IERC20(USDT).transferFrom(msg.sender, address(this), tokens), "Tokens cannot be transferred from funder account");
        
        uint256 tokens_ = bmul(tokens, bdiv(99, 100));
        totalWrap1 = badd(totalWrap1, tokens_);
        _addPayout1(tokens_);
    }
    
    function ADDFUNDS3(uint256 tokens) public onlyWhitelistAdmin{
        require(IERC20(BTC).transferFrom(msg.sender, address(this), tokens), "Tokens cannot be transferred from funder account");
        uint256 tokens_ = bmul(tokens, bdiv(99, 100));
        totalWrap2 = badd(totalWrap2, tokens_);
        _addPayout2(tokens_);
    }
    
    // ------------------------------------------------------------------------
    // Private function to register payouts
    // ------------------------------------------------------------------------
    function _addPayout(uint256 tokens_) private {
         // divide the funds among the currently staked tokens
        // scale the deposit and add the previous remainder
        uint256 totalShares = _totalSupply;
        uint256 available = (tokens_.mul(scaling)).add(scaledRemainder); 
        uint256 dividendPerToken = available.div(totalShares);
        scaledRemainder = available.mod(totalShares);
        totalDividends = totalDividends.add(dividendPerToken);
        payouts[round] = payouts[round - 1].add(dividendPerToken);
        emit PAYOUT(round, tokens_, msg.sender);
        round++;
        
    }
    
    function _addPayout1(uint256 tokens_1) private{
        // divide the funds among the currently staked tokens
        // scale the deposit and add the previous remainder
        uint256 totalShares = _totalSupply;
        uint256 available = (tokens_1.mul(scaling)).add(scaledRemainder1); 
        uint256 dividendPerToken = available.div(totalShares);
        scaledRemainder1 = available.mod(totalShares);
        totalDividends1 = totalDividends1.add(dividendPerToken);
        payouts1[round1] = payouts1[round1 - 1].add(dividendPerToken);
        emit PAYOUT1(round1, tokens_1, msg.sender);
        round1++;
    }
    
    function _addPayout2(uint256 tokens_2) private{
        // divide the funds among the currently staked tokens
        // scale the deposit and add the previous remainder
        uint256 totalShares = _totalSupply;
        uint256 available = (tokens_2.mul(scaling)).add(scaledRemainder2); 
        uint256 dividendPerToken = available.div(totalShares);
        scaledRemainder2 = available.mod(totalShares);
        totalDividends2 = totalDividends2.add(dividendPerToken);
        payouts2[round2] = payouts2[round2 - 1].add(dividendPerToken);
        emit PAYOUT2(round2, tokens_2, msg.sender);
        round2++;
    }
    
    // ------------------------------------------------------------------------
    // Stakers can claim their pending rewards using this function
    // ------------------------------------------------------------------------
    function CLAIMREWARD() public nonReentrant{
        
            uint256 owing = pendingReward(msg.sender);
        if(owing > 0){
            owing = owing.add(stakers[msg.sender].remainder);
            stakers[msg.sender].remainder = 0;
        
            require(IERC20(fETH).transfer(msg.sender,owing), "ERROR: error in sending reward from contract");
        
            emit CLAIMEDREWARD(msg.sender, owing);
            totalWrap = bsub(totalWrap, owing);
            stakers[msg.sender].lastDividends = owing; // unscaled
            stakers[msg.sender].round = round; // update the round
            stakers[msg.sender].fromTotalDividend = totalDividends; // scaled
        }
    }
    
    function CLAIMREWARD1() public nonReentrant {
        
            uint256 owing1 = pendingReward1(msg.sender);
        if(owing1 > 0){
            owing1 = owing1.add(stakers[msg.sender].remainder1);
            stakers[msg.sender].remainder1 = 0;
        
            require(IERC20(USDT).transfer(msg.sender,owing1), "ERROR: error in sending reward from contract");
        
            emit CLAIMEDREWARD1(msg.sender, owing1);
            totalWrap1 = bsub(totalWrap1, owing1);
            stakers[msg.sender].lastDividends1 = owing1; // unscaled
            stakers[msg.sender].round1 = round1; // update the round
            stakers[msg.sender].fromTotalDividend1 = totalDividends1; // scaled
        }
    }
    
    function CLAIMREWARD2() public nonReentrant {
      
            uint256 owing2 = pendingReward2(msg.sender);
        if(owing2 > 0){
            owing2 = owing2.add(stakers[msg.sender].remainder2);
            stakers[msg.sender].remainder2 = 0;
        
            require(IERC20(BTC).transfer(msg.sender, owing2), "ERROR: error in sending reward from contract");
        
            emit CLAIMEDREWARD2(msg.sender, owing2);
            totalWrap2 = bsub(totalWrap2, owing2);
            stakers[msg.sender].lastDividends2 = owing2; // unscaled
            stakers[msg.sender].round2 = round2; // update the round
            stakers[msg.sender].fromTotalDividend2 = totalDividends2; // scaled
        }
    }
    
    function CLAIMALLREWARD() public { 
        distribute12();
        CLAIMREWARD();
        CLAIMREWARD1();
        
        if(perform3==true){
        distribute23();    
        CLAIMREWARD2();   
        }
    }
    
    // ------------------------------------------------------------------------
    // Get the pending rewards of the staker
    // @param _staker the address of the staker
    // ------------------------------------------------------------------------    

    function pendingReward(address staker) private returns (uint256) {
        require(staker != address(0), "ERC20: sending to the zero address");
        uint256 yourBase = IERC20(poolShares).balanceOf(msg.sender);
        uint stakersRound = stakers[staker].round;
        uint256 amount =  ((totalDividends.sub(payouts[stakersRound - 1])).mul(yourBase)).div(scaling);
        stakers[staker].remainder += ((totalDividends.sub(payouts[stakersRound - 1])).mul(yourBase)) % scaling;
        return (bmul(amount, bdiv(scalerize, 100)));
    }
    
    function pendingReward1(address staker) private returns (uint256) {
        require(staker != address(0), "ERC20: sending to the zero address");
        uint256 yourBase = IERC20(poolShares).balanceOf(msg.sender);
        uint stakersRound = stakers[staker].round1;
        uint256 amount1 =  ((totalDividends1.sub(payouts1[stakersRound - 1])).mul(yourBase)).div(scaling);
        stakers[staker].remainder1 += ((totalDividends1.sub(payouts1[stakersRound - 1])).mul(yourBase)) % scaling;
        return (bmul(amount1, bdiv(scalerize, 100)));
    }
    
    function pendingReward2(address staker) private returns (uint256) {
        require(staker != address(0), "ERC20: sending to the zero address");
        uint256 yourBase = IERC20(poolShares).balanceOf(msg.sender);
        uint stakersRound = stakers[staker].round2;
        uint256 amount2 =  ((totalDividends2.sub(payouts2[stakersRound - 1])).mul(yourBase)).div(scaling);
        stakers[staker].remainder2 += ((totalDividends2.sub(payouts2[stakersRound - 1])).mul(yourBase)) % scaling;
        return (bmul(amount2, bdiv(scalerize, 100)));
    }
    
    function getPending1(address staker) public view returns(uint256 _pendingReward) {
        require(staker != address(0), "ERC20: sending to the zero address");
        uint256 yourBase = IERC20(poolShares).balanceOf(staker);
        uint stakersRound = stakers[staker].round; 
        uint256 amount =  ((totalDividends.sub(payouts[stakersRound - 1])).mul(yourBase)).div(scaling);
        amount += ((totalDividends.sub(payouts[stakersRound - 1])).mul(yourBase)) % scaling;
        return (bmul(amount.add(stakers[staker].remainder), bdiv(scalerize, 100)));
    }
    
    function getPending2(address staker) public view returns(uint256 _pendingReward) {
        require(staker != address(0), "ERC20: sending to the zero address");
        uint256 yourBase = IERC20(poolShares).balanceOf(staker);
        uint stakersRound = stakers[staker].round1;
        uint256 amount1 = ((totalDividends1.sub(payouts1[stakersRound - 1])).mul(yourBase)).div(scaling);
        amount1 += ((totalDividends1.sub(payouts1[stakersRound - 1])).mul(yourBase)) % scaling;
        return (bmul(amount1.add(stakers[staker].remainder1), bdiv(scalerize, 100)));
    }
    
    function getPending3(address staker) public view returns(uint256 _pendingReward) {
        require(staker != address(0), "ERC20: sending to the zero address");
        uint256 yourBase = IERC20(poolShares).balanceOf(staker);
        uint stakersRound = stakers[staker].round2;
        uint256 amount2 =  ((totalDividends2.sub(payouts2[stakersRound - 1])).mul(yourBase)).div(scaling);
        amount2 += ((totalDividends2.sub(payouts2[stakersRound - 1])).mul(yourBase)) % scaling;
        return (bmul(amount2.add(stakers[staker].remainder2), bdiv(scalerize, 100)));
    
    }
        // ------------------------------------------------------------------------
    // Get the FEG balance of the token holder
    // @param user the address of the token holder
    // ------------------------------------------------------------------------
    function userStakedFEG(address user) external view returns(uint256 StakedFEG){
        require(user != address(0), "ERC20: sending to the zero address");
        uint256 totalFEG = IERC20(FEG).balanceOf(address(this));
        uint256 yourStakedFEG = calcSingleOutGivenPoolIn(
                            totalFEG, 
                            bmul(BASE, 25),
                            _totalSupply,
                            bmul(BASE, 25),
                            IERC20(poolShares).balanceOf(address(user)),
                            0
                        );
        
        return yourStakedFEG;
    }
    
    // ------------------------------------------------------------------------
    // Stakers can un stake the staked tokens using this function
    // @param tokens the number of tokens to withdraw
    // ------------------------------------------------------------------------
    function WITHDRAW(address to, uint256 _tokens) external returns (uint tokenAmountOut) {
        uint256 totalFEG = IERC20(FEG).balanceOf(address(this));
        require(stakers[msg.sender].activated == true);
        
        if(perform==true) {
        regreward(regrewardContract).distributeV2();
        }
        
        CLAIMALLREWARD();

        uint256 tokens = calcPoolInGivenSingleOut(
                            totalFEG,
                            bmul(BASE, 25),
                            _totalSupply,
                            bmul(BASE, 25),
                            _tokens,
                            0
                        );
                        
        tokenAmountOut = calcSingleOutGivenPoolIn(
                            totalFEG, 
                            bmul(BASE, 25),
                            _totalSupply,
                            bmul(BASE, 25),
                            tokens,
                            0
                        ); 
        require(tokens <= IERC20(poolShares).balanceOf(msg.sender), "You don't have enough FEG");
        _pullPoolShare(tokens);
        _burn(tokens);
        require(IERC20(FEG).transfer(to, tokenAmountOut), "Error in un-staking tokens");
        
        emit UNSTAKED(msg.sender, tokens);
        
        return tokenAmountOut;
    }
    
    function _pullPoolShare(uint amount)
        internal
    {
        bool xfer = IERC20(poolShares).transferFrom(msg.sender, address(this), amount);
        require(xfer, "ERR_ERC20_FALSE");
    }    

    // ------------------------------------------------------------------------
    // Private function to calculate 1% percentage
    // ------------------------------------------------------------------------
    function onePercent(uint256 _tokens) private pure returns (uint256){
        uint256 roundValue = _tokens.ceil(100);
        uint onePercentofTokens = roundValue.mul(100).div(100 * 10**uint(2));
        return onePercentofTokens;
    }
    
    function emergencySaveLostTokens(address to, address _token, uint256 _amt) public onlyOwner {
        require(_token != FEG, "Cannot remove users FEG");
        require(_token != fETH, "Cannot remove users fETH");
        require(_token != USDT, "Cannot remove users fUSDT");
        require(_token != BTC, "Cannot remove users fBTC");
        require(IERC20(_token).transfer(to, _amt), "Error in retrieving tokens");
        payable(owner).transfer(address(this).balance);
    }
    
    function changeregrewardContract(address _regrewardContract) external onlyOwner{
        require(address(_regrewardContract) != address(0), "setting 0 to contract");
        regrewardContract = _regrewardContract;
    }
   
    function changePerform(bool _bool) external onlyOwner{
        perform = _bool;
    }

    function changePerform2(bool _bool) external onlyOwner{
        perform2 = _bool;
    }
    
    function changePerform3(bool _bool) external onlyOwner{
        perform3 = _bool;
    }
    
    function changeMust(uint256 _must) external onlyOwner{
        require(must !=0, "Cannot set to 0");
        require(must <= 3e15, "Cannot set over 0.003 fETH");
        must = _must;
    }
    
    function updateBase(address _BTC, address _ETH, address _USDT) external onlyOwner{ // Incase wraps ever update
        BTC = _BTC;
        fETH = _ETH;
        USDT = _USDT;
    }
    
    function distribute12() public {
        if (IERC20(fETH).balanceOf(address(this)) > badd(totalWrap, scaletor))  {
        distributeWrap1();
        }
        
        if(IERC20(USDT).balanceOf(address(this)) > badd(totalWrap1, scaletor1)){
        distributeWrap2();
        }
    }
    
    function distribute23() public {    
        if(perform3==true){
            if(IERC20(BTC).balanceOf(address(this)) > badd(totalWrap2, scaletor2)){
        distributeWrap3();}
        }
    }
    
    function changeScaletor(uint256 _sca, uint256 _sca1, uint256 _sca2) public onlyOwner {
        require(_sca !=0 && _sca1 !=0 && _sca2 !=0, "You cannot turn off");
        require(_sca >= 5e17 && _sca1 >= 20e18 && _sca2 >= 1e15, "Must be over minimum");
        scaletor = _sca;
        scaletor1 = _sca1;
        scaletor2 = _sca2;
    }
    
    function distributeWrap1() internal {
        uint256 wrapped = bsub(IERC20(fETH).balanceOf(address(this)), totalWrap);
        totalWrap = badd(totalWrap, wrapped);
        _addPayout(wrapped);
    }

    function distributeWrap2() internal {
        uint256 wrapped = bsub(IERC20(USDT).balanceOf(address(this)), totalWrap1);
        totalWrap1 = badd(totalWrap1, wrapped);
        _addPayout1(wrapped);
    }
    
    function distributeWrap3() internal {
        uint256 wrapped = bsub(IERC20(BTC).balanceOf(address(this)), totalWrap2);
        totalWrap2 = badd(totalWrap2, wrapped);
        _addPayout2(wrapped);
    }
    }