Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x173e314C7635B45322cd8Cb14f44b312e079F3af
Balance 0 ETH
Nonce 1
Code Size 5705 bytes
Indexed Transactions 1 (24,451,00124,451,001)
Gas Used (indexed) 117,689
External Etherscan · Sourcify

Contract Bytecode

5705 bytes
0x608060405234801561001057600080fd5b50600436106101ef5760003560e01c806372f702f31161010f578063a694fc3a116100a2578063d1af0c7d11610071578063d1af0c7d14610419578063df136d6514610440578063e9fad8ee14610449578063ebe2b12b1461045157600080fd5b8063a694fc3a146103e2578063c8f33c91146103f5578063cc1a378f146103fe578063cd3daf9d1461041157600080fd5b80638980f11f116100de5780638980f11f146103935780638b876347146103a65780638da5cb5b146103c657806391b4ded9146103d957600080fd5b806372f702f31461035357806379ba50971461037a5780637b0a47ee1461038257806380faa57d1461038b57600080fd5b8063386a95251161018757806342ea02c11161015657806342ea02c1146102e757806353a47bb7146102fa5780635c975abb1461030d57806370a082311461032a57600080fd5b8063386a9525146102985780633c6b16ab146102a15780633d18b912146102b45780633fc6df6e146102bc57600080fd5b806318160ddd116101c357806318160ddd14610262578063197621431461026a5780631c1f78eb1461027d5780632e1a7d4d1461028557600080fd5b80628cc262146101f45780630700037d1461021a5780631627540c1461023a57806316c38b3c1461024f575b600080fd5b610207610202366004611426565b61045a565b6040519081526020015b60405180910390f35b610207610228366004611426565b600c6020526000908152604090205481565b61024d610248366004611426565b6104d7565b005b61024d61025d366004611456565b610534565b600d54610207565b61024d610278366004611426565b6105a6565b6102076105fc565b61024d610293366004611473565b610613565b61020760085481565b61024d6102af366004611473565b610774565b61024d6109d7565b6005546102cf906001600160a01b031681565b6040516001600160a01b039091168152602001610211565b61024d6102f536600461148c565b610ad9565b6001546102cf906001600160a01b031681565b60035461031a9060ff1681565b6040519015158152602001610211565b610207610338366004611426565b6001600160a01b03166000908152600e602052604090205490565b6102cf7f000000000000000000000000dc035d45d973e3ec169d2276ddab16f1e407384f81565b61024d610b21565b61020760075481565b610207610c0b565b61024d6103a13660046114c3565b610c22565b6102076103b4366004611426565b600b6020526000908152604090205481565b6000546102cf906001600160a01b031681565b61020760025481565b61024d6103f0366004611473565b610d15565b61020760095481565b61024d61040c366004611473565b610ed9565b610207610fb5565b6102cf7f000000000000000000000000c20059e0317de91738d13af027dfc4a50781b06681565b610207600a5481565b61024d611016565b61020760065481565b6001600160a01b0381166000908152600c6020908152604080832054600b909252822054670de0b6b3a764000090610490610fb5565b61049a9190611503565b6001600160a01b0385166000908152600e60205260409020546104bd9190611516565b6104c79190611535565b6104d19190611557565b92915050565b6104df611037565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906020015b60405180910390a150565b61053c611037565b60035460ff161515811515146105a3576003805460ff191682151590811790915560ff161561056a57426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec590602001610529565b50565b6105ae611037565b600580546001600160a01b0319166001600160a01b0383169081179091556040519081527f1c794a043683a294127c95bc365bae91b63b651eb9884a2c9120afee2bb690b490602001610529565b600060085460075461060e9190611516565b905090565b61061b6110a9565b33610624610fb5565b600a5561062f610c0b565b6009556001600160a01b038116156106765761064a8161045a565b6001600160a01b0382166000908152600c6020908152604080832093909355600a54600b909152919020555b600082116106bf5760405162461bcd60e51b8152602060048201526011602482015270043616e6e6f74207769746864726177203607c1b60448201526064015b60405180910390fd5b81600d546106cd9190611503565b600d55336000908152600e60205260409020546106eb908390611503565b336000818152600e6020526040902091909155610733907f000000000000000000000000dc035d45d973e3ec169d2276ddab16f1e407384f6001600160a01b03169084611102565b60405182815233907f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5906020015b60405180910390a2506105a36001600455565b6005546001600160a01b031633146107e15760405162461bcd60e51b815260206004820152602a60248201527f43616c6c6572206973206e6f742052657761726473446973747269627574696f6044820152691b8818dbdb9d1c9858dd60b21b60648201526084016106b6565b60006107eb610fb5565b600a556107f6610c0b565b6009556001600160a01b0381161561083d576108118161045a565b6001600160a01b0382166000908152600c6020908152604080832093909355600a54600b909152919020555b600654421061085b576008546108539083611535565b60075561089d565b60004260065461086b9190611503565b905060006007548261087d9190611516565b60085490915061088d8286611557565b6108979190611535565b60075550505b6040516370a0823160e01b81523060048201526000907f000000000000000000000000c20059e0317de91738d13af027dfc4a50781b0666001600160a01b0316906370a0823190602401602060405180830381865afa158015610904573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610928919061156a565b9050600854816109389190611535565b60075411156109895760405162461bcd60e51b815260206004820152601860248201527f50726f76696465642072657761726420746f6f2068696768000000000000000060448201526064016106b6565b42600981905560085461099b91611557565b6006556040518381527fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d906020015b60405180910390a1505050565b6109df6110a9565b336109e8610fb5565b600a556109f3610c0b565b6009556001600160a01b03811615610a3a57610a0e8161045a565b6001600160a01b0382166000908152600c6020908152604080832093909355600a54600b909152919020555b336000908152600c60205260409020548015610acb57336000818152600c6020526040812055610a95907f000000000000000000000000c20059e0317de91738d13af027dfc4a50781b0666001600160a01b03169083611102565b60405181815233907fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e04869060200160405180910390a25b5050610ad76001600455565b565b610ae282610d15565b604051828152339061ffff8316907f16902e34d01e8d5f80ce64939920a1390ff67f2546ae43ae72ac4820333009689060200160405180910390a35050565b6001546001600160a01b03163314610b995760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b60648201526084016106b6565b600054600154604080516001600160a01b0393841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b60006006544210610c1d575060065490565b504290565b610c2a611037565b7f000000000000000000000000dc035d45d973e3ec169d2276ddab16f1e407384f6001600160a01b0316826001600160a01b031603610cb55760405162461bcd60e51b815260206004820152602160248201527f43616e6e6f7420776974686472617720746865207374616b696e6720746f6b656044820152603760f91b60648201526084016106b6565b600054610ccf906001600160a01b03848116911683611102565b604080516001600160a01b0384168152602081018390527f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa28910160405180910390a15050565b610d1d6110a9565b60035460ff1615610d965760405162461bcd60e51b815260206004820152603c60248201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060448201527f7768696c652074686520636f6e7472616374206973207061757365640000000060648201526084016106b6565b33610d9f610fb5565b600a55610daa610c0b565b6009556001600160a01b03811615610df157610dc58161045a565b6001600160a01b0382166000908152600c6020908152604080832093909355600a54600b909152919020555b60008211610e325760405162461bcd60e51b815260206004820152600e60248201526d043616e6e6f74207374616b6520360941b60448201526064016106b6565b81600d54610e409190611557565b600d55336000908152600e6020526040902054610e5e908390611557565b336000818152600e6020526040902091909155610ea7907f000000000000000000000000dc035d45d973e3ec169d2276ddab16f1e407384f6001600160a01b031690308561116a565b60405182815233907f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d90602001610761565b610ee1611037565b6000610eeb610fb5565b600a55610ef6610c0b565b6009556001600160a01b03811615610f3d57610f118161045a565b6001600160a01b0382166000908152600c6020908152604080832093909355600a54600b909152919020555b60065442811115610f8057600754600090610f584284611503565b610f629190611516565b9050610f6e8482611535565b600755610f7b8442611557565b600655505b60088390556040518381527ffb46ca5a5e06d4540d6387b930a7c978bce0db5f449ec6b3f5d07c6e1d44f2d3906020016109ca565b6000600d54600003610fc85750600a5490565b600d54600754600954610fd9610c0b565b610fe39190611503565b610fed9190611516565b610fff90670de0b6b3a7640000611516565b6110099190611535565b600a5461060e9190611557565b336000908152600e602052604090205461102f90610613565b610ad76109d7565b6000546001600160a01b03163314610ad75760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b60648201526084016106b6565b6002600454036110fb5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016106b6565b6002600455565b6040516001600160a01b03831660248201526044810182905261116590849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526111a8565b505050565b6040516001600160a01b03808516602483015283166044820152606481018290526111a29085906323b872dd60e01b9060840161112e565b50505050565b60006111fd826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661127a9092919063ffffffff16565b805190915015611165578080602001905181019061121b9190611583565b6111655760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016106b6565b60606112898484600085611291565b949350505050565b6060824710156112f25760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016106b6565b600080866001600160a01b0316858760405161130e91906115c4565b60006040518083038185875af1925050503d806000811461134b576040519150601f19603f3d011682016040523d82523d6000602084013e611350565b606091505b50915091506113618783838761136c565b979650505050505050565b606083156113db5782516000036113d4576001600160a01b0385163b6113d45760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016106b6565b5081611289565b61128983838151156113f05781518083602001fd5b8060405162461bcd60e51b81526004016106b691906115e0565b80356001600160a01b038116811461142157600080fd5b919050565b60006020828403121561143857600080fd5b6114418261140a565b9392505050565b80151581146105a357600080fd5b60006020828403121561146857600080fd5b813561144181611448565b60006020828403121561148557600080fd5b5035919050565b6000806040838503121561149f57600080fd5b82359150602083013561ffff811681146114b857600080fd5b809150509250929050565b600080604083850312156114d657600080fd5b6114df8361140a565b946020939093013593505050565b634e487b7160e01b600052601160045260246000fd5b818103818111156104d1576104d16114ed565b6000816000190483118215151615611530576115306114ed565b500290565b60008261155257634e487b7160e01b600052601260045260246000fd5b500490565b808201808211156104d1576104d16114ed565b60006020828403121561157c57600080fd5b5051919050565b60006020828403121561159557600080fd5b815161144181611448565b60005b838110156115bb5781810151838201526020016115a3565b50506000910152565b600082516115d68184602087016115a0565b9190910192915050565b60208152600082518060208401526115ff8160408501602087016115a0565b601f01601f1916919091016040019291505056fea2646970667358221220f4b2af5d8fc0126adf021ac5822fdd514a0792bcadfb824a888e5c8d94d8a9aa64736f6c63430008100033

