Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xAd37f4B08E90067E5Bb90D6D022CF5AAF7b1d718
Balance 0 ETH
Nonce 1
Code Size 8324 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

8324 bytes
0x608060405234801561005d5760405162461bcd60e51b815260206004820152602260248201527f45746865722073656e7420746f206e6f6e2d70617961626c652066756e637469604482019081526137b760f11b6064830152608482fd5b50600436106101585760003560e01c80638afe6418116100ef578063c0d92e8e116100be578063c0d92e8e14610327578063c16599311461033a578063f2fde38b14610342578063fa65639b14610355578063fc0c546a1461039157610158565b80638afe64181461026f5780638da5cb5b14610282578063a7310b58146102a7578063c07473f61461030657610158565b806373cf575a1161012b57806373cf575a1461022e57806373d6a889146102365780637d9aff4b14610249578063833e8bb61461025c57610158565b8063370158ea146101bd5780633a4b66f1146102145780634e71d92d1461021e578063715018a614610226575b60405162461bcd60e51b815260206004820152603560248201527f436f6e747261637420646f6573206e6f7420686176652066616c6c6261636b2060448201908152746e6f7220726563656976652066756e6374696f6e7360581b6064830152608482fd5b6101c56103b8565b604080519a8b5260208b0199909952978901969096526060880194909452608087019290925260a086015260c085015260e0840152610100830152610120820152610140015b60405180910390f35b61021c610477565b005b61021c6105e4565b61021c6107c1565b61021c6107d3565b61021c610244366004611e3b565b610b09565b61021c610257366004611e9e565b610c5e565b61021c61026a366004611ebc565b610e73565b61021c61027d366004611e3b565b610ef9565b6000546001600160a01b03165b6040516001600160a01b03909116815260200161020b565b6102ba6102b5366004611e9e565b611052565b604080519a8b5260208b0199909952978901969096526060880194909452608087019290925260a086015260c085015260e084015261010083015215156101208201526101400161020b565b610319610314366004611e9e565b611177565b60405190815260200161020b565b610319610335366004611ebc565b6111ce565b61021c61121c565b61021c610350366004611e9e565b611225565b610381610363366004611e9e565b6001600160a01b03166000908152600a602052604090205460ff1690565b604051901515815260200161020b565b61028f7f00000000000000000000000092d5942f468447f1f21c2092580f15544923b43481565b6000806000806000806000806000807f000000000000000000000000000000000000000000000000000000006724d0d07f0000000000000000000000000000000000000000000000000000000000015180600654610414611260565b60035461042c670de0b6b3a76400006207a120611eee565b600454600554610447670de0b6b3a7640000621e8480611eee565b6004546104549190611f05565b600254995099509950995099509950995099509950995090919293949596979899565b61047f611304565b33610495670de0b6b3a7640000621e8480611eee565b604051636eb1769f60e11b81526001600160a01b0383811660048301523060248301527f00000000000000000000000092d5942f468447f1f21c2092580f15544923b434169063dd62ed3e9060440160206040518083038186803b15801561053a5760405162461bcd60e51b8152602060048201526025602482015260008051602061202f833981519152604482019081526420636f646560d81b6064830152608482fd5b505afa15801561054e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105729190611f27565b10156105cf5760405162461bcd60e51b815260206004820152602160248201527f5354414b453a50524f3a496e73756666696369656e7420616c6c6f77616e63656044820152601760f91b60648201526084015b60405180910390fd5b6105d88161132e565b506105e260018055565b565b6105ec611304565b336000818152600760205260409020600481015460ff1661065e5760405162461bcd60e51b815260206004820152602660248201527f5354414b453a50524f3a596f752068617665206e6f6e65207374616b6564207460448201526537b5b2b7399760d11b60648201526084016105c6565b6000610668611260565b905061067381611700565b600061068383600301548361173f565b9050600081116106e75760405162461bcd60e51b815260206004820152602960248201527f5354414b453a50524f3a4e6f207265776172642068617665206265656e206561604482015268393732b2103cb2ba1760b91b60648201526084016105c6565b428360010181905550808360020160008282546107049190611f43565b90915550506003830182905560058054829190600090610725908490611f43565b9091555061075f90506001600160a01b037f00000000000000000000000092d5942f468447f1f21c2092580f15544923b4341685836117b6565b80600360008282546107719190611f56565b90915550506040518181526001600160a01b038516907fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a9060200160405180910390a2505050506105e260018055565b6107c961181a565b6105e26000611847565b6107db611304565b336000818152600760205260409020600481015460ff1661083e5760405162461bcd60e51b815260206004820152601960248201527f5354414b453a50524f3a4e6f20616374697665207374616b650000000000000060448201526064016105c6565b426000610849611260565b905082600301548110156108b65760405162461bcd60e51b815260206004820152602e60248201527f5354414b453a50524f3a596f752063616e6e6f742065786974206265666f726560448201526d103832b934b7b21034b9903ab81760911b60648201526084016105c6565b82546000906108e6907f0000000000000000000000000000000000000000000000000000000003b5380090611f43565b831015610971576064610904670de0b6b3a7640000621e8480611eee565b61090e9190611f05565b610919906014611eee565b9050806003600082825461092d9190611f43565b90915550506040518181526001600160a01b038616907f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d59060200160405180910390a25b600081610989670de0b6b3a7640000621e8480611eee565b6109939190611f56565b905060006109a586600301548561173f565b90508015610a0457808660020160008282546109c19190611f43565b909155506109d190508183611f43565b915080600560008282546109e59190611f43565b9250508190555080600360008282546109fe9190611f56565b90915550505b610a19670de0b6b3a7640000621e8480611eee565b60046000828254610a2a9190611f56565b9091555050600186018590556003860184905560048601805460ff199081169091556001600160a01b0380891660009081526009602052604090208054909216909155610a9a907f00000000000000000000000092d5942f468447f1f21c2092580f15544923b4341688846117b6565b610aad610aa8856001611f43565b611700565b60408051828152602081018490529081018490526001600160a01b038816907fdb5446d7bffdeb0b5301ec00f352321da31ef3ee552eca5a1a8c9d59b9ef68809060600160405180910390a2505050505050506105e260018055565b610b1e670de0b6b3a7640000621e8480611eee565b8514610b855760405162461bcd60e51b815260206004820152603060248201527f5354414b453a50524f3a45786163746c792032206d696c696f6e20746f6b656e60448201526f1036bab9ba1031329039ba30b5b2b21760811b60648201526084016105c6565b60405163d505accf60e01b81526001600160a01b037f00000000000000000000000092d5942f468447f1f21c2092580f15544923b434169063d505accf90610bdd90899030908a908a908a908a908a90600401611f69565b600060405180830381600087803b158015610c355760405162461bcd60e51b8152602060048201526025602482015260008051602061202f833981519152604482019081526420636f646560d81b6064830152608482fd5b505af1158015610c49573d6000803e3d6000fd5b50505050610c568661132e565b505050505050565b610c6661181a565b610c6e611304565b610c76611897565b610c7e611260565b1015610cde5760405162461bcd60e51b815260206004820152602960248201527f5354414b453a50524f3a446973747269627574696f6e206973207374696c6c2060448201526831b7b73a34b73ab29760b91b60648201526084016105c6565b610cf3670de0b6b3a7640000621e8480611eee565b600454610d009190611f05565b15610d735760405162461bcd60e51b815260206004820152603c60248201527f5354414b453a50524f3a5468657265206973207374696c6c2070726f2077616c60448201527f6c6574206163636f756e742061742074686520636f6e74726163742e0000000060648201526084016105c6565b306001600160a01b03821603610de85760405162461bcd60e51b815260206004820152603460248201527f5354414b453a50524f3a596f752063616e6e6f742073656e6420746f6b656e206044820152733a379039ba30b5b4b7339031b7b73a3930b1ba1760611b60648201526084016105c6565b600354610e21906001600160a01b037f00000000000000000000000092d5942f468447f1f21c2092580f15544923b434169083906117b6565b600060038190556040519081526001600160a01b038216907ffc3c901b54b3eda21b5c2fac44318971c059dede078e02337447a82649704d6b906020015b60405180910390a2610e7060018055565b50565b610e7b611304565b610eb07f00000000000000000000000092d5942f468447f1f21c2092580f15544923b4346001600160a01b03163330846118d8565b8060036000828254610ec29190611f43565b909155505060405181815233907f034d8d86948028af29be6db4d39c10fa2a7fc4ea889c3b3f08a37bc5fe3757a890602001610e5f565b610f0d670de0b6b3a7640000612710611eee565b8514610f815760405162461bcd60e51b815260206004820152603760248201527f5354414b453a524547554c41523a45786163746c792031302074686f7573616e60448201527f647320746f6b656e206d757374206265207374616b656400000000000000000060648201526084016105c6565b60405163d505accf60e01b81526001600160a01b037f00000000000000000000000092d5942f468447f1f21c2092580f15544923b434169063d505accf90610fd990899030908a908a908a908a908a90600401611f69565b600060405180830381600087803b1580156110315760405162461bcd60e51b8152602060048201526025602482015260008051602061202f833981519152604482019081526420636f646560d81b6064830152608482fd5b505af1158015611045573d6000803e3d6000fd5b50505050610c5686611917565b6001600160a01b0381166000908152600760209081526040808320815160a08101835281548082526001830154948201859052600283015493820184905260038301546060830181905260049093015460ff161515608083018190529095928392839283918291906110c38c611177565b9250806080015115611169576110e481606001516110df611260565b61173f565b97506110f38160600151611b23565b965061110a670de0b6b3a7640000621e8480611eee565b6004546111179190611f05565b61112c670de0b6b3a76400006207a120611eee565b6111369190611f05565b8151909650611166907f0000000000000000000000000000000000000000000000000000000003b5380090611f43565b93505b509193959799509193959799565b6001600160a01b03811660009081526009602052604081205460ff166111c5576001600160a01b0382166000908152600a602052604090205460ff166111be5760006111c8565b60016111c8565b60c85b92915050565b6000815b600081815260086020526040902054156111fb5760009081526008602052604090205492915050565b8015611213578061120b81611faa565b9150506111d2565b50600092915050565b6105e233611917565b61122d61181a565b6001600160a01b03811661125757604051631e4fbdf760e01b8152600060048201526024016105c6565b610e7081611847565b6000427f000000000000000000000000000000000000000000000000000000006724d0d081101561129357600091505090565b61129b611c06565b81106112af576112a9611897565b91505090565b7f00000000000000000000000000000000000000000000000000000000000151806112fa7f000000000000000000000000000000000000000000000000000000006724d0d083611f56565b6112a99190611f05565b60026001540361132757604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b427f000000000000000000000000000000000000000000000000000000006724d0d08110156113b35760405162461bcd60e51b815260206004820152602b60248201527f5354414b453a50524f3a5374616b696e672074696d6520686173206e6f74207360448201526a3a30b93a32b2103cb2ba1760a91b60648201526084016105c6565b60006113bd611260565b90506113c7611897565b81106114295760405162461bcd60e51b815260206004820152602b60248201527f5354414b453a50524f3a416c6c20746f6b656e732068617665206265656e206460448201526a34b9ba3934b13aba32b21760a91b60648201526084016105c6565b61143e670de0b6b3a7640000621e8480611eee565b6040516370a0823160e01b81526001600160a01b0385811660048301527f00000000000000000000000092d5942f468447f1f21c2092580f15544923b43416906370a082319060240160206040518083038186803b1580156114dd5760405162461bcd60e51b8152602060048201526025602482015260008051602061202f833981519152604482019081526420636f646560d81b6064830152608482fd5b505afa1580156114f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115159190611f27565b10156115635760405162461bcd60e51b815260206004820152601f60248201527f5354414b453a50524f3a496e73756666696369656e742062616c616e63652e0060448201526064016105c6565b6001600160a01b0383166000908152600760205260409020600481015460ff16156115d05760405162461bcd60e51b815260206004820152601960248201527f5354414b453a50524f3a416c7265616479207374616b65642e0000000000000060448201526064016105c6565b61161c84306115ea670de0b6b3a7640000621e8480611eee565b6001600160a01b037f00000000000000000000000092d5942f468447f1f21c2092580f15544923b434169291906118d8565b8281556000600180830191909155611635908390611f43565b6003820155600481018054600160ff1991821681179092556001600160a01b038616600090815260096020526040902080549091169091179055611684670de0b6b3a7640000621e8480611eee565b600460008282546116959190611f43565b909155506116a99050610aa8836001611f43565b6001600160a01b0384167f9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d6116e9670de0b6b3a7640000621e8480611eee565b60405190815260200160405180910390a250505050565b8060065411610e705761171e670de0b6b3a7640000621e8480611eee565b60045461172b9190611f05565b600082815260086020526040902055600655565b60008080845b848110156117ac576000818152600860205260409020548015611766578092505b82156117995782611782670de0b6b3a76400006207a120611eee565b61178c9190611f05565b6117969085611f43565b93505b50806117a481611fc1565b915050611745565b5090949350505050565b6040516001600160a01b0383811660248301526044820183905261181591859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050611c65565b505050565b6000546001600160a01b031633146105e25760405163118cdaa760e01b81523360048201526024016105c6565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006118ae670de0b6b3a76400006207a120611eee565b6005546003546118be9190611f43565b6118c89190611f05565b6118d3906001611f43565b905090565b6040516001600160a01b0384811660248301528381166044830152606482018390526119119186918216906323b872dd906084016117e3565b50505050565b6001600160a01b0381166000908152600a602052604090205460ff16156119a65760405162461bcd60e51b815260206004820152603760248201527f5354414b453a524547554c41523a54686973206164647265737320697320616c60448201527f7265616479206120726567756c61722077616c6c65742e00000000000000000060648201526084016105c6565b6001600160a01b037f00000000000000000000000092d5942f468447f1f21c2092580f15544923b434166379cc6790826119ea670de0b6b3a7640000612710611eee565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015611a6e5760405162461bcd60e51b8152602060048201526025602482015260008051602061202f833981519152604482019081526420636f646560d81b6064830152608482fd5b505af1158015611a82573d6000803e3d6000fd5b5050506001600160a01b0382166000818152600a60205260409020805460ff1916600117905590507f7b7143609d4b1153c3ce3693b9ac72040de91ab300469479a72f33127c7619c6611adf670de0b6b3a7640000612710611eee565b60408051918252600160208301520160405180910390a2611b0a670de0b6b3a7640000612710611eee565b60026000828254611b1b9190611f43565b909155505050565b6000807f0000000000000000000000000000000000000000000000000000000000015180611b717f000000000000000000000000000000000000000000000000000000006724d0d042611f56565b611b7b9190611f05565b905082611b86611260565b10611b9d57611b96600182611f43565b9050611bab565b611ba8600282611f43565b90505b611bd57f000000000000000000000000000000000000000000000000000000000001518082611eee565b611bff907f000000000000000000000000000000000000000000000000000000006724d0d0611f43565b9392505050565b60007f0000000000000000000000000000000000000000000000000000000000015180611c31611897565b611c3b9190611eee565b6118d3907f000000000000000000000000000000000000000000000000000000006724d0d0611f43565b6000611c7a6001600160a01b03841683611cc8565b90508051600014158015611c9f575080806020019051810190611c9d9190611fda565b155b1561181557604051635274afe760e01b81526001600160a01b03841660048201526024016105c6565b6060611bff8383600084600080856001600160a01b03168486604051611cee9190611fff565b60006040518083038185875af1925050503d8060008114611d2b576040519150601f19603f3d011682016040523d82523d6000602084013e611d30565b606091505b5091509150611d40868383611d4a565b9695505050505050565b606082611d5f57611d5a82611da6565b611bff565b8151158015611d7657506001600160a01b0384163b155b15611d9f57604051639996b31560e01b81526001600160a01b03851660048201526024016105c6565b5080611bff565b805115611db65780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b60405162461bcd60e51b815260206004820152602260248201527f414249206465636f64696e673a207475706c65206461746120746f6f2073686f6044820152611c9d60f21b6064820152608481fd5b80356001600160a01b0381168114611e3657600080fd5b919050565b60008060008060008060c08789031215611e5757611e57611dcf565b611e6087611e1f565b95506020870135945060408701359350606087013560ff81168114611e8457600080fd5b9598949750929560808101359460a0909101359350915050565b600060208284031215611eb357611eb3611dcf565b611bff82611e1f565b600060208284031215611ed157611ed1611dcf565b5035919050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176111c8576111c8611ed8565b600082611f2257634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215611f3c57611f3c611dcf565b5051919050565b808201808211156111c8576111c8611ed8565b818103818111156111c8576111c8611ed8565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b600081611fb957611fb9611ed8565b506000190190565b600060018201611fd357611fd3611ed8565b5060010190565b600060208284031215611fef57611fef611dcf565b81518015158114611bff57600080fd5b6000825160005b818110156120205760208186018101518583015201612006565b50600092019182525091905056fe54617267657420636f6e747261637420646f6573206e6f7420636f6e7461696ea2646970667358221220fee2d7f17dfedbebecee030d29ce866da25dc32b56fce20c8ee8ad0c253e9d3164736f6c63430008140033

