Contract 0x4024f711b40f9fa42620123fc179102a53e6a500 1

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xb52faefe8881fb68566e56639f15fecb94a6b0486191d643a04d83aa5a936bab0x60a0604033051122021-11-24 18:54:21441 days 52 mins ago0xc7f8d87734ab2cbf70030ac8aa82abfe3e8126cb IN  Create: FeeDiscount0 ETH0.036160408549 ETH
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xdb4c7f97dd7061603511e0d639360d61ef4617e78c840ae6d0a2b8b600dd1eea383300972022-11-15 15:26:4385 days 4 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x802995c491ddccbb0a0d882ed242149c4fdd6987c2076aea4e88fded775bff2c383298382022-11-15 15:25:2785 days 4 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0xde46c4c9e0b805597939d7efa27774aa4d9dc6eb2aee45de9db402c422570544383199652022-11-15 14:37:3585 days 5 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x603a66bdf47767a015b9e5a886d4736675713c63be6b80804aa44db9768605d1383134772022-11-15 14:07:1285 days 5 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x603a66bdf47767a015b9e5a886d4736675713c63be6b80804aa44db9768605d1383134772022-11-15 14:07:1285 days 5 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x78b25e9acea0633e57211c6827e17f37d8504bbbad17ec691a3216e99c216d4d383069762022-11-15 13:36:5985 days 6 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x5fdd31f4c6ec0307ab39acb105c7af5105f5631022a6a75dd22cc6d9b9871df7383065452022-11-15 13:35:1685 days 6 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0xdbf443be550a07e04f723e631f77e09469a6aea49670c26c3da417f8fa8bf33e383065022022-11-15 13:35:0585 days 6 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0xe5f9d8fdf0dfc1133ef8c72b76ea740a7ae58efd051f6ad5a3be10ebed647f7e383046802022-11-15 13:26:4285 days 6 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0xe9cd05604ba50d6f6f639bd5217e48982cd9ab8fdbbc9265b84bbed4e8effa8f383039642022-11-15 13:23:2385 days 6 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x88e78b100a4d6e4b6cf74ea47b706cb2fd3b50f55179b381c96aedddbef6a1a8382775392022-11-15 11:21:2085 days 8 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0xcb6922428edc4c2fd639f0ccfba14978ef3b1f221a9d0b4f91f1706210461905382650232022-11-15 10:20:3885 days 9 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x17ba3b0e6adcfa76025cd7dc38925994e48afe4f4765d838147dbe445f56f59f382600642022-11-15 9:56:2385 days 9 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x5bcdf6c27df88b73ae658a8ea6243ad806b79f6f6ba504b60de46c5b66dd6148382591692022-11-15 9:52:1285 days 9 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x2487e593b6545f67009e79cad888203315ab9d4d2ec8a6c549c69788e14231ad382540682022-11-15 9:28:0085 days 10 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0xc0c9400188daead576220412060c99e38a1c4e8565393ed0f7dd867ed32df11d382521492022-11-15 9:18:5885 days 10 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x7434a9817e0d595cbbdbfdf591b1b18b8bd2c8279414e994d540d7af0d85e1d9382456982022-11-15 8:48:3285 days 10 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x7434a9817e0d595cbbdbfdf591b1b18b8bd2c8279414e994d540d7af0d85e1d9382456982022-11-15 8:48:3285 days 10 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0xc6c4b2e066d0a21d922501a3ad6050bd8fb97cf8288d49ff3a353599130f900a382456652022-11-15 8:48:3085 days 10 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x25adb1013f6b76b932bf7c9087d2b806823d1b908a3f13e3cd5fa5a1cf791088382456492022-11-15 8:48:2685 days 10 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0xad031a204e4fa46b02787f31d6e03926801d915b98e54fa2e71ccbea13faed1b382456402022-11-15 8:48:2385 days 10 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0xf9052a55fe46694713355360bf41dcd1c1d77323e4f310f78bed07df367b223c382456282022-11-15 8:48:2085 days 10 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x7517fcaf5b7ac71018e1bba14d277103f4246150e157061b1dc7810ce060d3ff382456202022-11-15 8:48:1885 days 10 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x7517fcaf5b7ac71018e1bba14d277103f4246150e157061b1dc7810ce060d3ff382456202022-11-15 8:48:1885 days 10 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
0x7517fcaf5b7ac71018e1bba14d277103f4246150e157061b1dc7810ce060d3ff382456202022-11-15 8:48:1885 days 10 hrs ago Premia: Fee Discount L2 0x4024f711b40f9fa42620123fc179102a53e6a5000 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FeeDiscount

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 9 : FeeDiscount.sol
// SPDX-License-Identifier: BUSL-1.1
// For further clarification please see https://license.premia.legal