Verified Source Code Full Match

Compiler: v0.8.16+commit.07a7930e EVM: london Optimization: Yes (200 runs)
Owned.sol 61 lines
/**
 * NOTICE: This contract has been adapted from the original Synthetix source code:
 *  - Upgrade Solidity version from 0.5.x to 0.8.x.
 *
 * Original: https://github.com/Synthetixio/synthetix/blob/5e9096ac4aea6c4249828f1e8b95e3fb9be231f8/contracts/Owned.sol
 *     Diff: https://www.diffchecker.com/pvwfv0hH/
 */

// SPDX-FileCopyrightText: © 2019-2021 Synthetix
// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.16;

// https://developer.synthetix.io/contracts/source/contracts/Owned/
contract Owned {
    address public owner;
    address public nominatedOwner;

    constructor(address _owner) {
        require(_owner != address(0), "Owner address cannot be 0");
        owner = _owner;
        emit OwnerChanged(address(0), _owner);
    }

    function nominateNewOwner(address _owner) external onlyOwner {
        nominatedOwner = _owner;
        emit OwnerNominated(_owner);
    }

    function acceptOwnership() external {
        require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
        emit OwnerChanged(owner, nominatedOwner);
        owner = nominatedOwner;
        nominatedOwner = address(0);
    }

    modifier onlyOwner() {
        _onlyOwner();
        _;
    }

    function _onlyOwner() private view {
        require(msg.sender == owner, "Only the contract owner may perform this action");
    }

    event OwnerNominated(address newOwner);
    event OwnerChanged(address oldOwner, address newOwner);
}
StakingRewards.sol 216 lines
/**
 * NOTICE: This contract has been adapted from the original Synthetix source code:
 *  - Upgrade Solidity version from 0.5.x to 0.8.x.
 *  - Add referral code functionality for `stake()`.
 *  - Update `setRewardDuration()` to support changing the reward duration during an active distribution.
 *
 * Original: https://github.com/Synthetixio/synthetix/blob/5e9096ac4aea6c4249828f1e8b95e3fb9be231f8/contracts/StakingRewards.sol
 */