Verified Source Code Full Match

Compiler: v0.8.20+commit.a1b79de6 EVM: paris Optimization: Yes (200 runs)
DAOStaking.sol 572 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";

interface ITokenBurn {
    function burnFrom(address account, uint256 value) external;
}

/**
 * @title DAOStaking
 * @dev A contract for staking tokens with DAO features.
 */
contract DAOStaking is Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;

    event Staked(address indexed user, uint256 amount);
    event Claimed(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 penalty);
    event UnStake(
        address indexed user,
        uint256 reward,
        uint256 amount,
        uint256 penalty
    );
    event AddReward(address indexed user, uint256 amount);
    event RegularWallet(
        address indexed owner,
        uint256 burnAmount,
        uint256 votingPower
    );
    event RemainingClaim(address indexed owner, uint256 amount);
    event IncreasePool(address indexed owner, uint256 amount);

    address public immutable token;


    uint256 constant private RW_BURN_AMOUNT = 10_000 * TOKEN_DECIMAL; // Regular Wallet burn amount
    uint256 internal _rwTotalBurnAmount;

    uint256 internal constant TOKEN_DECIMAL = 1e18;
    uint256 internal constant PRO_WOTING = 200;
    uint256 internal constant REG_WOTING = 1;

    uint256 internal immutable LAUNCH_TIME;
    uint256 internal immutable LOCK_PERIOD; // 2 years 
    uint256 internal constant STAKE_AMOUNT = 2_000_000 * TOKEN_DECIMAL;
    uint256 internal constant PENALTY = 20; // 20% penalty for early withdrawal
    uint256 internal constant DAILY_REWARD = 500_000 * TOKEN_DECIMAL;
    uint256 internal immutable REWARD_PERIOD;

    uint256 internal _poolSize;
    uint256 internal _totalStaked;
    uint256 internal _totalClaimedReward;


    uint256 internal _lastUpdateDay; 

    struct Stake {
        uint256 startTime;
        uint256 lastClaimTime;
        uint256 totalClaim;
        uint256 epochDay;
        bool isActive;
    }
    
    mapping(address => Stake) internal stakes;
    mapping(uint256 => uint256) internal daysUser;

    mapping(address => bool) internal _votingProWallet; // PRO Wallet
    mapping(address => bool) internal _votingRegWallet; // Regular Wallet

    /**
     * @notice Constructor to initialize the DAOStaking contract.
     * @param initialOwner The address of the initial owner.
     * @param tokenAddress The address of the token contract.
     * @param pool The initial size of the reward pool.
     * @param launchTime The timestamp of when staking starts.
     * @param lockPeriod The duration for which staked tokens are locked.
     * @param rewardPeriod The duration of each reward period.
     */
    constructor(
        address initialOwner,
        address tokenAddress,
        uint256 pool,
        uint256 launchTime,
        uint256 lockPeriod,
        uint256 rewardPeriod
    ) Ownable(initialOwner) {
        require(
            initialOwner != address(0),
            "STAKE:PRO:Owner's address cannot be zero."
        );
        require(
            tokenAddress != address(0),
            "STAKE:PRO:Token address cannot be zero."
        );
        require(
            launchTime > block.timestamp,
            "STAKE:PRO:Launch time must be greater than present time."
        );

        token = tokenAddress;
        // Token will be sent in to contract amount of poolsize when its deployed.
        _poolSize = pool;
        LAUNCH_TIME = launchTime;
        LOCK_PERIOD = lockPeriod;
        REWARD_PERIOD = rewardPeriod;
    }

    /**
     * @notice Allows a user to stake tokens into the DAO.
     */
    function stake() external nonReentrant {
        address account = _msgSender();
        require(
            IERC20(token).allowance(account, address(this)) >= STAKE_AMOUNT,
            "STAKE:PRO:Insufficient allowance."
        );
        _stake(account);
    }

    /**
     * @notice Allows a user to stake tokens using permit signature.
     * @param account The owner of the tokens. 
     * @param amount The amount of tokens to stake.
     * @param deadline The deadline for the permit signature.
     * @param v The `v` component of the permit signature.
     * @param r The `r` component of the permit signature.
     * @param s The `s` component of the permit signature.
     */
    function stakeWithPermit(
        address account,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(
            amount == STAKE_AMOUNT,
            "STAKE:PRO:Exactly 2 milion token must be staked."
        );
        IERC20Permit(token).permit(
            account,
            address(this),
            amount,
            deadline,
            v,
            r,
            s
        );
        _stake(account); 
    }
    /**
     * @dev Internal function for staking tokens.
     * @param account Address of the account to stake tokens.
     */
    function _stake(address account) private {
        // Ensure staking time has started
        uint256 currentTime = block.timestamp;
        require(
            currentTime >= LAUNCH_TIME,
            "STAKE:PRO:Staking time has not started yet."
        );

        // Ensure staking period is still active
        uint256 currentDay = _currentDay();
        require(
            currentDay < _totalPeriod(),
            "STAKE:PRO:All tokens have been distributed."
        );

        // Ensure user has sufficient balance to stake
        require(
            IERC20(token).balanceOf(account) >= STAKE_AMOUNT,
            "STAKE:PRO:Insufficient balance."
        );

        Stake storage user = stakes[account];
        // Ensure user is not already staked
        require(!user.isActive, "STAKE:PRO:Already staked.");

        // Transfer tokens from user to contract
        IERC20(token).safeTransferFrom(account, address(this), STAKE_AMOUNT);

        // Update user stake information
        user.startTime = currentTime;
        user.lastClaimTime = 0;
        user.epochDay = currentDay + 1;
        user.isActive = true;

        // Update global variables
        _votingProWallet[account] = true;
        _totalStaked += STAKE_AMOUNT;
        _update(currentDay + 1);

        emit Staked(account, STAKE_AMOUNT);
    }

    /**
     * @notice Allows a user to claim their rewards.
     */
    function claim() external nonReentrant {
        address account = _msgSender();
        Stake storage user = stakes[account];
        require(user.isActive, "STAKE:PRO:You have none staked tokens.");
        uint256 currentDay = _currentDay();
        _update(currentDay);
        uint256 reward = _calculateReward(user.epochDay, currentDay);
        require(reward > 0, "STAKE:PRO:No reward have been earned yet.");

        user.lastClaimTime = block.timestamp;
        user.totalClaim += reward;
        user.epochDay = currentDay;
        _totalClaimedReward += reward;

        IERC20(token).safeTransfer(account, reward);

        _poolSize -= reward;

        emit Claimed(account, reward);
    }

    /**
     * @notice Unstakes tokens for the sender.
     */
    function unStake() external nonReentrant {
        address account = _msgSender();
        Stake storage user = stakes[account];

        // Ensure user has an active stake
        require(user.isActive, "STAKE:PRO:No active stake");

        uint256 currentTime = block.timestamp;
        uint256 currentDay = _currentDay();

        // Ensure user cannot unstake before their period is up
        require(
            currentDay >= user.epochDay,
            "STAKE:PRO:You cannot exit before period is up."
        );

        uint256 penaltyAmount;
        if (currentTime < user.startTime + LOCK_PERIOD) {
            penaltyAmount = PENALTY * (STAKE_AMOUNT / 100);
            _poolSize += penaltyAmount;
            emit Withdrawn(account, penaltyAmount);
        }

        uint256 returnAmount = STAKE_AMOUNT - penaltyAmount;

        // Give user any remaining claim rights
        uint256 reward = _calculateReward(user.epochDay, currentDay);
        if (reward > 0) {
            user.totalClaim += reward;
            returnAmount += reward;
            _totalClaimedReward += reward;
            _poolSize -= reward;
        }

        // Update global variables
        _totalStaked -= STAKE_AMOUNT;

        user.lastClaimTime = currentTime;
        user.epochDay = currentDay;
        user.isActive = false;

        _votingProWallet[account] = false;

        // Transfer remaining amount to user
        IERC20(token).safeTransfer(account, returnAmount);

        _update(currentDay + 1);

        emit UnStake(account, reward, returnAmount, penaltyAmount);
    }

    /**
     * @notice In order to lengthen stake contracts time, may increase the staking reward pool.
     * @param amount Stake amount can increase.
     */
    function increasePool(uint256 amount) external nonReentrant {
        IERC20(token).safeTransferFrom(_msgSender(), address(this), amount);
        _poolSize += amount;
        emit IncreasePool(_msgSender(), amount);
    }

    /**
     * @notice Owner can send to account rest amount token after contracts end.
     * @param account Transfer token to account
     */
    function remainingClaim(address account) external onlyOwner nonReentrant {
        require(_currentDay() >= _totalPeriod(), "STAKE:PRO:Distribution is still continue.");
        require(_totalStaked / STAKE_AMOUNT == 0, "STAKE:PRO:There is still pro wallet account at the contract.");
        require(account != address(this), "STAKE:PRO:You cannot send token to staking contract.");
        IERC20(token).safeTransfer(account, _poolSize);
        _poolSize = 0; 
        emit RemainingClaim(account, _poolSize);
    }

    // ------------------------------------------------------------
    // ===================== INTERNAL FUNCTIONS
    // ------------------------------------------------------------
    /**
     * @dev Updates the stake contract for the given day if necessary.
     * @param day The day to update the contract for.
     */
    function _update(uint256 day) internal {
        if (_lastUpdateDay <= day) {
            daysUser[day] = _totalStaked / STAKE_AMOUNT;
            _lastUpdateDay = day;
        }
    }
    /**
     * @dev Calculates the total reward to be distributed between two epoch days.
     * @param epochDay The starting epoch day.
     * @param claimDay The ending epoch day.
     * @return The total reward for the given period.
     */
    function _calculateReward(
        uint256 epochDay,
        uint256 claimDay
    ) internal view returns (uint256) {
        uint256 totalReward;
        uint256 lastUsers;
        for (uint256 i = epochDay; i < claimDay; i++) {
            uint256 dayUser = daysUser[i];
            if (dayUser != 0) {
                lastUsers = dayUser;
            }
            if(lastUsers > 0){
                totalReward += DAILY_REWARD / lastUsers; 
            }
        }
        return totalReward;
    }
    /**
     * @dev Calculates the timestamp for the next reward distribution. 
     * @param epochDay The epoch day to calculate the next reward for.
     * @return The timestamp for the next reward distribution.
     */
    function _nextRewardTime(uint256 epochDay) internal view returns (uint256) {
        uint256 daysPassed = (block.timestamp - LAUNCH_TIME) / REWARD_PERIOD;
        if (_currentDay() >= epochDay) {
            daysPassed += 1;
        } else {
            daysPassed += 2;
        }
        return LAUNCH_TIME + (daysPassed * REWARD_PERIOD);
    }
    /**
     * @dev Retrieves the current epoch day.
     * @return The current epoch day.
     */
    function _currentDay() internal view returns (uint256) {
        uint256 currentTime = block.timestamp;
        if (LAUNCH_TIME > currentTime) {
            return 0;
        }
        if (currentTime >= _endTime()) {
            return _totalPeriod(); 
        }
        return (currentTime - LAUNCH_TIME) / REWARD_PERIOD;
    }
    /**
     * @dev Calculates the total period of the stake contract.
     * @return The total period of the stake contract.
     */
    function _totalPeriod() internal view returns (uint256) { 
        return ((_poolSize + _totalClaimedReward) / DAILY_REWARD) + 1;
    }
    /**
     * @dev Calculates the end timestamp of the stake contract.
     * @return The end timestamp of the stake contract.
     */
    function _endTime() internal view returns (uint256) { 
        return LAUNCH_TIME + (_totalPeriod() * REWARD_PERIOD);
    }

    // ------------------------------------------------------------
    // ===================== PUBLIC FUNCTIONS
    // ------------------------------------------------------------

    /**
     * @notice Retrieves the voting power associated with an account.
     * @param account Address of the account to check.
     * @return The voting power of the account.
     */
    function votingPower(address account) public view returns (uint256) {
        return
            _votingProWallet[account]
                ? PRO_WOTING
                : _votingRegWallet[account]
                    ? REG_WOTING
                    : 0;
    }

    /**
     * @notice Checks if an account is a regular wallet.
     * @param account Address of the account to check.
     * @return A boolean indicating whether the account is a regular wallet.
     */
    function isRegularWallet(address account) public view returns (bool) {
        return _votingRegWallet[account];
    }

    /**
     * @notice Retrieves information about the stake account.
     * @param account Address of the stake account.
     * @return startTime The timestamp when the stake started.
     * @return lastClaimTime The timestamp of the last claim.
     * @return totalClaim Total claimed rewards.
     * @return unlockAmount Amount of rewards available for claiming.
     * @return nextUnlockTime Timestamp of the next reward unlock.
     * @return nextUnlockAmount Amount of rewards to unlock next.
     * @return epochDay The current day of the stake epoch.
     * @return endTime The timestamp when the stake ends.
     * @return power Voting power associated with the account.
     * @return isActive Whether the stake account is active.
     */
    function accountInfo(
        address account
    )
        public
        view
        returns (
            uint256 startTime,
            uint256 lastClaimTime,
            uint256 totalClaim,
            uint256 unlockAmount,
            uint256 nextUnlockTime,
            uint256 nextUnlockAmount,
            uint256 epochDay,
            uint256 endTime,
            uint256 power,
            bool isActive
        )
    {
        Stake memory user = stakes[account];

        startTime = user.startTime;
        lastClaimTime = user.lastClaimTime;
        totalClaim = user.totalClaim;
        epochDay = user.epochDay;
        isActive = user.isActive;

        power = votingPower(account);

        if (user.isActive) {
            unlockAmount = _calculateReward(user.epochDay, _currentDay());
            nextUnlockTime = _nextRewardTime(user.epochDay);
            nextUnlockAmount = DAILY_REWARD / (_totalStaked / STAKE_AMOUNT);
            endTime = user.startTime + LOCK_PERIOD; 
        }
    }


    /**
     * @notice Retrieves general information about the staking contract.
     * @return launchTime Timestamp of the staking contract launch.
     * @return rewardPeriod Duration of each reward period.
     * @return lastUpdateDay Last day the contract was updated.
     * @return currentDay The current day of the staking contract
     * @return poolSize Total size of the staking pool.
     * @return dailyReward The daily reward rate
     * @return totalStaked Total amount of tokens staked.
     * @return totalClaimedReward Total amount of claimed rewards.
     * @return userCount Number of users who have staked.
     * @return rwTotalBurnAmount Total amount burned from regular wallets.
     */
     function info() public view returns(
        uint256 launchTime,
        uint256 rewardPeriod,
        uint256 lastUpdateDay,
        uint256 currentDay,
        uint256 poolSize,
        uint256 dailyReward,
        uint256 totalStaked,
        uint256 totalClaimedReward,
        uint256 userCount,
        uint256 rwTotalBurnAmount
    ){
        return (
            LAUNCH_TIME,
            REWARD_PERIOD, 
            _lastUpdateDay, 
            _currentDay(), 
            _poolSize, 
            DAILY_REWARD,
            _totalStaked, 
            _totalClaimedReward, 
            _totalStaked / STAKE_AMOUNT, 
            _rwTotalBurnAmount
        );
    }

    /**
     * @notice Retrieves the number of users on a specific day.
     * @param day The day for which to retrieve the user count.
     * @return The number of users on the specified day.
     */
    function getDayUser(uint256 day) public view returns(uint256) {

        uint256 currentDay = day;
        while (true) {
            if (daysUser[currentDay] != 0) {
                return daysUser[currentDay];
            }
            if (currentDay == 0) {
                break;
            }
            currentDay--;
        }
        return 0;
    }

    /**
     * @notice Burns tokens from the sender's account and adds voting power.
     */
    function burnRW() public {
        _addVotingPower(_msgSender());
    }

    /**
     * @notice Burns tokens with permit and adds voting power.
     * @param account Address of the account to burn tokens from.
     * @param amount Amount of tokens to burn.
     * @param deadline Expiry timestamp for the permit.
     * @param v Component of the signature.
     * @param r Component of the signature.
     * @param s Component of the signature.
     */
    function burnRWWithPermit(
        address account,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public {
        require(
            amount == RW_BURN_AMOUNT,
            "STAKE:REGULAR:Exactly 10 thousands token must be staked"
        );
        IERC20Permit(address(token)).permit(
            account,
            address(this),
            amount,
            deadline,
            v,
            r,
            s
        );
        _addVotingPower(account);
    }

    /**
     * @dev Adds voting power to an account and emits an event.
     * @param account Address of the account to add voting power to.
     */
    function _addVotingPower(address account) internal {
        require(!_votingRegWallet[account], "STAKE:REGULAR:This address is already a regular wallet.");
        ITokenBurn(token).burnFrom(account, RW_BURN_AMOUNT);
        _votingRegWallet[account] = true;
        emit RegularWallet(account, RW_BURN_AMOUNT, REG_WOTING);
        _rwTotalBurnAmount += RW_BURN_AMOUNT;
    }
}
Address.sol 159 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * 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.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @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`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) 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 FailedInnerCall();
        }
    }
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
Ownable.sol 100 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}
ReentrancyGuard.sol 84 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @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;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    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
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

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

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
SafeERC20.sol 118 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../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;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @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);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // 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 cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @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.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
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].
     *
     * CAUTION: See Security Considerations above.
     */
    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

accountInfo 0xa7310b58 → uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, bool
getDayUser 0xc0d92e8e → uint256
info 0x370158ea → uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256
isRegularWallet 0xfa65639b → bool
owner 0x8da5cb5b → address
token 0xfc0c546a → address
votingPower 0xc07473f6 → uint256

Write Contract 10 functions

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

burnRW 0xc1659931
No parameters
burnRWWithPermit 0x8afe6418
address account
uint256 amount
uint256 deadline
uint8 v
bytes32 r
bytes32 s
claim 0x4e71d92d
No parameters
increasePool 0x833e8bb6
uint256 amount
remainingClaim 0x7d9aff4b
address account
renounceOwnership 0x715018a6
No parameters
stake 0x3a4b66f1
No parameters
stakeWithPermit 0x73d6a889
address account
uint256 amount
uint256 deadline
uint8 v
bytes32 r
bytes32 s
transferOwnership 0xf2fde38b
address newOwner
unStake 0x73cf575a
No parameters

Recent Transactions

No transactions found for this address