pragma solidity ^0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";
// ⢀⢀⣀⠤⠤⠤⣀
// ⠞ ⠈⠑⠢⢔⡶⣭⡿⠟⠋⠉⠢⡀
// ⢀⠞ ⠈⠛⠃ ⣵⡀
// ⢠⠎ ⢀⠠⢤⣤⣤⠄⣀ ⣀⢤⣤⣭⣕⣢⡀
// ⢀⡠⠊ ⢀⡠⣪⠗⣨⣶⠟⠓⠪⡔⠵⣄⠼⣪⣷⡾⠛⠲⣌⠛⡆
// ⢠⠊ ⠠⣔⡽⠊ ⢸⡿⢧ ⢀⣿⡀⠐⢠⣿⠿⡀ ⣸⡇⢸
// ⢰⠃ ⠒⠛⠛⠤⣀ ⠘⣧⣾⣿⣿⣿⣿⡠⣊⢿⣤⣟⣿⣿⣿⡣⠎
// ⢠⠇ ⠐⠻⠭⠭⠭⠿⠟⠋⠁ ⠈⠙⠛⠛⢛⡿⠋⠁
// ⢸ ⢠⣶⣦⣄⡀ ⠙ ⠃ ⢈⣆
// ⢸ ⠈⠻⢿⣿⣿⣷⣶⣶⣤⣤⣤⣤⣀⣀⣀⣤⣤⣤⣶⣶⣾⣿⣿⠆
// ⢸ ⠈⠉⠛⠻⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡻
// ⠘⡆ ⢟⡕⠁
// ⣀⣤⣷⣤ ⢟⡕
// ⣠⣾⣿⣿⣿⣿⣷ ⣟⣯
// ⢀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣷⣤⣈⠻⣿⣯⣶⣦⠴⢤⣀⣀⣀⣩⣭⡍⠊⣿⣧⣾⣿⣦⣄
// ⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷ ⢧⣿⣿⣿⣿⣿⣾ ⣿⣿⣿⣿⣿⣷⣶⣄
// ⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⠈⢻⣿⣿⣿⣿⡏ ⣡⣿⣿⣿⣿⣿⣿⣿⣿⡇
// ⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣆⣹⣿⣿⣿⣯⢳⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡀
// ⢰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇
// ⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧
// The Golden Frog DAO - Genesis Frogs
// Created by Mr F - https://twitter.com/MrFwashere
// Artwork by Fenoir - https://twitter.com/EscuerFlorian
// Special thanks and love to the entire Golden Frog DAO community
// Extra special thanks to the GFD admins Kronos, Oscom, SkYwArD and GayFrog
contract GoldenFrogs is ERC721, ERC721Enumerable, ERC721URIStorage {
uint maxFrogs = 500;
uint offsetBlock;
uint offset;
address public mrF = 0x38857Ed3a8fC5951289E58e20fB56A00e88f0BBD;
string public baseURI;
bool public locked;
uint public withdrawnEth;
mapping(uint => uint) public claimedEth;
struct rewardToken {
uint withdrawn;
mapping(uint => uint) claimedTokens;
}
mapping(address => rewardToken) rewardTokens;
constructor() ERC721("GoldenFrogs", "GF") {
}
fallback() external payable {
}
receive() external payable {
}
function setBaseURI(string memory uri) public {
require(msg.sender == mrF);
require(!locked);
baseURI = uri;
}
function lockBaseURI() public {
require(msg.sender == mrF);
locked = true;
}
function changeMrF(address newMrF) public {
require(msg.sender == mrF);
mrF = newMrF;
}
function mint(address og) public {
require(msg.sender == mrF);
require(totalSupply() < maxFrogs);
require(offset != 0);
_mint(og, ((totalSupply() + offset) % maxFrogs) + 1);
}
function mintMany(address[] memory addrs, uint32[] memory nums) public {
for (uint i = 0; i < addrs.length; i++) {
address addr = addrs[i];
uint num = nums[i];
for (uint j = 0; j < num; j++) {
mint(addr);
}
}
}
// Offset functions (provenance)
function initOffset() public {
require(msg.sender == mrF);
offsetBlock = block.number + 1;
}
function finalizeOffset() public {
require(offset == 0);
require(offsetBlock != 0);
require(block.number - offsetBlock < 255);
offset = uint(blockhash(offsetBlock)) % maxFrogs;
// Prevent default sequence
if (offset == 0) {
offset = 1;
}
delete offsetBlock;
}
// Withdrawing functions
function getUserFrogs(address user) public view returns (uint[] memory) {
uint count = balanceOf(user);
uint[] memory ids = new uint[](count);
for (uint i = 0; i<count; i++) {
uint token = tokenOfOwnerByIndex(user, i);
ids[i] = token;
}
return ids;
}
function withdrawEth(uint32[] memory ids) public {
uint totalRewards;
uint receivedEth = withdrawnEth + address(this).balance;
uint receivedEthPerFrog = receivedEth / totalSupply();
for (uint i = 0; i < ids.length; i++) {
require(ownerOf(ids[i]) == msg.sender, "Must own the token");
totalRewards += receivedEthPerFrog - claimedEth[ids[i]];
claimedEth[ids[i]] = receivedEthPerFrog;
}
withdrawnEth += totalRewards;
(bool success,) = address(msg.sender).call{value: totalRewards}('');
require(success, "Transfer failed");
}
function checkEth(uint32[] memory ids) public view returns (uint balance) {
uint totalRewards;
uint receivedEth = withdrawnEth + address(this).balance;
uint receivedEthPerToken = receivedEth / totalSupply();
for (uint i = 0; i < ids.length; i++) {
totalRewards += receivedEthPerToken - claimedEth[ids[i]];
}
return totalRewards;
}
function withdrawTokens(address token, uint32[] memory ids) public {
uint totalRewards;
uint receivedTokens = rewardTokens[token].withdrawn + ERC20(token).balanceOf(address(this));
uint receivedTokenPerFrog = receivedTokens / totalSupply();
for (uint i = 0; i < ids.length; i++) {
require(ownerOf(ids[i]) == msg.sender, "Must own the token");
totalRewards += receivedTokenPerFrog - rewardTokens[token].claimedTokens[ids[i]];
rewardTokens[token].claimedTokens[ids[i]] = receivedTokenPerFrog;
}
rewardTokens[token].withdrawn += totalRewards;
ERC20(token).transfer(msg.sender, totalRewards);
}
function checkTokens(address token, uint32[] memory ids) public view returns (uint balance) {
uint totalRewards;
uint receivedTokens = rewardTokens[token].withdrawn + ERC20(token).balanceOf(address(this));
uint receivedTokenPerFrog = receivedTokens / totalSupply();
for (uint i = 0; i < ids.length; i++) {
totalRewards += receivedTokenPerFrog - rewardTokens[token].claimedTokens[ids[i]];
}
return totalRewards;
}
// copy pasted ERC721 junk
function _beforeTokenTransfer(address from, address to, uint256 tokenId)
internal
override(ERC721, ERC721Enumerable)
{
super._beforeTokenTransfer(from, to, tokenId);
}
function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
super._burn(tokenId);
}
function tokenURI(uint256 tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC721Enumerable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
function _baseURI() internal view override returns (string memory) {
return baseURI;
}
}
{
"compilationTarget": {
"contracts/GoldenFrogs.sol": "GoldenFrogs"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newMrF","type":"address"}],"name":"changeMrF","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"ids","type":"uint32[]"}],"name":"checkEth","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32[]","name":"ids","type":"uint32[]"}],"name":"checkTokens","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"claimedEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finalizeOffset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserFrogs","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initOffset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"locked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"og","type":"address"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addrs","type":"address[]"},{"internalType":"uint32[]","name":"nums","type":"uint32[]"}],"name":"mintMany","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mrF","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"ids","type":"uint32[]"}],"name":"withdrawEth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32[]","name":"ids","type":"uint32[]"}],"name":"withdrawTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawnEth","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]