Contract 0xf609390d42881b00d3ae7aada22feff3230d3a32

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0xcdf0754083b69d0c2daf94f04737804321831962b86a69bf582631cfc2d74892Withdraw From Fa...447125262022-08-12 0:30:58111 days 23 hrs ago0x788df41c7d6fc5fbb1c5890042c038184f55f92a IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.000218311809
0x6589f38da5630a0c3cb566f565c74f4a88c1cdfcfd5c6ab870b342307f65d264Claim Rain From ...447125052022-08-12 0:30:25111 days 23 hrs ago0x788df41c7d6fc5fbb1c5890042c038184f55f92a IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.000260909677
0xfca6575994331777fdfe35edd2d97bf3f0965402f237f90ff201141ba8e84660Withdraw From Fa...405857752022-06-15 16:27:40169 days 7 hrs ago0x009a1d99cc92212200569afea8964cf9eb1ee013 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.005154658484
0xefd0c3627cee71d1cfd115fc7963356fcdb4f24b2d2f91947c0fd0d206eecc55Claim Rain From ...405857392022-06-15 16:27:04169 days 7 hrs ago0x009a1d99cc92212200569afea8964cf9eb1ee013 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.005744786536
0xd7ec51e6103c150b3564906961f1d48e8542b493047cc30dba06637eb6f05b36Withdraw From Fa...388780952022-05-23 12:03:09192 days 11 hrs ago0xb307d426a34a72a94b32bca5c5f0c8b36440fa80 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.004238596206
0x22b74ad1ba82b58ad60c2091af32b7eed6315f2f323574a53e29f1f054804a55Claim Rain From ...388780612022-05-23 12:02:34192 days 11 hrs ago0xb307d426a34a72a94b32bca5c5f0c8b36440fa80 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.005446675443
0xf63c56c46674beb628813cbeca247fefc39ce5041a977b8daebae36131275eddWithdraw From Fa...388752072022-05-23 11:05:52192 days 12 hrs ago0x248e0006472f08d5b06d3d252085b3cb7a595fb0 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.007184532199
0xcc8e11cf86dad1b2117b1c771c824407d042552c61f20d51ae7d8956e97a3429Withdraw From Fa...388743852022-05-23 10:48:47192 days 12 hrs ago0x3f942d1c7f6e1f00b4489208535b000f7df0cb48 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.007778135658
0xadccfbdfda2465387ca1c83c699b7dc8810bfc93e54ea85473eab00a3f76f14aWithdraw From Fa...388741202022-05-23 10:43:25192 days 12 hrs ago0x954c8952cfb768617141a2cbeb2b23faaf7b89cf IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.035223424267
0x7a4ccac955cd0b04f15b890a9be76368dfff2c45cb98a17f3670ea02695f5293Withdraw From Fa...388430912022-05-23 0:40:39192 days 22 hrs ago0x3e590fbb993bf1e97f04fe615c64bfa37dd84999 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.00209290277
0x31e82bb9a3b7d853e8fa1d62bda53904c5357c2005114f2e1c76126f4c30b0cbClaim Rain From ...388428712022-05-23 0:36:44192 days 22 hrs ago0x3e590fbb993bf1e97f04fe615c64bfa37dd84999 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.013922991759
0xcedcb8619bd4efb60558f70363e46ec41442906e75d36e126118de82ee6cee16Withdraw From Fa...388288492022-05-22 20:06:41193 days 3 hrs ago0xaf887decf323ea3cee6a808793704c59eb0998e3 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.004366009657
0x9a6e2d3716a04622950c667b103fb6156e81994a92f53adf668c75ca620b7599Claim Rain From ...388283462022-05-22 19:56:28193 days 3 hrs ago0xaf887decf323ea3cee6a808793704c59eb0998e3 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.002786433495
0x8d131afb174740b4b290e49ff630a90e6e147b0203f8f346b52d545c9c9897c9Claim Rain From ...388282902022-05-22 19:55:32193 days 3 hrs ago0xaf887decf323ea3cee6a808793704c59eb0998e3 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.004327911808
0x46036bdc67c8e6914a8dbbd73eb6dd7d10fcc5a342c08faf5668416f32aa9b46Withdraw From Fa...388156612022-05-22 15:59:15193 days 7 hrs ago0x8f12cee7b03f7863624fb08d07ddef8d1b7357bc IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.013451352333
0xb338b465d476dd7eefa8fd7bfd8765af8c0e8f4b68c576e84dcec87cd97dce43Claim Rain From ...388156432022-05-22 15:58:50193 days 7 hrs ago0x8f12cee7b03f7863624fb08d07ddef8d1b7357bc IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.014430498283
0x01381315f75c78d4b55f7eb10da6dc55874357924ff11b9bb1d5ebbd666936e7Withdraw From Fa...388140112022-05-22 15:27:26193 days 8 hrs ago0x8514e0a3cb13da6c4c56122a0782adac91fcdc54 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.014929139138
0xd2747d9c22ca015c89b472e2b85111b60d62916783b1cee202951905ad08346aClaim Rain From ...388139932022-05-22 15:27:07193 days 8 hrs ago0x8514e0a3cb13da6c4c56122a0782adac91fcdc54 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.019866525393
0x07d6b457e3c5b0c54189acc297af342e848fb4ddbf6d032d1882059714555772Withdraw From Fa...388067862022-05-22 12:56:59193 days 10 hrs ago0x7a0e7adb1b9a40a4b4656ad55102d46dfdd6c19d IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.007232445325
0x1d53234d1bf137aa744f191d9924bcffab721f80b135bd906c416684388c04a3Withdraw From Fa...388059472022-05-22 12:42:19193 days 10 hrs ago0x139f89702511940ae4589c394a3121385f48654e IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.004513898713
0x864fd31fbeb8a9c35d9e3608c90ca609b039a2e76f1afa5e38bef51b18f9f3aaWithdraw From Fa...388027432022-05-22 11:42:48193 days 11 hrs ago0xa67d2c03c3cfe6177a60caed0a4cfda7c7a563e0 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.012665776509
0x0db4dd5ec6318b4bfe3ccde1a75f5ce8fe9fc48074c9dd37e6a78cdc8bf0d371Claim Rain From ...388026002022-05-22 11:39:44193 days 11 hrs ago0xa67d2c03c3cfe6177a60caed0a4cfda7c7a563e0 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.006436821167
0x7317038b05cf7e656a3c08962531bbd670193b492223f249d4e343f8c4d4eae0Withdraw From Fa...387966602022-05-22 9:33:27193 days 14 hrs ago0x5d22db0ddc365dbdc0983441e2906d51855a142d IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.005369602796
0xbd593d441c424db6c07b659ceb7bfbe4c5c24db855744c59f4b42351a92df738Withdraw From Fa...387955202022-05-22 9:12:49193 days 14 hrs ago0xa4a62f7127c644aaef733029e0d052e411fdafb4 IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.009327792224
0xc1130f9dd24f7e7933e825b506a5669241351fccc6b7fa135531d8971bcad2efDeposit To Farm387943332022-05-22 8:49:37193 days 14 hrs ago0x8f12cee7b03f7863624fb08d07ddef8d1b7357bc IN  0xf609390d42881b00d3ae7aada22feff3230d3a320 FTM0.007673206994
[ Download CSV Export 
Latest 1 internal transaction
Parent Txn Hash Block From To Value
0x2a0164e779f2c035ee38823b476ab1f56558bbf8a123ac08a96ad8e49f8530b7339162332022-03-20 9:10:07256 days 14 hrs ago 0xc333a03e8040cad31993e02615fd1b3f88447d95  Contract Creation0 FTM
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SingleAssetRainFarm

Compiler Version
v0.8.12+commit.f00d7308

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 4 : SingleAssetRainFarm.sol
pragma solidity ^0.8.12;

import "./RainFarmStorage.sol";
import "./RainFarmInterface.sol";
import "../../Tools/Helpers/TransferHelper.sol";

interface IERC20ForRainFarmer {
    function decimals() external view returns (uint);
    function balanceOf(address account) external view returns (uint);
}

contract SingleAssetRainFarm is SingleAssetRainFarmStorage, RainFarmInterface {

    // ******* constants *******

    bytes32 public constant SingleAssetRainFarmContractHash = keccak256("SingleAssetRainFarm");

    // V1.0
    uint public constant version = 100;

    /// @notice The initial index for a farm
    uint public constant farmInitialIndex = 1e36;

    uint public constant baseUnit = 1e18;

    uint public immutable farmTokenUnit;
    uint public immutable rainTokenUnit;

    uint public immutable minDistributionPeriod;

    bool private _nonreentranceLock = false;


    // ******* events *******

    /**
      * @notice Emitted when pendingAdmin is changed
      */
    event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);

    /**
      * @notice Emitted when pendingAdmin is accepted, which means admin is updated
      */
    event NewAdmin(address oldAdmin, address newAdmin);

    event FarmAdded(address indexed farmToken);

    event AllocatedRainToFarmer(address indexed farmer, uint deltaFarmerRainfall, uint farmIndex);

    event DistributionPeriodSet(uint oldDistributionPeriod, uint newDistributionPeriod);

    event StartTimeSet(uint oldStartTime, uint startTime);

    event BudgetSet(uint oldBudget, uint newBudget);

    event FarmSpeedSet(uint oldFarmSpeed, uint newFarmSpeed);


    // ******* constructor *******

    constructor(
        address _admin,
        uint _minDistributionPeriod,
        address _farmToken,
        address _rainToken
    ) SingleAssetRainFarmStorage(_rainToken, _farmToken) {
        contractNameHash = SingleAssetRainFarmContractHash;
        admin = _admin;

        uint farmTokenDecimals = IERC20ForRainFarmer(_farmToken).decimals();
        uint rainTokenDecimals = IERC20ForRainFarmer(_rainToken).decimals();

        farmTokenUnit = 10**farmTokenDecimals;
        rainTokenUnit = 10**rainTokenDecimals;

        // farmSpeed = 0;
        uint currentTime = getCurrentTime();
        FarmCurrentState storage farmCurrentState = farmCurrentState;
        farmCurrentState.index = farmInitialIndex; // state
        farmCurrentState.timestamp = currentTime; // state
        // farmCurrentState.totalSupply = 0;

        minDistributionPeriod = _minDistributionPeriod;

        emit FarmAdded(_farmToken);
    }

    modifier nonreentrance {
        require(!_nonreentranceLock, "Reentrance not allowed");
        _nonreentranceLock = true;
        _;
        _nonreentranceLock = false;
    }


    // ******* getters *******

    /**
     * @return the block timestamp.
     */
    function getCurrentTime() public view returns (uint) {
        return block.timestamp;
    }

    /**
     * @return the (derived) timestamp in which the distribution ends
     */
    function endTime() public view returns (uint) {
        return startTime + distributionPeriod;
    }

    /**
     * @return the address of the farm token
     */
    function getFarmToken() public view returns (address) {
        return farmToken;
    }

    /**
     * @return the address of the rain token
     */
    function getRainToken() public view returns (address) {
        return rainToken;
    }

    function getFarmSpeed() external view returns (uint) {
        return farmSpeed;
    }

    /**
     * @return the amount of rainToken committed since 'startTimeStamp'
     */
    function budgetCommitmentUntilNow() public view returns (uint) {
        uint currentTime = getCurrentTime();
        if (currentTime >= endTime()) {
            return budget;
        }

        uint _startTime = startTime;
        if (currentTime <= _startTime || budget == 0 || distributionPeriod == 0) {
            return 0;
        }

        uint deltaSeconds = currentTime - _startTime;
        uint rainCommitted = (deltaSeconds * farmSpeed);

        return rainCommitted;
    }

    /**
     * @return the amount of rain tokens that are not yet committed to distribution in the current distribution period.
     * DEV_NOTE : If state is 'fresh' this should return the value of 'budget'
     */
    function getRemainingBudget() public view returns (uint) {
        uint rainCommitment = budgetCommitmentUntilNow();

        if (budget <= rainCommitment) {
            return 0;
        } else {
            return budget - rainCommitment;
        }
    }

    /**
     * @return total rain being emitted to the farm per second
     */
    function getTotalRainSpeed(uint totalRain, uint totalSeconds) public pure returns (uint) {
        if (totalRain == 0 || totalSeconds == 0) {
            return 0;
        }
        /// @notice if rainToken decimals are small there might be a significant rounding error here
        return totalRain / totalSeconds;
    }

    // ******* admin ownership control *******

    /**
     * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
     * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
     * @param newPendingAdmin New pending admin.
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _setPendingAdmin(address newPendingAdmin) override external nonreentrance returns (uint) {
        // Check caller = admin
        require(isAdmin(), "!admin");

        // Save current value, if any, for inclusion in log
        address oldPendingAdmin = pendingAdmin;

        // Store pendingAdmin with value newPendingAdmin
        pendingAdmin = newPendingAdmin;

        // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
        emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);

        return 0;
    }

    /**
      * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
      * @dev Admin function for pending admin to accept role and update admin
      * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
      */
    function _acceptAdmin() override external nonreentrance returns (uint) {
        // Check caller is pendingAdmin and pendingAdmin ≠ address(0)
        require(msg.sender == pendingAdmin && msg.sender != address(0), "Not the EXISTING pending admin");

        // Save current values for inclusion in log
        address oldAdmin = admin;
        address oldPendingAdmin = pendingAdmin;

        // Store admin with value pendingAdmin
        admin = pendingAdmin;

        // Clear the pending value
        pendingAdmin = address(0);

        emit NewAdmin(oldAdmin, admin);
        emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);

        return 0;
    }

    // ******* admin setters and farm control *******

    /**
     * Allows admin to add rain to the program while keeping the current end time.
     * Any pulled rain will be used to increase the rainSpeed for the remaining time.
     */
    function pullNewRain(address pullRainFromAddress, uint pullRainAmount) override external nonreentrance {
        require(isAdmin(), "!admin");
        updateFarmIndexInternal();
        pullNewRainInternal(pullRainFromAddress, pullRainAmount);
    }

    /**
     * @notice The common function to call for continuing the emission program as is
     * @notice sets 'startTime' to now
     * @notice doesn't let changing 'distributionPeriod'
     * @notice only allows for pulling new rain
     * @notice requires getting the farm's index up to date -- happens in setRainfallConfigurationInternal()
     */
    function extendWithExistingConfiguration(address pullRainFromAddress, uint pullRainAmount) override external nonreentrance {
        require(isAdmin(), "!admin");
        updateFarmIndexInternal();

        uint currentTime = getCurrentTime();
        require(currentTime < endTime(), "cannot extend a finished program");

        setRainfallConfigurationInternal(0, 0);

        pullNewRainInternal(pullRainFromAddress, pullRainAmount);
    }

    /**
     * @notice Setter for rainfall configuration - startTime and distributionPeriod
     * @notice Not allowed while an active distribution is taking place
     * @notice requires getting the farm's index up to date -- happens in setRainfallConfigurationInternal()
     * @param _newStartTime the new starting time to be set
     * @param newDistributionPeriod the new distribution period to be set
     */
    function restartWithNewConfiguration(uint _newStartTime, uint newDistributionPeriod, address pullRainFromAddress, uint pullRainAmount) override external nonreentrance {
        require(isAdmin(), "!admin");
        updateFarmIndexInternal();

        uint currentTime = getCurrentTime();
        require(currentTime > endTime(), "not during active distribution");

        setRainfallConfigurationInternal(_newStartTime, newDistributionPeriod);

        pullNewRainInternal(pullRainFromAddress, pullRainAmount);
    }

    // ******* user interactions - deposit, withdraw, claim rain *******

    /**
     * Deposits to farm. nothing beyond that.
     */
    function depositToFarm(uint amount) override external nonreentrance {
        address farmer = msg.sender;
        updateFarmIndexInternal();
        updateFarmerIndexInternal(farmer);
        depositToFarmInternal(farmer, farmer, amount);
    }

    /**
     * Sender will deposit farm tokens on behalf of the farmer.
     */
    function depositToFarmOnBehalf(address farmer, uint amount) override external nonreentrance {
        updateFarmIndexInternal();
        updateFarmerIndexInternal(farmer);
        depositToFarmInternal(farmer, msg.sender, amount);
    }

    /**
     * Withdraws all of the farmer's (msg.sender) supply.
     * In addition, claims all rainfall.
     */
    function withdrawAllFromFarm() override external nonreentrance {
        address farmer = msg.sender;
        updateFarmIndexInternal();
        updateFarmerIndexInternal(farmer);
        withdrawFromFarmInternal(farmer, farmersCurrentSupply[farmer]);
    }

    /**
     * Withdraws a specific amount of the farmer's (msg.sender) supply.
     * In addition, claims all rainfall.
     */
    function withdrawFromFarm(uint amount) override external nonreentrance {
        address farmer = msg.sender;
        updateFarmIndexInternal();
        updateFarmerIndexInternal(farmer);
        withdrawFromFarmInternal(farmer, amount);
    }

    /**
     * @notice Claim all the rain accrued by farmer in the farm
     * @param farmer The address to claim rain for
     */
    function claimRainFromFarm(address farmer) override external nonreentrance {
        updateFarmIndexInternal();
        updateFarmerIndexInternal(farmer);
        address[] memory farmers = new address[](1);
        farmers[0] = farmer;
        return claimRainFromFarmToSomeFarmersInternal(farmers);
    }

    /**
     * @notice Claim all the rain accrued by farmers in the farm
     * @param farmers The addresses to claim rain for
     */
    function claimRainFromFarmToSomeFarmers(address[] calldata farmers) override external nonreentrance {
        updateFarmIndexInternal();
        for (uint j = 0; j < farmers.length; j++) {
            address farmer = farmers[j];
            updateFarmerIndexInternal(farmer);
        }
        claimRainFromFarmToSomeFarmersInternal(farmers);
    }

    // ******* emergency *******

    function emergencyWithdrawFromFarm() override external nonreentrance {
        address farmer = msg.sender;
        emergencyWithdrawFromFarmInternal(farmer);
        farmersRainfall[farmer] = 0; // state
    }

    // ******* rain token accounting and speeds (internal logic) *******

    /**
     * Allows to update the farm's stored state from outside.
     */
    function updateFarmIndex() override external nonreentrance {
        updateFarmIndexInternal();
    }

    /**
     * Allows to update the farmer's stored state from outside.
     */
    function updateFarmerIndex(address farmer) override external nonreentrance {
        updateFarmIndexInternal();
        updateFarmerIndexInternal(farmer);
    }

    // ******* internal setters *******

    /**
     * @notice Handles program configuration.
     * @param _newStartTime State time for the program, 0 means 'start now'.
     * @param _newDistributionPeriod Distribution period for the program, 0 means 'keep current'.
     */
    function setRainfallConfigurationInternal(uint _newStartTime, uint _newDistributionPeriod) internal {
        setRemainingBudget();

        setStartTime(_newStartTime);

        /// @notice 'newDistributionPeriod' = 0 implies distribution period stays as is
        if (_newDistributionPeriod != 0) {
            setDistributionPeriodInternal(_newDistributionPeriod);
        }
    }

    /*
     * Sets the budget stored value to the program's remaining budget plus the 'budgetToAdd'.
     * @param budgetToAdd Amount of rain tokens to add to the remaining budget.
     */
    function increaseBudget(uint budgetToAdd) internal {
        uint currentBudget = getRemainingBudget();
        setBudgetInternal(currentBudget + budgetToAdd);
    }

    /*
     * Sets the budget stored value ot the program's remaining budget.
     */
    function setRemainingBudget() internal {
        uint remainingBudget = getRemainingBudget();
        setBudgetInternal(remainingBudget);
    }

    // ******* single param setters *******

    /**
     * @notice Directly sets the program start time.
     * @param _newStartTime The wanted start time, must be in the future.
     *                      a 0 value means immediate start.
     */
    function setStartTime(uint _newStartTime) internal {
        uint currentTime = getCurrentTime();
        uint newStartTime = _newStartTime == 0 ? currentTime : _newStartTime;
        require(newStartTime >= currentTime, "cannot start in the past");
        uint oldStartTime = startTime;
        startTime = newStartTime;
        emit StartTimeSet(oldStartTime, startTime);
    }

    /**
     * @notice Directly sets the 'budget'
     */
    function setBudgetInternal(uint newBudget) internal {
        uint oldBudget = budget;
        budget = newBudget;
        emit BudgetSet(oldBudget, newBudget);
    }

    /**
     * @notice Directly sets the 'distributionPeriod'
     */
    function setDistributionPeriodInternal(uint newDistributionPeriod) internal {
        require(newDistributionPeriod > minDistributionPeriod, "Distribution period too short");
        uint oldDistributionPeriod = distributionPeriod;
        distributionPeriod = newDistributionPeriod;
        emit DistributionPeriodSet(oldDistributionPeriod, newDistributionPeriod);
    }

    /**
     * @notice Directly sets the 'farmSpeed'
     */
    function setFarmSpeed(uint newFarmSpeed) internal {
        uint oldFarmSpeed = farmSpeed;
        farmSpeed = newFarmSpeed;
        emit FarmSpeedSet(oldFarmSpeed, newFarmSpeed);
    }

    // ******* rain pulling *******

    /**
     * @notice pullNewRain from a general address using transferFrom
     * @notice Requires re-calculating the farm's speed
     * @notice admin can always add budget but can't change the rainfall configuration, including startTime
     * @notice requires getting the farm's index up to date -- happens in calculateFarmSpeed()
     * @notice may be called by anyone
     * @param pullRainFromAddress an address from which rain tokens will be pulled. Must be approved previously.
     * @param pullRainAmount the amount of rain tokens to pull from 'pullbudgetFrom'
     */
    function pullNewRainInternal(address pullRainFromAddress, uint pullRainAmount) internal {
        uint rainGained = 0;

        if (pullRainAmount > 0) {
            rainGained += pullRainTokens(pullRainFromAddress, pullRainAmount);
        }

        increaseBudget(rainGained);

        calculateFarmSpeed();
    }

    // ******* rain token accounting and speeds (internal logic) *******

    /**
     * @notice Sets the rain speeds using the current budget and the time left to distribute it
     * @notice Any change to the budget should occur before calling this function
     */
    function calculateFarmSpeed() internal {
        uint currentTime = getCurrentTime();
        uint _endTime = endTime();
        uint _startTime = startTime;
       
        uint finalTime = _endTime;
        uint initialTime = currentTime < _startTime ? _startTime : currentTime;

        uint distributionTimeframeLeft = finalTime > initialTime ? finalTime - initialTime : 0;
        uint totalRainSpeed = getTotalRainSpeed(budget, distributionTimeframeLeft);

        setFarmSpeed(totalRainSpeed);
    }

    /**
     * @notice Accrue rain token to a given farm by updating its index
     * @dev index is a cumulative sum of rain token gained per farm token from the beginning of time.
     */
    function updateFarmIndexInternal() internal {
        FarmCurrentState storage farmCurrnetState = farmCurrentState;
        uint _farmSpeed = farmSpeed;

        // Note : This part needs to know the actual 'program active run time' in seconds since the last 'farm index' calculation
        //         (not counting any time after the program end)
        uint farmTimestamp = farmCurrnetState.timestamp;
        uint currentTime = getCurrentTime();
        uint _endTime = endTime();
        uint _startTime = startTime;
        uint finalTime = currentTime > _endTime ? _endTime : currentTime; // min(current,end)
        uint initialTime = _startTime > farmTimestamp ? _startTime : farmTimestamp; // max(start,farm)
        bool isDeltaSecondsZero = _startTime > currentTime || initialTime > finalTime;
        uint deltaSeconds = isDeltaSecondsZero ? 0 : finalTime - initialTime;

        if (deltaSeconds > 0 && _farmSpeed > 0) {
            uint _totalSupply = totalSupply;
            uint rainfall = deltaSeconds * _farmSpeed;
            uint ratio = _totalSupply > 0 ? (rainfall * farmInitialIndex) / _totalSupply : 0;
            farmCurrnetState.index += ratio; // state
        }
        
        if (farmTimestamp < currentTime) {
            farmCurrnetState.timestamp = currentTime; // state
        }
    }

    function updateFarmSupplyInternal(uint supplyAddition, uint supplyReduction) internal {
        if (supplyAddition > 0) {
            totalSupply += supplyAddition; // state
        } else if (supplyReduction > 0) {
            require(totalSupply >= supplyReduction, "more than farm balance");
            totalSupply -= supplyReduction; // state
        }
    }

    /**
     * @notice Calculate rainfall to a specific farmer in a given farm and possibly transfer it to them
     * @param farmer The address of the farmer
     */
    function updateFarmerIndexInternal(address farmer) internal {
        FarmCurrentState memory farmCurrentState = farmCurrentState;
        uint farmIndex = farmCurrentState.index;
        uint farmerIndex = farmersCurrentIndex[farmer];
        uint deltaFarmerRainfall;

        require(farmIndex >= farmerIndex, "farmer can't be more advanced than farm");

        if (farmerIndex > 0) {
            uint deltaIndex = farmIndex - farmerIndex;
            uint farmerTokens = farmersCurrentSupply[farmer];
            if (deltaIndex > 0) {
                // Calculate new rain accrued: farmerTokens * accruedPerToken
                deltaFarmerRainfall = (farmerTokens * deltaIndex) / farmInitialIndex;
                uint totalFarmerRainfall = farmersRainfall[farmer] + deltaFarmerRainfall;
                farmersRainfall[farmer] = totalFarmerRainfall; // state
            }
        }

        farmersCurrentIndex[farmer] = farmIndex; // state

        emit AllocatedRainToFarmer(farmer, deltaFarmerRainfall, farmIndex);
    }

    /**
     * @notice Properly and safely adds/reduces the amount to both the farmer's and the farm's supply
     * @param farmer The farmer
     * @param supplyAddition Amount to add
     * @param supplyReduction Amount to reduce
     */
    function updateFarmerSupplyInternal(address farmer, uint supplyAddition, uint supplyReduction) internal {
        // Update farmer's supply if supplyAddition > 0 or supplyReduction > 0
        if (supplyAddition > 0) {
            farmersCurrentSupply[farmer] += supplyAddition; // state
        } else if (supplyReduction > 0) {
            /// @notice important - must fail if farmerCurrentState.supply < supplyReduction
            require(farmersCurrentSupply[farmer] >= supplyReduction, "more than farmer balance");
            farmersCurrentSupply[farmer] -= supplyReduction; // state
        }
        updateFarmSupplyInternal(supplyAddition, supplyReduction);
    }

    /**
     * @notice Zeros farmer's index and supply.
     */
    function emergencyUpdateFarmerIndexInternal(address farmer) internal {
        farmersCurrentIndex[farmer] = 0; // state
        farmersCurrentSupply[farmer] = 0; // state
    }

    // ******* user interactions - deposit, withdraw, claim rain *******

    /**
     * @notice Handles all deposit logic.
     * Will not allow to deposit when there is neither active program nor a planned one.
     * @param farmer The account that will get the farm tokens added to their supply.
     * @param payer The account that will have the farm tokens pulled from.
     * @param amount The farm tokens to pull.
     */
    function depositToFarmInternal(address farmer, address payer, uint amount) internal {
        uint currentTime = getCurrentTime();
        require(currentTime < endTime(), "too late");

        amount = pullFarmTokens(payer, amount);
        
        updateFarmerSupplyInternal(farmer, amount, 0);
    }

    /**
     * @notice withdraw always claims from farm, to only withdraw set amount=0
     * @param farmer The withdrawing farmer.
     * @param amount The withdrawal amount
     */
    function withdrawFromFarmInternal(address farmer, uint amount) internal {
        claimRainFromFarmInternal(farmer);
        if (amount > 0) {
            updateFarmerSupplyInternal(farmer, 0, amount);
            sendFarmTokens(farmer, amount);
        }
    }

    /*
     * @notice Claims the rainfall for all given farmers.
     * @param farmers The farmers to claim rainfall for.
     */
    function claimRainFromFarmToSomeFarmersInternal(address[] memory farmers) internal {
        for (uint j = 0; j < farmers.length; j++) {
            address farmer = farmers[j];
            farmersRainfall[farmer] = transferRainInternalSafe(farmer, farmersRainfall[farmer]); // state
        }
    }

    /**
     * @notice Safe-Transfer to the farmer's the entire rainfall the address deserves
     *         and updates the remaining rainfall.
     * @param farmer The farmer to claim rainfall for.
     */
    function claimRainFromFarmInternal(address farmer) internal {
        farmersRainfall[farmer] = transferRainInternalSafe(farmer, farmersRainfall[farmer]); // state
    }

    // ******* Farm&Rain Tokens interactions *******

    /**
     * @notice Safe transfer farm token from this contract.
     * @param payer The paying account.
     * @param amount The amount to send.
     */
    function pullFarmTokens(address payer, uint amount) internal returns (uint farmTokensGain) {
        address _farmToken = farmToken;

        uint balanceBefore = IERC20ForRainFarmer(_farmToken).balanceOf(address(this));
        TransferHelper.safeTransferFrom(_farmToken, payer, address(this), amount);
        uint balanceAfter = IERC20ForRainFarmer(_farmToken).balanceOf(address(this));

        farmTokensGain = balanceAfter - balanceBefore;
        require(farmTokensGain == amount, "must receive exact amount");
    }

    /**
     * A simple 'transferFrom' from the 'pullRainFromAddress' to this contract
     * with sanity checks to ensure this contract got the exact amount.
     * @param pullRainFromAddress The address to pull the rain from.
     * @param pullRainAmount The amount of rain to pull.
     */
    function pullRainTokens(address pullRainFromAddress, uint pullRainAmount) internal returns (uint rainTokensGain) {
        address _rainToken = rainToken;

        uint balanceBefore = IERC20ForRainFarmer(_rainToken).balanceOf(address(this));
        TransferHelper.safeTransferFrom(rainToken, pullRainFromAddress, address(this), pullRainAmount);
        uint balanceAfter = IERC20ForRainFarmer(_rainToken).balanceOf(address(this));

        rainTokensGain = balanceAfter - balanceBefore;
        require(rainTokensGain == pullRainAmount, "must receive exact amount");
    }

    /**
     * @notice Safe transfer farm token from this contract.
     * @param farmer The receiving farmer.
     * @param amount The amount to send.
     */
    function sendFarmTokens(address farmer, uint amount) internal {
        TransferHelper.safeTransfer(farmToken, farmer, amount);
    }

    /**
     * @param farmer The address of the farmer to transfer rain tokens to
     * @param amount The amount of rain tokens to (possibly) transfer
     * @return The amount of rain tokens owed by the contract to the user after this function executes
     */
    function transferRainInternalSafe(address farmer, uint amount) internal returns (uint) {
        address _rainToken = rainToken;
        uint rainTokenRemaining = IERC20ForRainFarmer(_rainToken).balanceOf(address(this));
        if (amount > 0 && amount <= rainTokenRemaining) {
            TransferHelper.safeTransfer(_rainToken, farmer, amount);
            return 0;
        }
        return amount;
    }


    // ******* admin *******

    /**
     * @notice Checks caller is admin
     */
    function isAdmin() internal view returns (bool) {
        return msg.sender == admin;
    }


    // ******* emergency *******

    /**
     * Sends all suuply balance to farmer (skipping rain claiming).
     * Farmer position will be zeroed out after.
     */
    function emergencyWithdrawFromFarmInternal(address farmer) internal {
        uint amount = farmersCurrentSupply[farmer];
        require(amount > 0, "nothing to withdraw");
        emergencyUpdateFarmerIndexInternal(farmer);
        sendFarmTokens(farmer, amount);
    }
}

