// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
//////////////////////////////////////////////////
// ____ __ ___ __ __ //
// / __/__ ___ ______\ \/ (_)__ / /__/ / //
// _\ \/ _ \/ _ \/ __/ -_) / / -_) / _ / //
// /___/ .__/\___/_/ \__//_/_/\__/_/\_,_/ //
// /_/ //
// by 0xInuarashi.eth //
//////////////////////////////////////////////////
// Open0x ECDSA
library ECDSA {
///// Signer Address Recovery /////
// In its pure form, address recovery requires the following parameters
// params: hash, v, r ,s
// First, we define some standard checks
function checkValidityOf_s(bytes32 s) public pure returns (bool) {
if (uint256(s) >
0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
revert("recoverAddressFrom_hash_v_r_s: Invalid s value");
}
return true;
}
function checkValidityOf_v(uint8 v) public pure returns (bool) {
if (v != 27 && v != 28) {
revert("recoverAddressFrom_hash_v_r_s: Invalid v value");
}
return true;
}
// Then, we first define the pure form of recovery.
function recoverAddressFrom_hash_v_r_s(bytes32 hash, uint8 v, bytes32 r,
bytes32 s) public pure returns (address) {
// First, we need to make sure that s and v are in correct ranges
require(checkValidityOf_s(s) && checkValidityOf_v(v));
// call recovery using solidity's built-in ecrecover method
address _signer = ecrecover(hash, v, r, s);
require(_signer != address(0),
"_signer == address(0)");
return _signer;
}
// There are also other ways to receive input without v, r, s values which
// you will need to parse the unsupported data to find v, r, s and then
// use those to call ecrecover.
// For these, there are 2 other methods:
// 1. params: hash, r, vs
// 2. params: hash, signature
// These then return the v, r, s values required to use recoverAddressFrom_hash_v_r_s
// So, we will parse the first method to get v, r, s
function get_v_r_s_from_r_vs(bytes32 r, bytes32 vs) public pure
returns (uint8, bytes32, bytes32) {
bytes32 s = vs &
bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return (v, r, s);
}
function get_v_r_s_from_signature(bytes memory signature) public pure
returns (uint8, bytes32, bytes32) {
// signature.length can be 64 and 65. this depends on the method
// the standard is 65 bytes1, eip-2098 is 64 bytes1.
// so, we need to account for these differences
// in the case that it is a standard 65 bytes1 signature
if (signature.length == 65) {
uint8 v;
bytes32 r;
bytes32 s;
// assembly magic
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// return the v, r, s
return (v, r, s);
}
// in the case that it is eip-2098 64 bytes1 signature
else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
// assembly magic
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return get_v_r_s_from_r_vs(r, vs);
}
else {
revert("Invalid signature length");
}
}
// ///// Embedded toString /////
// // We need this in one of the methods of returning a signed message below.
// function _toString(uint256 value_) internal pure returns (string memory) {
// if (value_ == 0) { return "0"; }
// uint256 _iterate = value_; uint256 _digits;
// while (_iterate != 0) { _digits++; _iterate /= 10; } // get digits in value_
// bytes memory _buffer = new bytes(_digits);
// while (value_ != 0) { _digits--; _buffer[_digits] = bytes1(uint8(
// 48 + uint256(value_ % 10 ))); value_ /= 10; } // create bytes of value_
// return string(_buffer); // return string converted bytes of value_
// }
// ///// Generation of Hashes /////
// // We need these methods because these methods are used to compare
// // hash generated off-chain to hash generated on-chain to cross-check the
// // validity of the signatures
// // 1. A bytes32 hash to generate a bytes32 hash embedded with prefix
// // 2. A bytes memory s to generate a bytes32 hash embedded with prefix
// // 3. A bytes32 domain seperator and bytes32 structhash to generate
// // a bytes32 hash embedded with prefix
// // See: EIP-191
// function toEthSignedMessageHashBytes32(bytes32 hash) public pure
// returns (bytes32) {
// return keccak256(abi.encodePacked(
// // Magic prefix determined by the devs
// "\x19Ethereum Signed Message:\n32",
// hash
// ));
// }
// // See: EIP-191
// function toEthSignedMessageHashBytes(bytes memory s) public pure
// returns (bytes32) {
// return keccak256(abi.encodePacked(
// // Another magic prefix determined by the devs
// "\x19Ethereum Signed Message:\n",
// // The bytes length of s
// _toString(s.length),
// // s itself
// s
// ));
// }
// // See: EIP-712
// function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) public
// pure returns (bytes32) {
// return keccak256(abi.encodePacked(
// // Yet another magic prefix determined by the devs
// "\x19\x01",
// // The domain seperator (EIP-712)
// domainSeparator,
// // struct hash
// structHash
// ));
// }
}
abstract contract Ownable {
address public owner;
constructor() { owner = msg.sender; }
modifier onlyOwner { require(owner == msg.sender, "Not Owner!"); _; }
function transferOwnership(address new_) external onlyOwner { owner = new_; }
}
interface iSpore {
function transfer(address to_, uint256 amount_) external;
function mintAsController(address to_, uint256 amount_) external;
}
interface iNFF {
function totalSupply() external view returns (uint256);
function balanceOf(address address_) external view returns (uint256);
function ownerOf(uint256 tokenId_) external view returns (address);
function walletOfOwner(address address_) external view returns (uint256[] memory);
function transferFrom(address from_, address to_, uint256 tokenId_) external;
}
contract SporeYield is Ownable {
// Events
event Claim(address to_, uint256[] indexes_, uint256 totalClaimed);
// Interfaces
// NOTE: change this address (After spore token deployment)
iSpore public Spore = iSpore(0xD5E6d515b18004d0d4b2813078988cD67aDa6D7C);
function setSpore(address address_) external onlyOwner {
Spore = iSpore(address_);
}
iNFF public NFFGenerative = iNFF(0x90ee3Cf59FcDe2FE11838b9075Ea4681462362F1);
function setNFFGenerative(address address_) external onlyOwner {
NFFGenerative = iNFF(address_);
}
iNFF public NFFGenesis = iNFF(0x5f47079D0E45d95f5d5167A480B695883C4E47D9);
function setNFFGenesis(address address_) external onlyOwner {
NFFGenesis = iNFF(address_);
}
// // Constructor to set the contract addresses (optional)
// constructor(address spore, address generative, address genesis) Ownable() {
// Spore = iSpore(spore);
// NFFGenerative = iNFF(generative);
// NFFGenesis = iNFF(genesis);
// }
// Times
uint256 public yieldStartTime = 1653264000; // May 23 2022 14:00:00 GMT+0000
uint256 public yieldEndTime = 1732060800; // November 20 2024 14:00:00 GMT+0000
function setYieldEndTime(uint256 yieldEndTime_) external onlyOwner {
yieldEndTime = yieldEndTime_; }
// Yield Info
mapping(uint256 => uint256) public indexToYield;
// @dev this is a function to override yield setting. use it with caution.
function O_setIndexToYields(uint256[] calldata tokenIds_,
uint256[] calldata yields_) external onlyOwner {
require(tokenIds_.length == yields_.length,
"Array lengths mismatch!");
for (uint256 i = 0; i < tokenIds_.length; i++) {
indexToYield[tokenIds_[i]] = yields_[i];
}
}
// Yield Database
mapping(uint256 => uint256) public indexToClaimedTimestamp;
// // Timestamp Controller (Optional)
// mapping(address => bool) public addressToTimestampControllers;
// // Timestamp Controllers can be given externally to other addresses
// // in order to modify the timestamp of mappings.
// // Only use if you know what you are doing.
// modifier onlyTimestampControllers {
// require(addressToTimestampControllers[msg.sender],
// "Invalid timestamp controller!");
// _;
// }
// function controllerSetClaimTimestamps(uint256[] memory indexes_,
// uint256[] memory timestamps_) public onlyTimestampControllers {
// for (uint256 i = 0; i < indexes_.length; i++) {
// // The timestamp set must never be below the yieldStartTime
// require(yieldStartTime <= timestamps_[i],
// "Timestamp set below yieldStartTime!");
// indexToClaimedTimestamp[indexes_[i]] = timestamps_[i];
// }
// }
// ////
// Internal Calculators
function _getCurrentTimeOrEnded() public view returns (uint256) {
// Return block.timestamp if it's lower than yieldEndTime, otherwise
// return yieldEndTime instead.
return block.timestamp < yieldEndTime ?
block.timestamp : yieldEndTime;
}
function _getTimestampOfToken(uint256 index_) public view returns (uint256) {
// return indexToClaimedTimestamp[index_] == 0 ?
// Adjusted to yieldStartTime and hardcoded to save gas
return indexToClaimedTimestamp[index_] < 1653264000 ?
yieldStartTime : indexToClaimedTimestamp[index_];
}
// Yield Accountants
function getPendingTokens(uint256 index_) public view returns (uint256) {
// First, grab the timestamp of the token
uint256 _lastClaimedTimestamp = _getTimestampOfToken(index_);
// Then, we grab the current timestamp or ended
uint256 _timeCurrentOrEnded = _getCurrentTimeOrEnded();
// Lastly, we calculate the time-units in seconds of elapsed time
uint256 _timeElapsed = _timeCurrentOrEnded - _lastClaimedTimestamp;
// Now, return the calculation of yield
require(indexToYield[index_] != 0,
"Yield Lookup not Initialized!");
return (_timeElapsed * indexToYield[index_]) / 1 days;
}
function getInitializedTokenYields(uint256[] memory indexes_) public
view returns (uint256[] memory) {
uint256[] memory _tokenYields = new uint256[](indexes_.length);
for (uint256 i = 0; i < indexes_.length; i++) {
_tokenYields[i] = indexToYield[indexes_[i]];
}
// Then, return the final value
return _tokenYields;
}
function getPendingTokensMany(uint256[] memory indexes_) public
view returns (uint256) {
// First, create an empty MSTORE to store the pending tokens tracker
uint256 _pendingTokens;
// Now, run a loop through the entire indexes array to add it
for (uint256 i = 0; i < indexes_.length; i++) {
_pendingTokens += getPendingTokens(indexes_[i]);
}
// Then, return the final value
return _pendingTokens;
}
function getPendingTokensWithUninitialized(uint256 index_, uint256 yieldRate_) public view returns (uint256) {
// First, grab the timestamp of the token
uint256 _lastClaimedTimestamp = _getTimestampOfToken(index_);
// Then, we grab the current timestamp or ended
uint256 _timeCurrentOrEnded = _getCurrentTimeOrEnded();
// Lastly, we calculate the time-units in seconds of elapsed time
uint256 _timeElapsed = _timeCurrentOrEnded - _lastClaimedTimestamp;
// Now, return the calculation of yield
return (_timeElapsed * yieldRate_) / 1 days;
}
function getPendingTokensManyWithUninitialized(uint256[] memory indexes_, uint256[] calldata yieldRates_) public
view returns (uint256) {
require(indexes_.length == yieldRates_.length);
// First, create an empty MSTORE to store the pending tokens tracker
uint256 _pendingTokens;
// Now, run a loop through the entire indexes array to add it
for (uint256 i = 0; i < indexes_.length; i++) {
_pendingTokens += getPendingTokensWithUninitialized(indexes_[i], yieldRates_[i]);
}
// Then, return the final value
return _pendingTokens;
}
// Internal Timekeepers
function _updateTimestampOfTokens(uint256[] memory indexes_) internal {
// Get the timestamp using internal function
uint256 _timeCurrentOrEnded = _getCurrentTimeOrEnded();
// Loop through the entire indexes_ array and set the timestamps
for (uint256 i = 0; i < indexes_.length; i++) {
// Prevents duplicate setting of same token in the same block
require(indexToClaimedTimestamp[indexes_[i]] != _timeCurrentOrEnded,
"Unable to set timestamp duplication in the same block!");
indexToClaimedTimestamp[indexes_[i]] = _timeCurrentOrEnded;
}
}
function getIndexOfTokens(address[] memory contracts_,
uint256[] memory tokenIds_) public view returns (uint256[] memory) {
// Make sure the array lengths are equal
require(contracts_.length == tokenIds_.length,
"getIndexOfTokens(): Array lengths mismatch!");
// MSTORE to save GAS
uint256 _items = tokenIds_.length;
address _NFFGenerativeAddress = address(NFFGenerative);
address _NFFGenesisAddress = address(NFFGenesis);
// Make sure all items are of supported contracts
for (uint256 i = 0; i < _items; i++) {
require(contracts_[i] == _NFFGenerativeAddress ||
contracts_[i] == _NFFGenesisAddress,
"getIndexOfTokens(): Unsupported Contract!");
}
// MSTORE _indexes to return
uint256[] memory _indexes = new uint256[](_items);
// Generate the index array
for (uint256 i = 0; i < _items; i++) {
// Generate the offset. If generative, offeset is 10000, else, it's 0.
uint256 _offset = contracts_[i] == _NFFGenerativeAddress ? 0 : 10000;
_indexes[i] = tokenIds_[i] + _offset;
}
// Return the _indexes array
return _indexes;
}
function claim(uint256[] calldata tokenIds_)
public returns (uint256) {
// Make sure the sender owns all the tokens
for (uint256 i = 0; i < tokenIds_.length; i++) {
if(tokenIds_[i] < 10000)
{
require(msg.sender == NFFGenerative.ownerOf(tokenIds_[i]),
"You do not own this token!");
}
else
{
require(msg.sender == NFFGenesis.ownerOf(tokenIds_[i] - 10000),
"You do not own this token!");
}
}
// Calculate the total pending tokens to be claimed from index array
uint256 _pendingTokens = getPendingTokensMany(tokenIds_);
// Set the new timestamp of the tokens
// @dev: this step will fail if duplicate tokenIds_ are passed in
_updateTimestampOfTokens(tokenIds_);
// Mint the total tokens for the msg.sender
Spore.mintAsController(msg.sender, _pendingTokens);
// Emit claim of total tokens
emit Claim(msg.sender, tokenIds_, _pendingTokens);
// Return the claim amount
return _pendingTokens;
}
// NOTE: change this to the correct spore data signer!
address public sporeDataSigner = 0xe4535f8EE9b374BBc2c5A57B35f09A89fe43a657;
function setSporeDataSigner(address address_) public onlyOwner {
sporeDataSigner = address_;
}
// Data initializer controllers
mapping(address => bool) public addressToYieldDataInitializers;
function setYieldDataInitializers(address[] calldata initializers_,
bool bool_) external onlyOwner {
for (uint256 i = 0; i < initializers_.length; i++) {
addressToYieldDataInitializers[initializers_[i]] = bool_;
}
}
modifier onlyYieldDataInitializer {
require(addressToYieldDataInitializers[msg.sender],
"Invalid yield data initializer!");
_;
}
function controllerInitializeYieldDatas(uint256[] memory indexes_,
uint256[] memory yieldDatas_, bytes[] memory signatures_) public
onlyYieldDataInitializer {
_initializeYieldDatas(indexes_, yieldDatas_, signatures_);
}
////
// Core initialization logic
function _initializeYieldDatas(uint256[] memory indexes_,
uint256[] memory yieldDatas_, bytes[] memory signatures_) internal {
// In order to effectively use this function, the index and yielddata
// array must be passed in as uninitialized-FIRST with signature
// length only in the amount of uninitialized yield datas.
// The function itself supports input of both uninitialized and initialized
// tokens based on signature length.
// Make sure all the indexes to yieldDatas is valid through ECDSA
for (uint256 i = 0; i < signatures_.length; i++) {
// make sure the yieldDatas_[i] and signatures_[i] is correct
// thus we need to use get_v_r_s_from_signature function before
// address recovery
(uint8 v, bytes32 r, bytes32 s) =
ECDSA.get_v_r_s_from_signature(signatures_[i]);
// Create the token data hash to use with ecrecover
bytes32 _tokenDataHash = keccak256(abi.encodePacked(
indexes_[i],
yieldDatas_[i]
));
require(sporeDataSigner ==
ECDSA.recoverAddressFrom_hash_v_r_s(_tokenDataHash, v, r, s),
"Invalid signer");
// Initialize them if empty
if (indexToYield[indexes_[i]] == 0) {
// 10 Ether is the maximum per day as yield data is concerned.
// We added leeway for 20 Ether in case any future changes.
// We hardcoded this to save on gas.
require(20 ether >= yieldDatas_[i],
"Yield value not intended!");
indexToYield[indexes_[i]] = yieldDatas_[i];
}
}
}
function claimWithInitializable(
uint256[] calldata tokenIds_, uint256[] calldata yieldDatas_,
bytes[] calldata signatures_) external returns (uint256) {
require(tokenIds_.length >= yieldDatas_.length &&
tokenIds_.length >= signatures_.length,
"Array Lengths Mismatch!");
for (uint256 i = 0; i < tokenIds_.length; i++) {
if(tokenIds_[i] < 10000)
{
require(msg.sender == NFFGenerative.ownerOf(tokenIds_[i]),
"You do not own this token!");
}
else
{
require(msg.sender == NFFGenesis.ownerOf(tokenIds_[i] - 10000),
"You do not own this token!");
}
}
// Initialize the Yield Datas
_initializeYieldDatas(tokenIds_, yieldDatas_, signatures_);
// Calculate the total pending tokens to be claimed from index array
// Without _initializeYieldDatas, this function would revert.
uint256 _pendingTokens = getPendingTokensMany(tokenIds_);
// Set the new timestamp of the tokens
// If there are duplicate indexes in the array, this function will revert.
_updateTimestampOfTokens(tokenIds_);
// Mint the total tokens for the msg.sender
Spore.mintAsController(msg.sender, _pendingTokens);
// Emit claim of total tokens
emit Claim(msg.sender, tokenIds_, _pendingTokens);
// Return token amount
return _pendingTokens;
}
// Public View Functions for Helpers
function walletOfGenesis(address address_) public view
returns (uint256[] memory) {
return NFFGenesis.walletOfOwner(address_);
}
function walletOfGenerative(address address_) public view
returns (uint256[] memory) {
return NFFGenerative.walletOfOwner(address_);
}
function walletIndexOfOwner(address address_) public view
returns (uint256[] memory) {
// For this function, we want to return a unified index
uint256 _genesisBalance = NFFGenesis.balanceOf(address_);
uint256 _generativeBalance = NFFGenerative.balanceOf(address_);
uint256 _totalBalance = _genesisBalance + _generativeBalance;
// Create the indexes based on a combined balance to input datas
uint256[] memory _indexes = new uint256[] (_totalBalance);
// Call both wallet of owners
uint256[] memory _walletOfGenesis = walletOfGenesis(address_);
uint256[] memory _walletOfGenerative = walletOfGenerative(address_);
// Now start inserting into the index with both wallets with offsets
uint256 _currentIndex;
for (uint256 i = 0; i < _walletOfGenerative.length; i++) {
// Generative has an offset of 0
_indexes[_currentIndex++] = _walletOfGenerative[i];
}
for (uint256 i = 0; i < _walletOfGenesis.length; i++) {
// Genesis has an offset of 10000
_indexes[_currentIndex++] = _walletOfGenesis[i] + 10000;
}
return _indexes;
}
}
{
"compilationTarget": {
"SporeYield.sol": "SporeYield"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to_","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"indexes_","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"totalClaimed","type":"uint256"}],"name":"Claim","type":"event"},{"inputs":[],"name":"NFFGenerative","outputs":[{"internalType":"contract iNFF","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NFFGenesis","outputs":[{"internalType":"contract iNFF","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds_","type":"uint256[]"},{"internalType":"uint256[]","name":"yields_","type":"uint256[]"}],"name":"O_setIndexToYields","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"Spore","outputs":[{"internalType":"contract iSpore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_getCurrentTimeOrEnded","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index_","type":"uint256"}],"name":"_getTimestampOfToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addressToYieldDataInitializers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds_","type":"uint256[]"}],"name":"claim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds_","type":"uint256[]"},{"internalType":"uint256[]","name":"yieldDatas_","type":"uint256[]"},{"internalType":"bytes[]","name":"signatures_","type":"bytes[]"}],"name":"claimWithInitializable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"indexes_","type":"uint256[]"},{"internalType":"uint256[]","name":"yieldDatas_","type":"uint256[]"},{"internalType":"bytes[]","name":"signatures_","type":"bytes[]"}],"name":"controllerInitializeYieldDatas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"contracts_","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds_","type":"uint256[]"}],"name":"getIndexOfTokens","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"indexes_","type":"uint256[]"}],"name":"getInitializedTokenYields","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index_","type":"uint256"}],"name":"getPendingTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"indexes_","type":"uint256[]"}],"name":"getPendingTokensMany","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"indexes_","type":"uint256[]"},{"internalType":"uint256[]","name":"yieldRates_","type":"uint256[]"}],"name":"getPendingTokensManyWithUninitialized","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index_","type":"uint256"},{"internalType":"uint256","name":"yieldRate_","type":"uint256"}],"name":"getPendingTokensWithUninitialized","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"indexToClaimedTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"indexToYield","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"setNFFGenerative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"setNFFGenesis","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"setSpore","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"setSporeDataSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"initializers_","type":"address[]"},{"internalType":"bool","name":"bool_","type":"bool"}],"name":"setYieldDataInitializers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"yieldEndTime_","type":"uint256"}],"name":"setYieldEndTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sporeDataSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"new_","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"walletIndexOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"walletOfGenerative","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"walletOfGenesis","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"yieldEndTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"yieldStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]