pragma solidity ^0.8.0;

import {SafeCast} from "@solidstate/contracts/utils/SafeCast.sol";
import {IERC20, SafeERC20} from "@solidstate/contracts/utils/SafeERC20.sol";
import {IERC2612} from "@solidstate/contracts/token/ERC20/permit/IERC2612.sol";

import {FeeDiscountStorage} from "./FeeDiscountStorage.sol";
import {IFeeDiscount} from "./IFeeDiscount.sol";

/**
 * @author Premia
 * @title A contract allowing you to lock xPremia to get Premia protocol fee discounts
 */
contract FeeDiscount is IFeeDiscount {
    using SafeERC20 for IERC20;
    using SafeCast for uint256;

    address internal immutable xPREMIA;
    uint256 internal constant INVERSE_BASIS_POINT = 1e4;

    constructor(address xPremia) {
        xPREMIA = xPremia;
    }

    //////////////////////////////////////////////////

    //////////
    // Main //
    //////////

    /**
     * @inheritdoc IFeeDiscount
     */
    function stakeWithPermit(
        uint256 amount,
        uint256 period,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        IERC2612(address(xPREMIA)).permit(
            msg.sender,
            address(this),
            amount,
            deadline,
            v,
            r,
            s
        );
        _stake(amount, period);
    }

    /**
     * @inheritdoc IFeeDiscount
     */
    function stake(uint256 amount, uint256 period) external override {
        _stake(amount, period);
    }

    function _stake(uint256 amount, uint256 period) internal {
        FeeDiscountStorage.Layout storage l = FeeDiscountStorage.layout();

        require(
            _getStakePeriodMultiplier(period) > 0,
            "Stake period does not exists"
        );
        FeeDiscountStorage.UserInfo storage user = l.userInfo[msg.sender];

        uint256 lockedUntil = block.timestamp + period;
        require(
            lockedUntil > user.lockedUntil,
            "Cannot add stake with lower stake period"
        );

        _transferXPremia(msg.sender, address(this), amount);
        user.balance = user.balance + amount;
        user.lockedUntil = lockedUntil.toUint64();
        user.stakePeriod = period.toUint64();

        emit Staked(msg.sender, amount, period, lockedUntil);
    }

    /**
     * @inheritdoc IFeeDiscount
     */
    function unstake(uint256 amount) external override {
        FeeDiscountStorage.Layout storage l = FeeDiscountStorage.layout();

        FeeDiscountStorage.UserInfo storage user = l.userInfo[msg.sender];

        // We allow unstake if the stakePeriod that the user used has been disabled
        require(user.lockedUntil <= block.timestamp, "Stake still locked");

        user.balance -= amount;
        _transferXPremia(address(this), msg.sender, amount);

        emit Unstaked(msg.sender, amount);
    }

    //////////////////////////////////////////////////

    //////////
    // View //
    //////////

    /**
     * @inheritdoc IFeeDiscount
     */
    function getStakeAmountWithBonus(address user)
        external
        view
        override
        returns (uint256)
    {
        return _getStakeAmountWithBonus(user);
    }

    /**
     * @inheritdoc IFeeDiscount
     */
    function getDiscount(address user)
        external
        view
        override
        returns (uint256)
    {
        uint256 userBalance = _getStakeAmountWithBonus(user);

        IFeeDiscount.StakeLevel[] memory stakeLevels = _getStakeLevels();

        for (uint256 i = 0; i < stakeLevels.length; i++) {
            IFeeDiscount.StakeLevel memory level = stakeLevels[i];

            if (userBalance < level.amount) {
                uint256 amountPrevLevel;
                uint256 discountPrevLevel;

                // If stake is lower, user is in this level, and we need to LERP with prev level to get discount value
                if (i > 0) {
                    amountPrevLevel = stakeLevels[i - 1].amount;
                    discountPrevLevel = stakeLevels[i - 1].discount;
                } else {
                    // If this is the first level, prev level is 0 / 0
                    amountPrevLevel = 0;
                    discountPrevLevel = 0;
                }

                uint256 remappedDiscount = level.discount - discountPrevLevel;

                uint256 remappedAmount = level.amount - amountPrevLevel;
                uint256 remappedBalance = userBalance - amountPrevLevel;
                uint256 levelProgress = (remappedBalance *
                    INVERSE_BASIS_POINT) / remappedAmount;

                return
                    discountPrevLevel +
                    ((remappedDiscount * levelProgress) / INVERSE_BASIS_POINT);
            }
        }

        // If no match found it means user is >= max possible stake, and therefore has max discount possible
        return stakeLevels[stakeLevels.length - 1].discount;
    }

    /**
     * @inheritdoc IFeeDiscount
     */
    function getStakeLevels()
        external
        pure
        override
        returns (IFeeDiscount.StakeLevel[] memory stakeLevels)
    {
        return _getStakeLevels();
    }

    /**
     * @inheritdoc IFeeDiscount
     */
    function getStakePeriodMultiplier(uint256 period)
        external
        pure
        override
        returns (uint256)
    {
        return _getStakePeriodMultiplier(period);
    }

    /**
     * @inheritdoc IFeeDiscount
     */
    function getUserInfo(address user)
        external
        view
        override
        returns (FeeDiscountStorage.UserInfo memory)
    {
        return FeeDiscountStorage.layout().userInfo[user];
    }

    //////////////////////////////////////////////////

    //////////////
    // Internal //
    //////////////

    /**
     * @notice Utility function to check if a value is inside an array
     * @param value The value to look for
     * @param array The array to check
     * @return Whether the value is in the array or not
     */
    function _isInArray(uint256 value, uint256[] memory array)
        internal
        pure
        returns (bool)
    {
        uint256 length = array.length;
        for (uint256 i = 0; i < length; ++i) {
            if (array[i] == value) {
                return true;
            }
        }

        return false;
    }

    function _getStakeLevels()
        internal
        pure
        returns (IFeeDiscount.StakeLevel[] memory stakeLevels)
    {
        stakeLevels = new IFeeDiscount.StakeLevel[](4);

        stakeLevels[0] = IFeeDiscount.StakeLevel(5000 * 1e18, 2500); // -25%
        stakeLevels[1] = IFeeDiscount.StakeLevel(50000 * 1e18, 5000); // -50%
        stakeLevels[2] = IFeeDiscount.StakeLevel(250000 * 1e18, 7500); // -75%
        stakeLevels[3] = IFeeDiscount.StakeLevel(500000 * 1e18, 9500); // -95%
    }

    function _getStakePeriodMultiplier(uint256 period)
        internal
        pure
        returns (uint256)
    {
        if (period == 30 days) return 10000; // x1
        if (period == 90 days) return 12500; // x1.25
        if (period == 180 days) return 15000; // x1.5
        if (period == 360 days) return 20000; // x2

        return 0;
    }

    function _getStakeAmountWithBonus(address user)
        internal
        view
        returns (uint256)
    {
        FeeDiscountStorage.Layout storage l = FeeDiscountStorage.layout();

        FeeDiscountStorage.UserInfo memory userInfo = l.userInfo[user];
        return
            (userInfo.balance *
                _getStakePeriodMultiplier(userInfo.stakePeriod)) /
            INVERSE_BASIS_POINT;
    }

    /**
     * @notice transfer tokens from holder to recipient
     * @param holder owner of tokens to be transferred
     * @param recipient beneficiary of transfer
     * @param amount quantity of tokens transferred
     */
    function _transferXPremia(
        address holder,
        address recipient,
        uint256 amount
    ) internal virtual {
        if (holder == address(this)) {
            IERC20(xPREMIA).safeTransfer(recipient, amount);
        } else {
            IERC20(xPREMIA).safeTransferFrom(holder, recipient, amount);
        }
    }
}