File 2 of 4 : RainFarmStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;

contract RainFarmAdminStorage {
    /**
    * @notice Administrator for this contract
    */
    address public admin;

    /**
    * @notice Pending administrator for this contract
    */
    address public pendingAdmin;
}

contract SingleAssetRainFarmStorage is RainFarmAdminStorage {

    // ******* rain data *******

    uint256 public budget;

    /// @notice Represented in seconds
    uint256 public startTime;
    uint256 public distributionPeriod;

    address public immutable rainToken;

    // ******* specific farm data *******

    address public immutable farmToken; // rainSPIRIT

    uint public farmSpeed;

    struct FarmCurrentState {
        /// @notice The farm's last updated index
        uint index;

        /// @notice The timestamp the index was last updated at
        uint timestamp;
    }
    FarmCurrentState public farmCurrentState;

    /// @notice The totalSupply deposited in farm token
    uint public totalSupply;


    // ******* user data *******

    /// @notice farmer address => farmer current index
    mapping(address => uint) public farmersCurrentIndex;

    /// @notice farmer address => farmer current supply
    mapping(address => uint) public farmersCurrentSupply;

    /// @notice The amount of rain tokens accrued but not yet transferred to each farmer
    mapping(address => uint) public farmersRainfall;

    constructor(address _rainToken, address _farmToken) {
        rainToken = _rainToken;
        farmToken = _farmToken;
    }
}

