Contracts

SnowsightManager.sol

0x727Dc3C412cCb942c6b5f220190ebAB3eFE0Eb93

/**
 *Submitted for verification at snowtrace.io on 2022-05-12
*/

//  ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄        ▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄  ▄         ▄  ▄▄▄▄▄▄▄▄▄▄▄       ▄            ▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄   ▄▄▄▄▄▄▄▄▄▄▄
//  ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░▌      ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌     ▐░▌          ▐░░░░░░░░░░░▌▐░░░░░░░░░░▌ ▐░░░░░░░░░░░▌
//  ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌▐░█▀▀▀▀▀▀▀█░▌ ▀▀▀▀█░█▀▀▀▀ ▐░▌░▌     ▐░▌▐░█▀▀▀▀▀▀▀▀▀  ▀▀▀▀█░█▀▀▀▀ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌       ▐░▌ ▀▀▀▀█░█▀▀▀▀      ▐░▌          ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀
//  ▐░▌          ▐░▌       ▐░▌▐░▌       ▐░▌     ▐░▌     ▐░▌▐░▌    ▐░▌▐░▌               ▐░▌     ▐░▌          ▐░▌       ▐░▌     ▐░▌          ▐░▌          ▐░▌       ▐░▌▐░▌       ▐░▌▐░▌
//  ▐░▌          ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄█░▌     ▐░▌     ▐░▌ ▐░▌   ▐░▌▐░█▄▄▄▄▄▄▄▄▄      ▐░▌     ▐░▌ ▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌     ▐░▌          ▐░▌          ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄
//  ▐░▌          ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌     ▐░▌  ▐░▌  ▐░▌▐░░░░░░░░░░░▌     ▐░▌     ▐░▌▐░░░░░░░░▌▐░░░░░░░░░░░▌     ▐░▌          ▐░▌          ▐░░░░░░░░░░░▌▐░░░░░░░░░░▌ ▐░░░░░░░░░░░▌
//  ▐░▌          ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌     ▐░▌     ▐░▌   ▐░▌ ▐░▌ ▀▀▀▀▀▀▀▀▀█░▌     ▐░▌     ▐░▌ ▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌     ▐░▌          ▐░▌          ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌ ▀▀▀▀▀▀▀▀▀█░▌
//  ▐░▌          ▐░▌       ▐░▌▐░▌       ▐░▌     ▐░▌     ▐░▌    ▐░▌▐░▌          ▐░▌     ▐░▌     ▐░▌       ▐░▌▐░▌       ▐░▌     ▐░▌          ▐░▌          ▐░▌       ▐░▌▐░▌       ▐░▌          ▐░▌
//  ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌       ▐░▌▐░▌       ▐░▌ ▄▄▄▄█░█▄▄▄▄ ▐░▌     ▐░▐░▌ ▄▄▄▄▄▄▄▄▄█░▌ ▄▄▄▄█░█▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌▐░▌       ▐░▌     ▐░▌          ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌       ▐░▌▐░█▄▄▄▄▄▄▄█░▌ ▄▄▄▄▄▄▄▄▄█░▌
//  ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░▌       ▐░▌▐░░░░░░░░░░░▌▐░▌      ▐░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌       ▐░▌     ▐░▌          ▐░░░░░░░░░░░▌▐░▌       ▐░▌▐░░░░░░░░░░▌ ▐░░░░░░░░░░░▌
//  ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀         ▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀        ▀▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀       ▀            ▀▀▀▀▀▀▀▀▀▀▀  ▀         ▀  ▀▀▀▀▀▀▀▀▀▀   ▀▀▀▀▀▀▀▀▀▀▀
//
//  .----------------. .-----------------..----------------. .----------------. .----------------. .----------------. .----------------. .----------------. .----------------.
//  | .--------------. | .--------------. | .--------------. | .--------------. | .--------------. | .--------------. | .--------------. | .--------------. | .--------------. |
//  | |    _______   | | | ____  _____  | | |     ____     | | | _____  _____ | | |    _______   | | |     _____    | | |    ______    | | |  ____  ____  | | |  _________   | |
//  | |   /  ___  |  | | ||_   \|_   _| | | |   .'    `.   | | ||_   _||_   _|| | |   /  ___  |  | | |    |_   _|   | | |  .' ___  |   | | | |_   ||   _| | | | |  _   _  |  | |
//  | |  |  (__ \_|  | | |  |   \ | |   | | |  /  .--.  \  | | |  | | /\ | |  | | |  |  (__ \_|  | | |      | |     | | | / .'   \_|   | | |   | |__| |   | | | |_/ | | \_|  | |
//  | |   '.___`-.   | | |  | |\ \| |   | | |  | |    | |  | | |  | |/  \| |  | | |   '.___`-.   | | |      | |     | | | | |    ____  | | |   |  __  |   | | |     | |      | |
//  | |  |`\____) |  | | | _| |_\   |_  | | |  \  `--'  /  | | |  |   /\   |  | | |  |`\____) |  | | |     _| |_    | | | \ `.___]  _| | | |  _| |  | |_  | | |    _| |_     | |
//  | |  |_______.'  | | ||_____|\____| | | |   `.____.'   | | |  |__/  \__|  | | |  |_______.'  | | |    |_____|   | | |  `._____.'   | | | |____||____| | | |   |_____|    | |
//  | |   *******    | | |   *******    | | |   *******    | | |   *******    | | |   *******    | | |    *******   | | |   *******    | | |    *******   | | |   *******    | |
//  | '--------------' | '--------------' | '--------------' | '--------------' | '--------------' | '--------------' | '--------------' | '--------------' | '--------------' |
//  '----------------' '----------------' '----------------' '----------------' '----------------' '----------------' '----------------' '----------------' '----------------'
//
//  Version: 2
//  May 9, 2022
//


