Untitled
unknown
plain_text
3 years ago
6.5 kB
11
Indexable
// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; /* Simple ERC20 token contract to issue rewards */ contract Energy is ERC20, Ownable { mapping(address => bool) minters; constructor() ERC20("ENERGY", "NRG") { _mint(msg.sender, 100 * 10**decimals()); } modifier isMinter() { require(minters[msg.sender], "Caller is not authorized to mint!"); _; } function mintRewards(address to, uint256 amount) external isMinter { _mint(to, amount * 10**decimals()); } function addMinter(address account) public onlyOwner { minters[account] = true; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract Fuel is ERC721, ERC721Burnable, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIdCounter; constructor() ERC721("Fuel", "FUEL") {} function safeMint(address to) public onlyOwner { uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to, tokenId); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "./Energy.sol"; import "./Fuel.sol"; struct Loader { uint256[] fuelIds; mapping(uint256 => uint256) loadBlock; } contract Generator is Ownable, ReentrancyGuard, IERC721Receiver { Fuel fuel; Energy energy; uint256 rewardsPerBlock = 5; mapping(address => Loader) loaders; // Enumeration of fuelIds staked indexes of a loader mapping(address => mapping(uint256 => uint256)) public fuelIdIndex; // tracks owner of a fuelId mapping(uint256 => address) public loaderOf; constructor(address _fuel, address _energy) { fuel = Fuel(_fuel); energy = Energy(_energy); } function stake(uint256 fuelId) public nonReentrant { // safe checks require( fuel.ownerOf(fuelId) == msg.sender, "You're not the owner of this NFT" ); // push new token to staking collection loaders[msg.sender].fuelIds.push(fuelId); // updates index reference of fuelId uint256 totalFuel = loaders[msg.sender].fuelIds.length; fuelIdIndex[msg.sender][fuelId] = totalFuel - 1; // inits staking block loaders[msg.sender].loadBlock[fuelId] = block.number; // add it to reference loaderOf[fuelId] = msg.sender; fuel.safeTransferFrom(address(msg.sender), address(this), fuelId); } function unstake(uint256 fuelId) public nonReentrant { // safe checks require(ownedByThis(fuelId), "This fuel is not being loaded here!"); require( _loaderOf(fuelId) == address(msg.sender), "You haven't loaded this fuel here!" ); uint256 lastFuelIndex = loaders[msg.sender].fuelIds.length - 1; uint256 fuelIndex = fuelIdIndex[msg.sender][fuelId]; // swap current fuelId to last position if (lastFuelIndex != fuelIndex) { uint256 lastFuelId = loaders[msg.sender].fuelIds[lastFuelIndex]; loaders[msg.sender].fuelIds[fuelIndex] = lastFuelIndex; // Move the last token to the slot of the to-delete token fuelIdIndex[msg.sender][lastFuelId] = fuelIndex; // Update the moved token's index } // remove the last element from mapping and array delete fuelIdIndex[msg.sender][fuelId]; delete loaders[msg.sender].fuelIds[lastFuelIndex]; delete loaders[msg.sender].loadBlock[fuelId]; delete loaderOf[fuelId]; // Transfer back to the owner fuel.safeTransferFrom(address(this), address(msg.sender), fuelId); claim(fuelId); } function claim(uint256 fuelId) public { // safe checks require(ownedByThis(fuelId), "This fuel is not being loaded here!"); require( _loaderOf(fuelId) == address(msg.sender), "You haven't loaded this fuel here!" ); uint256 rewardsToClaim = getPendingRewards(msg.sender, fuelId); energy.mintRewards(msg.sender, rewardsToClaim); loaders[msg.sender].loadBlock[fuelId] = block.number; } function claimAll() public nonReentrant { // safe checks require( loaders[msg.sender].fuelIds.length > 0, "You have no fuel loaded here!" ); uint256 totalFuelLoaded = totalFuelLoadedBy(msg.sender); for (uint256 i = 0; i < totalFuelLoaded; i++) { uint256 fuelId = loaders[msg.sender].fuelIds[i]; claim(fuelId); } } function getPendingRewards(address account, uint256 fuelId) public view returns (uint256) { uint256 loadBlock = loaders[account].loadBlock[fuelId]; uint256 blocksElapsed = block.number - loadBlock; return blocksElapsed * rewardsPerBlock; } function getAllPendingRewards() public view returns (uint256) { uint256 totalFuelLoaded = totalFuelLoadedBy(msg.sender); uint256 totalRewards = 0; for (uint256 i = 0; i < totalFuelLoaded; i++) { uint256 fuelId = loaders[msg.sender].fuelIds[i]; totalRewards += getPendingRewards(msg.sender, fuelId); } return totalRewards; } function _loaderOf(uint256 fuelId) public view returns (address) { return loaderOf[fuelId]; } function totalFuelLoadedBy(address account) public view returns (uint256) { return loaders[account].fuelIds.length; } function generatorAddress() public view returns (address) { return address(this); } function ownedByThis(uint256 fuelId) public view returns (bool) { return address(fuel.ownerOf(fuelId)) == address(this); } function onERC721Received( address operator, address from, uint256 fuelId, bytes calldata data ) external override returns (bytes4) { return this.onERC721Received.selector; } }
Editor is loading...