// SPDX-FileCopyrightText: © 2019-2021 Synthetix
// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.16;

import {SafeERC20, IERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "openzeppelin-contracts/security/ReentrancyGuard.sol";
import {IStakingRewards} from "./interfaces/IStakingRewards.sol";
import {Pausable} from "./utils/Pausable.sol";

// https://developer.synthetix.io/contracts/source/contracts/StakingRewards/
contract StakingRewards is IStakingRewards, Pausable, ReentrancyGuard {
    using SafeERC20 for IERC20;

    /* ========== STATE VARIABLES ========== */

    IERC20 public immutable rewardsToken;
    IERC20 public immutable stakingToken;

    address public rewardsDistribution;
    uint256 public periodFinish = 0;
    uint256 public rewardRate = 0;
    uint256 public rewardsDuration = 7 days;
    uint256 public lastUpdateTime;
    uint256 public rewardPerTokenStored;

    mapping(address => uint256) public userRewardPerTokenPaid;
    mapping(address => uint256) public rewards;

    uint256 private _totalSupply;
    mapping(address => uint256) private _balances;

    /* ========== CONSTRUCTOR ========== */

    constructor(
        address _owner,
        address _rewardsDistribution,
        address _rewardsToken,
        address _stakingToken
    ) Pausable(_owner) {
        require(_rewardsToken != _stakingToken, "Rewards and staking tokens must not be the same");

        rewardsToken = IERC20(_rewardsToken);
        stakingToken = IERC20(_stakingToken);
        rewardsDistribution = _rewardsDistribution;
    }

    /* ========== VIEWS ========== */

    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 block.timestamp < periodFinish ? block.timestamp : periodFinish;
    }

    function rewardPerToken() public view returns (uint256) {
        if (_totalSupply == 0) {
            return rewardPerTokenStored;
        }
        return
            rewardPerTokenStored + (((lastTimeRewardApplicable() - lastUpdateTime) * rewardRate * 1e18) / _totalSupply);
    }

    function earned(address account) public view returns (uint256) {
        return (_balances[account] * (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18 + rewards[account];
    }

    function getRewardForDuration() external view returns (uint256) {
        return rewardRate * rewardsDuration;
    }

    /* ========== MUTATIVE FUNCTIONS ========== */

    function stake(uint256 amount) public nonReentrant notPaused updateReward(msg.sender) {
        require(amount > 0, "Cannot stake 0");
        _totalSupply = _totalSupply + amount;
        _balances[msg.sender] = _balances[msg.sender] + amount;
        stakingToken.safeTransferFrom(msg.sender, address(this), amount);
        emit Staked(msg.sender, amount);
    }

    function stake(uint256 amount, uint16 referral) external {
        stake(amount);
        emit Referral(referral, msg.sender, amount);
    }

    function withdraw(uint256 amount) public nonReentrant updateReward(msg.sender) {
        require(amount > 0, "Cannot withdraw 0");
        _totalSupply = _totalSupply - amount;
        _balances[msg.sender] = _balances[msg.sender] - amount;
        stakingToken.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;
            rewardsToken.safeTransfer(msg.sender, reward);
            emit RewardPaid(msg.sender, reward);
        }
    }

    function exit() external {
        withdraw(_balances[msg.sender]);
        getReward();
    }

    /* ========== RESTRICTED FUNCTIONS ========== */

    /// Before calling this function, the caller should send at least `reward` tokens to the contract.
    /// Otherwise, if the amount sent is less than `reward` passed as a parameter,
    /// the unclaimed rewards of other users would be used in the new period.
    /// This would result in missing tokens, which might cause `getReward` to fail for some users.
    /// We advise the use of wrapper contracts to perform the transfer and call this function atomically.
    function notifyRewardAmount(uint256 reward) external override onlyRewardsDistribution updateReward(address(0)) {
        if (block.timestamp >= periodFinish) {
            rewardRate = reward / rewardsDuration;
        } else {
            uint256 remaining = periodFinish - block.timestamp;
            uint256 leftover = remaining * rewardRate;
            rewardRate = (reward + leftover) / rewardsDuration;
        }

        // 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.
        uint256 balance = rewardsToken.balanceOf(address(this));
        require(rewardRate <= balance / rewardsDuration, "Provided reward too high");

        lastUpdateTime = block.timestamp;
        periodFinish = block.timestamp + rewardsDuration;
        emit RewardAdded(reward);
    }

    // Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders
    function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyOwner {
        require(tokenAddress != address(stakingToken), "Cannot withdraw the staking token");
        IERC20(tokenAddress).safeTransfer(owner, tokenAmount);
        emit Recovered(tokenAddress, tokenAmount);
    }

    function setRewardsDuration(uint256 _rewardsDuration) external onlyOwner updateReward(address(0)) {
        uint256 periodFinish_ = periodFinish;
        if (block.timestamp < periodFinish_) {
            uint256 leftover = (periodFinish_ - block.timestamp) * rewardRate;
            rewardRate = leftover / _rewardsDuration;
            periodFinish = block.timestamp + _rewardsDuration;
        }

        rewardsDuration = _rewardsDuration;
        emit RewardsDurationUpdated(rewardsDuration);
    }

    function setRewardsDistribution(address _rewardsDistribution) external onlyOwner {
        rewardsDistribution = _rewardsDistribution;
        emit RewardsDistributionUpdated(rewardsDistribution);
    }

    /* ========== MODIFIERS ========== */

    modifier updateReward(address account) {
        rewardPerTokenStored = rewardPerToken();
        lastUpdateTime = lastTimeRewardApplicable();
        if (account != address(0)) {
            rewards[account] = earned(account);
            userRewardPerTokenPaid[account] = rewardPerTokenStored;
        }
        _;
    }

    modifier onlyRewardsDistribution() {
        require(msg.sender == rewardsDistribution, "Caller is not RewardsDistribution contract");
        _;
    }

    /* ========== EVENTS ========== */

    event RewardAdded(uint256 reward);
    event Staked(address indexed user, uint256 amount);
    event Referral(uint16 indexed referral, address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 amount);
    event RewardPaid(address indexed user, uint256 reward);
    event RewardsDurationUpdated(uint256 newDuration);
    event RewardsDistributionUpdated(address newRewardsDistribution);
    event Recovered(address token, uint256 amount);
}
Pausable.sol 65 lines
/**
 * NOTICE: This contract has been adapted from the original Synthetix source code:
 *  - Upgrade Solidity version from 0.5.x to 0.8.x.
 *
 * Original: https://github.com/Synthetixio/synthetix/blob/5e9096ac4aea6c4249828f1e8b95e3fb9be231f8/contracts/Pausable.sol
 *     Diff: https://www.diffchecker.com/ZxqWAZxN/
 */

// SPDX-FileCopyrightText: © 2019-2021 Synthetix
// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.16;

// Inheritance
import {Owned} from "./Owned.sol";

// https://developer.synthetix.io/contracts/source/contracts/Pausable/
abstract contract Pausable is Owned {
    uint public lastPauseTime;
    bool public paused;

    constructor(address _owner) Owned(_owner) {}

    /**
     * @notice Change the paused state of the contract
     * @dev Only the contract owner may call this.
     */
    function setPaused(bool _paused) external onlyOwner {
        // Ensure we're actually changing the state before we do anything
        if (_paused == paused) {
            return;
        }

        // Set our paused state.
        paused = _paused;

        // If applicable, set the last pause time.
        if (paused) {
            lastPauseTime = block.timestamp;
        }

        // Let everyone know that our pause state has changed.
        emit PauseChanged(paused);
    }

    event PauseChanged(bool isPaused);

    modifier notPaused() {
        require(!paused, "This action cannot be performed while the contract is paused");
        _;
    }
}
IStakingRewards.sol 62 lines
// SPDX-FileCopyrightText: © 2019-2021 Synthetix
// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: MIT AND AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.16;

import {IERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";

// https://docs.synthetix.io/contracts/source/interfaces/istakingrewards
interface IStakingRewards {
    // Views

    function balanceOf(address account) external view returns (uint256);

    function earned(address account) external view returns (uint256);

    function getRewardForDuration() external view returns (uint256);

    function lastTimeRewardApplicable() external view returns (uint256);

    function rewardPerToken() external view returns (uint256);

    function rewardsDistribution() external view returns (address);

    function rewardsToken() external view returns (IERC20);

    function stakingToken() external view returns (IERC20);

    function totalSupply() external view returns (uint256);

    // Mutative

    function exit() external;

    function getReward() external;

    function stake(uint256 amount) external;

    function stake(uint256 amount, uint16 referral) external;

    function withdraw(uint256 amount) external;

    function notifyRewardAmount(uint256 reward) external;

    function setRewardsDistribution(address _rewardsDistribution) external;

    function setRewardsDuration(uint256 _rewardsDuration) external;

    function recoverERC20(address tokenAddress, uint256 tokenAmount) external;
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @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
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // 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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
IERC20.sol 82 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev 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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
}
ReentrancyGuard.sol 69 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @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() {
        _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 making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}
SafeERC20.sol 116 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}
draft-IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

Read Contract

balanceOf 0x70a08231 → uint256
earned 0x008cc262 → uint256
getRewardForDuration 0x1c1f78eb → uint256
lastPauseTime 0x91b4ded9 → uint256
lastTimeRewardApplicable 0x80faa57d → uint256
lastUpdateTime 0xc8f33c91 → uint256
nominatedOwner 0x53a47bb7 → address
owner 0x8da5cb5b → address
paused 0x5c975abb → bool
periodFinish 0xebe2b12b → uint256
rewardPerToken 0xcd3daf9d → uint256
rewardPerTokenStored 0xdf136d65 → uint256
rewardRate 0x7b0a47ee → uint256
rewards 0x0700037d → uint256
rewardsDistribution 0x3fc6df6e → address
rewardsDuration 0x386a9525 → uint256
rewardsToken 0xd1af0c7d → address
stakingToken 0x72f702f3 → address
totalSupply 0x18160ddd → uint256
userRewardPerTokenPaid 0x8b876347 → uint256

Write Contract 12 functions

These functions modify contract state and require a wallet transaction to execute.

acceptOwnership 0x79ba5097
No parameters
exit 0xe9fad8ee
No parameters
getReward 0x3d18b912
No parameters
nominateNewOwner 0x1627540c
address _owner
notifyRewardAmount 0x3c6b16ab
uint256 reward
recoverERC20 0x8980f11f
address tokenAddress
uint256 tokenAmount
setPaused 0x16c38b3c
bool _paused
setRewardsDistribution 0x19762143
address _rewardsDistribution
setRewardsDuration 0xcc1a378f
uint256 _rewardsDuration
stake 0x42ea02c1
uint256 amount
uint16 referral
stake 0xa694fc3a
uint256 amount
withdraw 0x2e1a7d4d
uint256 amount

Top Interactions

AddressTxnsSentReceived
0x961972a5...9480 1 1

Recent Transactions

CSV
|
Hash Method Block Age From/To Value Txn Fee Type
0xde1e16e6...69b4f2 0xe9fad8ee 24,451,001 IN 0x961972a5...9480 0 ETH EIP-1559