File 3 of 4 : RainFarmInterface.sol
pragma solidity ^0.8.12;

abstract contract RainFarmInterface {
    bool public isRainFarmer = true;
    bytes32 public contractNameHash;

    /*** admin setters and farm control ***/
    function pullNewRain(address pullRainFromAddress, uint pullRainAmount) virtual external;
    function extendWithExistingConfiguration(address pullRainFromAddress, uint pullRainAmount) virtual external;
    function restartWithNewConfiguration(
        uint _newStartTime,
        uint newDistributionPeriod,
        address pullRainFromAddress,
        uint pullRainAmount
    ) virtual external;

    /*** farm accounting and indexes ***/
    function updateFarmIndex() virtual external;
    function updateFarmerIndex(address farmer) virtual external;

    // *** user interactions - deposit, withdraw, claim rain ***/
    function depositToFarm(uint amount) virtual external;
    function depositToFarmOnBehalf(address farmer, uint amount) virtual external;
    function withdrawAllFromFarm() virtual external;
    function withdrawFromFarm(uint amount) virtual external;
    function claimRainFromFarm(address farmer) virtual external;
    function claimRainFromFarmToSomeFarmers(address[] calldata farmers) virtual external;

    // *** emergency ***
    function emergencyWithdrawFromFarm() virtual external;

    // *** admin ***
    function _setPendingAdmin(address newPendingAdmin) virtual external returns (uint);
    function _acceptAdmin() virtual external returns (uint);
}