File 2 of 9 : SafeCast.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @notice Helper library for safe casting of uint and int values
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
 */
library SafeCast {
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, 'SafeCast: value does not fit');
        return uint224(value);
    }

    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, 'SafeCast: value does not fit');
        return uint128(value);
    }

    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, 'SafeCast: value does not fit');
        return uint96(value);
    }

    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, 'SafeCast: value does not fit');
        return uint64(value);
    }

    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, 'SafeCast: value does not fit');
        return uint32(value);
    }

    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, 'SafeCast: value does not fit');
        return uint16(value);
    }

    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, 'SafeCast: value does not fit');
        return uint8(value);
    }

    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, 'SafeCast: value must be positive');
        return uint256(value);
    }

    function toInt128(int256 value) internal pure returns (int128) {
        require(
            value >= type(int128).min && value <= type(int128).max,
            'SafeCast: value does not fit'
        );
        return int128(value);
    }

    function toInt64(int256 value) internal pure returns (int64) {
        require(
            value >= type(int64).min && value <= type(int64).max,
            'SafeCast: value does not fit'
        );
        return int64(value);
    }

    function toInt32(int256 value) internal pure returns (int32) {
        require(
            value >= type(int32).min && value <= type(int32).max,
            'SafeCast: value does not fit'
        );
        return int32(value);
    }

    function toInt16(int256 value) internal pure returns (int16) {
        require(
            value >= type(int16).min && value <= type(int16).max,
            'SafeCast: value does not fit'
        );
        return int16(value);
    }

    function toInt8(int256 value) internal pure returns (int8) {
        require(
            value >= type(int8).min && value <= type(int8).max,
            'SafeCast: value does not fit'
        );
        return int8(value);
    }

    function toInt256(uint256 value) internal pure returns (int256) {
        require(
            value <= uint256(type(int256).max),
            'SafeCast: value does not fit'
        );
        return int256(value);
    }
}

