ETH Price: $1,982.86 (-4.86%)

Transaction Decoder

Block:
12386752 at May-07-2021 10:59:18 AM +UTC
Transaction Fee:
0.006314082 ETH $12.52
Gas Used:
154,002 Gas / 41 Gwei

Emitted Events:

395 Gauge.Staked( user=[Sender] 0x72c8c4c9c1a03d7898bfcb2582205443a4bf618d, amount=93805678814959185965 )
396 PickleJar.Transfer( from=[Sender] 0x72c8c4c9c1a03d7898bfcb2582205443a4bf618d, to=[Receiver] Gauge, value=93805678814959185965 )
397 PickleJar.Approval( owner=[Sender] 0x72c8c4c9c1a03d7898bfcb2582205443a4bf618d, spender=[Receiver] Gauge, value=115792089237316195423570985008687907853269984665640564039363778329098170453970 )

Account State Difference:

  Address   Before After State Difference Code
0x72C8c4C9...3a4Bf618D
(Abyss Finance: Eth2 Depositor 10)
25.957185310395163625 Eth
Nonce: 7
25.950871228395163625 Eth
Nonce: 8
0.006314082
(Flexpool.io)
948.588734406946232644 Eth948.595048488946232644 Eth0.006314082
0xCeD67a18...20099e378
0xDA481b27...57CF31634

Execution Trace