File 4 of 4 : TransferHelper.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity >=0.6.0;

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
    function safeApprove(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::safeApprove: approve failed'
        );
    }

    function safeTransfer(
        address token,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::safeTransfer: transfer failed'
        );
    }

    function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            'TransferHelper::transferFrom: transferFrom failed'
        );
    }

    function safeTransferETH(address to, uint256 value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"uint256","name":"_minDistributionPeriod","type":"uint256"},{"internalType":"address","name":"_farmToken","type":"address"},{"internalType":"address","name":"_rainToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"farmer","type":"address"},{"indexed":false,"internalType":"uint256","name":"deltaFarmerRainfall","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"farmIndex","type":"uint256"}],"name":"AllocatedRainToFarmer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldBudget","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBudget","type":"uint256"}],"name":"BudgetSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldDistributionPeriod","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDistributionPeriod","type":"uint256"}],"name":"DistributionPeriodSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"farmToken","type":"address"}],"name":"FarmAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldFarmSpeed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFarmSpeed","type":"uint256"}],"name":"FarmSpeedSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldStartTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"}],"name":"StartTimeSet","type":"event"},{"inputs":[],"name":"SingleAssetRainFarmContractHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_acceptAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseUnit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"budget","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"budgetCommitmentUntilNow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"farmer","type":"address"}],"name":"claimRainFromFarm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"farmers","type":"address[]"}],"name":"claimRainFromFarmToSomeFarmers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractNameHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositToFarm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"farmer","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositToFarmOnBehalf","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributionPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyWithdrawFromFarm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pullRainFromAddress","type":"address"},{"internalType":"uint256","name":"pullRainAmount","type":"uint256"}],"name":"extendWithExistingConfiguration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"farmCurrentState","outputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"farmInitialIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"farmSpeed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"farmToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"farmTokenUnit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"farmersCurrentIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"farmersCurrentSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"farmersRainfall","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFarmSpeed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFarmToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRainToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRemainingBudget","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"totalRain","type":"uint256"},{"internalType":"uint256","name":"totalSeconds","type":"uint256"}],"name":"getTotalRainSpeed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"isRainFarmer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minDistributionPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pullRainFromAddress","type":"address"},{"internalType":"uint256","name":"pullRainAmount","type":"uint256"}],"name":"pullNewRain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rainToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rainTokenUnit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newStartTime","type":"uint256"},{"internalType":"uint256","name":"newDistributionPeriod","type":"uint256"},{"internalType":"address","name":"pullRainFromAddress","type":"address"},{"internalType":"uint256","name":"pullRainAmount","type":"uint256"}],"name":"restartWithNewConfiguration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateFarmIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"farmer","type":"address"}],"name":"updateFarmerIndex","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawAllFromFarm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawFromFarm","outputs":[],"stateMutability":"nonpayable","type":"function"}]