File 3 of 9 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IERC20 } from '../token/ERC20/IERC20.sol';
import { AddressUtils } from './AddressUtils.sol';

/**
 * @title Safe ERC20 interaction library
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)
 */
library SafeERC20 {
    using AddressUtils 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 safeApprove (like approve) should only be called when setting an initial allowance or when resetting it to zero; otherwise prefer safeIncreaseAllowance and safeDecreaseAllowance
     */
    function safeApprove(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            'SafeERC20: approve from non-zero to non-zero allowance'
        );

        _callOptionalReturn(
            token,
            abi.encodeWithSelector(token.approve.selector, spender, value)
        );
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + 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
                )
            );
        }
    }

    /**
     * @notice send transaction data and check validity of return value, if present
     * @param token ERC20 token interface
     * @param data transaction data
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        bytes memory returndata = address(token).functionCall(
            data,
            'SafeERC20: low-level call failed'
        );

        if (returndata.length > 0) {
            require(
                abi.decode(returndata, (bool)),
                'SafeERC20: ERC20 operation did not succeed'
            );
        }
    }
}

File 4 of 9 : IERC2612.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @notice ERC2612 interface
 * @dev see https://eips.ethereum.org/EIPS/eip-2612.
 */
interface IERC2612 {
    /**
     * @notice approve spender to transfer tokens held by owner via signature
     * @dev this function may be vulnerable to approval replay attacks
     * @param owner holder of tokens and signer of permit
     * @param spender beneficiary of approval
     * @param amount quantity of tokens to approve
     * @param v secp256k1 'v' value
     * @param r secp256k1 'r' value
     * @param s secp256k1 's' value
     */
    function permit(
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice get the current ERC2612 nonce for the given address
     * @return current nonce
     */
    function nonces(address owner) external view returns (uint256);
}

File 5 of 9 : FeeDiscountStorage.sol
// SPDX-License-Identifier: BUSL-1.1
// For further clarification please see https://license.premia.legal

pragma solidity ^0.8.0;

library FeeDiscountStorage {
    bytes32 internal constant STORAGE_SLOT =
        keccak256("premia.contracts.staking.PremiaFeeDiscount");

    struct UserInfo {
        uint256 balance; // Balance staked by user
        uint64 stakePeriod; // Stake period selected by user
        uint64 lockedUntil; // Timestamp at which the lock ends
    }

    struct Layout {
        // User data with xPREMIA balance staked and date at which lock ends
        mapping(address => UserInfo) userInfo;
    }

    function layout() internal pure returns (Layout storage l) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            l.slot := slot
        }
    }
}

