Created
October 25, 2022 05:40
-
-
Save manolingam/d62b6e62579a2e3e1cf4407eb634e98d to your computer and use it in GitHub Desktop.
Revisions
-
manolingam created this gist
Oct 25, 2022 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,225 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; error NotYourBufficorn(); error BufficornOnSwap(); error BufficornNotGrazing(); error BufficornGrazing(); error InvalidSwapId(); error SwapAlreadyComplete(); error SwapExpired(); error NotOwner(); error DuplicateRecord(); error Paused(); contract GrazingAndSwapping { uint public SWAP_PERIOD; uint public MIN_GRAZING_PERIOD; address public BUFFICORN_NFT_ADDRESS; bool public IS_PAUSED; address public OWNER; // GRAZING INFORMATION STRUCT struct GrazingInfo { address grazer; uint bufficornId; bool isGrazing; uint grazingStartTime; uint grazingEndTime; } // MAPPING OF BUFFICORN IDS TO GRAZING INFO STRUCT mapping (uint => GrazingInfo) public grazingInfo; // SWAP STATUS ENUM enum SwapStatus {Created, Completed} // SWAP INFORMATION STRUCT struct SwapInfo { address swapInitiator; uint initiatorBufficornId; address swapExecutor; uint executorBufficornId; uint swapInitiatedTimestamp; uint swapExecutedTimestamp; string[] traitsSelectedForSwap; SwapStatus swapStatus; } // COUNTING NUMBER OF SWAPS FOR SWAP ID uint public numberOfSwapsCreated; // MAPPING OF SWAP IDS TO ENCODED SWAP STRUCT mapping (uint => bytes) internal swapHash; // RECORDING THE LATEST SWAP PARTICIPATION TIME OF A BUFFICORN mapping (uint => uint) public bufficornLastestSwapParticipationTime; // EVENTS event newSwap(address initiator, uint initiatorBufficornId, uint executorBufficornId, uint swapInitiatedTimestamp, string[] traitsSelectedForSwap, SwapStatus); event finishSwap(address executor, uint initiatorBufficornId, uint executorBufficornId, uint swapExecutedTimestamp, SwapStatus); event goneGrazing(address grazer, uint grazingBufficorn, uint grazingStartTime); event goneHome(address grazer, uint grazingBufficorn, uint grazingEndTime); // CONSTRUCTOR AND MODIFIERS --------------------------------------------------------- constructor(uint _swapPeriod, uint _minGrazingPeriod, address _bufficornNftAddress) { SWAP_PERIOD = _swapPeriod; MIN_GRAZING_PERIOD = _minGrazingPeriod; BUFFICORN_NFT_ADDRESS = _bufficornNftAddress; OWNER = msg.sender; IS_PAUSED = false; } modifier onlyOwner() { if(msg.sender != OWNER) revert NotOwner(); _; } modifier notPaused() { if(IS_PAUSED == true) revert Paused(); _; } // PUBLIC FUNTIONS --------------------------------------------------------- function initiateTraitSwap(uint _initiatorBufficornId, uint _executorBufficornId, string[] calldata _traitsToSwap) public notPaused returns (uint) { // revert if the caller is not the owner of the bufficorn he initiates the swap with if (IERC721(BUFFICORN_NFT_ADDRESS).ownerOf(_initiatorBufficornId) != msg.sender) revert NotYourBufficorn(); // revert if the initiating bufficorn is on an another swap and the swap period has not past the current time if(grazingInfo[_initiatorBufficornId].isGrazing == true) { if (bufficornLastestSwapParticipationTime[_initiatorBufficornId] + SWAP_PERIOD < block.timestamp) revert BufficornOnSwap(); } // revert if the executing bufficorn is on an another swap and the swap period has not past the current time if(grazingInfo[_executorBufficornId].isGrazing == true) { if (bufficornLastestSwapParticipationTime[_executorBufficornId] + SWAP_PERIOD < block.timestamp) revert BufficornOnSwap(); } // if the initiating bufficorn is not already grazing, put it to grazing if(grazingInfo[_initiatorBufficornId].isGrazing == false) { _goGrazin(_initiatorBufficornId); } // locking the swap initiated time to use in other variable assignments uint _swapInitiatedTimestamp = block.timestamp; // creating the swap information SwapInfo memory _swapInfo = SwapInfo(msg.sender, _initiatorBufficornId, address(0), _executorBufficornId, _swapInitiatedTimestamp, 0, _traitsToSwap, SwapStatus.Created); // increment the number of swaps created numberOfSwapsCreated = numberOfSwapsCreated + 1; // using the swap id from previous and storing the swap info encoded as hash swapHash[numberOfSwapsCreated] = abi.encode(_swapInfo); // updating the lastest swap participation time of the initiated bufficorn so that it can't be used on another swap bufficornLastestSwapParticipationTime[_initiatorBufficornId] = _swapInitiatedTimestamp; emit newSwap(msg.sender, _initiatorBufficornId, _executorBufficornId, _swapInitiatedTimestamp, _traitsToSwap, SwapStatus.Created); return numberOfSwapsCreated; } function executeTraitSwap(uint _swapId) public notPaused { // revert if swap id not found if(_swapId > numberOfSwapsCreated) revert InvalidSwapId(); // decoding the swap info SwapInfo memory _swapInfo = abi.decode(swapHash[_swapId], (SwapInfo)); // revert if the caller is not the owner of the executing bufficorn if (IERC721(BUFFICORN_NFT_ADDRESS).ownerOf(_swapInfo.executorBufficornId) != msg.sender) revert NotYourBufficorn(); // revert if swap already complete or the time has expired if(_swapInfo.swapStatus == SwapStatus.Completed) revert SwapAlreadyComplete(); if(_swapInfo.swapInitiatedTimestamp + SWAP_PERIOD < block.timestamp) revert SwapExpired(); // if the executor bufficorn not under grazing, let it graze if(grazingInfo[_swapInfo.executorBufficornId].isGrazing == false) { _goGrazin(_swapInfo.executorBufficornId); } // updating the swap info and encoding the updated information _swapInfo.swapExecutor = msg.sender; _swapInfo.swapExecutedTimestamp = block.timestamp; _swapInfo.swapStatus = SwapStatus.Completed; swapHash[_swapId] = abi.encode(_swapInfo); emit finishSwap(msg.sender, _swapInfo.initiatorBufficornId, _swapInfo.executorBufficornId, _swapInfo.swapExecutedTimestamp, SwapStatus.Completed); } function withdrawBufficornFromGrazing(uint _bufficornId) public { // revert if caller is not the bufficorn grazer if (grazingInfo[_bufficornId].grazer != msg.sender) revert NotYourBufficorn(); // revert if bufficorn not already grazing if (grazingInfo[_bufficornId].isGrazing == false) revert BufficornNotGrazing(); // revert if minimum grazing time has not past if(grazingInfo[_bufficornId].grazingStartTime + MIN_GRAZING_PERIOD > block.timestamp) revert BufficornGrazing(); // revert if the bufficorn is currently on a swap period if(bufficornLastestSwapParticipationTime[_bufficornId] + SWAP_PERIOD > block.timestamp) revert BufficornOnSwap(); // claim back bufficorn to wallet _backToRanch(_bufficornId); } // INTERNAL FUNCTIONS --------------------------------------------------------- function _goGrazin(uint _tokenId) internal { IERC721(BUFFICORN_NFT_ADDRESS).approve(address(this), _tokenId); IERC721(BUFFICORN_NFT_ADDRESS).safeTransferFrom(msg.sender, address(this), _tokenId); uint _grazingStartTime = block.timestamp; grazingInfo[_tokenId] = GrazingInfo(msg.sender, _tokenId, true, _grazingStartTime, 0); emit goneGrazing(msg.sender, _tokenId, _grazingStartTime); } function _backToRanch(uint _tokenId) internal { IERC721(BUFFICORN_NFT_ADDRESS).approve(address(this), _tokenId); IERC721(BUFFICORN_NFT_ADDRESS).safeTransferFrom(address(this), msg.sender, _tokenId); uint _grazingEndTime = block.timestamp; grazingInfo[_tokenId].isGrazing = false; grazingInfo[_tokenId].grazingEndTime = _grazingEndTime; emit goneHome(msg.sender, _tokenId, _grazingEndTime); } // VIEW FUNCTION --------------------------------------------------------- function decodeSwap(uint _swapId) public view returns(SwapInfo memory) { if(_swapId > numberOfSwapsCreated) revert InvalidSwapId(); return abi.decode(swapHash[_swapId], (SwapInfo)); } // ADMIN FUNCTIONS --------------------------------------------------------- function updateSwapPeriod(uint _swapPeriod) public onlyOwner { if(SWAP_PERIOD == _swapPeriod) revert DuplicateRecord(); SWAP_PERIOD = _swapPeriod; } function updateMinGrazingPeriod(uint _minGrazingPeriod) public onlyOwner { if(MIN_GRAZING_PERIOD == _minGrazingPeriod) revert DuplicateRecord(); MIN_GRAZING_PERIOD = _minGrazingPeriod; } function toggleContractState() public onlyOwner { IS_PAUSED = !IS_PAUSED; } }