610120604052600c805460ff19908116600117909155600e805490911690553480156200002b57600080fd5b506040516200245b3803806200245b8339810160408190526200004e916200020f565b6001600160a01b0381811660805282811660a08190527f5d60379cc35a07cb65119bc8e992e72a714154d36c0d35d04f15b840f7ce1560600d55600080546001600160a01b0319169287169290921782556040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa158015620000db573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000101919062000263565b90506000826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000144573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200016a919062000263565b90506200017982600a62000392565b60c0526200018981600a62000392565b60e0526ec097ce7bc90715b34b9f100000000060069081554260078190556101008790526040519091906001600160a01b038716907fbfca0af3a33c0bcae29f7d07934c90e392c5d6c2ba686ce898528ee63bea8f5290600090a25050505050505050620003a7565b80516001600160a01b03811681146200020a57600080fd5b919050565b600080600080608085870312156200022657600080fd5b6200023185620001f2565b9350602085015192506200024860408601620001f2565b91506200025860608601620001f2565b905092959194509250565b6000602082840312156200027657600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b600181815b80851115620002d4578160001904821115620002b857620002b86200027d565b80851615620002c657918102915b93841c939080029062000298565b509250929050565b600082620002ed575060016200038c565b81620002fc575060006200038c565b8160018114620003155760028114620003205762000340565b60019150506200038c565b60ff8411156200033457620003346200027d565b50506001821b6200038c565b5060208310610133831016604e8410600b841016171562000365575081810a6200038c565b62000371838362000293565b80600019048211156200038857620003886200027d565b0290505b92915050565b6000620003a08383620002dc565b9392505050565b60805160a05160c05160e0516101005161203762000424600039600081816103a101526117c40152600061052a015260006104650152600081816103c5015281816104e10152818161139b01526116150152600081816102fe01528181610551015281816116530152818161188f015261190d01526120376000f3fe608060405234801561001057600080fd5b50600436106102735760003560e01c80637b48746d11610151578063c78ca939116100c3578063e3a775c311610087578063e3a775c3146105a6578063e46c3c72146105b9578063e9c714f2146105cc578063ed01bf29146105d4578063ee20d5dc146105dd578063f851a4401461060057600080fd5b8063c78ca93914610512578063cb68c77314610525578063cc74216a1461054c578063cdbd662a14610573578063da9f88f21461058657600080fd5b80639b610cd9116101155780639b610cd914610490578063a1170054146104a3578063b71d1a0c146104b6578063c1cb88eb146104c9578063c2442f93146104dc578063c2930f911461050357600080fd5b80637b48746d14610432578063817f8bd91461044557806382bb1bf51461044d5780639052bc86146104605780639b5017231461048757600080fd5b8063320aade0116101ea5780635064f76d116101ae5780635064f76d146103e95780635387014c146103f157806354fd4d50146104115780636ca7970b1461041957806377a15b611461042157806378e979251461042957600080fd5b8063320aade014610375578063469ab1e31461038a57806346ffc9c014610393578063480492b01461039c5780634d98493d146103c357600080fd5b80631c8b4abb1161023c5780631c8b4abb146102fc57806320b4aa0b1461033657806323862ba31461034c578063267822471461035457806329cb924d146103675780633197cbb61461036d57600080fd5b806291f24714610278578063019131621461028f5780630a7cfa18146102af57806315d2ff08146102d657806318160ddd146102f3575b600080fd5b6005545b6040519081526020015b60405180910390f35b61027c61029d366004611d4b565b600a6020526000908152604090205481565b61027c7f5d60379cc35a07cb65119bc8e992e72a714154d36c0d35d04f15b840f7ce156081565b600c546102e39060ff1681565b6040519015158152602001610286565b61027c60085481565b7f00000000000000000000000000000000000000000000000000000000000000005b6040516001600160a01b039091168152602001610286565b61027c6ec097ce7bc90715b34b9f100000000081565b61027c610613565b60015461031e906001600160a01b031681565b4261027c565b61027c61067e565b610388610383366004611d66565b610695565b005b61027c600d5481565b61027c60055481565b61027c7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000061031e565b6103886106f9565b61027c6103ff366004611d4b565b60096020526000908152604090205481565b61027c606481565b61038861076c565b61027c6107b0565b61027c60035481565b610388610440366004611d4b565b6107e2565b610388610823565b61038861045b366004611d7f565b610881565b61027c7f000000000000000000000000000000000000000000000000000000000000000081565b61027c60045481565b61038861049e366004611da9565b6108cd565b6103886104b1366004611e1e565b610994565b61027c6104c4366004611d4b565b610a74565b6103886104d7366004611d66565b610b42565b61031e7f000000000000000000000000000000000000000000000000000000000000000081565b61027c670de0b6b3a764000081565b610388610520366004611d7f565b610b8e565b61027c7f000000000000000000000000000000000000000000000000000000000000000081565b61031e7f000000000000000000000000000000000000000000000000000000000000000081565b61027c610581366004611e5b565b610c6d565b61027c610594366004611d4b565b600b6020526000908152604090205481565b6103886105b4366004611d7f565b610c9a565b6103886105c7366004611d4b565b610d08565b61027c610da8565b61027c60025481565b6006546007546105eb919082565b60408051928352602083019190915201610286565b60005461031e906001600160a01b031681565b60004261061e61067e565b811061062c57505060025490565b600354808211158061063e5750600254155b806106495750600454155b156106575760009250505090565b60006106638284611e93565b90506000600554826106759190611eaa565b95945050505050565b60006004546003546106909190611ec9565b905090565b600e5460ff16156106c15760405162461bcd60e51b81526004016106b890611ee1565b60405180910390fd5b600e805460ff19166001179055336106d7610f05565b6106e081611007565b6106eb818284611199565b5050600e805460ff19169055565b600e5460ff161561071c5760405162461bcd60e51b81526004016106b890611ee1565b600e805460ff1916600117905533610732610f05565b61073b81611007565b6001600160a01b0381166000908152600a602052604090205461075f9082906111f9565b50600e805460ff19169055565b600e5460ff161561078f5760405162461bcd60e51b81526004016106b890611ee1565b600e805460ff191660011790556107a4610f05565b600e805460ff19169055565b6000806107bb610613565b905080600254116107ce57600091505090565b806002546107dc9190611e93565b91505090565b600e5460ff16156108055760405162461bcd60e51b81526004016106b890611ee1565b600e805460ff1916600117905561081a610f05565b61075f81611007565b600e5460ff16156108465760405162461bcd60e51b81526004016106b890611ee1565b600e805460ff191660011790553361085d81611222565b6001600160a01b03166000908152600b6020526040812055600e805460ff19169055565b600e5460ff16156108a45760405162461bcd60e51b81526004016106b890611ee1565b600e805460ff191660011790556108b9610f05565b6108c282611007565b6106eb823383611199565b600e5460ff16156108f05760405162461bcd60e51b81526004016106b890611ee1565b600e805460ff19166001179055610905610f05565b60005b8181101561095757600083838381811061092457610924611f11565b90506020020160208101906109399190611d4b565b905061094481611007565b508061094f81611f27565b915050610908565b506106eb8282808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506112a992505050565b600e5460ff16156109b75760405162461bcd60e51b81526004016106b890611ee1565b600e8054600160ff199091161790556000546001600160a01b031633146109f05760405162461bcd60e51b81526004016106b890611f42565b6109f8610f05565b42610a0161067e565b8111610a4f5760405162461bcd60e51b815260206004820152601e60248201527f6e6f7420647572696e672061637469766520646973747269627574696f6e000060448201526064016106b8565b610a59858561132e565b610a63838361134e565b5050600e805460ff19169055505050565b600e5460009060ff1615610a9a5760405162461bcd60e51b81526004016106b890611ee1565b600e8054600160ff199091161790556000546001600160a01b03163314610ad35760405162461bcd60e51b81526004016106b890611f42565b600180546001600160a01b038481166001600160a01b031983168117909355604080519190921680825260208201939093527fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9910160405180910390a15050600e805460ff1916905550600090565b600e5460ff1615610b655760405162461bcd60e51b81526004016106b890611ee1565b600e805460ff1916600117905533610b7b610f05565b610b8481611007565b6106eb81836111f9565b600e5460ff1615610bb15760405162461bcd60e51b81526004016106b890611ee1565b600e8054600160ff199091161790556000546001600160a01b03163314610bea5760405162461bcd60e51b81526004016106b890611f42565b610bf2610f05565b42610bfb61067e565b8110610c495760405162461bcd60e51b815260206004820181905260248201527f63616e6e6f7420657874656e6420612066696e69736865642070726f6772616d60448201526064016106b8565b610c5460008061132e565b610c5e838361134e565b5050600e805460ff1916905550565b6000821580610c7a575081155b15610c8757506000610c94565b610c918284611f62565b90505b92915050565b600e5460ff1615610cbd5760405162461bcd60e51b81526004016106b890611ee1565b600e8054600160ff199091161790556000546001600160a01b03163314610cf65760405162461bcd60e51b81526004016106b890611f42565b610cfe610f05565b6106eb828261134e565b600e5460ff1615610d2b5760405162461bcd60e51b81526004016106b890611ee1565b600e805460ff19166001179055610d40610f05565b610d4981611007565b604080516001808252818301909252600091602080830190803683370190505090508181600081518110610d7f57610d7f611f11565b60200260200101906001600160a01b031690816001600160a01b0316815250506106eb816112a9565b600e5460009060ff1615610dce5760405162461bcd60e51b81526004016106b890611ee1565b600e8054600160ff199091168117909155546001600160a01b031633148015610df657503315155b610e425760405162461bcd60e51b815260206004820152601e60248201527f4e6f7420746865204558495354494e472070656e64696e672061646d696e000060448201526064016106b8565b60008054600180546001600160a01b038082166001600160a01b031980861682179096559490911690915560408051919092168082526020820184905292917ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc910160405180910390a1600154604080516001600160a01b03808516825290921660208301527fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9910160405180910390a15050600e805460ff1916905550600090565b60055460075460069190426000610f1a61067e565b6003549091506000828411610f2f5783610f31565b825b90506000858311610f425785610f44565b825b9050600085841180610f5557508282115b9050600081610f6d57610f688385611e93565b610f70565b60005b9050600081118015610f825750600089115b15610feb576008546000610f968b84611eaa565b90506000808311610fa8576000610fcc565b82610fc26ec097ce7bc90715b34b9f100000000084611eaa565b610fcc9190611f62565b9050808d6000016000828254610fe29190611ec9565b90915550505050505b86881015610ffb5760018a018790555b50505050505050505050565b6040805180820182526006548082526007546020808401919091526001600160a01b038516600090815260099091529283205491929091908183101561109f5760405162461bcd60e51b815260206004820152602760248201527f6661726d65722063616e2774206265206d6f726520616476616e636564207468604482015266616e206661726d60c81b60648201526084016106b8565b811561113d5760006110b18385611e93565b6001600160a01b0387166000908152600a6020526040902054909150811561113a576ec097ce7bc90715b34b9f10000000006110ed8383611eaa565b6110f79190611f62565b6001600160a01b0388166000908152600b60205260408120549194509061111f908590611ec9565b6001600160a01b0389166000908152600b6020526040902055505b50505b6001600160a01b03851660008181526009602090815260409182902086905581518481529081018690527f05cbb560cbcdea0e35c47338bcaa0ec9b5b43b8aae1a7f205be6a9a206a0aa97910160405180910390a25050505050565b426111a261067e565b81106111db5760405162461bcd60e51b8152602060048201526008602482015267746f6f206c61746560c01b60448201526064016106b8565b6111e58383611383565b91506111f3848360006114f1565b50505050565b611202826115d0565b801561121e57611214826000836114f1565b61121e8282611610565b5050565b6001600160a01b0381166000908152600a60205260409020548061127e5760405162461bcd60e51b81526020600482015260136024820152726e6f7468696e6720746f20776974686472617760681b60448201526064016106b8565b611214826001600160a01b03166000908152600960209081526040808320839055600a909152812055565b60005b815181101561121e5760008282815181106112c9576112c9611f11565b6020026020010151905061130281600b6000846001600160a01b03166001600160a01b031681526020019081526020016000205461163b565b6001600160a01b039091166000908152600b60205260409020558061132681611f27565b9150506112ac565b611336611700565b61133f82611718565b801561121e5761121e816117c2565b6000811561136d576113608383611877565b61136a9082611ec9565b90505b61137681611934565b61137e611952565b505050565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000009082906001600160a01b038316906370a0823190602401602060405180830381865afa1580156113ee573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114129190611f84565b9050611420828630876119b5565b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015611467573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061148b9190611f84565b90506114978282611e93565b93508484146114e85760405162461bcd60e51b815260206004820152601960248201527f6d757374207265636569766520657861637420616d6f756e740000000000000060448201526064016106b8565b50505092915050565b811561152a576001600160a01b0383166000908152600a60205260408120805484929061151f908490611ec9565b909155506115c69050565b80156115c6576001600160a01b0383166000908152600a60205260409020548111156115985760405162461bcd60e51b815260206004820152601860248201527f6d6f7265207468616e206661726d65722062616c616e6365000000000000000060448201526064016106b8565b6001600160a01b0383166000908152600a6020526040812080548392906115c0908490611e93565b90915550505b61137e8282611af3565b6001600160a01b0381166000908152600b60205260409020546115f490829061163b565b6001600160a01b039091166000908152600b6020526040902055565b61121e7f00000000000000000000000000000000000000000000000000000000000000008383611b82565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000009082906001600160a01b038316906370a0823190602401602060405180830381865afa1580156116a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116ca9190611f84565b90506000841180156116dc5750808411155b156116f7576116ec828686611b82565b600092505050610c94565b50919392505050565b600061170a6107b0565b905061171581611cb3565b50565b42600082156117275782611729565b815b90508181101561177b5760405162461bcd60e51b815260206004820152601860248201527f63616e6e6f7420737461727420696e207468652070617374000000000000000060448201526064016106b8565b600380549082905560408051828152602081018490527f229a47a590a5e543f42b381d9c7913437d6a22cbea20cdaa6c3fb425ce9ac28a910160405180910390a150505050565b7f000000000000000000000000000000000000000000000000000000000000000081116118315760405162461bcd60e51b815260206004820152601d60248201527f446973747269627574696f6e20706572696f6420746f6f2073686f727400000060448201526064016106b8565b600480549082905560408051828152602081018490527fb0b24597cd10b18eac3d1526dde0d6eb9326a5668442ee0a383a8aac2271cc5591015b60405180910390a15050565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000009082906001600160a01b038316906370a0823190602401602060405180830381865afa1580156118e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119069190611f84565b90506114207f00000000000000000000000000000000000000000000000000000000000000008630876119b5565b600061193e6107b0565b905061121e61194d8383611ec9565b611cb3565b42600061195d61067e565b6003549091508160008285106119735784611975565b825b90506000818311611987576000611991565b6119918284611e93565b905060006119a160025483610c6d565b90506119ac81611cf1565b50505050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b1790529151600092839290881691611a199190611f9d565b6000604051808303816000865af19150503d8060008114611a56576040519150601f19603f3d011682016040523d82523d6000602084013e611a5b565b606091505b5091509150818015611a85575080511580611a85575080806020019051810190611a859190611fd8565b611aeb5760405162461bcd60e51b815260206004820152603160248201527f5472616e7366657248656c7065723a3a7472616e7366657246726f6d3a207472604482015270185b9cd9995c919c9bdb4819985a5b1959607a1b60648201526084016106b8565b505050505050565b8115611b16578160086000828254611b0b9190611ec9565b9091555061121e9050565b801561121e57806008541015611b675760405162461bcd60e51b81526020600482015260166024820152756d6f7265207468616e206661726d2062616c616e636560501b60448201526064016106b8565b8060086000828254611b799190611e93565b90915550505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839290871691611bde9190611f9d565b6000604051808303816000865af19150503d8060008114611c1b576040519150601f19603f3d011682016040523d82523d6000602084013e611c20565b606091505b5091509150818015611c4a575080511580611c4a575080806020019051810190611c4a9190611fd8565b611cac5760405162461bcd60e51b815260206004820152602d60248201527f5472616e7366657248656c7065723a3a736166655472616e736665723a20747260448201526c185b9cd9995c8819985a5b1959609a1b60648201526084016106b8565b5050505050565b600280549082905560408051828152602081018490527f869e2577b006bf47ee981cf6fec2e25583548081c14b98deab587f77b5068038910161186b565b600580549082905560408051828152602081018490527f7440a7baa46bbca19ebcb4a4750505d363768047c87f4934d3e3410fc8f2f995910161186b565b80356001600160a01b0381168114611d4657600080fd5b919050565b600060208284031215611d5d57600080fd5b610c9182611d2f565b600060208284031215611d7857600080fd5b5035919050565b60008060408385031215611d9257600080fd5b611d9b83611d2f565b946020939093013593505050565b60008060208385031215611dbc57600080fd5b823567ffffffffffffffff80821115611dd457600080fd5b818501915085601f830112611de857600080fd5b813581811115611df757600080fd5b8660208260051b8501011115611e0c57600080fd5b60209290920196919550909350505050565b60008060008060808587031215611e3457600080fd5b8435935060208501359250611e4b60408601611d2f565b9396929550929360600135925050565b60008060408385031215611e6e57600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b600082821015611ea557611ea5611e7d565b500390565b6000816000190483118215151615611ec457611ec4611e7d565b500290565b60008219821115611edc57611edc611e7d565b500190565b6020808252601690820152751499595b9d1c985b98d9481b9bdd08185b1b1bddd95960521b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b6000600019821415611f3b57611f3b611e7d565b5060010190565b60208082526006908201526510b0b236b4b760d11b604082015260600190565b600082611f7f57634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215611f9657600080fd5b5051919050565b6000825160005b81811015611fbe5760208186018101518583015201611fa4565b81811115611fcd576000828501525b509190910192915050565b600060208284031215611fea57600080fd5b81518015158114611ffa57600080fd5b939250505056fea2646970667358221220499ddbf9d698ac2f4fe95bf49fd7782f3895426978028ad4bff5f06a2eff827664736f6c634300080c0033000000000000000000000000c333a03e8040cad31993e02615fd1b3f88447d950000000000000000000000000000000000000000000000000000000000015180000000000000000000000000f9c6e3c123f0494a4447100bd7dbd536f43cc33a0000000000000000000000002ad402655243203fcfa7dcb62f8a08cc2ba88ae0

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