File 6 of 9 : IFeeDiscount.sol
// SPDX-License-Identifier: LGPL-3.0-or-later

pragma solidity ^0.8.0;

import {FeeDiscountStorage} from "./FeeDiscountStorage.sol";

interface IFeeDiscount {
    event Staked(
        address indexed user,
        uint256 amount,
        uint256 stakePeriod,
        uint256 lockedUntil
    );
    event Unstaked(address indexed user, uint256 amount);

    struct StakeLevel {
        uint256 amount; // Amount to stake
        uint256 discount; // Discount when amount is reached
    }

    /**
     * @notice Stake using IERC2612 permit
     * @param amount The amount of xPremia to stake
     * @param period The lockup period (in seconds)
     * @param deadline Deadline after which permit will fail
     * @param v V
     * @param r R
     * @param s S
     */
    function stakeWithPermit(
        uint256 amount,
        uint256 period,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @notice Lockup xPremia for protocol fee discounts
     *          Longer period of locking will apply a multiplier on the amount staked, in the fee discount calculation
     * @param amount The amount of xPremia to stake
     * @param period The lockup period (in seconds)
     */
    function stake(uint256 amount, uint256 period) external;

    /**
     * @notice Unstake xPremia (If lockup period has ended)
     * @param amount The amount of xPremia to unstake
     */
    function unstake(uint256 amount) external;

    //////////
    // View //
    //////////

    /**
     * Calculate the stake amount of a user, after applying the bonus from the lockup period chosen
     * @param user The user from which to query the stake amount
     * @return The user stake amount after applying the bonus
     */
    function getStakeAmountWithBonus(address user)
        external
        view
        returns (uint256);

    /**
     * @notice Calculate the % of fee discount for user, based on his stake
     * @param user The _user for which the discount is for
     * @return Percentage of protocol fee discount (in basis point)
     *         Ex : 1000 = 10% fee discount
     */
    function getDiscount(address user) external view returns (uint256);

    /**
     * @notice Get stake levels
     * @return Stake levels
     *         Ex : 2500 = -25%
     */
    function getStakeLevels() external returns (StakeLevel[] memory);

    /**
     * @notice Get stake period multiplier
     * @param period The duration (in seconds) for which tokens are locked
     * @return The multiplier for this staking period
     *         Ex : 20000 = x2
     */
    function getStakePeriodMultiplier(uint256 period)
        external
        returns (uint256);

    /**
     * @notice Get staking infos of a user
     * @param user The user address for which to get staking infos
     * @return The staking infos of the user
     */
    function getUserInfo(address user)
        external
        view
        returns (FeeDiscountStorage.UserInfo memory);
}

File 7 of 9 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IERC20Internal } from './IERC20Internal.sol';

/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
interface IERC20 is IERC20Internal {
    /**
     * @notice query the total minted token supply
     * @return token supply
     */
    function totalSupply() external view returns (uint256);