pragma solidity 0.8.13;

abstract contract IERC20
{
    function transfer(address to, uint value) virtual external returns (bool);
}

struct PaymentTierSettings
{
    uint256 ratePerSecond;
    uint256 timeMaximum;
    uint256 timeMinimum;
    bool enabled;
}

struct PaymentEntry
{
    bool exists;
    uint endTimestamp;
}

contract SnowsightManager
{
    address public admin;
    address public feeCollector;
    uint8 public constant version = 2;

    enum paymentTierTypes 
    {
        FREE,
        STANDARD,
        PRIORITY
    }
    uint constant numPaymentTiers = 3;
    PaymentTierSettings[numPaymentTiers] public tierSettings;

    mapping(address => PaymentEntry[numPaymentTiers]) public payments;

    event AccountPayment(address account, uint256 payment, uint256 endTimestamp);

    constructor()
    {
        admin = msg.sender;
        feeCollector = msg.sender;
        for (uint i = 0 ; i < numPaymentTiers; i++)
        {
            tierSettings[i].enabled = true;
        }

        tierSettings[uint(paymentTierTypes.FREE)].ratePerSecond = 1;
        tierSettings[uint(paymentTierTypes.STANDARD)].ratePerSecond = 3360215053763; 
        tierSettings[uint(paymentTierTypes.PRIORITY)].ratePerSecond = 12445240939864;

        tierSettings[uint(paymentTierTypes.FREE)].timeMaximum = 1 days;
        tierSettings[uint(paymentTierTypes.STANDARD)].timeMaximum = 60 days;
        tierSettings[uint(paymentTierTypes.PRIORITY)].timeMaximum = 60 days;

        tierSettings[uint(paymentTierTypes.FREE)].timeMinimum = 1 days;
        tierSettings[uint(paymentTierTypes.STANDARD)].timeMinimum = 3 days;
        tierSettings[uint(paymentTierTypes.PRIORITY)].timeMinimum = 3 days;
    }

    modifier onlyValidTier(uint tier) {
       require (tier < numPaymentTiers);
        _;
    }

    modifier onlyAdmin {
        require (msg.sender == admin, "ERR_ADMIN_PROTECT");
        _;
    }

    // Function to receive Ether. msg.data must be empty
    receive() external payable {}

    // Fallback function is called when msg.data is not empty
    fallback() external payable {}

    function setAdmin(address payable newAdmin) external onlyAdmin
    {
        admin = newAdmin;
    }

    function setFeeCollector(address payable newfeeCollector) external onlyAdmin
    {
        feeCollector = newfeeCollector;
    }

    function setPaymentsEnabled(uint tier, bool enabled) external onlyAdmin onlyValidTier(tier)
    {
        tierSettings[tier].enabled = enabled;
    }

    function setPaymentRatePerSecond(uint tier, uint256 newPaymentRatePerSecond) external onlyAdmin onlyValidTier(tier)
    {
        tierSettings[tier].ratePerSecond = newPaymentRatePerSecond;
    }

    function setMaximumPaymentTime(uint tier, uint256 newPaymentTimeMaximum) external onlyAdmin onlyValidTier(tier)
    {
        tierSettings[tier].timeMaximum = newPaymentTimeMaximum;
    }

    function setMinimumPaymentTime(uint tier, uint256 newPaymentTimeMinimum) external onlyAdmin onlyValidTier(tier)
    {
        tierSettings[tier].timeMinimum = newPaymentTimeMinimum;
    }

    function calculateMaxPayment(uint tier) public onlyValidTier(tier) view returns (uint256)
    {
        return calculate_max_payment(msg.sender, tier);
    }

    function calculateMinPayment(uint tier) public onlyValidTier(tier) view returns (uint256)
    {
        return tierSettings[tier].timeMinimum * tierSettings[tier].ratePerSecond;
    }

    function calculatePaymentByTierAndTime(uint tier, uint256 _seconds) public onlyValidTier(tier) view returns (uint256)
    {
        uint256 payment = _seconds * tierSettings[tier].ratePerSecond;

        require (payment <= calculate_max_payment(msg.sender, tier), "ERROR_PAYMENT_TOO_LARGE");
        require (payment >= tierSettings[tier].timeMinimum * tierSettings[tier].ratePerSecond, "ERROR_PAYMENT_TOO_SMALL");

        return payment;
    }

    function calculate_max_payment(address payer, uint tier) internal view returns (uint256)
    {
        uint256 maxPayment = 0;

        if (payments[payer][tier].exists && payments[payer][tier].endTimestamp > block.timestamp)
        {
            maxPayment = (tierSettings[tier].timeMaximum - (payments[payer][tier].endTimestamp - block.timestamp)) * tierSettings[tier].ratePerSecond;
        }
        else
        {
            maxPayment = tierSettings[tier].timeMaximum * tierSettings[tier].ratePerSecond;
        }

        return maxPayment;
    }

    function pay(uint tier) external payable onlyValidTier(tier)
    {
        require (tierSettings[tier].enabled == true, "ERROR_PAYMENTS_DISABLED");
        require (msg.value <= calculate_max_payment(msg.sender, tier), "ERROR_PAYMENT_TOO_LARGE");
        require (msg.value >= calculateMinPayment(tier), "ERROR_PAYMENT_TOO_SMALL");

        if (payments[msg.sender][tier].exists)
        {
            if (payments[msg.sender][tier].endTimestamp > block.timestamp)
            {
                // account has a payment active
                payments[msg.sender][tier].endTimestamp = payments[msg.sender][tier].endTimestamp + (msg.value / tierSettings[tier].ratePerSecond);
            }
            else
            {
                // account exists, but payment expired
                payments[msg.sender][tier].endTimestamp = block.timestamp + (msg.value / tierSettings[tier].ratePerSecond);
            }
        }
        else
        {
            // account does not exist yet
            payments[msg.sender][tier].exists = true;
            payments[msg.sender][tier].endTimestamp = block.timestamp + (msg.value / tierSettings[tier].ratePerSecond);
        }

        emit AccountPayment(msg.sender, msg.value, payments[msg.sender][tier].endTimestamp);
        payable(feeCollector).transfer(msg.value);
    }

    function grant(address[] calldata addresses, uint256[] calldata timestamps, uint8[] calldata tiers) onlyAdmin external
    {
        for (uint i = 0; i < addresses.length; i++)
        {
            if (payments[addresses[i]][tiers[i]].exists == false)
            {
                payments[addresses[i]][tiers[i]].exists = true;
            }

            payments[addresses[i]][tiers[i]].endTimestamp = timestamps[i];
        }
    }

    function transferEth(uint256 amount) onlyAdmin external
    {
        payable(msg.sender).transfer(amount);
    }

    function transferToken(address tokenAddress, uint256 amount) onlyAdmin external
    {
        IERC20 token = IERC20(tokenAddress);
        token.transfer(msg.sender, amount);
    }
}

Last updated