Gauge.deposit( amount=93805678814959185965 )
  • PickleJar.transferFrom( sender=0x72C8c4C9c1A03D7898BfCB2582205443a4Bf618D, recipient=0xDA481b277dCe305B97F4091bD66595d57CF31634, amount=93805678814959185965 ) => ( True )
  • Vyper_contract.STATICCALL( )
  • Vyper_contract.balanceOf( addr=0x72C8c4C9c1A03D7898BfCB2582205443a4Bf618D ) => ( 0 )
    File 1 of 3: Gauge
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.7; //^0.7.5;
    library SafeMath {
        function add(uint a, uint b) internal pure returns (uint) {
            uint c = a + b;
            require(c >= a, "add: +");
            return c;
        }
        function add(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
            uint c = a + b;
            require(c >= a, errorMessage);
            return c;
        }
        function sub(uint a, uint b) internal pure returns (uint) {
            return sub(a, b, "sub: -");
        }
        function sub(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
            require(b <= a, errorMessage);
            uint c = a - b;
            return c;
        }
        function mul(uint a, uint b) internal pure returns (uint) {
            // 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;
            }
            uint c = a * b;
            require(c / a == b, "mul: *");
            return c;
        }
        function mul(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
            if (a == 0) {
                return 0;
            }
            uint c = a * b;
            require(c / a == b, errorMessage);
            return c;
        }
        function div(uint a, uint b) internal pure returns (uint) {
            return div(a, b, "div: /");
        }
        function div(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
            require(b > 0, errorMessage);
            uint c = a / b;
            return c;
        }
    }
    library Address {
        function isContract(address account) internal view returns (bool) {
            bytes32 codehash;
            bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
            // solhint-disable-next-line no-inline-assembly
            assembly { codehash := extcodehash(account) }
            return (codehash != 0x0 && codehash != accountHash);
        }
        function toPayable(address account) internal pure returns (address payable) {
            return address(uint160(account));
        }
        function sendValue(address payable recipient, uint256 amount) internal {
            require(address(this).balance >= amount, "Address: insufficient balance");
            // solhint-disable-next-line avoid-call-value
            (bool success, ) = recipient.call{value:amount}("");
            require(success, "Address: unable to send value, recipient may have reverted");
        }
    }
    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);
    }
    library SafeERC20 {
        using SafeMath for uint256;
        using Address for address;
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
        }
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
        }
        function safeApprove(IERC20 token, address spender, uint256 value) internal {
            require((value == 0) || (token.allowance(address(this), spender) == 0),
                "SafeERC20: approve from non-zero to non-zero allowance"
            );
            callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
        }
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 newAllowance = token.allowance(address(this), spender).add(value);
            callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
            callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        function callOptionalReturn(IERC20 token, bytes memory data) private {
            require(address(token).isContract(), "SafeERC20: call to non-contract");
            // solhint-disable-next-line avoid-low-level-calls
            (bool success, bytes memory returndata) = address(token).call(data);
            require(success, "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 Math {
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a >= b ? a : b;
        }
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow, so we distribute
            return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
        }
    }
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
        uint256 private _status;
        constructor () public {
            _status = _NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and make it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            // On the first call to nonReentrant, _notEntered will be true
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
            _;
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            _status = _NOT_ENTERED;
        }
    }
    contract Gauge is ReentrancyGuard {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        
        IERC20 public constant PICKLE = IERC20(0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5);
        IERC20 public constant DILL = IERC20(0xbBCf169eE191A1Ba7371F30A1C344bFC498b29Cf);
        address public constant TREASURY = address(0x066419EaEf5DE53cc5da0d8702b990c5bc7D1AB3);
        
        IERC20 public immutable TOKEN;
        address public immutable DISTRIBUTION;
        uint256 public constant DURATION = 7 days;
        
        uint256 public periodFinish = 0;
        uint256 public rewardRate = 0;
        uint256 public lastUpdateTime;
        uint256 public rewardPerTokenStored;
        
        modifier onlyDistribution() {
            require(msg.sender == DISTRIBUTION, "Caller is not RewardsDistribution contract");
            _;
        }
        
        mapping(address => uint256) public userRewardPerTokenPaid;
        mapping(address => uint256) public rewards;
        uint256 private _totalSupply;
        uint public derivedSupply;
        mapping(address => uint256) private _balances;
        mapping(address => uint256) public derivedBalances;
        mapping(address => uint) private _base;
        
        constructor(address _token) public {
            TOKEN = IERC20(_token);
            DISTRIBUTION = msg.sender;
        }
        
        function totalSupply() external view returns (uint256) {
            return _totalSupply;
        }
        function balanceOf(address account) external view returns (uint256) {
            return _balances[account];
        }
        function lastTimeRewardApplicable() public view returns (uint256) {
            return Math.min(block.timestamp, periodFinish);
        }
        function rewardPerToken() public view returns (uint256) {
            if (_totalSupply == 0) {
                return rewardPerTokenStored;
            }
            return
                rewardPerTokenStored.add(
                    lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(1e18).div(derivedSupply)
                );
        }
        
        function derivedBalance(address account) public view returns (uint) {
            uint _balance = _balances[account];
            uint _derived = _balance.mul(40).div(100);
            uint _adjusted = (_totalSupply.mul(DILL.balanceOf(account)).div(DILL.totalSupply())).mul(60).div(100);
            return Math.min(_derived.add(_adjusted), _balance);
        }
        
        function kick(address account) public {
            uint _derivedBalance = derivedBalances[account];
            derivedSupply = derivedSupply.sub(_derivedBalance);
            _derivedBalance = derivedBalance(account);
            derivedBalances[account] = _derivedBalance;
            derivedSupply = derivedSupply.add(_derivedBalance);
        }
        function earned(address account) public view returns (uint256) {
            return derivedBalances[account].mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add(rewards[account]);
        }
        function getRewardForDuration() external view returns (uint256) {
            return rewardRate.mul(DURATION);
        }
        
        function depositAll() external {
            _deposit(TOKEN.balanceOf(msg.sender), msg.sender);
        }
        
        function deposit(uint256 amount) external {
            _deposit(amount, msg.sender);
        }
        
        function depositFor(uint256 amount, address account) external {
            _deposit(amount, account);
        }
        
        function _deposit(uint amount, address account) internal nonReentrant updateReward(account) {
            require(amount > 0, "Cannot stake 0");
            _totalSupply = _totalSupply.add(amount);
            _balances[account] = _balances[account].add(amount);
            emit Staked(account, amount);
            TOKEN.safeTransferFrom(account, address(this), amount);
        }
        
        function withdrawAll() external {
            _withdraw(_balances[msg.sender]);
        }
        function withdraw(uint256 amount) external {
            _withdraw(amount);
        }
        
        function _withdraw(uint amount) internal nonReentrant updateReward(msg.sender) {
            require(amount > 0, "Cannot withdraw 0");
            _totalSupply = _totalSupply.sub(amount);
            _balances[msg.sender] = _balances[msg.sender].sub(amount);
            TOKEN.safeTransfer(msg.sender, amount);
            emit Withdrawn(msg.sender, amount);
        }
        function getReward() public nonReentrant updateReward(msg.sender) {
            uint256 reward = rewards[msg.sender];
            if (reward > 0) {
                rewards[msg.sender] = 0;
                PICKLE.safeTransfer(msg.sender, reward);
                emit RewardPaid(msg.sender, reward);
            }
        }
        function exit() external {
           _withdraw(_balances[msg.sender]);
            getReward();
        }
        
        function notifyRewardAmount(uint256 reward) external onlyDistribution updateReward(address(0)) {
            PICKLE.safeTransferFrom(DISTRIBUTION, address(this), reward);
            if (block.timestamp >= periodFinish) {
                rewardRate = reward.div(DURATION);
            } else {
                uint256 remaining = periodFinish.sub(block.timestamp);
                uint256 leftover = remaining.mul(rewardRate);
                rewardRate = reward.add(leftover).div(DURATION);
            }
            // Ensure the provided reward amount is not more than the balance in the contract.
            // This keeps the reward rate in the right range, preventing overflows due to
            // very high values of rewardRate in the earned and rewardsPerToken functions;
            // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.
            uint balance = PICKLE.balanceOf(address(this));
            require(rewardRate <= balance.div(DURATION), "Provided reward too high");
            lastUpdateTime = block.timestamp;
            periodFinish = block.timestamp.add(DURATION);
            emit RewardAdded(reward);
        }
        modifier updateReward(address account) {
            rewardPerTokenStored = rewardPerToken();
            lastUpdateTime = lastTimeRewardApplicable();
            if (account != address(0)) {
                rewards[account] = earned(account);
                userRewardPerTokenPaid[account] = rewardPerTokenStored;
            }
            _;
            if (account != address(0)) {
                kick(account);
            }
        }
        event RewardAdded(uint256 reward);
        event Staked(address indexed user, uint256 amount);
        event Withdrawn(address indexed user, uint256 amount);
        event RewardPaid(address indexed user, uint256 reward);
    }
    interface MasterChef {
        function deposit(uint, uint) external;
        function withdraw(uint, uint) external;
        function userInfo(uint, address) external view returns (uint, uint);
    }
    contract ProtocolGovernance {
        /// @notice governance address for the governance contract
        address public governance;
        address public pendingGovernance;
        
        /**
         * @notice Allows governance to change governance (for future upgradability)
         * @param _governance new governance address to set
         */
        function setGovernance(address _governance) external {
            require(msg.sender == governance, "setGovernance: !gov");
            pendingGovernance = _governance;
        }
        /**
         * @notice Allows pendingGovernance to accept their role as governance (protection pattern)
         */
        function acceptGovernance() external {
            require(msg.sender == pendingGovernance, "acceptGovernance: !pendingGov");
            governance = pendingGovernance;
        }
    }
    contract MasterDill {
        using SafeMath for uint;
        /// @notice EIP-20 token name for this token
        string public constant name = "Master DILL";
        /// @notice EIP-20 token symbol for this token
        string public constant symbol = "mDILL";
        /// @notice EIP-20 token decimals for this token
        uint8 public constant decimals = 18;
        /// @notice Total number of tokens in circulation
        uint public totalSupply = 1e18;
        mapping (address => mapping (address => uint)) internal allowances;
        mapping (address => uint) internal balances;
        /// @notice The standard EIP-20 transfer event
        event Transfer(address indexed from, address indexed to, uint amount);
        /// @notice The standard EIP-20 approval event
        event Approval(address indexed owner, address indexed spender, uint amount);
        
        constructor() public {
            balances[msg.sender] = 1e18;
            emit Transfer(address(0x0), msg.sender, 1e18);
        }
        /**
         * @notice Get the number of tokens `spender` is approved to spend on behalf of `account`
         * @param account The address of the account holding the funds
         * @param spender The address of the account spending the funds
         * @return The number of tokens approved
         */
        function allowance(address account, address spender) external view returns (uint) {
            return allowances[account][spender];
        }
        /**
         * @notice Approve `spender` to transfer up to `amount` from `src`
         * @dev This will overwrite the approval amount for `spender`
         *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
         * @param spender The address of the account which may transfer tokens
         * @param amount The number of tokens that are approved (2^256-1 means infinite)
         * @return Whether or not the approval succeeded
         */
        function approve(address spender, uint amount) external returns (bool) {
            allowances[msg.sender][spender] = amount;
            emit Approval(msg.sender, spender, amount);
            return true;
        }
        /**
         * @notice Get the number of tokens held by the `account`
         * @param account The address of the account to get the balance of
         * @return The number of tokens held
         */
        function balanceOf(address account) external view returns (uint) {
            return balances[account];
        }
        /**
         * @notice Transfer `amount` tokens from `msg.sender` to `dst`
         * @param dst The address of the destination account
         * @param amount The number of tokens to transfer
         * @return Whether or not the transfer succeeded
         */
        function transfer(address dst, uint amount) external returns (bool) {
            _transferTokens(msg.sender, dst, amount);
            return true;
        }
        /**
         * @notice Transfer `amount` tokens from `src` to `dst`
         * @param src The address of the source account
         * @param dst The address of the destination account
         * @param amount The number of tokens to transfer
         * @return Whether or not the transfer succeeded
         */
        function transferFrom(address src, address dst, uint amount) external returns (bool) {
            address spender = msg.sender;
            uint spenderAllowance = allowances[src][spender];
            if (spender != src && spenderAllowance != uint(-1)) {
                uint newAllowance = spenderAllowance.sub(amount, "transferFrom: exceeds spender allowance");
                allowances[src][spender] = newAllowance;
                emit Approval(src, spender, newAllowance);
            }
            _transferTokens(src, dst, amount);
            return true;
        }
        function _transferTokens(address src, address dst, uint amount) internal {
            require(src != address(0), "_transferTokens: zero address");
            require(dst != address(0), "_transferTokens: zero address");
            balances[src] = balances[src].sub(amount, "_transferTokens: exceeds balance");
            balances[dst] = balances[dst].add(amount, "_transferTokens: overflows");
            emit Transfer(src, dst, amount);
        }
    }
    contract GaugeProxy is ProtocolGovernance {
        using SafeMath for uint256;
        using SafeERC20 for IERC20;
        
        MasterChef public constant MASTER = MasterChef(0xbD17B1ce622d73bD438b9E658acA5996dc394b0d);
        IERC20 public constant DILL = IERC20(0xbBCf169eE191A1Ba7371F30A1C344bFC498b29Cf);
        IERC20 public constant PICKLE = IERC20(0x429881672B9AE42b8EbA0E26cD9C73711b891Ca5);
        
        IERC20 public immutable TOKEN;
        
        uint public pid;
        uint public totalWeight;
        
        address[] internal _tokens;
        mapping(address => address) public gauges; // token => gauge
        mapping(address => uint) public weights; // token => weight
        mapping(address => mapping(address => uint)) public votes; // msg.sender => votes
        mapping(address => address[]) public tokenVote;// msg.sender => token
        mapping(address => uint) public usedWeights;  // msg.sender => total voting weight of user
        
        function tokens() external view returns (address[] memory) {
            return _tokens;
        }
        
        function getGauge(address _token) external view returns (address) {
            return gauges[_token];
        }
        
        constructor() public {
            TOKEN = IERC20(address(new MasterDill()));
            governance = msg.sender;
        }
        
        // Reset votes to 0
        function reset() external {
            _reset(msg.sender);
        }
        
        // Reset votes to 0
        function _reset(address _owner) internal {
            address[] storage _tokenVote = tokenVote[_owner];
            uint256 _tokenVoteCnt = _tokenVote.length;
            for (uint i = 0; i < _tokenVoteCnt; i ++) {
                address _token = _tokenVote[i];
                uint _votes = votes[_owner][_token];
                
                if (_votes > 0) {
                    totalWeight = totalWeight.sub(_votes);
                    weights[_token] = weights[_token].sub(_votes);
                    
                    votes[_owner][_token] = 0;
                }
            }
            delete tokenVote[_owner];
        }
        
        // Adjusts _owner's votes according to latest _owner's DILL balance
        function poke(address _owner) public {
            address[] memory _tokenVote = tokenVote[_owner];
            uint256 _tokenCnt = _tokenVote.length;
            uint256[] memory _weights = new uint[](_tokenCnt);
            
            uint256 _prevUsedWeight = usedWeights[_owner];
            uint256 _weight = DILL.balanceOf(_owner);        
            for (uint256 i = 0; i < _tokenCnt; i ++) {
                uint256 _prevWeight = votes[_owner][_tokenVote[i]];
                _weights[i] = _prevWeight.mul(_weight).div(_prevUsedWeight);
            }
            _vote(_owner, _tokenVote, _weights);
        }
        
        function _vote(address _owner, address[] memory _tokenVote, uint256[] memory _weights) internal {
            // _weights[i] = percentage * 100
            _reset(_owner);
            uint256 _tokenCnt = _tokenVote.length;
            uint256 _weight = DILL.balanceOf(_owner);
            uint256 _totalVoteWeight = 0;
            uint256 _usedWeight = 0;
            for (uint256 i = 0; i < _tokenCnt; i ++) {
                _totalVoteWeight = _totalVoteWeight.add(_weights[i]);
            }
            for (uint256 i = 0; i < _tokenCnt; i ++) {
                address _token = _tokenVote[i];
                address _gauge = gauges[_token];
                uint256 _tokenWeight = _weights[i].mul(_weight).div(_totalVoteWeight);
                if (_gauge != address(0x0)) {
                    _usedWeight = _usedWeight.add(_tokenWeight);
                    totalWeight = totalWeight.add(_tokenWeight);
                    weights[_token] = weights[_token].add(_tokenWeight);
                    tokenVote[_owner].push(_token);
                    votes[_owner][_token] = _tokenWeight;
                }
            }
            usedWeights[_owner] = _usedWeight;
        }
        
        
        // Vote with DILL on a gauge
        function vote(address[] calldata _tokenVote, uint256[] calldata _weights) external {
            require(_tokenVote.length == _weights.length);
            _vote(msg.sender, _tokenVote, _weights);
        }
        
        // Add new token gauge
        function addGauge(address _token) external {
            require(msg.sender == governance, "!gov");
            require(gauges[_token] == address(0x0), "exists");
            gauges[_token] = address(new Gauge(_token));
            _tokens.push(_token);
        }
        
        
        // Sets MasterChef PID
        function setPID(uint _pid) external {
            require(msg.sender == governance, "!gov");
            require(pid == 0, "pid has already been set");
            require(_pid > 0, "invalid pid");
            pid = _pid;
        }
        
        
        // Deposits mDILL into MasterChef
        function deposit() public {
            require(pid > 0, "pid not initialized");
            IERC20 _token = TOKEN;
            uint _balance = _token.balanceOf(address(this));
            _token.safeApprove(address(MASTER), 0);
            _token.safeApprove(address(MASTER), _balance);
            MASTER.deposit(pid, _balance);
        }
        
        
        // Fetches Pickle
        function collect() public {
            (uint _locked,) = MASTER.userInfo(pid, address(this));
            MASTER.withdraw(pid, _locked);
            deposit();
        }
        
        function length() external view returns (uint) {
            return _tokens.length;
        }
        
        function distribute() external {
            collect();
            uint _balance = PICKLE.balanceOf(address(this));
            if (_balance > 0 && totalWeight > 0) {
                for (uint i = 0; i < _tokens.length; i++) {
                    address _token = _tokens[i];
                    address _gauge = gauges[_token];
                    uint _reward = _balance.mul(weights[_token]).div(totalWeight);
                    if (_reward > 0) {
                        PICKLE.safeApprove(_gauge, 0);
                        PICKLE.safeApprove(_gauge, _reward);
                        Gauge(_gauge).notifyRewardAmount(_reward);
                    }
                }
            }
        }
    }
    

    File 2 of 3: PickleJar
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    interface IController {
        function jars(address) external view returns (address);
        function rewards() external view returns (address);
        function devfund() external view returns (address);
        function treasury() external view returns (address);
        function balanceOf(address) external view returns (uint256);
        function withdraw(address, uint256) external;
        function earn(address, uint256) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.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 GSN 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 payable) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes memory) {
            this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
            return msg.data;
        }
    }
    
    // File: contracts/GSN/Context.sol
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    import "./safe-math.sol";
    import "./context.sol";
    // File: contracts/token/ERC20/IERC20.sol
    /**
     * @dev Interface of the ERC20 standard as defined in the EIP.
     */
    interface IERC20 {
        /**
         * @dev Returns the amount of tokens in existence.
         */
        function totalSupply() external view returns (uint256);
        /**
         * @dev Returns the amount of tokens owned by `account`.
         */
        function balanceOf(address account) external view returns (uint256);
        /**
         * @dev Moves `amount` tokens from the caller's account to `recipient`.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transfer(address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Returns the remaining number of tokens that `spender` will be
         * allowed to spend on behalf of `owner` through {transferFrom}. This is
         * zero by default.
         *
         * This value changes when {approve} or {transferFrom} are called.
         */
        function allowance(address owner, address spender) external view returns (uint256);
        /**
         * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * IMPORTANT: Beware that changing an allowance with this method brings the risk
         * that someone may use both the old and the new allowance by unfortunate
         * transaction ordering. One possible solution to mitigate this race
         * condition is to first reduce the spender's allowance to 0 and set the
         * desired value afterwards:
         * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
         *
         * Emits an {Approval} event.
         */
        function approve(address spender, uint256 amount) external returns (bool);
        /**
         * @dev Moves `amount` tokens from `sender` to `recipient` using the
         * allowance mechanism. `amount` is then deducted from the caller's
         * allowance.
         *
         * Returns a boolean value indicating whether the operation succeeded.
         *
         * Emits a {Transfer} event.
         */
        function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
        /**
         * @dev Emitted when `value` tokens are moved from one account (`from`) to
         * another (`to`).
         *
         * Note that `value` may be zero.
         */
        event Transfer(address indexed from, address indexed to, uint256 value);
        /**
         * @dev Emitted when the allowance of a `spender` for an `owner` is set by
         * a call to {approve}. `value` is the new allowance.
         */
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    // File: contracts/utils/Address.sol
    /**
     * @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");
            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);
                }
            }
        }
    }
    // File: contracts/token/ERC20/ERC20.sol
    /**
     * @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 Context, IERC20 {
        using SafeMath for uint256;
        using Address for address;
        mapping (address => uint256) private _balances;
        mapping (address => mapping (address => uint256)) private _allowances;
        uint256 private _totalSupply;
        string private _name;
        string private _symbol;
        uint8 private _decimals;
        /**
         * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
         * a default value of 18.
         *
         * To select a different value for {decimals}, use {_setupDecimals}.
         *
         * All three of these values are immutable: they can only be set once during
         * construction.
         */
        constructor (string memory name, string memory symbol) public {
            _name = name;
            _symbol = symbol;
            _decimals = 18;
        }
        /**
         * @dev Returns the name of the token.
         */
        function name() public view returns (string memory) {
            return _name;
        }
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        function symbol() public view returns (string memory) {
            return _symbol;
        }
        /**
         * @dev Returns the number of decimals used to get its user representation.
         * For example, if `decimals` equals `2`, a balance of `505` tokens should
         * be displayed to a user as `5,05` (`505 / 10 ** 2`).
         *
         * Tokens usually opt for a value of 18, imitating the relationship between
         * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
         * called.
         *
         * NOTE: This information is only used for _display_ purposes: it in
         * no way affects any of the arithmetic of the contract, including
         * {IERC20-balanceOf} and {IERC20-transfer}.
         */
        function decimals() public view returns (uint8) {
            return _decimals;
        }
        /**
         * @dev See {IERC20-totalSupply}.
         */
        function totalSupply() public view override returns (uint256) {
            return _totalSupply;
        }
        /**
         * @dev See {IERC20-balanceOf}.
         */
        function balanceOf(address account) public view override returns (uint256) {
            return _balances[account];
        }
        /**
         * @dev See {IERC20-transfer}.
         *
         * Requirements:
         *
         * - `recipient` cannot be the zero address.
         * - the caller must have a balance of at least `amount`.
         */
        function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
            _transfer(_msgSender(), 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(_msgSender(), 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, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
            return true;
        }
        /**
         * @dev Atomically increases the allowance granted to `spender` by the caller.
         *
         * This is an alternative to {approve} that can be used as a mitigation for
         * problems described in {IERC20-approve}.
         *
         * Emits an {Approval} event indicating the updated allowance.
         *
         * Requirements:
         *
         * - `spender` cannot be the zero address.
         */
        function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
            _approve(_msgSender(), spender, _allowances[_msgSender()][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(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
            return true;
        }
        /**
         * @dev Moves tokens `amount` from `sender` to `recipient`.
         *
         * This is internal function is equivalent to {transfer}, and can be used to
         * e.g. implement automatic token fees, slashing mechanisms, etc.
         *
         * Emits a {Transfer} event.
         *
         * Requirements:
         *
         * - `sender` cannot be the zero address.
         * - `recipient` cannot be the zero address.
         * - `sender` must have a balance of at least `amount`.
         */
        function _transfer(address sender, address recipient, uint256 amount) internal virtual {
            require(sender != address(0), "ERC20: transfer from the zero address");
            require(recipient != address(0), "ERC20: transfer to the zero address");
            _beforeTokenTransfer(sender, recipient, amount);
            _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
            _balances[recipient] = _balances[recipient].add(amount);
            emit Transfer(sender, recipient, amount);
        }
        /** @dev Creates `amount` tokens and assigns them to `account`, increasing
         * the total supply.
         *
         * Emits a {Transfer} event with `from` set to the zero address.
         *
         * Requirements
         *
         * - `to` cannot be the zero address.
         */
        function _mint(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: mint to the zero address");
            _beforeTokenTransfer(address(0), account, amount);
            _totalSupply = _totalSupply.add(amount);
            _balances[account] = _balances[account].add(amount);
            emit Transfer(address(0), account, amount);
        }
        /**
         * @dev Destroys `amount` tokens from `account`, reducing the
         * total supply.
         *
         * Emits a {Transfer} event with `to` set to the zero address.
         *
         * Requirements
         *
         * - `account` cannot be the zero address.
         * - `account` must have at least `amount` tokens.
         */
        function _burn(address account, uint256 amount) internal virtual {
            require(account != address(0), "ERC20: burn from the zero address");
            _beforeTokenTransfer(account, address(0), amount);
            _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
            _totalSupply = _totalSupply.sub(amount);
            emit Transfer(account, address(0), amount);
        }
        /**
         * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
         *
         * This internal function is equivalent to `approve`, and can be used to
         * e.g. set automatic allowances for certain subsystems, etc.
         *
         * Emits an {Approval} event.
         *
         * Requirements:
         *
         * - `owner` cannot be the zero address.
         * - `spender` cannot be the zero address.
         */
        function _approve(address owner, address spender, uint256 amount) internal virtual {
            require(owner != address(0), "ERC20: approve from the zero address");
            require(spender != address(0), "ERC20: approve to the zero address");
            _allowances[owner][spender] = amount;
            emit Approval(owner, spender, amount);
        }
        /**
         * @dev Sets {decimals} to a value other than the default one of 18.
         *
         * WARNING: This function should only be called from the constructor. Most
         * applications that interact with token contracts will not expect
         * {decimals} to ever change, and may work incorrectly if it does.
         */
        function _setupDecimals(uint8 decimals_) internal {
            _decimals = decimals_;
        }
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be to transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
    }
    /**
     * @title SafeERC20
     * @dev Wrappers around ERC20 operations that throw on failure (when the token
     * contract returns false). Tokens that return no value (and instead revert or
     * throw on failure) are also supported, non-reverting calls are assumed to be
     * successful.
     * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
     * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
     */
    library SafeERC20 {
        using SafeMath for uint256;
        using Address for address;
        function safeTransfer(IERC20 token, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
        }
        function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
            _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
        }
        /**
         * @dev Deprecated. This function has issues similar to the ones found in
         * {IERC20-approve}, and its usage is discouraged.
         *
         * Whenever possible, use {safeIncreaseAllowance} and
         * {safeDecreaseAllowance} instead.
         */
        function safeApprove(IERC20 token, address spender, uint256 value) internal {
            // safeApprove should only be called when setting an initial allowance,
            // or when resetting it to zero. To increase and decrease it, use
            // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
            // solhint-disable-next-line max-line-length
            require((value == 0) || (token.allowance(address(this), spender) == 0),
                "SafeERC20: approve from non-zero to non-zero allowance"
            );
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
        }
        function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 newAllowance = token.allowance(address(this), spender).add(value);
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
            uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
        /**
         * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
         * on the return value: the return value is optional (but if data is returned, it must not be false).
         * @param token The token targeted by the call.
         * @param data The call data (encoded using abi.encode or one of its variants).
         */
        function _callOptionalReturn(IERC20 token, bytes memory data) private {
            // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
            // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
            // the target address contains contract code and also asserts for success in the low-level call.
            bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
            if (returndata.length > 0) { // Return data is optional
                // solhint-disable-next-line max-line-length
                require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
            }
        }
    }// SPDX-License-Identifier: MIT
    pragma solidity ^0.6.0;
    /**
     * @dev Wrappers over Solidity's arithmetic operations with added overflow
     * checks.
     *
     * Arithmetic operations in Solidity wrap on overflow. This can easily result
     * in bugs, because programmers usually assume that an overflow raises an
     * error, which is the standard behavior in high level programming languages.
     * `SafeMath` restores this intuition by reverting the transaction when an
     * operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     */
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         *
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            uint256 c = a + b;
            require(c >= a, "SafeMath: addition overflow");
            return c;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            return sub(a, b, "SafeMath: subtraction overflow");
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b <= a, errorMessage);
            uint256 c = a - b;
            return c;
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         *
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) {
                return 0;
            }
            uint256 c = a * b;
            require(c / a == b, "SafeMath: multiplication overflow");
            return c;
        }
        /**
         * @dev Returns the integer division of two unsigned integers. Reverts on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            return div(a, b, "SafeMath: division by zero");
        }
        /**
         * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b > 0, errorMessage);
            uint256 c = a / b;
            // assert(a == b * c + a % b); // There is no case in which this doesn't hold
            return c;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * Reverts when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            return mod(a, b, "SafeMath: modulo by zero");
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * Reverts with custom message when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            require(b != 0, errorMessage);
            return a % b;
        }
    }// https://github.com/iearn-finance/vaults/blob/master/contracts/vaults/yVault.sol
    pragma solidity ^0.6.7;
    import "./interfaces/controller.sol";
    import "./lib/erc20.sol";
    import "./lib/safe-math.sol";
    contract PickleJar is ERC20 {
        using SafeERC20 for IERC20;
        using Address for address;
        using SafeMath for uint256;
        IERC20 public token;
        uint256 public min = 9500;
        uint256 public constant max = 10000;
        address public governance;
        address public timelock;
        address public controller;
        constructor(address _token, address _governance, address _timelock, address _controller)
            public
            ERC20(
                string(abi.encodePacked("pickling ", ERC20(_token).name())),
                string(abi.encodePacked("p", ERC20(_token).symbol()))
            )
        {
            _setupDecimals(ERC20(_token).decimals());
            token = IERC20(_token);
            governance = _governance;
            timelock = _timelock;
            controller = _controller;
        }
        function balance() public view returns (uint256) {
            return
                token.balanceOf(address(this)).add(
                    IController(controller).balanceOf(address(token))
                );
        }
        function setMin(uint256 _min) external {
            require(msg.sender == governance, "!governance");
            require(_min <= max, "numerator cannot be greater than denominator");
            min = _min;
        }
        function setGovernance(address _governance) public {
            require(msg.sender == governance, "!governance");
            governance = _governance;
        }
        function setTimelock(address _timelock) public {
            require(msg.sender == timelock, "!timelock");
            timelock = _timelock;
        }
        function setController(address _controller) public {
            require(msg.sender == timelock, "!timelock");
            controller = _controller;
        }
        // Custom logic in here for how much the jars allows to be borrowed
        // Sets minimum required on-hand to keep small withdrawals cheap
        function available() public view returns (uint256) {
            return token.balanceOf(address(this)).mul(min).div(max);
        }
        function earn() public {
            uint256 _bal = available();
            token.safeTransfer(controller, _bal);
            IController(controller).earn(address(token), _bal);
        }
        function depositAll() external {
            deposit(token.balanceOf(msg.sender));
        }
        function deposit(uint256 _amount) public {
            uint256 _pool = balance();
            uint256 _before = token.balanceOf(address(this));
            token.safeTransferFrom(msg.sender, address(this), _amount);
            uint256 _after = token.balanceOf(address(this));
            _amount = _after.sub(_before); // Additional check for deflationary tokens
            uint256 shares = 0;
            if (totalSupply() == 0) {
                shares = _amount;
            } else {
                shares = (_amount.mul(totalSupply())).div(_pool);
            }
            _mint(msg.sender, shares);
        }
        function withdrawAll() external {
            withdraw(balanceOf(msg.sender));
        }
        // Used to swap any borrowed reserve over the debt limit to liquidate to 'token'
        function harvest(address reserve, uint256 amount) external {
            require(msg.sender == controller, "!controller");
            require(reserve != address(token), "token");
            IERC20(reserve).safeTransfer(controller, amount);
        }
        // No rebalance implementation for lower fees and faster swaps
        function withdraw(uint256 _shares) public {
            uint256 r = (balance().mul(_shares)).div(totalSupply());
            _burn(msg.sender, _shares);
            // Check balance
            uint256 b = token.balanceOf(address(this));
            if (b < r) {
                uint256 _withdraw = r.sub(b);
                IController(controller).withdraw(address(token), _withdraw);
                uint256 _after = token.balanceOf(address(this));
                uint256 _diff = _after.sub(b);
                if (_diff < _withdraw) {
                    r = b.add(_diff);
                }
            }
            token.safeTransfer(msg.sender, r);
        }
        function getRatio() public view returns (uint256) {
            return balance().mul(1e18).div(totalSupply());
        }
    }
    

    File 3 of 3: Vyper_contract
    # @version 0.2.4
    """
    @title Voting Escrow
    @author Curve Finance
    @license MIT
    @notice Votes have a weight depending on time, so that users are
            committed to the future of (whatever they are voting for)
    @dev Vote weight decays linearly over time. Lock time cannot be
         more than `MAXTIME` (4 years).
    """
    
    # Voting escrow to have time-weighted votes
    # Votes have a weight depending on time, so that users are committed
    # to the future of (whatever they are voting for).
    # The weight in this implementation is linear, and lock cannot be more than maxtime:
    # w ^
    # 1 +        /
    #   |      /
    #   |    /
    #   |  /
    #   |/
    # 0 +--------+------> time
    #       maxtime (4 years?)
    
    struct Point:
        bias: int128
        slope: int128  # - dweight / dt
        ts: uint256
        blk: uint256  # block
    # We cannot really do block numbers per se b/c slope is per time, not per block
    # and per block could be fairly bad b/c Ethereum changes blocktimes.
    # What we can do is to extrapolate ***At functions
    
    struct LockedBalance:
        amount: int128
        end: uint256
    
    
    interface ERC20:
        def decimals() -> uint256: view
        def name() -> String[64]: view
        def symbol() -> String[32]: view
        def transfer(to: address, amount: uint256) -> bool: nonpayable
        def transferFrom(spender: address, to: address, amount: uint256) -> bool: nonpayable
    
    
    # Interface for checking whether address belongs to a whitelisted
    # type of a smart wallet.
    # When new types are added - the whole contract is changed
    # The check() method is modifying to be able to use caching
    # for individual wallet addresses
    interface SmartWalletChecker:
        def check(addr: address) -> bool: nonpayable
    
    DEPOSIT_FOR_TYPE: constant(int128) = 0
    CREATE_LOCK_TYPE: constant(int128) = 1
    INCREASE_LOCK_AMOUNT: constant(int128) = 2
    INCREASE_UNLOCK_TIME: constant(int128) = 3
    
    
    event CommitOwnership:
        admin: address
    
    event ApplyOwnership:
        admin: address
    
    event Deposit:
        provider: indexed(address)
        value: uint256
        locktime: indexed(uint256)
        type: int128
        ts: uint256
    
    event Withdraw:
        provider: indexed(address)
        value: uint256
        ts: uint256
    
    event Supply:
        prevSupply: uint256
        supply: uint256
    
    
    WEEK: constant(uint256) = 7 * 86400  # all future times are rounded by week
    MAXTIME: constant(uint256) = 4 * 365 * 86400  # 4 years
    MULTIPLIER: constant(uint256) = 10 ** 18
    
    token: public(address)
    supply: public(uint256)
    
    locked: public(HashMap[address, LockedBalance])
    
    epoch: public(uint256)
    point_history: public(Point[100000000000000000000000000000])  # epoch -> unsigned point
    user_point_history: public(HashMap[address, Point[1000000000]])  # user -> Point[user_epoch]
    user_point_epoch: public(HashMap[address, uint256])
    slope_changes: public(HashMap[uint256, int128])  # time -> signed slope change
    
    # Aragon's view methods for compatibility
    controller: public(address)
    transfersEnabled: public(bool)
    
    name: public(String[64])
    symbol: public(String[32])
    version: public(String[32])
    decimals: public(uint256)
    
    # Checker for whitelisted (smart contract) wallets which are allowed to deposit
    # The goal is to prevent tokenizing the escrow
    future_smart_wallet_checker: public(address)
    smart_wallet_checker: public(address)
    
    admin: public(address)  # Can and will be a smart contract
    future_admin: public(address)
    
    
    @external
    def __init__(token_addr: address, _name: String[64], _symbol: String[32], _version: String[32]):
        """
        @notice Contract constructor
        @param token_addr `ERC20CRV` token address
        @param _name Token name
        @param _symbol Token symbol
        @param _version Contract version - required for Aragon compatibility
        """
        self.admin = msg.sender
        self.token = token_addr
        self.point_history[0].blk = block.number
        self.point_history[0].ts = block.timestamp
        self.controller = msg.sender
        self.transfersEnabled = True
    
        _decimals: uint256 = ERC20(token_addr).decimals()
        assert _decimals <= 255
        self.decimals = _decimals
    
        self.name = _name
        self.symbol = _symbol
        self.version = _version
    
    
    @external
    def commit_transfer_ownership(addr: address):
        """
        @notice Transfer ownership of VotingEscrow contract to `addr`
        @param addr Address to have ownership transferred to
        """
        assert msg.sender == self.admin  # dev: admin only
        self.future_admin = addr
        log CommitOwnership(addr)
    
    
    @external
    def apply_transfer_ownership():
        """
        @notice Apply ownership transfer
        """
        assert msg.sender == self.admin  # dev: admin only
        _admin: address = self.future_admin
        assert _admin != ZERO_ADDRESS  # dev: admin not set
        self.admin = _admin
        log ApplyOwnership(_admin)
    
    
    @external
    def commit_smart_wallet_checker(addr: address):
        """
        @notice Set an external contract to check for approved smart contract wallets
        @param addr Address of Smart contract checker
        """
        assert msg.sender == self.admin
        self.future_smart_wallet_checker = addr
    
    
    @external
    def apply_smart_wallet_checker():
        """
        @notice Apply setting external contract to check approved smart contract wallets
        """
        assert msg.sender == self.admin
        self.smart_wallet_checker = self.future_smart_wallet_checker
    
    
    @internal
    def assert_not_contract(addr: address):
        """
        @notice Check if the call is from a whitelisted smart contract, revert if not
        @param addr Address to be checked
        """
        if addr != tx.origin:
            checker: address = self.smart_wallet_checker
            if checker != ZERO_ADDRESS:
                if SmartWalletChecker(checker).check(addr):
                    return
            raise "Smart contract depositors not allowed"
    
    
    @external
    @view
    def get_last_user_slope(addr: address) -> int128:
        """
        @notice Get the most recently recorded rate of voting power decrease for `addr`
        @param addr Address of the user wallet
        @return Value of the slope
        """
        uepoch: uint256 = self.user_point_epoch[addr]
        return self.user_point_history[addr][uepoch].slope
    
    
    @external
    @view
    def user_point_history__ts(_addr: address, _idx: uint256) -> uint256:
        """
        @notice Get the timestamp for checkpoint `_idx` for `_addr`
        @param _addr User wallet address
        @param _idx User epoch number
        @return Epoch time of the checkpoint
        """
        return self.user_point_history[_addr][_idx].ts
    
    
    @external
    @view
    def locked__end(_addr: address) -> uint256:
        """
        @notice Get timestamp when `_addr`'s lock finishes
        @param _addr User wallet
        @return Epoch time of the lock end
        """
        return self.locked[_addr].end
    
    
    @internal
    def _checkpoint(addr: address, old_locked: LockedBalance, new_locked: LockedBalance):
        """
        @notice Record global and per-user data to checkpoint
        @param addr User's wallet address. No user checkpoint if 0x0
        @param old_locked Pevious locked amount / end lock time for the user
        @param new_locked New locked amount / end lock time for the user
        """
        u_old: Point = empty(Point)
        u_new: Point = empty(Point)
        old_dslope: int128 = 0
        new_dslope: int128 = 0
        _epoch: uint256 = self.epoch
    
        if addr != ZERO_ADDRESS:
            # Calculate slopes and biases
            # Kept at zero when they have to
            if old_locked.end > block.timestamp and old_locked.amount > 0:
                u_old.slope = old_locked.amount / MAXTIME
                u_old.bias = u_old.slope * convert(old_locked.end - block.timestamp, int128)
            if new_locked.end > block.timestamp and new_locked.amount > 0:
                u_new.slope = new_locked.amount / MAXTIME
                u_new.bias = u_new.slope * convert(new_locked.end - block.timestamp, int128)
    
            # Read values of scheduled changes in the slope
            # old_locked.end can be in the past and in the future
            # new_locked.end can ONLY by in the FUTURE unless everything expired: than zeros
            old_dslope = self.slope_changes[old_locked.end]
            if new_locked.end != 0:
                if new_locked.end == old_locked.end:
                    new_dslope = old_dslope
                else:
                    new_dslope = self.slope_changes[new_locked.end]
    
        last_point: Point = Point({bias: 0, slope: 0, ts: block.timestamp, blk: block.number})
        if _epoch > 0:
            last_point = self.point_history[_epoch]
        last_checkpoint: uint256 = last_point.ts
        # initial_last_point is used for extrapolation to calculate block number
        # (approximately, for *At methods) and save them
        # as we cannot figure that out exactly from inside the contract
        initial_last_point: Point = last_point
        block_slope: uint256 = 0  # dblock/dt
        if block.timestamp > last_point.ts:
            block_slope = MULTIPLIER * (block.number - last_point.blk) / (block.timestamp - last_point.ts)
        # If last point is already recorded in this block, slope=0
        # But that's ok b/c we know the block in such case
    
        # Go over weeks to fill history and calculate what the current point is
        t_i: uint256 = (last_checkpoint / WEEK) * WEEK
        for i in range(255):
            # Hopefully it won't happen that this won't get used in 5 years!
            # If it does, users will be able to withdraw but vote weight will be broken
            t_i += WEEK
            d_slope: int128 = 0
            if t_i > block.timestamp:
                t_i = block.timestamp
            else:
                d_slope = self.slope_changes[t_i]
            last_point.bias -= last_point.slope * convert(t_i - last_checkpoint, int128)
            last_point.slope += d_slope
            if last_point.bias < 0:  # This can happen
                last_point.bias = 0
            if last_point.slope < 0:  # This cannot happen - just in case
                last_point.slope = 0
            last_checkpoint = t_i
            last_point.ts = t_i
            last_point.blk = initial_last_point.blk + block_slope * (t_i - initial_last_point.ts) / MULTIPLIER
            _epoch += 1
            if t_i == block.timestamp:
                last_point.blk = block.number
                break
            else:
                self.point_history[_epoch] = last_point
    
        self.epoch = _epoch
        # Now point_history is filled until t=now
    
        if addr != ZERO_ADDRESS:
            # If last point was in this block, the slope change has been applied already
            # But in such case we have 0 slope(s)
            last_point.slope += (u_new.slope - u_old.slope)
            last_point.bias += (u_new.bias - u_old.bias)
            if last_point.slope < 0:
                last_point.slope = 0
            if last_point.bias < 0:
                last_point.bias = 0
    
        # Record the changed point into history
        self.point_history[_epoch] = last_point
    
        if addr != ZERO_ADDRESS:
            # Schedule the slope changes (slope is going down)
            # We subtract new_user_slope from [new_locked.end]
            # and add old_user_slope to [old_locked.end]
            if old_locked.end > block.timestamp:
                # old_dslope was <something> - u_old.slope, so we cancel that
                old_dslope += u_old.slope
                if new_locked.end == old_locked.end:
                    old_dslope -= u_new.slope  # It was a new deposit, not extension
                self.slope_changes[old_locked.end] = old_dslope
    
            if new_locked.end > block.timestamp:
                if new_locked.end > old_locked.end:
                    new_dslope -= u_new.slope  # old slope disappeared at this point
                    self.slope_changes[new_locked.end] = new_dslope
                # else: we recorded it already in old_dslope
    
            # Now handle user history
            user_epoch: uint256 = self.user_point_epoch[addr] + 1
    
            self.user_point_epoch[addr] = user_epoch
            u_new.ts = block.timestamp
            u_new.blk = block.number
            self.user_point_history[addr][user_epoch] = u_new
    
    
    @internal
    def _deposit_for(_addr: address, _value: uint256, unlock_time: uint256, locked_balance: LockedBalance, type: int128):
        """
        @notice Deposit and lock tokens for a user
        @param _addr User's wallet address
        @param _value Amount to deposit
        @param unlock_time New time when to unlock the tokens, or 0 if unchanged
        @param locked_balance Previous locked amount / timestamp
        """
        _locked: LockedBalance = locked_balance
        supply_before: uint256 = self.supply
    
        self.supply = supply_before + _value
        old_locked: LockedBalance = _locked
        # Adding to existing lock, or if a lock is expired - creating a new one
        _locked.amount += convert(_value, int128)
        if unlock_time != 0:
            _locked.end = unlock_time
        self.locked[_addr] = _locked
    
        # Possibilities:
        # Both old_locked.end could be current or expired (>/< block.timestamp)
        # value == 0 (extend lock) or value > 0 (add to lock or extend lock)
        # _locked.end > block.timestamp (always)
        self._checkpoint(_addr, old_locked, _locked)
    
        if _value != 0:
            assert ERC20(self.token).transferFrom(_addr, self, _value)
    
        log Deposit(_addr, _value, _locked.end, type, block.timestamp)
        log Supply(supply_before, supply_before + _value)
    
    
    @external
    def checkpoint():
        """
        @notice Record global data to checkpoint
        """
        self._checkpoint(ZERO_ADDRESS, empty(LockedBalance), empty(LockedBalance))
    
    
    @external
    @nonreentrant('lock')
    def deposit_for(_addr: address, _value: uint256):
        """
        @notice Deposit `_value` tokens for `_addr` and add to the lock
        @dev Anyone (even a smart contract) can deposit for someone else, but
             cannot extend their locktime and deposit for a brand new user
        @param _addr User's wallet address
        @param _value Amount to add to user's lock
        """
        _locked: LockedBalance = self.locked[_addr]
    
        assert _value > 0  # dev: need non-zero value
        assert _locked.amount > 0, "No existing lock found"
        assert _locked.end > block.timestamp, "Cannot add to expired lock. Withdraw"
    
        self._deposit_for(_addr, _value, 0, self.locked[_addr], DEPOSIT_FOR_TYPE)
    
    
    @external
    @nonreentrant('lock')
    def create_lock(_value: uint256, _unlock_time: uint256):
        """
        @notice Deposit `_value` tokens for `msg.sender` and lock until `_unlock_time`
        @param _value Amount to deposit
        @param _unlock_time Epoch time when tokens unlock, rounded down to whole weeks
        """
        self.assert_not_contract(msg.sender)
        unlock_time: uint256 = (_unlock_time / WEEK) * WEEK  # Locktime is rounded down to weeks
        _locked: LockedBalance = self.locked[msg.sender]
    
        assert _value > 0  # dev: need non-zero value
        assert _locked.amount == 0, "Withdraw old tokens first"
        assert unlock_time > block.timestamp, "Can only lock until time in the future"
        assert unlock_time <= block.timestamp + MAXTIME, "Voting lock can be 4 years max"
    
        self._deposit_for(msg.sender, _value, unlock_time, _locked, CREATE_LOCK_TYPE)
    
    
    @external
    @nonreentrant('lock')
    def increase_amount(_value: uint256):
        """
        @notice Deposit `_value` additional tokens for `msg.sender`
                without modifying the unlock time
        @param _value Amount of tokens to deposit and add to the lock
        """
        self.assert_not_contract(msg.sender)
        _locked: LockedBalance = self.locked[msg.sender]
    
        assert _value > 0  # dev: need non-zero value
        assert _locked.amount > 0, "No existing lock found"
        assert _locked.end > block.timestamp, "Cannot add to expired lock. Withdraw"
    
        self._deposit_for(msg.sender, _value, 0, _locked, INCREASE_LOCK_AMOUNT)
    
    
    @external
    @nonreentrant('lock')
    def increase_unlock_time(_unlock_time: uint256):
        """
        @notice Extend the unlock time for `msg.sender` to `_unlock_time`
        @param _unlock_time New epoch time for unlocking
        """
        self.assert_not_contract(msg.sender)
        _locked: LockedBalance = self.locked[msg.sender]
        unlock_time: uint256 = (_unlock_time / WEEK) * WEEK  # Locktime is rounded down to weeks
    
        assert _locked.end > block.timestamp, "Lock expired"
        assert _locked.amount > 0, "Nothing is locked"
        assert unlock_time > _locked.end, "Can only increase lock duration"
        assert unlock_time <= block.timestamp + MAXTIME, "Voting lock can be 4 years max"
    
        self._deposit_for(msg.sender, 0, unlock_time, _locked, INCREASE_UNLOCK_TIME)
    
    
    @external
    @nonreentrant('lock')
    def withdraw():
        """
        @notice Withdraw all tokens for `msg.sender`
        @dev Only possible if the lock has expired
        """
        _locked: LockedBalance = self.locked[msg.sender]
        assert block.timestamp >= _locked.end, "The lock didn't expire"
        value: uint256 = convert(_locked.amount, uint256)
    
        old_locked: LockedBalance = _locked
        _locked.end = 0
        _locked.amount = 0
        self.locked[msg.sender] = _locked
        supply_before: uint256 = self.supply
        self.supply = supply_before - value
    
        # old_locked can have either expired <= timestamp or zero end
        # _locked has only 0 end
        # Both can have >= 0 amount
        self._checkpoint(msg.sender, old_locked, _locked)
    
        assert ERC20(self.token).transfer(msg.sender, value)
    
        log Withdraw(msg.sender, value, block.timestamp)
        log Supply(supply_before, supply_before - value)
    
    
    # The following ERC20/minime-compatible methods are not real balanceOf and supply!
    # They measure the weights for the purpose of voting, so they don't represent
    # real coins.
    
    @internal
    @view
    def find_block_epoch(_block: uint256, max_epoch: uint256) -> uint256:
        """
        @notice Binary search to estimate timestamp for block number
        @param _block Block to find
        @param max_epoch Don't go beyond this epoch
        @return Approximate timestamp for block
        """
        # Binary search
        _min: uint256 = 0
        _max: uint256 = max_epoch
        for i in range(128):  # Will be always enough for 128-bit numbers
            if _min >= _max:
                break
            _mid: uint256 = (_min + _max + 1) / 2
            if self.point_history[_mid].blk <= _block:
                _min = _mid
            else:
                _max = _mid - 1
        return _min
    
    
    @external
    @view
    def balanceOf(addr: address, _t: uint256 = block.timestamp) -> uint256:
        """
        @notice Get the current voting power for `msg.sender`
        @dev Adheres to the ERC20 `balanceOf` interface for Aragon compatibility
        @param addr User wallet address
        @param _t Epoch time to return voting power at
        @return User voting power
        """
        _epoch: uint256 = self.user_point_epoch[addr]
        if _epoch == 0:
            return 0
        else:
            last_point: Point = self.user_point_history[addr][_epoch]
            last_point.bias -= last_point.slope * convert(_t - last_point.ts, int128)
            if last_point.bias < 0:
                last_point.bias = 0
            return convert(last_point.bias, uint256)
    
    
    @external
    @view
    def balanceOfAt(addr: address, _block: uint256) -> uint256:
        """
        @notice Measure voting power of `addr` at block height `_block`
        @dev Adheres to MiniMe `balanceOfAt` interface: https://github.com/Giveth/minime
        @param addr User's wallet address
        @param _block Block to calculate the voting power at
        @return Voting power
        """
        # Copying and pasting totalSupply code because Vyper cannot pass by
        # reference yet
        assert _block <= block.number
    
        # Binary search
        _min: uint256 = 0
        _max: uint256 = self.user_point_epoch[addr]
        for i in range(128):  # Will be always enough for 128-bit numbers
            if _min >= _max:
                break
            _mid: uint256 = (_min + _max + 1) / 2
            if self.user_point_history[addr][_mid].blk <= _block:
                _min = _mid
            else:
                _max = _mid - 1
    
        upoint: Point = self.user_point_history[addr][_min]
    
        max_epoch: uint256 = self.epoch
        _epoch: uint256 = self.find_block_epoch(_block, max_epoch)
        point_0: Point = self.point_history[_epoch]
        d_block: uint256 = 0
        d_t: uint256 = 0
        if _epoch < max_epoch:
            point_1: Point = self.point_history[_epoch + 1]
            d_block = point_1.blk - point_0.blk
            d_t = point_1.ts - point_0.ts
        else:
            d_block = block.number - point_0.blk
            d_t = block.timestamp - point_0.ts
        block_time: uint256 = point_0.ts
        if d_block != 0:
            block_time += d_t * (_block - point_0.blk) / d_block
    
        upoint.bias -= upoint.slope * convert(block_time - upoint.ts, int128)
        if upoint.bias >= 0:
            return convert(upoint.bias, uint256)
        else:
            return 0
    
    
    @internal
    @view
    def supply_at(point: Point, t: uint256) -> uint256:
        """
        @notice Calculate total voting power at some point in the past
        @param point The point (bias/slope) to start search from
        @param t Time to calculate the total voting power at
        @return Total voting power at that time
        """
        last_point: Point = point
        t_i: uint256 = (last_point.ts / WEEK) * WEEK
        for i in range(255):
            t_i += WEEK
            d_slope: int128 = 0
            if t_i > t:
                t_i = t
            else:
                d_slope = self.slope_changes[t_i]
            last_point.bias -= last_point.slope * convert(t_i - last_point.ts, int128)
            if t_i == t:
                break
            last_point.slope += d_slope
            last_point.ts = t_i
    
        if last_point.bias < 0:
            last_point.bias = 0
        return convert(last_point.bias, uint256)
    
    
    @external
    @view
    def totalSupply(t: uint256 = block.timestamp) -> uint256:
        """
        @notice Calculate total voting power
        @dev Adheres to the ERC20 `totalSupply` interface for Aragon compatibility
        @return Total voting power
        """
        _epoch: uint256 = self.epoch
        last_point: Point = self.point_history[_epoch]
        return self.supply_at(last_point, t)
    
    
    @external
    @view
    def totalSupplyAt(_block: uint256) -> uint256:
        """
        @notice Calculate total voting power at some point in the past
        @param _block Block to calculate the total voting power at
        @return Total voting power at `_block`
        """
        assert _block <= block.number
        _epoch: uint256 = self.epoch
        target_epoch: uint256 = self.find_block_epoch(_block, _epoch)
    
        point: Point = self.point_history[target_epoch]
        dt: uint256 = 0
        if target_epoch < _epoch:
            point_next: Point = self.point_history[target_epoch + 1]
            if point.blk != point_next.blk:
                dt = (_block - point.blk) * (point_next.ts - point.ts) / (point_next.blk - point.blk)
        else:
            if point.blk != block.number:
                dt = (_block - point.blk) * (block.timestamp - point.ts) / (block.number - point.blk)
        # Now dt contains info on how far are we beyond point
    
        return self.supply_at(point, point.ts + dt)
    
    
    # Dummy methods for compatibility with Aragon
    
    @external
    def changeController(_newController: address):
        """
        @dev Dummy method required for Aragon compatibility
        """
        assert msg.sender == self.controller
        self.controller = _newController