    /**
     * @notice query the token balance of given account
     * @param account address to query
     * @return token balance
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @notice query the allowance granted from given holder to given spender
     * @param holder approver of allowance
     * @param spender recipient of allowance
     * @return token allowance
     */
    function allowance(address holder, address spender)
        external
        view
        returns (uint256);

    /**
     * @notice grant approval to spender to spend tokens
     * @dev prefer ERC20Extended functions to avoid transaction-ordering vulnerability (see https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729)
     * @param spender recipient of allowance
     * @param amount quantity of tokens approved for spending
     * @return success status (always true; otherwise function should revert)
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @notice transfer tokens to given recipient
     * @param recipient beneficiary of token transfer
     * @param amount quantity of tokens to transfer
     * @return success status (always true; otherwise function should revert)
     */
    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    /**
     * @notice transfer tokens to given recipient on behalf of given holder
     * @param holder holder of tokens prior to transfer
     * @param recipient beneficiary of token transfer
     * @param amount quantity of tokens to transfer
     * @return success status (always true; otherwise function should revert)
     */
    function transferFrom(
        address holder,
        address recipient,
        uint256 amount
    ) external returns (bool);
}

File 8 of 9 : AddressUtils.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

library AddressUtils {
    function toString(address account) internal pure returns (string memory) {
        bytes32 value = bytes32(uint256(uint160(account)));
        bytes memory alphabet = '0123456789abcdef';
        bytes memory chars = new bytes(42);

        chars[0] = '0';
        chars[1] = 'x';

        for (uint256 i = 0; i < 20; i++) {
            chars[2 + i * 2] = alphabet[uint8(value[i + 12] >> 4)];
            chars[3 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)];
        }

        return string(chars);
    }

    function isContract(address account) internal view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    function sendValue(address payable account, uint256 amount) internal {
        (bool success, ) = account.call{ value: amount }('');
        require(success, 'AddressUtils: failed to send value');
    }

    function functionCall(address target, bytes memory data)
        internal
        returns (bytes memory)
    {
        return
            functionCall(target, data, 'AddressUtils: failed low-level call');
    }

    function functionCall(
        address target,
        bytes memory data,
        string memory error
    ) internal returns (bytes memory) {
        return _functionCallWithValue(target, data, 0, error);
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return
            functionCallWithValue(
                target,
                data,
                value,
                'AddressUtils: failed low-level call with value'
            );
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory error
    ) internal returns (bytes memory) {
        require(
            address(this).balance >= value,
            'AddressUtils: insufficient balance for call'
        );
        return _functionCallWithValue(target, data, value, error);
    }

    function _functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory error
    ) private returns (bytes memory) {
        require(
            isContract(target),
            'AddressUtils: function call to non-contract'
        );

        (bool success, bytes memory returnData) = target.call{ value: value }(
            data
        );

        if (success) {
            return returnData;
        } else if (returnData.length > 0) {
            assembly {
                let returnData_size := mload(returnData)
                revert(add(32, returnData), returnData_size)
            }
        } else {
            revert(error);
        }
    }
}

File 9 of 9 : IERC20Internal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title Partial ERC20 interface needed by internal functions
 */