000000000000000000000000c333a03e8040cad31993e02615fd1b3f88447d950000000000000000000000000000000000000000000000000000000000015180000000000000000000000000f9c6e3c123f0494a4447100bd7dbd536f43cc33a0000000000000000000000002ad402655243203fcfa7dcb62f8a08cc2ba88ae0

-----Decoded View---------------
Arg [0] : _admin (address): 0xc333a03e8040cad31993e02615fd1b3f88447d95
Arg [1] : _minDistributionPeriod (uint256): 86400
Arg [2] : _farmToken (address): 0xf9c6e3c123f0494a4447100bd7dbd536f43cc33a
Arg [3] : _rainToken (address): 0x2ad402655243203fcfa7dcb62f8a08cc2ba88ae0

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000c333a03e8040cad31993e02615fd1b3f88447d95
Arg [1] : 0000000000000000000000000000000000000000000000000000000000015180
Arg [2] : 000000000000000000000000f9c6e3c123f0494a4447100bd7dbd536f43cc33a
Arg [3] : 0000000000000000000000002ad402655243203fcfa7dcb62f8a08cc2ba88ae0


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Validator ID :
0 FTM

Amount Staked
0

Amount Delegated
0

Staking Total
0

Staking Start Epoch
0

Staking Start Time
0

Proof of Importance
0

Origination Score
0

Validation Score
0

Active
0

Online
0

Downtime
0 s
Address Amount claimed Rewards Created On Epoch Created On
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.