# Contracts

SnowsightManager.sol

[0x727Dc3C412cCb942c6b5f220190ebAB3eFE0Eb93](https://snowtrace.io/address/0x727dc3c412ccb942c6b5f220190ebab3efe0eb93#code)

```
/**
 *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);
    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.snowsight.chainsight.dev/snowsight/contracts.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