interface IERC20Internal {
    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"xPremia","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stakePeriod","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockedUntil","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unstaked","type":"event"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getDiscount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getStakeAmountWithBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakeLevels","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"discount","type":"uint256"}],"internalType":"struct IFeeDiscount.StakeLevel[]","name":"stakeLevels","type":"tuple[]"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"period","type":"uint256"}],"name":"getStakePeriodMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserInfo","outputs":[{"components":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint64","name":"stakePeriod","type":"uint64"},{"internalType":"uint64","name":"lockedUntil","type":"uint64"}],"internalType":"struct FeeDiscountStorage.UserInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"period","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"period","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"stakeWithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a060405234801561001057600080fd5b50604051610fbf380380610fbf83398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b608051610f26610099600039600081816104a001528181610715015261074e0152610f266000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80635668b02e1161005b5780635668b02e146100f05780636386c1c7146101035780637b0472f0146101b7578063af34c07d146101ca57600080fd5b806306f2791a1461008d578063263aaee7146100b35780632e17de78146100c857806343482fb9146100dd575b600080fd5b6100a061009b366004610c69565b6101dd565b6040519081526020015b60405180910390f35b6100bb610362565b6040516100aa9190610c99565b6100db6100d6366004610ce8565b610371565b005b6100a06100eb366004610c69565b61044f565b6100db6100fe366004610d01565b610460565b610183610111366004610c69565b6040805160608082018352600080835260208084018290529284018190526001600160a01b03949094168452600080516020610ed183398151915282529282902082519384018352805484526001015467ffffffffffffffff80821692850192909252600160401b9004169082015290565b604080518251815260208084015167ffffffffffffffff9081169183019190915292820151909216908201526060016100aa565b6100db6101c5366004610d5a565b610516565b6100a06101d8366004610ce8565b610524565b6000806101e98361052f565b905060006101f56105b7565b905060005b815181101561032d57600082828151811061021757610217610d7c565b60200260200101519050806000015184101561031a5760008083156102915784610242600186610da8565b8151811061025257610252610d7c565b60200260200101516000015191508460018561026e9190610da8565b8151811061027e5761027e610d7c565b6020026020010151602001519050610298565b5060009050805b60008184602001516102aa9190610da8565b905060008385600001516102be9190610da8565b905060006102cc858a610da8565b90506000826102dd61271084610dbf565b6102e79190610dde565b90506127106102f68286610dbf565b6103009190610dde565b61030a9086610e00565b9c9b505050505050505050505050565b508061032581610e18565b9150506101fa565b50806001825161033d9190610da8565b8151811061034d5761034d610d7c565b60200260200101516020015192505050919050565b606061036c6105b7565b905090565b336000908152600080516020610ed183398151915260208190526040909120600181015442600160401b90910467ffffffffffffffff1611156103f05760405162461bcd60e51b815260206004820152601260248201527114dd185ad9481cdd1a5b1b081b1bd8dad95960721b60448201526064015b60405180910390fd5b828160000160008282546104049190610da8565b9091555061041590503033856106f7565b60405183815233907f0f5bb82176feb1b5e747e28471aa92156a04d9f3ab9f45f28e2d704232b93f759060200160405180910390a2505050565b600061045a8261052f565b92915050565b60405163d505accf60e01b8152336004820152306024820152604481018790526064810185905260ff8416608482015260a4810183905260c481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d505accf9060e401600060405180830381600087803b1580156104ec57600080fd5b505af1158015610500573d6000803e3d6000fd5b5050505061050e8686610776565b505050505050565b6105208282610776565b5050565b600061045a82610933565b6001600160a01b0381166000908152600080516020610ed18339815191526020818152604080842081516060810183528154815260019091015467ffffffffffffffff808216948301859052600160401b9091041691810191909152906127109061059990610933565b82516105a59190610dbf565b6105af9190610dde565b949350505050565b60408051600480825260a08201909252606091816020015b60408051808201909152600080825260208201528152602001906001900390816105cf579050509050604051806040016040528069010f0cf064dd5920000081526020016109c48152508160008151811061062c5761062c610d7c565b60200260200101819052506040518060400160405280690a968163f0a57b40000081526020016113888152508160018151811061066b5761066b610d7c565b602002602001018190525060405180604001604052806934f086f3b33b684000008152602001611d4c815250816002815181106106aa576106aa610d7c565b602002602001018190525060405180604001604052806969e10de76676d0800000815260200161251c815250816003815181106106e9576106e9610d7c565b602002602001018190525090565b6001600160a01b0383163014156107415761073c6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016838361098e565b505050565b61073c6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168484846109f1565b600080516020610ed1833981519152600061079083610933565b116107dd5760405162461bcd60e51b815260206004820152601c60248201527f5374616b6520706572696f6420646f6573206e6f74206578697374730000000060448201526064016103e7565b336000908152602082905260408120906107f78442610e00565b6001830154909150600160401b900467ffffffffffffffff16811161086f5760405162461bcd60e51b815260206004820152602860248201527f43616e6e6f7420616464207374616b652077697468206c6f776572207374616b60448201526719481c195c9a5bd960c21b60648201526084016103e7565b61087a3330876106f7565b8154610887908690610e00565b825561089281610a2f565b8260010160086101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506108c584610a2f565b60018301805467ffffffffffffffff191667ffffffffffffffff92909216919091179055604080518681526020810186905290810182905233907fb4caaf29adda3eefee3ad552a8e85058589bf834c7466cae4ee58787f70589ed9060600160405180910390a25050505050565b60008162278d0014156109495750612710919050565b816276a700141561095d57506130d4919050565b8162ed4e0014156109715750613a98919050565b816301da9c0014156109865750614e20919050565b506000919050565b6040516001600160a01b03831660248201526044810182905261073c90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152610a8d565b6040516001600160a01b0380851660248301528316604482015260648101829052610a299085906323b872dd60e01b906084016109ba565b50505050565b600067ffffffffffffffff821115610a895760405162461bcd60e51b815260206004820152601c60248201527f53616665436173743a2076616c756520646f6573206e6f74206669740000000060448201526064016103e7565b5090565b6000610ae2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316610b5f9092919063ffffffff16565b80519091501561073c5780806020019051810190610b009190610e33565b61073c5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016103e7565b60606105af84846000856060843b610bcd5760405162461bcd60e51b815260206004820152602b60248201527f416464726573735574696c733a2066756e6374696f6e2063616c6c20746f206e60448201526a1bdb8b58dbdb9d1c9858dd60aa1b60648201526084016103e7565b600080866001600160a01b03168587604051610be99190610e81565b60006040518083038185875af1925050503d8060008114610c26576040519150601f19603f3d011682016040523d82523d6000602084013e610c2b565b606091505b50915091508115610c3f5791506105af9050565b805115610c4f5780518082602001fd5b8360405162461bcd60e51b81526004016103e79190610e9d565b600060208284031215610c7b57600080fd5b81356001600160a01b0381168114610c9257600080fd5b9392505050565b602080825282518282018190526000919060409081850190868401855b82811015610cdb57815180518552860151868501529284019290850190600101610cb6565b5091979650505050505050565b600060208284031215610cfa57600080fd5b5035919050565b60008060008060008060c08789031215610d1a57600080fd5b863595506020870135945060408701359350606087013560ff81168114610d4057600080fd5b9598949750929560808101359460a0909101359350915050565b60008060408385031215610d6d57600080fd5b50508035926020909101359150565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600082821015610dba57610dba610d92565b500390565b6000816000190483118215151615610dd957610dd9610d92565b500290565b600082610dfb57634e487b7160e01b600052601260045260246000fd5b500490565b60008219821115610e1357610e13610d92565b500190565b6000600019821415610e2c57610e2c610d92565b5060010190565b600060208284031215610e4557600080fd5b81518015158114610c9257600080fd5b60005b83811015610e70578181015183820152602001610e58565b83811115610a295750506000910152565b60008251610e93818460208701610e55565b9190910192915050565b6020815260008251806020840152610ebc816040850160208701610e55565b601f01601f1916919091016040019291505056fec03464a5d016cc56dc72424af16756f48831d5ab9e7bcd4c20495fa72ad49923a2646970667358221220d3bc6ccc44c1cb0e652e5ac723000343c946d2e7537b69eb39456e4fb02adc7464736f6c634300080900330000000000000000000000000d7d0efdcbfe5466b387e127709f24603920f671

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000000d7d0efdcbfe5466b387e127709f24603920f671

-----Decoded View---------------
Arg [0] : xPremia (address): 0x0d7d0efdcbfe5466b387e127709f24603920f671

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000000d7d0efdcbfe5466b387e127709f24603920f671


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.