// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.10;
import "./Ownable.sol";
import "./Strings.sol";
import "./ERC721.sol";
/*
______ __ ______ _ ____
/ ____/_ __/ /_ ___ _____/ ____/___ _____(_) / /___ ______
/ / / / / / __ \/ _ \/ ___/ / __/ __ \/ ___/ / / / __ `/ ___/
/ /___/ /_/ / /_/ / __/ / / /_/ / /_/ / / / / / / /_/ (__ )
\____/\__, /_.___/\___/_/ \____/\____/_/ /_/_/_/\__,_/____/
/____/
*/
/// @title Cyber Gorillas Babies
/// @author delta devs (https://twitter.com/deltadevelopers)
contract CyberGorillaBabies is ERC721, Ownable {
using Strings for uint256;
/// @notice The address which is allowed to breed Cyber Gorillas.
address private gorillaBreeder;
/// @notice Base URI pointing to CyberGorillaBabies metadata.
string public baseURI;
/// @notice Returns true if the requested gorilla baby has the genesis trait, false otherwise.
mapping(uint256 => bool) public isGenesis;
constructor(string memory initialBaseURI)
ERC721("Cyber Gorilla Babies", "CyberGorillaBabies")
{
baseURI = initialBaseURI;
}
/// @notice Set the address which is allowed to breed gorillas.
/// @param newGorillaBreeder The target address, authorized to breed.
function setGorillaBreeder(address newGorillaBreeder) public onlyOwner {
gorillaBreeder = newGorillaBreeder;
}
/// @notice Allows the contract deployer to set the Base URI for CyberGorillaBabies' metadata.
/// @param newBaseURI The new Base URI.
function setBaseURI(string memory newBaseURI) public onlyOwner {
baseURI = newBaseURI;
}
/// @notice Allows the authorized breeder address to mint a gorilla baby to a specific address.
/// @param to The address to receive the minted gorilla baby.
/// @param _isGenesis Whether the baby to be minted has the genesis trait or not.
function mintBaby(address to, bool _isGenesis) public {
require(msg.sender == gorillaBreeder, "Not Authorized");
isGenesis[totalSupply] = _isGenesis;
_mint(to, totalSupply);
}
/// @notice Returns the token URI of a specific gorilla baby.
/// @param tokenId The token ID of the requested gorilla baby.
/// @return The full URI of the requested gorilla baby.
function tokenURI(uint256 tokenId)
public
view
override
returns (string memory)
{
return
bytes(baseURI).length > 0
? string(abi.encodePacked(baseURI, tokenId.toString(), ".json"))
: "";
}
function supportsInterface(bytes4 interfaceId) public pure override(ERC721, Ownable) returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f || // ERC165 Interface ID for ERC721Metadata
interfaceId == 0x7f5828d0; // ERC165 Interface ID for ERC173
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "./Strings.sol";
import "./ERC721.sol";
import "./Ownable.sol";
error SoldOut();
error SaleClosed();
error InvalidMintParameters();
error MintingTooMany();
error NotWhitelisted();
error NotAuthorized();
/*
______ __ ______ _ ____
/ ____/_ __/ /_ ___ _____/ ____/___ _____(_) / /___ ______
/ / / / / / __ \/ _ \/ ___/ / __/ __ \/ ___/ / / / __ `/ ___/
/ /___/ /_/ / /_/ / __/ / / /_/ / /_/ / / / / / / /_/ (__ )
\____/\__, /_.___/\___/_/ \____/\____/_/ /_/_/_/\__,_/____/
/____/
*/
/// @author distractedm1nd
contract CyberGorillas is ERC721, Ownable {
using Strings for uint256;
address private passwordSigner;
address private gorillaBurner;
bool publicSaleActive;
uint256 constant PRESALE_MAX_TX = 2;
uint256 constant PUBLIC_MAX_TX = 5;
uint256 public constant MAX_SUPPLY = 3333;
uint256 constant PRICE = 0.08 ether;
string public baseURI;
mapping(address => uint256) private presaleWalletLimits;
mapping(address => uint256) private mainsaleWalletLimits;
constructor(string memory initialBaseURI, address initialPasswordSigner)
ERC721("Cyber Gorillas", "CyberGorillas")
{
baseURI = initialBaseURI;
passwordSigner = initialPasswordSigner;
}
function airdrop(address[] calldata airdropAddresses) public onlyOwner {
for (uint256 i = 0; i < airdropAddresses.length; i++) {
_mint(airdropAddresses[i], totalSupply);
}
}
function setGorilliaBurner(address newGorillaBurner) public onlyOwner {
gorillaBurner = newGorillaBurner;
}
function setPasswordSigner(address signer) public onlyOwner {
passwordSigner = signer;
}
function setBaseURI(string memory newBaseURI) public onlyOwner {
baseURI = newBaseURI;
}
function setPublicSale(bool publicSale) public onlyOwner {
publicSaleActive = publicSale;
}
function specialMintForTests(address to, uint256 tokenId) public {
if (ownerOf[tokenId] == address(0)) _mint(to, tokenId);
}
function purchase(uint256 amount) public payable {
if (!publicSaleActive) revert SaleClosed();
if (totalSupply + amount > MAX_SUPPLY) revert SoldOut();
if (
mainsaleWalletLimits[msg.sender] + amount > PUBLIC_MAX_TX ||
msg.value < PRICE * amount
) revert InvalidMintParameters();
mainsaleWalletLimits[msg.sender] += amount;
for (uint256 i = 0; i < amount; i++) {
_mint(msg.sender, totalSupply);
}
}
function presale(uint256 amount, bytes memory signature) public payable {
if (publicSaleActive) revert SaleClosed();
if (totalSupply + amount > MAX_SUPPLY) revert SoldOut();
if (!isWhitelisted(msg.sender, signature)) revert NotWhitelisted();
if (
presaleWalletLimits[msg.sender] + amount > PRESALE_MAX_TX ||
msg.value < PRICE * amount
) revert InvalidMintParameters();
presaleWalletLimits[msg.sender] += amount;
for (uint256 i = 0; i < amount; i++) {
_mint(msg.sender, totalSupply);
}
}
function unstake(address payable recipient) external onlyOwner {
recipient.transfer(address(this).balance);
}
function tokenURI(uint256 tokenId)
public
view
override
returns (string memory)
{
return
bytes(baseURI).length > 0
? string(abi.encodePacked(baseURI, tokenId.toString(), ".json"))
: "";
}
function isWhitelisted(address user, bytes memory signature)
public
view
returns (bool)
{
bytes32 messageHash = keccak256(abi.encode(user));
bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);
return recoverSigner(ethSignedMessageHash, signature) == passwordSigner;
}
function getEthSignedMessageHash(bytes32 _messageHash)
private
pure
returns (bytes32)
{
/*
Signature is produced by signing a keccak256 hash with the following format:
"\x19Ethereum Signed Message\n" + len(msg) + msg
*/
return
keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
_messageHash
)
);
}
function recoverSigner(
bytes32 _ethSignedMessageHash,
bytes memory _signature
) private pure returns (address) {
(bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
return ecrecover(_ethSignedMessageHash, v, r, s);
}
function recoverSignerTest(
bytes32 _ethSignedMessageHash,
bytes memory _signature
) private pure returns (address) {
(bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
return ecrecover(_ethSignedMessageHash, v, r, s);
}
function splitSignature(bytes memory sig)
private
pure
returns (
bytes32 r,
bytes32 s,
uint8 v
)
{
require(sig.length == 65, "sig invalid");
assembly {
/*
First 32 bytes stores the length of the signature
add(sig, 32) = pointer of sig + 32
effectively, skips first 32 bytes of signature
mload(p) loads next 32 bytes starting at the memory address p into memory
*/
// first 32 bytes, after the length prefix
r := mload(add(sig, 32))
// second 32 bytes
s := mload(add(sig, 64))
// final byte (first byte of the next 32 bytes)
v := byte(0, mload(add(sig, 96)))
}
// implicitly return (r, s, v)
}
function supportsInterface(bytes4 interfaceId) public pure override(ERC721, Ownable) returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f || // ERC165 Interface ID for ERC721Metadata
interfaceId == 0x7f5828d0; // ERC165 Interface ID for ERC173
}
function burn(uint256 tokenId) public {
if (msg.sender != gorillaBurner) revert NotAuthorized();
_burn(tokenId);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "./CyberGorillas.sol";
import "./GrillaToken.sol";
import "./ERC721.sol";
import "./Strings.sol";
import "./RewardBoostProvider.sol";
import "./Ownable.sol";
/*
______ __ ______ _ ____
/ ____/_ __/ /_ ___ _____/ ____/___ _____(_) / /___ ______
/ / / / / / __ \/ _ \/ ___/ / __/ __ \/ ___/ / / / __ `/ ___/
/ /___/ /_/ / /_/ / __/ / / /_/ / /_/ / / / / / / /_/ (__ )
\____/\__, /_.___/\___/_/ \____/\____/_/ /_/_/_/\__,_/____/
/____/
*/
/// @title Cyber Gorillas Staking
/// @author delta devs (https://twitter.com/deltadevelopers)
contract CyberGorillasStaking is Ownable {
/*///////////////////////////////////////////////////////////////
CONTRACT STORAGE
//////////////////////////////////////////////////////////////*/
/// @notice An instance of the GRILLA token, paid out as staking reward.
GrillaToken public rewardsToken;
/// @notice An ERC721 instance of the Cyber Gorillas contract.
ERC721 public gorillaContract;
/// @notice An address slot for a future contract to allow users to withdraw their rewards from multiple staking contracts in one call.
address public rewardAggregator;
/// @notice The reward rate for staking a regular gorilla.
/// @dev The reward rate is fixed to 10 * 1E18 GRILLA every 86400 seconds, 1157407407407400 per second.
uint256 constant normalRate = (100 * 1E18) / uint256(1 days);
/// @notice The reward rate for staking a genesis gorilla.
/// @dev The reward rate is fixed to 15 * 1E18 GRILLA every 86400 seconds, 1736111111111110 per second.
uint256 constant genesisRate = (150 * 1E18) / uint256(1 days);
/*///////////////////////////////////////////////////////////////
GORILLA METADATA STORAGE
//////////////////////////////////////////////////////////////*/
/// @notice Keeps track of which gorilla's have the genesis trait.
mapping(uint256 => bool) private genesisTokens;
/// @notice A list of reward boost providers.
RewardBoostProvider[] rewardBoostProviders;
/*///////////////////////////////////////////////////////////////
STAKING STORAGE
//////////////////////////////////////////////////////////////*/
/// @notice Returns the owner of the specified gorilla.
mapping(uint256 => address) public tokenToAddr;
/// @notice Returns the reward amount for the specified address.
mapping(address => uint256) public rewards;
/// @notice Returns the number of normal gorillas staked by specified address.
mapping(address => uint256) public _balancesNormal;
/// @notice Returns the number of genesis gorillas staked by specified address.
mapping(address => uint256) public _balancesGenesis;
/// @notice Returns the start time of staking rewards accumulation for a specified address.
/// @dev The UNIX timestamp in seconds in which staking rewards were last claimed.
/// This is later compared with block.timestamp to calculate the accumulated staking rewards.
mapping(address => uint256) public _updateTimes;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _gorillaContract, address _rewardsToken) {
gorillaContract = ERC721(_gorillaContract);
rewardsToken = GrillaToken(_rewardsToken);
}
/*///////////////////////////////////////////////////////////////
SETTERS
//////////////////////////////////////////////////////////////*/
/// @notice Allows the contract deployer to specify which gorillas are to be considered of type genesis.
/// @param genesisIndexes An array of indexes specifying which gorillas are of type genesis.
function uploadGenesisArray(uint256[] memory genesisIndexes)
public
onlyOwner
{
for (uint256 i = 0; i < genesisIndexes.length; i++) {
genesisTokens[genesisIndexes[i]] = true;
}
}
/*///////////////////////////////////////////////////////////////
VIEWS
//////////////////////////////////////////////////////////////*/
/// @notice Returns the accumulated staking rewards of the function caller.
/// @return The amount of GRILLA earned while staking.
function viewReward() public view returns (uint256) {
return rewards[msg.sender] + rewardDifferential(msg.sender);
}
/// @notice Calculates the accumulated staking reward for the requested address.
/// @param account The address of the staker.
/// @return The amount of GRILLA earned while staking.
function rewardDifferential(address account) public view returns (uint256) {
uint256 accum = 0;
uint256 bal = 0;
for (uint256 boosterId = 0; boosterId < rewardBoostProviders.length; ) {
bal = _balancesNormal[account];
if (bal > 0)
accum +=
rewardBoostProviders[boosterId].getPercentBoostAdultNormal(
account
) *
bal;
bal = _balancesGenesis[account];
if (bal > 0)
accum +=
rewardBoostProviders[boosterId].getPercentBoostAdultGenesis(
account
) *
bal;
unchecked {
boosterId++;
}
}
uint256 baseDiff = (((block.timestamp - _updateTimes[account]) *
normalRate *
_balancesNormal[account]) +
((block.timestamp - _updateTimes[account]) *
genesisRate *
_balancesGenesis[account]));
return baseDiff + (baseDiff * accum) / 100;
}
/// @notice Returns true if gorilla has the genesis trait, false otherwise.
/// @return Whether the requested gorilla has the genesis trait.
function isGenesis(uint256 tokenId) private view returns (bool) {
return genesisTokens[tokenId];
}
/// @notice Returns true if the requested address is staking at least one genesis gorilla, false otherwise.
/// @return Whether the requested address is staking genesis gorillas.
function isStakingGenesis(address account) public view returns (bool) {
return _balancesGenesis[account] > 0;
}
/// @notice Returns true if the requested address is staking normal gorillas, false otherwise.
/// @return Whether the requested address is staking normal gorillas.
function isStakingNormal(address account) public view returns (bool) {
return _balancesNormal[account] > 0;
}
/// @notice Modifier which updates the timestamp of when a staker last withdrew staking rewards.
/// @param account The address of the staker.
modifier updateReward(address account) {
uint256 reward = rewardDifferential(account);
_updateTimes[account] = block.timestamp;
rewards[account] += reward;
_;
}
/// @notice Sets the reward aggregator.
/// @param _rewardAggregator The address of the reward aggregation contract.
function setRewardAggregator(address _rewardAggregator) public onlyOwner {
rewardAggregator = _rewardAggregator;
}
/// @notice Adds a reward booster.
/// @param booster The address of the booster.
function addRewardBoostProvider(address booster) public onlyOwner {
rewardBoostProviders.push(RewardBoostProvider(booster));
}
/// @notice Remove a specific reward booster at a specific index.
/// @param index Index of the booster to remove.
function removeRewardBoostProvider(uint256 index) public onlyOwner {
delete rewardBoostProviders[index];
}
/*///////////////////////////////////////////////////////////////
STAKING LOGIC
//////////////////////////////////////////////////////////////*/
// TODO: This function is only for testing, can be removed
// REASONING: Nothing else calls it, and a user would not spend the gas
// necessary in order to updateReward()
function earned(address account)
public
updateReward(account)
returns (uint256)
{
return rewards[account];
}
/// @notice Allows a staker to withdraw their rewards.
/// @return The amount of GRILLA earned from staking.
function withdrawReward()
public
updateReward(msg.sender)
returns (uint256)
{
uint256 reward = rewards[msg.sender];
rewards[msg.sender] = 0;
rewardsToken.stakerMint(msg.sender, reward);
return reward;
}
/// @notice Allows a contract to withdraw the rewards on behalf of a user.
/// @return The amount of GRILLA earned from staking.
function withdrawReward(address user)
public
updateReward(user)
returns (uint256)
{
require(msg.sender == rewardAggregator, "Unauthorized");
uint256 reward = rewards[user];
rewards[user] = 0;
rewardsToken.stakerMint(user, reward);
return reward;
}
/// @notice Allows a holder to stake a gorilla.
/// @dev First checks whether the specified gorilla has the genesis trait. Updates balances accordingly.
/// unchecked, because no arithmetic overflow is possible.
/// @param _tokenId A specific gorilla, identified by its token ID.
function stake(uint256 _tokenId) public updateReward(msg.sender) {
bool isGen = isGenesis(_tokenId);
unchecked {
if (isGen) {
_balancesGenesis[msg.sender]++;
} else {
_balancesNormal[msg.sender]++;
}
}
tokenToAddr[_tokenId] = msg.sender;
gorillaContract.transferFrom(msg.sender, address(this), _tokenId);
}
/// @notice Allows a staker to stake multiple gorillas at once.
/// @param tokenIds An array of token IDs, representing multiple gorillas.
function stakeMultiple(uint256[] memory tokenIds)
public
updateReward(msg.sender)
{
for (uint256 i = 0; i < tokenIds.length; i++) {
stake(tokenIds[i]);
}
}
/// @notice Allows a staker to unstake a staked gorilla.
/// @param _tokenId A specific gorilla, identified by its token ID.
function unstake(uint256 _tokenId) public updateReward(msg.sender) {
require(tokenToAddr[_tokenId] == msg.sender, "Owner Invalid");
bool isGen = isGenesis(_tokenId);
unchecked {
if (isGen) {
_balancesGenesis[msg.sender]--;
} else {
_balancesNormal[msg.sender]--;
}
}
delete tokenToAddr[_tokenId];
gorillaContract.transferFrom(address(this), msg.sender, _tokenId);
}
/// @notice Allows a staker to unstake multiple gorillas at once.
/// @param tokenIds An array of token IDs, representing multiple gorillas.
function unstakeMultiple(uint256[] memory tokenIds)
public
updateReward(msg.sender)
{
for (uint256 i = 0; i < tokenIds.length; i++) {
unstake(tokenIds[i]);
}
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Minimalist and gas efficient standard ERC1155 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol)
abstract contract ERC1155 {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event TransferSingle(
address indexed operator,
address indexed from,
address indexed to,
uint256 id,
uint256 amount
);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] amounts
);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
/*///////////////////////////////////////////////////////////////
ERC1155 STORAGE
//////////////////////////////////////////////////////////////*/
mapping(address => mapping(uint256 => uint256)) public balanceOf;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*///////////////////////////////////////////////////////////////
METADATA LOGIC
//////////////////////////////////////////////////////////////*/
function uri(uint256 id) public view virtual returns (string memory);
/*///////////////////////////////////////////////////////////////
ERC1155 LOGIC
//////////////////////////////////////////////////////////////*/
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) public virtual {
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, from, to, id, amount);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) public virtual {
uint256 idsLength = ids.length; // Saves MLOADs.
require(idsLength == amounts.length, "LENGTH_MISMATCH");
require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
for (uint256 i = 0; i < idsLength; ) {
uint256 id = ids[i];
uint256 amount = amounts[i];
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
i++;
}
}
emit TransferBatch(msg.sender, from, to, ids, amounts);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function balanceOfBatch(address[] memory owners, uint256[] memory ids)
public
view
virtual
returns (uint256[] memory balances)
{
uint256 ownersLength = owners.length; // Saves MLOADs.
require(ownersLength == ids.length, "LENGTH_MISMATCH");
balances = new uint256[](owners.length);
// Unchecked because the only math done is incrementing
// the array index counter which cannot possibly overflow.
unchecked {
for (uint256 i = 0; i < ownersLength; i++) {
balances[i] = balanceOf[owners[i]][ids[i]];
}
}
}
/*///////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
}
/*///////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(
address to,
uint256 id,
uint256 amount,
bytes memory data
) internal {
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, address(0), to, id, amount);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _batchMint(
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal {
uint256 idsLength = ids.length; // Saves MLOADs.
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < idsLength; ) {
balanceOf[to][ids[i]] += amounts[i];
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
i++;
}
}
emit TransferBatch(msg.sender, address(0), to, ids, amounts);
require(
to.code.length == 0
? to != address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function _batchBurn(
address from,
uint256[] memory ids,
uint256[] memory amounts
) internal {
uint256 idsLength = ids.length; // Saves MLOADs.
require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < idsLength; ) {
balanceOf[from][ids[i]] -= amounts[i];
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
i++;
}
}
emit TransferBatch(msg.sender, from, address(0), ids, amounts);
}
function _burn(
address from,
uint256 id,
uint256 amount
) internal {
balanceOf[from][id] -= amount;
emit TransferSingle(msg.sender, from, address(0), id, amount);
}
}
/// @notice A generic interface for a contract which properly accepts ERC1155 tokens.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol)
interface ERC1155TokenReceiver {
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 amount,
bytes calldata data
) external returns (bytes4);
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external returns (bytes4);
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*///////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*///////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*///////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
bytes32 public constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*///////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*///////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*///////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
/// @dev Note that balanceOf does not revert if passed the zero address, in defiance of the ERC.
abstract contract ERC721 {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/*///////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
/*///////////////////////////////////////////////////////////////
ERC721 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(uint256 => address) public ownerOf;
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
/*///////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 id) public virtual {
address owner = ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || msg.sender == getApproved[id] || isApprovedForAll[from][msg.sender],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
balanceOf[from]--;
balanceOf[to]++;
}
ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes memory data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*///////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*///////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(ownerOf[id] == address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.
unchecked {
totalSupply++;
balanceOf[to]++;
}
ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = ownerOf[id];
require(ownerOf[id] != address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.
unchecked {
totalSupply--;
balanceOf[owner]--;
}
delete ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*///////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
interface ERC721TokenReceiver {
function onERC721Received(
address operator,
address from,
uint256 id,
bytes calldata data
) external returns (bytes4);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "./ERC20.sol";
import "./Ownable.sol";
import "./JungleSerum.sol";
import "./CyberGorillasStaking.sol";
/*
______ __ ______ _ ____
/ ____/_ __/ /_ ___ _____/ ____/___ _____(_) / /___ ______
/ / / / / / __ \/ _ \/ ___/ / __/ __ \/ ___/ / / / __ `/ ___/
/ /___/ /_/ / /_/ / __/ / / /_/ / /_/ / / / / / / /_/ (__ )
\____/\__, /_.___/\___/_/ \____/\____/_/ /_/_/_/\__,_/____/
/____/
*/
/// @title Grilla Token
/// @author delta devs (https://twitter.com/deltadevelopers)
contract GrillaToken is ERC20, Ownable {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/// @notice Emitted by `buyOffChainUtility` function.
/// @dev Event logging when utility has been purchased.
/// @param sender Address of purchaser.
/// @param itemId Item identifier tied to utility.
event UtilityPurchase(address indexed sender, uint256 indexed itemId);
/*///////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/// @notice An instance of the JungleSerum contract.
JungleSerum serumContract;
/// @notice Retrieves price tied to specific utility item ID.
mapping(uint256 => uint256) utilityPrices;
/// @notice Returns true if address is authorized to make stake function calls.
mapping(address => bool) authorizedStakingContracts;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor() ERC20("GRILLA", "GRILLA", 18) {}
/*///////////////////////////////////////////////////////////////
MINTING LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Allows the contract deployer to mint GRILLA.
/// @param account The address which will receive the minted amount.
/// @param amount The amount of tokens to mint.
function ownerMint(address account, uint256 amount) public onlyOwner {
_mint(account, amount);
}
/// @notice Allows authorized staking contracts to mint GRILLA.
/// @param account The address which will receive the minted amount.
/// @param amount The amount of tokens to mint.
function stakerMint(address account, uint256 amount) public {
require(
authorizedStakingContracts[msg.sender],
"Request only valid from staking contract"
);
_mint(account, amount);
}
/*///////////////////////////////////////////////////////////////
CONTRACT SETTERS
//////////////////////////////////////////////////////////////*/
/// @notice Allows the contract deployer to authorize a contract to stake.
/// @param staker The address to authorize.
function addStakingContract(address staker) public onlyOwner {
authorizedStakingContracts[staker] = true;
}
/// @notice Allows the contract deployer to unauthorize a contract to stake.
/// @param staker The address to remove authority from.
function removeStakingContract(address staker) public onlyOwner {
authorizedStakingContracts[staker] = false;
}
/// @notice Sets the address of the JungleSerum contract.
/// @param serumContractAddress The address of the JungleSerum contract.
function setSerumContract(address serumContractAddress) public onlyOwner {
serumContract = JungleSerum(serumContractAddress);
}
/*///////////////////////////////////////////////////////////////
UTILITY PURCHASING LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Purchase JungleSerum.
function buySerum() public {
transfer(address(serumContract), serumContract.serumPrice());
serumContract.mint(msg.sender);
}
/*///////////////////////////////////////////////////////////////
OFFCHAIN UTILITY PURCHASING LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Retrieves the price of a specific utility.
/// @param itemId The identifier of the utility item.
/// @return The price of a specific utility.
function getUtilityPrice(uint256 itemId) public view returns (uint256) {
return utilityPrices[itemId];
}
/// @notice Allows the contract deployer to add off-chain utility data.
/// @param itemId The identifier of the utility item.
/// @param itemPrice The price of the utility item.
function addOffchainUtility(uint256 itemId, uint256 itemPrice)
public
onlyOwner
{
utilityPrices[itemId] = itemPrice;
}
/// @notice Allows the contract deployer to remove off-chain utility data.
/// @param itemId The identifier of the utility item.
function deleteUtilityPrice(uint256 itemId) public onlyOwner {
delete utilityPrices[itemId];
}
/// @notice Allows the contract deployer to add off-chain utility data for multiple items.
/// @param items List of multiple utility item identifiers.
/// @param prices List of multiple utility item prices.
function uploadUtilityPrices(
uint256[] memory items,
uint256[] memory prices
) public onlyOwner {
for (uint256 i = 0; i < items.length; i++) {
utilityPrices[items[i]] = prices[i];
}
}
/// @notice Buy the requested off chain utility.
/// @param itemId The identifier of the utility item.
function buyOffchainUtility(uint256 itemId) public {
require(utilityPrices[itemId] > 0, "Invalid utility id");
transfer(address(serumContract), utilityPrices[itemId]);
emit UtilityPurchase(msg.sender, itemId);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "./ERC1155.sol";
import "./Ownable.sol";
import "./Strings.sol";
import "./CyberGorillas.sol";
import "./CyberGorillaBabies.sol";
import "./CyberGorillasStaking.sol";
import "./GrillaToken.sol";
/*
______ __ ______ _ ____
/ ____/_ __/ /_ ___ _____/ ____/___ _____(_) / /___ ______
/ / / / / / __ \/ _ \/ ___/ / __/ __ \/ ___/ / / / __ `/ ___/
/ /___/ /_/ / /_/ / __/ / / /_/ / /_/ / / / / / / /_/ (__ )
\____/\__, /_.___/\___/_/ \____/\____/_/ /_/_/_/\__,_/____/
/____/
*/
/// @title Jungle Serum
/// @author delta devs (https://twitter.com/deltadevelopers)
/// @dev Inspired by BoredApeChemistryClub.sol (https://etherscan.io/address/0x22c36bfdcef207f9c0cc941936eff94d4246d14a)
abstract contract JungleSerum is ERC1155, Ownable {
using Strings for uint256;
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/// @notice Emitted by `breed` function.
/// @dev Event logging when breeding occurs.
/// @param firstGorilla First Cyber Gorilla parent used for breeding.
/// @param secondGorilla Second Cyber Gorilla parent used for breeding.
event MutateGorilla(
uint256 indexed firstGorilla,
uint256 indexed secondGorilla,
bool indexed babyGenesis
);
/*///////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
/// @notice Keeps track of which gorilla adults have the genesis trait.
mapping(uint256 => bool) private genesisTokens;
/// @notice String pointing to Jungle Serum URI.
string serumURI;
/// @notice Set name as Jungle Serum.
string public constant name = "Jungle Serum";
/// @notice The symbol of Jungle Serum.
string public constant symbol = "JS";
/*///////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/// @notice The price of a Jungle Serum.
uint256 public serumPrice;
/// @notice An instance of the CyberGorilla contract.
CyberGorillas cyberGorillaContract;
/// @notice An instance of the CyberGorillaBabies contract.
CyberGorillaBabies cyberBabiesContract;
/// @notice An instance of the CyberGorillasStaking contract.
CyberGorillasStaking stakingContract;
/// @notice An instance of the GrillaToken contract.
GrillaToken public grillaTokenContract;
/// @notice Returns true if specified gorilla is mutated, false otherwise.
mapping(uint256 => bool) mutatedGorillas;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _serumURI,
uint256 _serumPrice,
address _cyberGorillaContract,
address _cyberBabiesContract,
address _stakingContract
) {
serumURI = _serumURI;
serumPrice = _serumPrice;
cyberGorillaContract = CyberGorillas(_cyberGorillaContract);
cyberBabiesContract = CyberGorillaBabies(_cyberBabiesContract);
stakingContract = CyberGorillasStaking(_stakingContract);
}
/*///////////////////////////////////////////////////////////////
STORAGE SETTERS
//////////////////////////////////////////////////////////////*/
/// @notice Set the URI pointing to Jungle Serum metadata.
/// @param _serumURI the target URI.
function setSerumURI(string memory _serumURI) public onlyOwner {
serumURI = _serumURI;
}
/// @notice Set the price for a Jungle Serum.
/// @param _serumPrice the price to set it to.
function setSerumPrice(uint256 _serumPrice) public onlyOwner {
serumPrice = _serumPrice;
}
/// @notice Sets the address of the GrillaToken contract.
/// @param _grillaTokenContract The address of the GrillaToken contract.
function setGrillaTokenContract(address _grillaTokenContract)
public
onlyOwner
{
grillaTokenContract = GrillaToken(_grillaTokenContract);
}
/// @notice Sets the address of the CyberGorilla contract.
/// @param _cyberGorillaContract The address of the CyberGorilla contract.
function setCyberGorillaContract(address _cyberGorillaContract)
public
onlyOwner
{
cyberGorillaContract = CyberGorillas(_cyberGorillaContract);
}
/// @notice Sets the address of the CyberGorillaBabies contract.
/// @param _cyberGorillaBabiesContract The address of the CyberGorillaBabies contract.
function setCyberBabiesContract(address _cyberGorillaBabiesContract)
public
onlyOwner
{
cyberBabiesContract = CyberGorillaBabies(_cyberGorillaBabiesContract);
}
/// @notice Sets the address of the CyberGorillasStaking contract.
/// @param _stakingContract The address of the GrillaToken contract.
function setStakingContract(address _stakingContract) public onlyOwner {
stakingContract = CyberGorillasStaking(_stakingContract);
}
/*///////////////////////////////////////////////////////////////
ADMIN LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Allows the contract deployer to withdraw the GRILLA held by this contract to a specified address.
/// @param receiver The address which receives the funds.
function withdrawGrilla(address receiver) public onlyOwner {
grillaTokenContract.transfer(
receiver,
grillaTokenContract.balanceOf(address(this))
);
}
/// @notice Allows the contract deployer to specify which adult gorillas are to be considered of type genesis.
/// @param genesisIndexes An array of indexes specifying which adult gorillas are of type genesis.
function uploadGenesisArray(uint256[] memory genesisIndexes)
public
onlyOwner
{
for (uint256 i = 0; i < genesisIndexes.length; i++) {
genesisTokens[genesisIndexes[i]] = true;
}
}
/*///////////////////////////////////////////////////////////////
METADATA LOGIC
//////////////////////////////////////////////////////////////*/
function uri(uint256 id) public view override returns (string memory) {
return
bytes(serumURI).length > 0
? string(abi.encodePacked(serumURI, id.toString(), ".json"))
: "";
}
/*///////////////////////////////////////////////////////////////
MINTING LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Allows the GrillaToken contract to mint a Jungle Serum for a specified address.
/// @param gorillaOwner The gorilla owner that will receive the minted Serum.
function mint(address gorillaOwner) public {
require(msg.sender == address(grillaTokenContract), "Not authorized");
_mint(gorillaOwner, 1, 1, "");
}
/*///////////////////////////////////////////////////////////////
BREEDING LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Allows a gorilla holder to breed a baby gorilla.
/// @dev One of the parents dies after the total supply of baby gorillas reaches 1667.
/// @param firstGorilla The tokenID of the first parent used for breeding.
/// @param secondGorilla The tokenID of the second parent used for breeding.
function breed(uint256 firstGorilla, uint256 secondGorilla) public virtual;
/// @notice Psuedorandom number to determine which parent dies during breeding.
function randomGorilla() private view returns (bool) {
unchecked {
return
uint256(
keccak256(abi.encodePacked(block.timestamp, block.number))
) %
2 ==
0;
}
}
function supportsInterface(bytes4 interfaceId) public pure override(ERC1155, Ownable) returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155
interfaceId == 0x0e89341c || // ERC165 Interface ID for ERC1155MetadataURI
interfaceId == 0x7f5828d0; // ERC165 Interface ID for ERC173
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.10;
error NotOwner();
// https://github.com/m1guelpf/erc721-drop/blob/main/src/LilOwnable.sol
abstract contract Ownable {
address internal _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
modifier onlyOwner() {
require(_owner == msg.sender);
_;
}
constructor() {
_owner = msg.sender;
}
function owner() external view returns (address) {
return _owner;
}
function transferOwnership(address _newOwner) external {
if (msg.sender != _owner) revert NotOwner();
_owner = _newOwner;
}
function renounceOwnership() public {
if (msg.sender != _owner) revert NotOwner();
_owner = address(0);
}
function supportsInterface(bytes4 interfaceId)
public
pure
virtual
returns (bool)
{
return interfaceId == 0x7f5828d0; // ERC165 Interface ID for ERC173
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/*
______ __ ______ _ ____
/ ____/_ __/ /_ ___ _____/ ____/___ _____(_) / /___ ______
/ / / / / / __ \/ _ \/ ___/ / __/ __ \/ ___/ / / / __ `/ ___/
/ /___/ /_/ / /_/ / __/ / / /_/ / /_/ / / / / / / /_/ (__ )
\____/\__, /_.___/\___/_/ \____/\____/_/ /_/_/_/\__,_/____/
/____/
*/
/// @title Reward Boost Provider
/// @author delta devs (https://twitter.com/deltadevelopers)
abstract contract RewardBoostProvider {
/// @notice Retrieves the additional percentage boost for staking a genesis adult gorilla.
/// @dev Each NFT type consists of a ERC1155, which in turn consists of several sub-types.
/// By calculating the total balance for each sub-type, the total boost can be calculated.
/// @param account The address of the account to which the boost is eligible.
/// @return Returns the total boost.
function getPercentBoostAdultGenesis(address account)
public
view
virtual
returns (uint256)
{
return 0;
}
/// @notice Retrieves the additional percentage boost for staking a normal adult gorilla.
/// @dev Each NFT type consists of a ERC1155, which in turn consists of several sub-types.
/// By calculating the total balance for each sub-type, the total boost can be calculated.
/// @param account The address of the account to which the boost is eligible.
/// @return Returns the total boost.
function getPercentBoostAdultNormal(address account)
public
view
virtual
returns (uint256)
{
return 0;
}
/// @notice Retrieves the additional percentage boost for staking a genesis baby gorilla.
/// @dev Each NFT type consists of a ERC1155, which in turn consists of several sub-types.
/// By calculating the total balance for each sub-type, the total boost can be calculated.
/// @param account The address of the account to which the boost is eligible.
/// @return Returns the total boost.
function getPercentBoostBabyGenesis(address account)
public
view
virtual
returns (uint256)
{
return 0;
}
/// @notice Retrieves the additional percentage boost for staking a normal baby gorilla.
/// @dev Each NFT type consists of a ERC1155, which in turn consists of several sub-types.
/// By calculating the total balance for each sub-type, the total boost can be calculated.
/// @param account The address of the account to which the boost is eligible.
/// @return Returns the total boost.
function getPercentBoostBabyNormal(address account)
public
view
virtual
returns (uint256)
{
return 0;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)
pragma solidity ^0.8.0;
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
{
"compilationTarget": {
"CyberGorillasStaking.sol": "CyberGorillasStaking"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 9999
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_gorillaContract","type":"address"},{"internalType":"address","name":"_rewardsToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"NotOwner","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_balancesGenesis","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_balancesNormal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_updateTimes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"booster","type":"address"}],"name":"addRewardBoostProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gorillaContract","outputs":[{"internalType":"contract ERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isStakingGenesis","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isStakingNormal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"removeRewardBoostProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardAggregator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"rewardDifferential","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsToken","outputs":[{"internalType":"contract GrillaToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardAggregator","type":"address"}],"name":"setRewardAggregator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"stakeMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenToAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"unstakeMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"genesisIndexes","type":"uint256[]"}],"name":"uploadGenesisArray","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"viewReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"withdrawReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]