Transaction Hash:
Block:
15537146 at Sep-15-2022 05:45:52 AM +UTC
Transaction Fee:
0.001001881121151421 ETH
$1.89
Gas Used:
117,439 Gas / 8.531076739 Gwei
Emitted Events:
1147 |
SpringToken.Mint( from=[Receiver] 0x1cfd117f377a23100081f1785ca53a4662b2b990, rewardAmount=504000000000000000000, epochCount=53971, newChallengeNumber=3971E829354602BB7E251FBFC585E395458F5F252221D132A29663DC25EB83F0 )
|
1148 |
SpringToken.Approval( tokenOwner=[Receiver] 0x1cfd117f377a23100081f1785ca53a4662b2b990, spender=SeasonalTokenFarm, tokens=37800000000000000000 )
|
1149 |
SeasonalTokenFarm.Donate( from=[Receiver] 0x1cfd117f377a23100081f1785ca53a4662b2b990, seasonalTokenAddress=SpringToken, amount=37800000000000000000 )
|
1150 |
SpringToken.Transfer( from=[Receiver] 0x1cfd117f377a23100081f1785ca53a4662b2b990, to=SeasonalTokenFarm, tokens=37800000000000000000 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x7d2a86D9...17B708906 | (Seasonal Tokens: Deployer) |
0.860175818747904615 Eth
Nonce: 13544
|
0.859173937626753194 Eth
Nonce: 13545
| 0.001001881121151421 | |
0xE8adB011...FfC319404 | |||||
0xEA674fdD...16B898ec8
Miner
| (Ethermine) | 2,138.742208953186522012 Eth | 2,138.742322920558972405 Eth | 0.000113967372450393 | |
0xf04aF3f4...18373d4FE |
Execution Trace
0x1cfd117f377a23100081f1785ca53a4662b2b990.96e87e8d( )
-
SpringToken.mint( nonce=325266225183824570232651739413940119344603750286078258543064716421475150720 ) => ( success=True )
SpringToken.safeApproveAndCall( spender=0xE8adB0111CcB570e366c73eE799242eFfC319404, previousAllowance=0, newAllowance=37800000000000000000, data=0x ) => ( success=True )
SeasonalTokenFarm.receiveApproval( from=0x1cfd117F377a23100081F1785ca53A4662B2b990, tokens=37800000000000000000, token=0xf04aF3f4E4929F7CD25A751E6149A3318373d4FE, data=0x )
-
SpringToken.transferFrom( from=0x1cfd117F377a23100081F1785ca53A4662B2b990, to=0xE8adB0111CcB570e366c73eE799242eFfC319404, tokens=37800000000000000000 ) => ( success=True )
-
File 1 of 2: SpringToken
File 2 of 2: SeasonalTokenFarm
{"ApproveAndCallFallBack.sol":{"content":" //SPDX-License-Identifier: MIT\n pragma solidity 0.8.5;\n\n\n\n // ----------------------------------------------------------------------------\n\n // Contract function to receive approval and execute function in one call\n\n // Borrowed from MiniMeToken\n\n // ----------------------------------------------------------------------------\n\n abstract contract ApproveAndCallFallBack {\n\n function receiveApproval(address from, uint256 tokens, address token, bytes memory data) public virtual ;\n\n }\n\n"},"ERC20.sol":{"content":" //SPDX-License-Identifier: MIT\n pragma solidity 0.8.5;\n\n\n // ----------------------------------------------------------------------------\n\n // ERC Token Standard #20 Interface\n\n // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md\n\n // ----------------------------------------------------------------------------\n\n abstract contract ERC20Interface {\n\n function totalSupply() public view virtual returns (uint);\n\n function balanceOf(address tokenOwner) public view virtual returns (uint balance);\n\n function allowance(address tokenOwner, address spender) public view virtual returns (uint remaining);\n\n function transfer(address to, uint tokens) public virtual returns (bool success);\n\n function approve(address spender, uint tokens) public virtual returns (bool success);\n\n function transferFrom(address from, address to, uint tokens) public virtual returns (bool success);\n\n\n event Transfer(address indexed from, address indexed to, uint tokens);\n\n event Approval(address indexed tokenOwner, address indexed spender, uint tokens);\n\n }\n\n\n"},"ERC918.sol":{"content":" //SPDX-License-Identifier: MIT\n pragma solidity 0.8.5;\n\n\n // ----------------------------------------------------------------------------\n\n // ERC Token Standard #918 Interface\n\n // https://eips.ethereum.org/EIPS/eip-918\n\n // ----------------------------------------------------------------------------\n\n interface ERC918 {\n\n function mint(uint256 nonce) external returns (bool success);\n\n function getAdjustmentInterval() external view returns (uint);\n\n function getChallengeNumber() external view returns (bytes32);\n\n function getMiningDifficulty() external view returns (uint);\n\n function getMiningTarget() external view returns (uint);\n\n function getMiningReward() external view returns (uint);\n \n function decimals() external view returns (uint8);\n\n\n event Mint(address indexed from, uint rewardAmount, uint epochCount, bytes32 newChallengeNumber);\n }"},"Owned.sol":{"content":" //SPDX-License-Identifier: MIT\n pragma solidity 0.8.5;\n\n\n\n // ----------------------------------------------------------------------------\n\n // Owned contract\n\n // ----------------------------------------------------------------------------\n\n abstract contract Owned {\n\n address public owner;\n\n address public newOwner;\n\n\n event OwnershipTransferred(address indexed _from, address indexed _to);\n\n\n constructor() {\n\n owner = msg.sender;\n\n }\n\n\n modifier onlyOwner {\n\n require(msg.sender == owner);\n\n _;\n\n }\n\n\n function transferOwnership(address _newOwner) public onlyOwner {\n\n require(_newOwner != address(0), \"Invalid address\");\n\n newOwner = _newOwner;\n\n }\n\n function acceptOwnership() external {\n\n require(msg.sender == newOwner);\n\n emit OwnershipTransferred(owner, newOwner);\n\n owner = newOwner;\n\n newOwner = address(0);\n\n }\n\n }\n\n"},"SpringToken.sol":{"content":"//SPDX-License-Identifier: MIT\npragma solidity 0.8.5;\n\n\nimport \"./ERC20.sol\";\nimport \"./ERC918.sol\";\nimport \"./Owned.sol\";\nimport \"./ApproveAndCallFallBack.sol\";\n\n\n\n// ----------------------------------------------------------------------------\n\n// \u0027Spring Token\u0027 contract\n\n// ERC20 \u0026 ERC918 Mineable Token using Proof Of Work\n\n// Symbol : SPRING\n\n// Name : Spring Token\n\n// Total supply: 33,112,800.00\n\n// Decimals : 18\n\n// Initial mining reward: 168\n\n// Fraction of total supply released before first halving: 1/5\n\n// ----------------------------------------------------------------------------\n\n\n\n\n\ncontract SpringToken is ERC20Interface, ERC918, Owned {\n\n string private constant SYMBOL = \"SPRING\";\n\n string private constant NAME = \"Spring Token\";\n\n uint256 public constant TOKEN_IDENTIFIER = 1;\n\n uint8 public constant DECIMALS = 18;\n\n uint256 public constant TOTAL_SUPPLY = 33112800 * 10**18;\n\n uint256 public constant INITIAL_REWARD = 168 * 10**18;\n\n uint256 public constant MAX_REWARDS_AVAILABLE = 72; // no more than 72 rewards per mint\n\n uint256 public constant REWARD_INTERVAL = 600; // rewards every ten minutes on average\n\n uint256 public constant DURATION_OF_FIRST_ERA = (365 * 24 * 60 * 60 * 3) / 4; // 9 months\n\n uint256 public constant DURATION_OF_ERA = 3 * 365 * 24 * 60 * 60; // three years\n\n uint256 public constant MINIMUM_TARGET = 2**16;\n\n uint256 public constant MAXIMUM_TARGET = 2**234;\n\n uint256 public immutable contractCreationTime;\n\n uint256 public lastRewardBlockTime;\n\n uint256 public maxNumberOfRewardsPerMint;\n\n bytes32 private challengeNumber;\n \n uint256 private miningTarget;\n\n uint256 public tokensMinted;\n\n mapping(address =\u003e uint256) internal balances;\n\n mapping(address =\u003e mapping(address =\u003e uint256)) internal allowed;\n\n\n constructor() {\n\n miningTarget = MAXIMUM_TARGET / 2**19;\n\n contractCreationTime = block.timestamp;\n lastRewardBlockTime = block.timestamp;\n\n maxNumberOfRewardsPerMint = 1;\n\n challengeNumber = _getNewChallengeNumber(0);\n\n }\n\n function name() public pure returns (string memory) {\n return NAME;\n }\n\n function symbol() public pure returns (string memory) {\n return SYMBOL;\n }\n\n function mint(uint256 nonce) override public returns (bool success) {\n\n uint256 _lastRewardBlockTime = lastRewardBlockTime;\n \n uint256 singleRewardAmount = _getMiningReward(_lastRewardBlockTime);\n\n // no more minting when reward reaches zero\n if (singleRewardAmount == 0) revert(\"Reward has reached zero\");\n\n // the PoW must contain work that includes the challenge number and the msg.sender\u0027s address\n bytes32 digest = keccak256(abi.encodePacked(challengeNumber, msg.sender, nonce));\n\n uint256 _miningTarget = miningTarget;\n // the digest must be smaller than the target\n if (uint256(digest) \u003e _miningTarget) revert(\"Digest is larger than mining target\");\n\n uint256 _previousMaxNumberOfRewards = maxNumberOfRewardsPerMint;\n uint256 numberOfRewardsToGive = _numberOfRewardsToGive(_miningTarget / uint256(digest), \n _lastRewardBlockTime,\n _previousMaxNumberOfRewards,\n block.timestamp);\n uint256 totalRewardAmount = singleRewardAmount * numberOfRewardsToGive;\n\n uint256 _tokensMinted = _giveRewards(totalRewardAmount);\n \n _setNextMaxNumberOfRewards(numberOfRewardsToGive, _previousMaxNumberOfRewards);\n\n miningTarget = _adjustDifficulty(_miningTarget, _lastRewardBlockTime,\n numberOfRewardsToGive, block.timestamp);\n\n bytes32 newChallengeNumber = _getNewChallengeNumber(_tokensMinted);\n challengeNumber = newChallengeNumber;\n\n lastRewardBlockTime = block.timestamp;\n\n emit Mint(msg.sender, totalRewardAmount, _scheduledNumberOfRewards(block.timestamp), \n newChallengeNumber);\n\n return true;\n }\n\n function _numberOfRewardsAvailable(uint256 _lastRewardBlockTime, \n uint256 _previousMaxNumberOfRewards, \n uint256 currentTime) internal pure returns (uint256) {\n\n uint256 numberAvailable = _previousMaxNumberOfRewards;\n uint256 intervalsSinceLastReward = (currentTime - _lastRewardBlockTime) / REWARD_INTERVAL;\n \n if (intervalsSinceLastReward \u003e numberAvailable)\n numberAvailable = intervalsSinceLastReward;\n\n if (numberAvailable \u003e MAX_REWARDS_AVAILABLE)\n numberAvailable = MAX_REWARDS_AVAILABLE;\n\n return numberAvailable;\n }\n\n function _numberOfRewardsToGive(uint256 numberEarned, uint256 _lastRewardBlockTime, \n uint256 _previousMaxNumberOfRewards,\n uint256 currentTime) internal pure returns (uint256) {\n\n uint256 numberAvailable = _numberOfRewardsAvailable(_lastRewardBlockTime,\n _previousMaxNumberOfRewards,\n currentTime);\n if (numberEarned \u003c numberAvailable)\n return numberEarned;\n\n return numberAvailable;\n }\n\n function _giveRewards(uint256 totalReward) internal returns (uint256) {\n\n balances[msg.sender] += totalReward;\n uint256 _tokensMinted = tokensMinted + totalReward;\n tokensMinted = _tokensMinted;\n return _tokensMinted;\n }\n\n function _setNextMaxNumberOfRewards(uint256 numberOfRewardsGivenNow, \n uint256 _previousMaxNumberOfRewards) internal {\n\n // the value of the rewards given to this miner presumably exceed the gas costs\n // for processing the transaction. the next miner can submit a proof of enough work\n // to claim up to the same number of rewards immediately, or, if gas costs have increased,\n // wait until the maximum number of rewards claimable has increased enough to overcome\n // the costs.\n\n if (numberOfRewardsGivenNow != _previousMaxNumberOfRewards)\n maxNumberOfRewardsPerMint = numberOfRewardsGivenNow;\n }\n\n // backwards compatible mint function\n function mint(uint256 _nonce, bytes32 _challengeDigest) external returns (bool) {\n\n bytes32 digest = keccak256(abi.encodePacked(challengeNumber, msg.sender, _nonce));\n require(digest == _challengeDigest, \"Challenge digest does not match expected digest on token contract\");\n \n return mint(_nonce);\n }\n\n function _getNewChallengeNumber(uint256 _tokensMinted) internal view returns (bytes32) {\n \n // make the latest ethereum block hash a part of the next challenge\n\n // xor with a number unique to this token to avoid merged mining\n \n // xor with the number of tokens minted to ensure that the challenge changes\n // even if there are multiple mints in the same ethereum block\n \n return bytes32(uint256(blockhash(block.number - 1)) ^ _tokensMinted ^ TOKEN_IDENTIFIER);\n }\n\n\n function _scheduledNumberOfRewards(uint256 currentTime) internal view returns (uint256) {\n return (currentTime - contractCreationTime) / REWARD_INTERVAL;\n }\n\n function _adjustDifficulty(uint256 _miningTarget, \n uint256 _lastRewardBlockTime, \n uint256 rewardsGivenNow,\n uint256 currentTime) internal pure returns (uint256){\n\n uint256 timeSinceLastReward = currentTime - _lastRewardBlockTime;\n\n // we target a median interval of 10 minutes multiplied by log(2) ~ 61/88 \n // this gives a mean interval of 10 minutes per reward\n\n if (timeSinceLastReward * 88 \u003c rewardsGivenNow * REWARD_INTERVAL * 61)\n _miningTarget = (_miningTarget * 99) / 100; // slow down\n else\n _miningTarget = (_miningTarget * 100) / 99; // speed up\n\n if (_miningTarget \u003c MINIMUM_TARGET)\n _miningTarget = MINIMUM_TARGET;\n \n if (_miningTarget \u003e MAXIMUM_TARGET) \n _miningTarget = MAXIMUM_TARGET;\n\n return _miningTarget;\n }\n\n\n function rewardEra(uint256 _time) public view returns (uint256) {\n\n uint256 timeSinceContractCreation = _time - contractCreationTime;\n\n if (timeSinceContractCreation \u003c DURATION_OF_FIRST_ERA)\n return 0;\n else\n return 1 + (timeSinceContractCreation - DURATION_OF_FIRST_ERA) / DURATION_OF_ERA;\n }\n\n function getAdjustmentInterval() public view override returns (uint256) {\n return REWARD_INTERVAL * maxNumberOfRewardsPerMint;\n }\n\n function getChallengeNumber() public view override returns (bytes32) {\n return challengeNumber;\n }\n\n function getMiningDifficulty() public view override returns (uint256) {\n // 64 f\u0027s: 1234567890123456789012345678901234567890123456789012345678901234\n uint256 maxInt = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;\n return maxInt / miningTarget;\n }\n\n function getMiningTarget() public view override returns (uint256) {\n return miningTarget;\n }\n\n function getMiningReward() public view override returns (uint256) {\n\n // use the timestamp of the ethereum block that gave the last reward\n // because ethereum miners can manipulate the value of block.timestamp\n return _getMiningReward(lastRewardBlockTime);\n }\n\n function _getMiningReward(uint256 _time) internal view returns (uint256) {\n return INITIAL_REWARD / 2**rewardEra(_time);\n }\n\n function getNumberOfRewardsAvailable(uint256 currentTime) external view returns (uint256) {\n return _numberOfRewardsAvailable(lastRewardBlockTime, \n maxNumberOfRewardsPerMint, \n currentTime);\n }\n\n function getRewardAmountForAchievingTarget(uint256 targetAchieved, uint256 currentTime) external view returns (uint256) {\n uint256 numberOfRewardsToGive = _numberOfRewardsToGive(miningTarget / targetAchieved, \n lastRewardBlockTime, \n maxNumberOfRewardsPerMint, \n currentTime);\n return _getMiningReward(currentTime) * numberOfRewardsToGive;\n }\n\n function decimals() public pure override returns (uint8) {\n return DECIMALS;\n }\n\n function totalSupply() public view override returns (uint256) {\n\n return tokensMinted;\n }\n\n\n // ------------------------------------------------------------------------\n\n // Get the token balance for account `tokenOwner`\n\n // ------------------------------------------------------------------------\n\n function balanceOf(address tokenOwner) public view override returns (uint256 balance) {\n\n return balances[tokenOwner];\n\n }\n\n\n\n // ------------------------------------------------------------------------\n\n // Transfer the balance from token owner\u0027s account to `to` account\n\n // - Owner\u0027s account must have sufficient balance to transfer\n\n // - 0 value transfers are allowed\n\n // ------------------------------------------------------------------------\n\n function transfer(address to, uint256 tokens) public override returns (bool success) {\n \n require(to != address(0) \u0026\u0026 to != address(this), \"Invalid address\");\n \n balances[msg.sender] = balances[msg.sender] - tokens;\n\n balances[to] = balances[to] + tokens;\n\n emit Transfer(msg.sender, to, tokens);\n\n return true;\n\n }\n\n\n\n // ------------------------------------------------------------------------\n\n // Token owner can approve for `spender` to transferFrom(...) `tokens`\n\n // from the token owner\u0027s account\n\n //\n\n // Warning: This function is vulnerable to double-spend attacks and is\n\n // included for backwards compatibility. Use safeApprove instead.\n\n // ------------------------------------------------------------------------\n\n function approve(address spender, uint256 tokens) public override returns (bool success) {\n \n require(spender != address(0) \u0026\u0026 spender != address(this), \"Invalid address\");\n\n allowed[msg.sender][spender] = tokens;\n\n emit Approval(msg.sender, spender, tokens);\n\n return true;\n\n }\n\n\n\n // ------------------------------------------------------------------------\n\n // Allow token owner to cancel the approval if the approved amount changes from its last\n\n // known value before this transaction is processed. This allows the owner to avoid \n\n // unintentionally re-approving funds that have already been spent.\n\n // ------------------------------------------------------------------------\n\n function safeApprove(address spender, uint256 previousAllowance, uint256 newAllowance) external returns (bool success) {\n\n require(allowed[msg.sender][spender] == previousAllowance,\n \"Current spender allowance does not match specified value\");\n\n return approve(spender, newAllowance);\n }\n\n\n\n // ------------------------------------------------------------------------\n\n // Transfer `tokens` from the `from` account to the `to` account\n\n //\n\n // The calling account must already have sufficient tokens approve(...)-d\n\n // for spending from the `from` account and\n\n // - From account must have sufficient balance to transfer\n\n // - Spender must have sufficient allowance to transfer\n\n // - 0 value transfers are allowed\n\n // ------------------------------------------------------------------------\n\n function transferFrom(address from, address to, uint256 tokens) public override returns (bool success) {\n \n require(to != address(0) \u0026\u0026 to != address(this), \"Invalid address\");\n\n balances[from] = balances[from] - tokens;\n\n allowed[from][msg.sender] = allowed[from][msg.sender] - tokens;\n\n balances[to] = balances[to] + tokens;\n\n emit Transfer(from, to, tokens);\n\n return true;\n\n }\n\n\n\n // ------------------------------------------------------------------------\n\n // Returns the amount of tokens approved by the owner that can be\n\n // transferred to the spender\u0027s account\n\n // ------------------------------------------------------------------------\n\n function allowance(address tokenOwner, address spender) public view override returns (uint256 remaining){\n\n return allowed[tokenOwner][spender];\n\n }\n\n\n // ------------------------------------------------------------------------\n\n // Token owner can approve for `spender` to transferFrom(...) `tokens`\n\n // from the token owner\u0027s account. The `spender` contract function\n\n // `receiveApproval(...)` is then executed. This is vulnerable to double-spend attacks\n\n // when called directly, so it is declared internal and called by safeApproveAndCall\n\n // ------------------------------------------------------------------------\n\n function approveAndCall(address spender, uint256 tokens, bytes memory data) internal returns (bool success) {\n \n require(spender != address(0) \u0026\u0026 spender != address(this), \"Invalid address\");\n\n allowed[msg.sender][spender] = tokens;\n\n emit Approval(msg.sender, spender, tokens);\n\n ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, address(this), data);\n\n return true;\n\n }\n\n\n // ------------------------------------------------------------------------\n\n // Allow safe approvals with calls to receiving contract\n\n // ------------------------------------------------------------------------\n\n function safeApproveAndCall(address spender, uint256 previousAllowance, \n uint256 newAllowance, bytes memory data) external returns (bool success) {\n\n require(allowed[msg.sender][spender] == previousAllowance,\n \"Current spender allowance does not match specified value\");\n\n return approveAndCall(spender, newAllowance, data);\n }\n\n\n // ------------------------------------------------------------------------\n\n // Owner can transfer out any accidentally sent ERC20 tokens\n\n // ------------------------------------------------------------------------\n\n function transferAnyERC20Token(address tokenAddress, uint256 tokens) external onlyOwner returns (bool success) {\n\n return ERC20Interface(tokenAddress).transfer(owner, tokens);\n\n }\n\n}\n"}}
File 2 of 2: SeasonalTokenFarm
{"ApproveAndCallFallBack.sol":{"content":" //SPDX-License-Identifier: MIT\n pragma solidity 0.8.5;\n\n\n\n // ----------------------------------------------------------------------------\n\n // Contract function to receive approval and execute function in one call\n\n // Borrowed from MiniMeToken\n\n // ----------------------------------------------------------------------------\n\n abstract contract ApproveAndCallFallBack {\n\n function receiveApproval(address from, uint256 tokens, address token, bytes memory data) public virtual ;\n\n }\n\n"},"ERC20.sol":{"content":" //SPDX-License-Identifier: MIT\n pragma solidity 0.8.5;\n\n\n // ----------------------------------------------------------------------------\n\n // ERC Token Standard #20 Interface\n\n // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md\n\n // ----------------------------------------------------------------------------\n\n abstract contract ERC20Interface {\n\n function totalSupply() public view virtual returns (uint);\n\n function balanceOf(address tokenOwner) public view virtual returns (uint balance);\n\n function allowance(address tokenOwner, address spender) public view virtual returns (uint remaining);\n\n function transfer(address to, uint tokens) public virtual returns (bool success);\n\n function approve(address spender, uint tokens) public virtual returns (bool success);\n\n function transferFrom(address from, address to, uint tokens) public virtual returns (bool success);\n\n\n event Transfer(address indexed from, address indexed to, uint tokens);\n\n event Approval(address indexed tokenOwner, address indexed spender, uint tokens);\n\n }\n\n\n"},"ERC721TokenReceiver.sol":{"content":"//SPDX-License-Identifier: MIT\npragma solidity 0.8.5;\npragma abicoder v2;\n\ninterface ERC721TokenReceiver {\n /// @notice Handle the receipt of an NFT\n /// @dev The ERC721 smart contract calls this function on the\n /// recipient after a `transfer`. This function MAY throw to revert and reject the transfer. Return\n /// of other than the magic value MUST result in the transaction being reverted.\n /// @notice The contract address is always the message sender.\n /// @param _operator The address which called `safeTransferFrom` function\n /// @param _from The address which previously owned the token\n /// @param _tokenId The NFT identifier which is being transferred\n /// @param _data Additional data with no specified format\n /// @return `bytes4(keccak256(\"onERC721Received(address,address,uint256,bytes)\"))`\n /// unless throwing\n function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes memory _data) external returns(bytes4);\n }"},"safeTransferFrom.sol":{"content":"//SPDX-License-Identifier: MIT\npragma solidity 0.8.5;\npragma abicoder v2;\n\nimport \"./ERC20.sol\";\n\n// Contract function calls from the OpenZeppelin library\n\nlibrary Address {\n\n function isContract(address account) internal view returns (bool) {\n uint256 size;\n assembly {\n size := extcodesize(account)\n }\n return size \u003e 0;\n }\n\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length \u003e 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n\n\n// safeTransferFrom function from OpenZeppelin\u0027s SafeERC20 library\n\nlibrary SafeERC20 {\n using Address for address;\n\n function safeTransferFrom(\n ERC20Interface token,\n address from,\n address to,\n uint256 value\n ) internal {\n _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n }\n\n function _callOptionalReturn(ERC20Interface token, bytes memory data) private {\n\n bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n if (returndata.length \u003e 0) {\n // Return data is optional\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n"},"SeasonalTokenFarm.sol":{"content":"//SPDX-License-Identifier: MIT\npragma solidity 0.8.5;\npragma abicoder v2;\n\nimport \"./ERC20.sol\";\nimport \"./ERC721TokenReceiver.sol\";\nimport \"./ApproveAndCallFallBack.sol\";\nimport \"./safeTransferFrom.sol\";\n\n\n/*\n * Seasonal Token Farm\n *\n * This contract receives donations of seasonal tokens and distributes them to providers of liquidity\n * for the token/ETH trading pairs on Uniswap v3.\n *\n * Warning: Tokens can be lost if they are not transferred to the farm contract in the correct way.\n *\n * Seasonal tokens must be donated using the safeApproveAndCall() function of the seasonal token contracts.\n * Tokens sent directly to the farm address will be lost.\n *\n * Contracts that deposit Uniswap liquidy tokens need to implement the onERC721Received() function in order\n * to be able to withdraw those tokens. Any contracts that interact with the farm must be tested prior to \n * deployment on the main network.\n * \n * The developers accept no responsibility for tokens irretrievably lost in accidental transfers.\n * \n */\n\n\n\n\n\ninterface INonfungiblePositionManager {\n function positions(uint256 tokenId)\n external\n view\n returns (\n uint96 nonce,\n address operator,\n address token0,\n address token1,\n uint24 fee,\n int24 tickLower,\n int24 tickUpper,\n uint128 liquidity,\n uint256 feeGrowthInside0LastX128,\n uint256 feeGrowthInside1LastX128,\n uint128 tokensOwed0,\n uint128 tokensOwed1\n );\n function safeTransferFrom(address _from, address _to, uint256 _tokenId) external;\n}\n\n\nstruct LiquidityToken {\n address owner;\n address seasonalToken;\n uint256 depositTime;\n uint256 initialCumulativeSpringTokensFarmed;\n uint256 initialCumulativeSummerTokensFarmed;\n uint256 initialCumulativeAutumnTokensFarmed;\n uint256 initialCumulativeWinterTokensFarmed;\n uint256 liquidity;\n uint256 position;\n}\n\n\ncontract SeasonalTokenFarm is ERC721TokenReceiver, ApproveAndCallFallBack {\n\n // The Seasonal Token Farm runs on voluntary donations.\n\n // Incoming donated tokens are distributed to liquidity providers for the ETH/Token trading pairs.\n // Each trading pair has an allocationSize. Incoming tokens are allocated to trading pairs in\n // proportion to their allocationSizes. The fraction of tokens allocated to a trading pair is\n // equal to that trading pair\u0027s allocationSize divided by the sum of the allocationSizes.\n\n // The initial allocationSizes are 5, 6, 7 and 8 for Spring, Summer, Autumn and Winter.\n // Four months after each token\u0027s halving, the allocationSize for the ETH/Token trading pair\n // doubles. \n //\n // When the doubling of the Winter allocation occurs, the allocationSizes become 10, 12, 14 and 16,\n // which are simplified to 5, 6, 7, 8, and then the cycle repeats.\n\n // Initially, the allocationSizes will be 5, 6, 7 and 8.\n //\n // After the Spring halving, they will be 10, 6, 7 and 8.\n // After the Summer halving, they will be 10, 12, 7 and 8.\n // After the Autumn halving, they will be 10, 12, 14 and 8.\n // After the Winter halving, they will be 5, 6, 7 and 8 again.\n\n // The reduction of the allocationSizes from 10, 12, 14, 16 to 5, 6, 7, 8 doesn\u0027t change the\n // payouts received. The fraction of farm rewards allocated to Spring, for example, \n // is 10/(10+12+14+16) = 5/(5+6+7+8).\n\n uint256 public constant REALLOCATION_INTERVAL = (365 * 24 * 60 * 60 * 3) / 4; // 9 months\n\n\n // Liquidity positions must cover the full range of prices\n\n int24 public constant REQUIRED_TICK_UPPER = 887200;\n int24 public constant REQUIRED_TICK_LOWER = -887200;\n\n\n // Liquidity tokens can be withdrawn for 7 days out of every 37.\n //\n // This means that about one fifth of the liquidity can be withdrawn at any given time,\n // preventing liquidity from disappearing in a panic, but liquidity providers can withdraw\n // to adjust their positions monthly.\n\n uint256 public constant WITHDRAWAL_UNAVAILABLE_DAYS = 30;\n uint256 public constant WITHDRAWAL_AVAILABLE_DAYS = 7;\n\n\n // Each liquidity token deposited adds a specific amount of liquidity to the ETH/Seasonal Token\n // trading pair. Incoming tokens allocated to that trading pair are distributed to liquidity\n // token owners in proportion to the liquidity they have provided.\n\n mapping(address =\u003e uint256) public totalLiquidity;\n mapping(address =\u003e uint256[]) public tokenOfOwnerByIndex;\n mapping(uint256 =\u003e LiquidityToken) public liquidityTokens;\n\n address public immutable springTokenAddress;\n address public immutable summerTokenAddress;\n address public immutable autumnTokenAddress;\n address public immutable winterTokenAddress;\n address public immutable wethAddress;\n\n INonfungiblePositionManager public immutable nonfungiblePositionManager;\n\n uint256 public immutable startTime;\n \n\n // We keep track of the cumulative number of farmed (donated and allocated) tokens of each type per unit\n // liquidity, for each trading pair. This allows us to calculate the payout for each liquidity token.\n // \n // When a liquidity token is deposited, the value of the cumulative number of farmed tokens per unit\n // liquidity is recorded. The number of tokens farmed by that liquidity position is given by the\n // amount of liquidity multiplied by the increase in the cumulative number of tokens farmed per\n // unit liquidity.\n //\n // cumulativeTokensFarmedPerUnitLiquidity[trading_pair_token][farmed_token] = farmed tokens/liquidity\n\n mapping(address =\u003e mapping(address =\u003e uint256)) public cumulativeTokensFarmedPerUnitLiquidity;\n\n event Deposit(address indexed from, uint256 liquidityTokenId);\n event Withdraw(address indexed tokenOwner, uint256 liquidityTokenId);\n event Donate(address indexed from, address seasonalTokenAddress, uint256 amount);\n event Harvest(address indexed tokenOwner, uint256 liquidityTokenId, \n uint256 springAmount, uint256 summerAmount, uint256 autumnAmount, uint256 winterAmount);\n\n\n constructor (INonfungiblePositionManager nonfungiblePositionManager_, \n address springTokenAddress_, \n address summerTokenAddress_,\n address autumnTokenAddress_,\n address winterTokenAddress_,\n address wethAddress_,\n uint256 startTime_) {\n\n nonfungiblePositionManager = nonfungiblePositionManager_;\n\n springTokenAddress = springTokenAddress_;\n summerTokenAddress = summerTokenAddress_;\n autumnTokenAddress = autumnTokenAddress_;\n winterTokenAddress = winterTokenAddress_;\n wethAddress = wethAddress_;\n\n startTime = startTime_;\n }\n\n function balanceOf(address liquidityProvider) external view returns (uint256) {\n return tokenOfOwnerByIndex[liquidityProvider].length;\n }\n\n function numberOfReallocations() internal view returns (uint256) {\n if (block.timestamp \u003c startTime + REALLOCATION_INTERVAL)\n return 0;\n uint256 timeSinceStart = block.timestamp - startTime;\n return timeSinceStart / REALLOCATION_INTERVAL;\n }\n\n function hasDoubledAllocation(uint256 tokenNumber) internal view returns (uint256) {\n\n if (numberOfReallocations() % 4 \u003c tokenNumber)\n return 0;\n \n return 1;\n }\n\n function springAllocationSize() public view returns (uint256) {\n return 5 * 2 ** hasDoubledAllocation(1);\n }\n\n function summerAllocationSize() public view returns (uint256) {\n return 6 * 2 ** hasDoubledAllocation(2);\n }\n\n function autumnAllocationSize() public view returns (uint256) {\n return 7 * 2 ** hasDoubledAllocation(3);\n }\n\n function winterAllocationSize() public pure returns (uint256) {\n return 8;\n }\n\n function getEffectiveTotalAllocationSize(uint256 totalSpringLiquidity, \n uint256 totalSummerLiquidity,\n uint256 totalAutumnLiquidity,\n uint256 totalWinterLiquidity) internal view returns (uint256) {\n uint256 effectiveTotal = 0;\n\n if (totalSpringLiquidity \u003e 0)\n effectiveTotal += springAllocationSize();\n if (totalSummerLiquidity \u003e 0)\n effectiveTotal += summerAllocationSize();\n if (totalAutumnLiquidity \u003e 0)\n effectiveTotal += autumnAllocationSize();\n if (totalWinterLiquidity \u003e 0)\n effectiveTotal += winterAllocationSize();\n \n return effectiveTotal;\n }\n\n function allocateIncomingTokensToTradingPairs(address incomingTokenAddress, uint256 amount) internal {\n\n uint256 totalSpringLiquidity = totalLiquidity[springTokenAddress];\n uint256 totalSummerLiquidity = totalLiquidity[summerTokenAddress];\n uint256 totalAutumnLiquidity = totalLiquidity[autumnTokenAddress];\n uint256 totalWinterLiquidity = totalLiquidity[winterTokenAddress];\n\n uint256 effectiveTotalAllocationSize = getEffectiveTotalAllocationSize(totalSpringLiquidity,\n totalSummerLiquidity,\n totalAutumnLiquidity,\n totalWinterLiquidity);\n\n require(effectiveTotalAllocationSize \u003e 0, \"No liquidity in farm\");\n\n uint256 springPairAllocation = (amount * springAllocationSize()) / effectiveTotalAllocationSize;\n uint256 summerPairAllocation = (amount * summerAllocationSize()) / effectiveTotalAllocationSize;\n uint256 autumnPairAllocation = (amount * autumnAllocationSize()) / effectiveTotalAllocationSize;\n uint256 winterPairAllocation = (amount * winterAllocationSize()) / effectiveTotalAllocationSize;\n\n if (totalSpringLiquidity \u003e 0)\n cumulativeTokensFarmedPerUnitLiquidity[springTokenAddress][incomingTokenAddress] \n += (2 ** 128) * springPairAllocation / totalSpringLiquidity;\n\n if (totalSummerLiquidity \u003e 0)\n cumulativeTokensFarmedPerUnitLiquidity[summerTokenAddress][incomingTokenAddress] \n += (2 ** 128) * summerPairAllocation / totalSummerLiquidity;\n\n if (totalAutumnLiquidity \u003e 0)\n cumulativeTokensFarmedPerUnitLiquidity[autumnTokenAddress][incomingTokenAddress] \n += (2 ** 128) * autumnPairAllocation / totalAutumnLiquidity;\n\n if (totalWinterLiquidity \u003e 0)\n cumulativeTokensFarmedPerUnitLiquidity[winterTokenAddress][incomingTokenAddress] \n += (2 ** 128) * winterPairAllocation / totalWinterLiquidity;\n }\n\n function receiveApproval(address from, uint256 tokens, address token, bytes calldata data) public override {\n data; // suppress unused variable compiler warnings\n receiveSeasonalTokens(from, token, tokens);\n }\n\n function receiveSeasonalTokens(address from, address tokenAddress, uint256 amount) internal {\n\n require(msg.sender == springTokenAddress || msg.sender == summerTokenAddress\n || msg.sender == autumnTokenAddress || msg.sender == winterTokenAddress,\n \"Only Seasonal Tokens can be donated\");\n \n allocateIncomingTokensToTradingPairs(tokenAddress, amount);\n\n emit Donate(from, tokenAddress, amount);\n\n SafeERC20.safeTransferFrom(ERC20Interface(tokenAddress), from, address(this), amount);\n }\n\n function onERC721Received(address _operator, address _from, uint256 liquidityTokenId, bytes calldata _data) \n external override returns(bytes4) {\n\n require(msg.sender == address(nonfungiblePositionManager), \n \"Only Uniswap v3 liquidity tokens can be deposited\");\n\n LiquidityToken memory liquidityToken = getLiquidityToken(liquidityTokenId);\n \n liquidityToken.owner = _from;\n liquidityToken.depositTime = block.timestamp;\n\n liquidityToken.position = tokenOfOwnerByIndex[_from].length;\n tokenOfOwnerByIndex[_from].push(liquidityTokenId);\n\n liquidityToken.initialCumulativeSpringTokensFarmed\n = cumulativeTokensFarmedPerUnitLiquidity[liquidityToken.seasonalToken][springTokenAddress];\n\n liquidityToken.initialCumulativeSummerTokensFarmed\n = cumulativeTokensFarmedPerUnitLiquidity[liquidityToken.seasonalToken][summerTokenAddress];\n\n liquidityToken.initialCumulativeAutumnTokensFarmed\n = cumulativeTokensFarmedPerUnitLiquidity[liquidityToken.seasonalToken][autumnTokenAddress];\n\n liquidityToken.initialCumulativeWinterTokensFarmed\n = cumulativeTokensFarmedPerUnitLiquidity[liquidityToken.seasonalToken][winterTokenAddress];\n\n liquidityTokens[liquidityTokenId] = liquidityToken;\n totalLiquidity[liquidityToken.seasonalToken] += liquidityToken.liquidity;\n\n emit Deposit(_from, liquidityTokenId);\n\n _data; _operator; // suppress unused variable compiler warnings\n return bytes4(keccak256(\"onERC721Received(address,address,uint256,bytes)\"));\n }\n\n function getLiquidityToken(uint256 tokenId) internal view returns(LiquidityToken memory) {\n\n LiquidityToken memory liquidityToken;\n address token0;\n address token1;\n int24 tickLower;\n int24 tickUpper;\n uint256 liquidity;\n uint24 fee;\n \n (token0, token1, fee, tickLower, tickUpper, liquidity) = getPositionDataForLiquidityToken(tokenId);\n liquidityToken.liquidity = liquidity;\n \n if (token0 == wethAddress)\n liquidityToken.seasonalToken = token1;\n else if (token1 == wethAddress)\n liquidityToken.seasonalToken = token0;\n\n require(liquidityToken.seasonalToken == springTokenAddress ||\n liquidityToken.seasonalToken == summerTokenAddress ||\n liquidityToken.seasonalToken == autumnTokenAddress ||\n liquidityToken.seasonalToken == winterTokenAddress,\n \"Invalid trading pair\");\n\n require(tickLower == REQUIRED_TICK_LOWER \u0026\u0026 tickUpper == REQUIRED_TICK_UPPER,\n \"Liquidity must cover full range of prices\");\n\n require(fee == 10000, \"Fee tier must be 1%\");\n\n return liquidityToken;\n }\n\n function getPositionDataForLiquidityToken(uint256 tokenId) \n internal view returns (address, address, uint24, int24, int24, uint256){\n address token0;\n address token1;\n int24 tickLower;\n int24 tickUpper;\n uint256 liquidity;\n uint24 fee;\n\n (,, token0, token1, fee, tickLower, tickUpper, liquidity,,,,) \n = nonfungiblePositionManager.positions(tokenId);\n\n return (token0, token1, fee, tickLower, tickUpper, liquidity);\n }\n\n function setCumulativeSpringTokensFarmedToCurrentValue(uint256 liquidityTokenId, address seasonalToken) internal {\n liquidityTokens[liquidityTokenId].initialCumulativeSpringTokensFarmed\n = cumulativeTokensFarmedPerUnitLiquidity[seasonalToken][springTokenAddress];\n }\n\n function setCumulativeSummerTokensFarmedToCurrentValue(uint256 liquidityTokenId, address seasonalToken) internal {\n liquidityTokens[liquidityTokenId].initialCumulativeSummerTokensFarmed\n = cumulativeTokensFarmedPerUnitLiquidity[seasonalToken][summerTokenAddress];\n }\n\n function setCumulativeAutumnTokensFarmedToCurrentValue(uint256 liquidityTokenId, address seasonalToken) internal {\n liquidityTokens[liquidityTokenId].initialCumulativeAutumnTokensFarmed\n = cumulativeTokensFarmedPerUnitLiquidity[seasonalToken][autumnTokenAddress];\n }\n\n function setCumulativeWinterTokensFarmedToCurrentValue(uint256 liquidityTokenId, address seasonalToken) internal {\n liquidityTokens[liquidityTokenId].initialCumulativeWinterTokensFarmed\n = cumulativeTokensFarmedPerUnitLiquidity[seasonalToken][winterTokenAddress];\n }\n\n function getPayoutSize(uint256 liquidityTokenId, address farmedSeasonalToken, \n address tradingPairSeasonalToken) internal view returns (uint256) {\n\n uint256 initialCumulativeTokensFarmed;\n\n if (farmedSeasonalToken == springTokenAddress)\n initialCumulativeTokensFarmed = liquidityTokens[liquidityTokenId].initialCumulativeSpringTokensFarmed;\n else if (farmedSeasonalToken == summerTokenAddress)\n initialCumulativeTokensFarmed = liquidityTokens[liquidityTokenId].initialCumulativeSummerTokensFarmed;\n else if (farmedSeasonalToken == autumnTokenAddress)\n initialCumulativeTokensFarmed = liquidityTokens[liquidityTokenId].initialCumulativeAutumnTokensFarmed;\n else\n initialCumulativeTokensFarmed = liquidityTokens[liquidityTokenId].initialCumulativeWinterTokensFarmed;\n\n uint256 tokensFarmedPerUnitLiquiditySinceDeposit \n = cumulativeTokensFarmedPerUnitLiquidity[tradingPairSeasonalToken][farmedSeasonalToken]\n - initialCumulativeTokensFarmed;\n\n return (tokensFarmedPerUnitLiquiditySinceDeposit \n * liquidityTokens[liquidityTokenId].liquidity) / (2 ** 128);\n }\n\n function getPayoutSizes(uint256 liquidityTokenId) external view returns (uint256, uint256, uint256, uint256) {\n\n address tradingPairSeasonalToken = liquidityTokens[liquidityTokenId].seasonalToken;\n\n uint256 springPayout = getPayoutSize(liquidityTokenId, springTokenAddress, tradingPairSeasonalToken);\n uint256 summerPayout = getPayoutSize(liquidityTokenId, summerTokenAddress, tradingPairSeasonalToken);\n uint256 autumnPayout = getPayoutSize(liquidityTokenId, autumnTokenAddress, tradingPairSeasonalToken);\n uint256 winterPayout = getPayoutSize(liquidityTokenId, winterTokenAddress, tradingPairSeasonalToken);\n\n return (springPayout, summerPayout, autumnPayout, winterPayout);\n }\n\n function harvestSpring(uint256 liquidityTokenId, address tradingPairSeasonalToken) internal returns(uint256) {\n\n uint256 amount = getPayoutSize(liquidityTokenId, springTokenAddress, tradingPairSeasonalToken);\n setCumulativeSpringTokensFarmedToCurrentValue(liquidityTokenId, tradingPairSeasonalToken);\n return amount;\n }\n\n function harvestSummer(uint256 liquidityTokenId, address tradingPairSeasonalToken) internal returns(uint256) {\n\n uint256 amount = getPayoutSize(liquidityTokenId, summerTokenAddress, tradingPairSeasonalToken);\n setCumulativeSummerTokensFarmedToCurrentValue(liquidityTokenId, tradingPairSeasonalToken);\n return amount;\n }\n\n function harvestAutumn(uint256 liquidityTokenId, address tradingPairSeasonalToken) internal returns(uint256) {\n\n uint256 amount = getPayoutSize(liquidityTokenId, autumnTokenAddress, tradingPairSeasonalToken);\n setCumulativeAutumnTokensFarmedToCurrentValue(liquidityTokenId, tradingPairSeasonalToken);\n return amount;\n }\n\n function harvestWinter(uint256 liquidityTokenId, address tradingPairSeasonalToken) internal returns(uint256) {\n\n uint256 amount = getPayoutSize(liquidityTokenId, winterTokenAddress, tradingPairSeasonalToken);\n setCumulativeWinterTokensFarmedToCurrentValue(liquidityTokenId, tradingPairSeasonalToken);\n return amount;\n }\n\n function harvestAll(uint256 liquidityTokenId, address tradingPairSeasonalToken) \n internal returns (uint256, uint256, uint256, uint256) {\n\n uint256 springAmount = harvestSpring(liquidityTokenId, tradingPairSeasonalToken);\n uint256 summerAmount = harvestSummer(liquidityTokenId, tradingPairSeasonalToken);\n uint256 autumnAmount = harvestAutumn(liquidityTokenId, tradingPairSeasonalToken);\n uint256 winterAmount = harvestWinter(liquidityTokenId, tradingPairSeasonalToken);\n\n return (springAmount, summerAmount, autumnAmount, winterAmount);\n }\n\n function sendHarvestedTokensToOwner(address tokenOwner, uint256 springAmount, uint256 summerAmount,\n uint256 autumnAmount, uint256 winterAmount) internal {\n\n if (springAmount \u003e 0)\n ERC20Interface(springTokenAddress).transfer(tokenOwner, springAmount);\n if (summerAmount \u003e 0)\n ERC20Interface(summerTokenAddress).transfer(tokenOwner, summerAmount);\n if (autumnAmount \u003e 0)\n ERC20Interface(autumnTokenAddress).transfer(tokenOwner, autumnAmount);\n if (winterAmount \u003e 0)\n ERC20Interface(winterTokenAddress).transfer(tokenOwner, winterAmount);\n }\n\n function harvest(uint256 liquidityTokenId) external {\n \n LiquidityToken storage liquidityToken = liquidityTokens[liquidityTokenId];\n require(msg.sender == liquidityToken.owner, \"Only owner can harvest\");\n \n (uint256 springAmount, \n uint256 summerAmount,\n uint256 autumnAmount,\n uint256 winterAmount) = harvestAll(liquidityTokenId, liquidityToken.seasonalToken);\n\n emit Harvest(msg.sender, liquidityTokenId, springAmount, summerAmount, autumnAmount, winterAmount);\n \n sendHarvestedTokensToOwner(msg.sender, springAmount, summerAmount, autumnAmount, winterAmount);\n }\n\n function canWithdraw(uint256 liquidityTokenId) public view returns (bool) {\n\n uint256 depositTime = liquidityTokens[liquidityTokenId].depositTime;\n uint256 timeSinceDepositTime = block.timestamp - depositTime;\n uint256 daysSinceDepositTime = timeSinceDepositTime / (24 * 60 * 60);\n\n return (daysSinceDepositTime) % (WITHDRAWAL_UNAVAILABLE_DAYS + WITHDRAWAL_AVAILABLE_DAYS) \n \u003e= WITHDRAWAL_UNAVAILABLE_DAYS;\n }\n\n function nextWithdrawalTime(uint256 liquidityTokenId) external view returns (uint256) {\n \n uint256 depositTime = liquidityTokens[liquidityTokenId].depositTime;\n uint256 timeSinceDepositTime = block.timestamp - depositTime;\n uint256 withdrawalUnavailableTime = WITHDRAWAL_UNAVAILABLE_DAYS * 24 * 60 * 60;\n uint256 withdrawalAvailableTime = WITHDRAWAL_AVAILABLE_DAYS * 24 * 60 * 60;\n\n if (timeSinceDepositTime \u003c withdrawalUnavailableTime)\n return depositTime + withdrawalUnavailableTime;\n\n uint256 numberOfWithdrawalCyclesUntilNextWithdrawalTime \n = 1 + (timeSinceDepositTime - withdrawalUnavailableTime) \n / (withdrawalUnavailableTime + withdrawalAvailableTime);\n\n return depositTime + withdrawalUnavailableTime \n + numberOfWithdrawalCyclesUntilNextWithdrawalTime\n * (withdrawalUnavailableTime + withdrawalAvailableTime);\n }\n\n function withdraw(uint256 liquidityTokenId) external {\n\n require(canWithdraw(liquidityTokenId), \"This token cannot be withdrawn at this time\");\n\n LiquidityToken memory liquidityToken = liquidityTokens[liquidityTokenId];\n\n require(msg.sender == liquidityToken.owner, \"Only owner can withdraw\");\n\n (uint256 springAmount, \n uint256 summerAmount,\n uint256 autumnAmount,\n uint256 winterAmount) = harvestAll(liquidityTokenId, liquidityToken.seasonalToken);\n\n totalLiquidity[liquidityToken.seasonalToken] -= liquidityToken.liquidity;\n removeTokenFromListOfOwnedTokens(msg.sender, liquidityToken.position, liquidityTokenId);\n \n emit Harvest(msg.sender, liquidityTokenId, springAmount, summerAmount, autumnAmount, winterAmount);\n emit Withdraw(msg.sender, liquidityTokenId);\n\n sendHarvestedTokensToOwner(msg.sender, springAmount, summerAmount, autumnAmount, winterAmount);\n nonfungiblePositionManager.safeTransferFrom(address(this), liquidityToken.owner, liquidityTokenId);\n }\n\n function removeTokenFromListOfOwnedTokens(address owner, uint256 index, uint256 liquidityTokenId) internal {\n\n // to remove an element from a list efficiently, we copy the last element in the list into the\n // position of the element we want to remove, and then remove the last element from the list\n\n uint256 length = tokenOfOwnerByIndex[owner].length;\n if (length \u003e 1) {\n uint256 liquidityTokenIdOfLastTokenInList = tokenOfOwnerByIndex[owner][length - 1];\n LiquidityToken memory lastToken = liquidityTokens[liquidityTokenIdOfLastTokenInList];\n lastToken.position = index;\n tokenOfOwnerByIndex[owner][index] = liquidityTokenIdOfLastTokenInList;\n liquidityTokens[liquidityTokenIdOfLastTokenInList] = lastToken;\n }\n tokenOfOwnerByIndex[owner].pop();\n delete liquidityTokens[liquidityTokenId];\n }\n\n}\n"}}