/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: Library 2981
* @author: Max Flow O2 -> @MaxFlowO2 on bird app/GitHub
* @notice: Library for EIP 2981
* @custom:error-code R:1 Permille out of bounds
* @custom:change-log Custom errors added above
*
* Include with 'using Lib2981 for Lib2981.Royalties;' -- unique per collection
* Include with 'using Lib2981 for Lib2981.MappedRoyalties;' -- unique per token
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
library Lib2981c {
struct Royalties {
address receiver;
uint16 permille;
}
event RoyaltiesSet(uint256 token, address recipient, uint16 value);
event RoyaltiesSet(address recipient, uint16 value);
error MaxSplaining(string reason);
function setRoyalties(
Royalties storage royalties
, address receiver
, uint16 permille
) internal {
if (permille >= 1000 || permille == 0) {
revert MaxSplaining({
reason: "R:1"
});
}
royalties.receiver = receiver;
royalties.permille = permille;
emit RoyaltiesSet(
royalties.receiver
, royalties.permille
);
}
function revokeRoyalties(
Royalties storage royalties
) internal {
delete royalties.receiver;
delete royalties.permille;
emit RoyaltiesSet(
royalties.receiver
, royalties.permille
);
}
function royaltyInfo(
Royalties storage royalties
, uint256 tokenId
, uint256 salePrice
) internal
view
returns (
address receiver
, uint256 royaltyAmount
) {
receiver = royalties.receiver;
royaltyAmount = salePrice * royalties.permille / 1000;
}
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: Library 721
* @author: Max Flow O2 -> @MaxFlowO2 on bird app/GitHub
* @notice: Library for EIP 721
* @custom:error-code L:1 "non-existent tokenId"
* @custom:error-code L:2 "approval to current owner"
* @custom:error-code L:3 "approve caller is not token owner nor approved for all"
* @custom:error-code L:4 "approve to caller"
* @custom:error-code L:5 "caller is not token owner nor approved"
* @custom:error-code L:6 "transfer from incorrect owner"
* @custom:error-code L:7 "transfer to the zero address"
* @custom:error-code L:8 "mint to the zero address"
* @custom:error-code L:9 "token already minted"
* @custom:change-log Custom errors added above
*
* Include with 'using Lib721 for Lib721.Token;'
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "./Strings.sol";
import "./CountersV2.sol";
library Lib721 {
using Strings for uint256;
using CountersV2 for CountersV2.Counter;
struct Token {
mapping(uint256 => address) owners;
mapping(address => uint256) balances;
mapping(uint256 => address) tokenApprovals;
mapping(address => mapping(address => bool)) operatorApprovals;
string name;
string symbol;
string baseURI;
CountersV2.Counter supply;
}
event NameSet(string name);
event SymbolSet(string symbol);
event NewBaseURI(string baseURI);
error MaxSplaining(string reason);
function getBalanceOf(
Token storage token
, address owner
) internal
view
returns (uint256) {
return token.balances[owner];
}
function getOwnerOf(
Token storage token
, uint256 tokenId
) internal
view
returns (address) {
return token.owners[tokenId];
}
function setName(
Token storage token
, string memory newName
) internal {
token.name = newName;
emit NameSet(newName);
}
function getName(
Token storage token
) internal
view
returns (string memory) {
return token.name;
}
function setSymbol(
Token storage token
, string memory newSymbol
) internal {
token.symbol = newSymbol;
emit SymbolSet(newSymbol);
}
function getSymbol(
Token storage token
) internal
view
returns (string memory) {
return token.symbol;
}
function getSupply(
Token storage token
) internal
view
returns (uint256) {
return token.supply.current();
}
function setBaseURI(
Token storage token
, string memory newURI
) internal {
token.baseURI = newURI;
emit NewBaseURI(newURI);
}
function getTokenURI(
Token storage token
, uint256 tokenId
) internal
view
returns (string memory) {
if (getOwnerOf(token, tokenId) == address(0)) {
revert MaxSplaining({
reason: "L:1"
});
}
return bytes(token.baseURI).length > 0 ? string(abi.encodePacked(token.baseURI, tokenId.toString())) : "";
}
function setApprove(
Token storage token
, address to
, address by
, uint256 tokenId
) internal {
address owner = getOwnerOf(token, tokenId);
if (to == owner) {
revert MaxSplaining({
reason: "L:2"
});
} else if (!isApprovedOrOwner(token, by, tokenId)) {
revert MaxSplaining({
reason: "L:3"
});
}
token.tokenApprovals[tokenId] = to;
}
function getApproved(
Token storage token
, uint256 tokenId
) internal
view
returns (address) {
if (getOwnerOf(token, tokenId) == address(0)) {
revert MaxSplaining({
reason: "L:1"
});
}
return token.tokenApprovals[tokenId];
}
function setApprovalForAll(
Token storage token
, address operator
, address from
, bool approved
) internal {
if (from == operator) {
revert MaxSplaining({
reason: "L:4"
});
}
token.operatorApprovals[from][operator] = approved;
}
function isApprovedForAll(
Token storage token
, address owner
, address operator
) internal
view
returns (bool) {
return token.operatorApprovals[owner][operator];
}
function isApprovedOrOwner(
Token storage token
, address spender
, uint256 tokenId
) internal
view
returns (bool) {
address owner = getOwnerOf(token, tokenId);
return (
spender == owner ||
isApprovedForAll(token, owner, spender) ||
getApproved(token, tokenId) == spender
);
}
function doTransferFrom(
Token storage token
, address from
, address to
, address by
, uint256 tokenId
) internal {
if (!isApprovedOrOwner(token, by, tokenId)) {
revert MaxSplaining({
reason: "L:5"
});
}
address owner = getOwnerOf(token, tokenId);
if (owner != from) {
revert MaxSplaining({
reason: "L:6"
});
} else if (to == address(0)) {
revert MaxSplaining({
reason: "L:7"
});
}
// Clear approvals from the previous owner
setApprove(token, address(0), by, tokenId);
// Change balances
token.balances[from] -= 1;
token.balances[to] += 1;
// Move tokenId
token.owners[tokenId] = to;
}
function mint(
Token storage token
, address to
, uint256 tokenId
) internal {
if (to == address(0)) {
revert MaxSplaining({
reason: "L:8"
});
} else if (getOwnerOf(token, tokenId) != address(0)) {
revert MaxSplaining({
reason: "L:9"
});
}
token.balances[to] += 1;
token.owners[tokenId] = to;
token.supply.increment();
}
function burn(
Token storage token
, address by
, uint256 tokenId
) internal {
address owner = getOwnerOf(token, tokenId);
// Clear approvals
setApprove(token, address(0), by, tokenId);
// Change balances
token.balances[owner] -= 1;
delete token.owners[tokenId];
token.supply.decrement();
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @notice : Collection of functions related to the address type
*/
library Address {
/**
* @notice : Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @notice : Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @notice : Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @notice : Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @notice : Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @notice : Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @notice : Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @notice : Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @notice : Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @notice : Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @notice : Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @notice : Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides a set of functions to operate with Base64 strings.
*
* _Available since v4.5._
*/
library Base64 {
/**
* @dev Base64 Encoding/Decoding Table
*/
string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* @dev Converts a `bytes` to its Bytes64 `string` representation.
*/
function encode(bytes memory data) internal pure returns (string memory) {
/**
* Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
* https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
*/
if (data.length == 0) return "";
// Loads the table into memory
string memory table = _TABLE;
// Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
// and split into 4 numbers of 6 bits.
// The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
// - `data.length + 2` -> Round up
// - `/ 3` -> Number of 3-bytes chunks
// - `4 *` -> 4 characters for each chunk
string memory result = new string(4 * ((data.length + 2) / 3));
/// @solidity memory-safe-assembly
assembly {
// Prepare the lookup table (skip the first "length" byte)
let tablePtr := add(table, 1)
// Prepare result pointer, jump over length
let resultPtr := add(result, 32)
// Run over the input, 3 bytes at a time
for {
let dataPtr := data
let endPtr := add(data, mload(data))
} lt(dataPtr, endPtr) {
} {
// Advance 3 bytes
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
// To write each character, shift the 3 bytes (18 bits) chunk
// 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
// and apply logical AND with 0x3F which is the number of
// the previous character in the ASCII table prior to the Base64 Table
// The result is then added to the table to get the character to write,
// and finally write it in the result pointer but with a left shift
// of 256 (1 byte) - 8 (1 ASCII char) = 248 bits
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1) // Advance
}
// When data `bytes` is not exactly 3 bytes long
// it is padded with `=` characters at the end
switch mod(mload(data), 3)
case 1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case 2 {
mstore8(sub(resultPtr, 1), 0x3d)
}
}
return result;
}
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: CountersV2.sol
* @author Matt Condon (@shrugs)
* @notice Provides counters that can only be incremented, decremented, reset or set.
* This can be used e.g. to track the number of elements in a mapping, issuing ERC721 ids
* or counting request ids.
* @custom:change-log MIT -> Apache-2.0
* @custom:change-log Edited for more NFT functionality added .set(uint)
* @custom:change-log added event CounterNumberChangedTo(uint _number).
* @custom:change-log added error MaxSplaining(string reason).
* @custom:change-log internal -> internal functions
* @custom:error-code C2:1 "No negatives in uints" - overflow protection
*
* Include with `using CountersV2 for CountersV2.Counter;`
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
library CountersV2 {
struct Counter {
uint256 value;
}
event CounterNumberChangedTo(uint _number);
error MaxSplaining(string reason);
function current(
Counter storage counter
) internal
view
returns (uint256) {
return counter.value;
}
function increment(
Counter storage counter
) internal {
unchecked {
++counter.value;
}
}
function decrement(
Counter storage counter
) internal {
if (counter.value == 0) {
revert MaxSplaining({
reason : "C2:1"
});
}
unchecked {
--counter.value;
}
}
function reset(
Counter storage counter
) internal {
counter.value = 0;
emit CounterNumberChangedTo(counter.value);
}
function set(
Counter storage counter
, uint number
) internal {
counter.value = number;
emit CounterNumberChangedTo(counter.value);
}
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: [EIP721] HOW NFT
* @author: Max Flow O2 -> @MaxFlowO2 on bird app/GitHub
* @notice ERC721 Implementation with:
* Enhanced EIP173 - Ownership via roles
* EIP2981 - NFT Royalties
* PaymentSplitter v2 - For "ETH" payments
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.17 <0.9.0;
import "./Max-721-2981-PSv2.sol";
import "./lib/721.sol";
import "./lib/Lists.sol";
import "./lib/CountersV2.sol";
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/utils/Base64.sol";
contract HOW is Max721 {
using Lib721 for Lib721.Token;
using Lists for Lists.Access;
using CountersV2 for CountersV2.Counter;
CountersV2.Counter internal theTokenID;
event Claimed(uint256 index, address account, uint256 amount, string what);
constructor(
string memory _name
, string memory _symbol
, address _admin
, address _dev
, address _owner
, uint256 _start
) {
__Max721_init(_name, _symbol, _admin, _dev, _owner);
theTokenID.set(_start);
}
// set time block
modifier preSale() {
if (block.timestamp < startTime + period && block.timestamp >= startTime) {
_;
} else {
revert Unauthorized();
}
}
modifier Sale() {
if (block.timestamp >= startTime + period) {
_;
} else {
revert Unauthorized();
}
}
function setTime(
uint256 _start
, uint256 _period
) external
onlyDev() {
startTime = _start;
period = _period;
}
function showTimes()
external
view
returns (uint256, uint256) {
return (startTime, startTime + period);
}
function setCap(
uint256 _amount
) external
onlyDev() {
maxCap = _amount;
}
function setMerks(
bytes32 _admin
, bytes32 _homies
, bytes32 _normies
) external
onlyDev() {
admin = _admin;
homies = _homies;
normies = _normies;
}
function isClaimedAdmin(
uint256 index
) public
view
returns (bool) {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
uint256 claimedWord = claimedAdmin[claimedWordIndex];
uint256 mask = (1 << claimedBitIndex);
return claimedWord & mask == mask;
}
function isClaimedHomies(
uint256 index
) public
view
returns (bool) {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
uint256 claimedWord = claimedHomies[claimedWordIndex];
uint256 mask = (1 << claimedBitIndex);
return claimedWord & mask == mask;
}
function isClaimedNormies(
uint256 index
) public
view
returns (bool) {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
uint256 claimedWord = claimedNormies[claimedWordIndex];
uint256 mask = (1 << claimedBitIndex);
return claimedWord & mask == mask;
}
function _setClaimedAdmin(
uint256 index
) internal {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
claimedAdmin[claimedWordIndex] = claimedAdmin[claimedWordIndex] | (1 << claimedBitIndex);
}
function _setClaimedHomies(
uint256 index
) internal {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
claimedHomies[claimedWordIndex] = claimedHomies[claimedWordIndex] | (1 << claimedBitIndex);
}
function _setClaimedNormies(
uint256 index
) internal {
uint256 claimedWordIndex = index / 256;
uint256 claimedBitIndex = index % 256;
claimedNormies[claimedWordIndex] = claimedNormies[claimedWordIndex] | (1 << claimedBitIndex);
}
function adminMint(
uint256 index
, address account
, uint256 amount
, bytes32[] calldata merkleProof
) external
preSale() {
if (startTime == 0) {
revert Unauthorized();
}
if (isClaimedAdmin(index)) {
revert MaxSplaining({
reason: "Already Claimed"
});
}
// Verify the merkle proof.
bytes32 node = keccak256(abi.encodePacked(index, account, amount));
if (!MerkleProof.verify(merkleProof, admin, node)) {
revert MaxSplaining({
reason: "Invalid Proof"
});
}
// Mark it claimed and send the token.
_setClaimedAdmin(index);
if (token721.getSupply() + amount > maxCap) {
revert Unauthorized();
} else {
for (uint c = 0; c < amount;) {
// mint each
token721.mint(account, theTokenID.current());
emit Transfer(address(0), account, theTokenID.current());
theTokenID.increment();
unchecked { ++c; }
}
}
emit Claimed(index, account, amount, "Admin");
}
function homiesMint(
uint256 index
, address account
, bytes32[] calldata merkleProof
) external
preSale() {
if (startTime == 0) {
revert Unauthorized();
}
if (isClaimedHomies(index)) {
revert MaxSplaining({
reason: "Already Claimed"
});
}
// Verify the merkle proof.
bytes32 node = keccak256(abi.encodePacked(index, account));
if (!MerkleProof.verify(merkleProof, homies, node)) {
revert MaxSplaining({
reason: "Invalid Proof"
});
}
// Mark it claimed and send the token.
_setClaimedHomies(index);
if (token721.getSupply() + 1 > maxCap) {
revert Unauthorized();
} else {
token721.mint(account, theTokenID.current());
emit Transfer(address(0), account, theTokenID.current());
theTokenID.increment();
}
emit Claimed(index, account, 1, "Homies");
}
function normiesMint(
uint256 index
, address account
, bytes32[] calldata merkleProof
) external
payable
preSale() {
if (startTime == 0) {
revert Unauthorized();
}
if (msg.value != normiesCost) {
revert MaxSplaining ({
reason: "msg.value too low"
});
}
if (isClaimedNormies(index)) {
revert MaxSplaining({
reason: "Already Claimed"
});
}
// Verify the merkle proof.
bytes32 node = keccak256(abi.encodePacked(index, account));
if (!MerkleProof.verify(merkleProof, normies, node)) {
revert MaxSplaining({
reason: "Invalid Proof"
});
}
// Mark it claimed and send the token.
_setClaimedNormies(index);
if (token721.getSupply() + 1 > maxCap) {
revert Unauthorized();
} else {
token721.mint(account, theTokenID.current());
emit Transfer(address(0), account, theTokenID.current());
theTokenID.increment();
}
emit Claimed(index, account, 1, "Normies");
}
function publicMint(
uint256 quant
) external
payable
Sale() {
if (quant > 5) {
revert Unauthorized();
}
if (startTime == 0) {
revert Unauthorized();
}
if (msg.value != publicCost * quant) {
revert MaxSplaining ({
reason: "msg.value too low"
});
}
if (token721.getSupply() + quant > maxCap) {
revert Unauthorized();
} else {
for (uint c = 0; c < quant;) {
// mint each
token721.mint(msg.sender, theTokenID.current());
emit Transfer(address(0), msg.sender, theTokenID.current());
theTokenID.increment();
unchecked { ++c; }
}
}
}
function setFees(
uint256 _normies
, uint256 _public
) external
onlyDev() {
normiesCost = _normies;
publicCost = _public;
}
function setContractURI(
string memory newURI
) external
onlyDev() {
contractURL = newURI;
}
function contractURI()
public
view
returns (string memory) {
return contractURL;
}
function setJSON(
string memory _description
, string memory _image
, string memory _animation
) external
onlyDev() {
description = _description;
image = _image;
animationURI = _animation;
}
function tokenURI(
uint256 tokenId
) public
view
virtual
override
returns (string memory) {
bytes memory json = abi.encodePacked(
"{",
'"name": "Homies Genesis #',
Strings.toString(uint256(tokenId)),
'",',
'"description": "',
description,
'",',
'"image": "',
image,
'",',
'"animation_url": "',
animationURI,
'"',
"}"
);
return string(
abi.encodePacked(
"data:application/json;base64,",
Base64.encode(json)
)
);
}
/// @dev Function to receive ether, msg.data must be empty
receive()
external
payable {
// From PaymentSplitter.sol
emit PaymentReceived(msg.sender, msg.value);
}
/// @dev Function to receive ether, msg.data is not empty
fallback()
external
payable {
// From PaymentSplitter.sol
emit PaymentReceived(msg.sender, msg.value);
}
/// @dev this is a public getter for ETH blance on contract
function getBalance()
external
view
returns (uint) {
return address(this).balance;
}
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: EIP-165: Standard Interface Detection
* @author: Christian Reitwießner, Nick Johnson, Fabian Vogelsteller, Jordi Baylina, Konrad Feldmeier, William Entriken
* @notice: Creates a standard method to publish and detect what interfaces a smart contract implements.
* @custom:source https://eips.ethereum.org/EIPS/eip-165
* @custom:change-log interface ERC165 -> interface IERC165
* @custom:change-log readability enhanced
* @custom:change-log MIT -> Apache-2.0
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright and related rights waived via CC0. *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
interface IERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @notice Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(
bytes4 interfaceID
) external
view
returns (bool);
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: EIP-173: Contract Ownership Standard
* @author: Nick Mudge, Dan Finlay
* @notice: This specification defines standard functions for owning or controlling a contract.
* the ERC-165 identifier for this interface is 0x7f5828d0
* @custom:URI https://eips.ethereum.org/EIPS/eip-173
* @custom:change-log MIT -> Apache-2.0
* @custom:change-log readability modification
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright and related rights waived via CC0. *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "../../eip/165/IERC165.sol";
interface IERC173 is IERC165 {
/// @dev This emits when ownership of a contract changes.
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @notice Get the address of the owner
/// @return The address of the owner.
function owner()
view
external
returns(address);
/// @notice Set the address of the new owner of the contract
/// @dev Set _newOwner to address(0) to renounce any ownership.
/// @param _newOwner The address of the new owner of the contract
function transferOwnership(
address _newOwner
) external;
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: EIP-2981: NFT Royalty Standard
* @author: Zach Burks, James Morgan, Blaine Malone, James Seibel
* @notice: the ERC-165 identifier for this interface is 0x2a55205a.
* @custom:source https://eips.ethereum.org/EIPS/eip-2981
* @custom:change-log MIT -> Apache-2.0
*
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright and related rights waived via CC0. *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "../165/IERC165.sol";
interface IERC2981 is IERC165 {
/// ERC165 bytes to add to interface array - set in parent contract
/// implementing this standard
/// bytes4(keccak256("royaltyInfo(uint256,uint256)")) == 0x2a55205a
/// bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a;
/// _registerInterface(_INTERFACE_ID_ERC2981);
/// @notice Called with the sale price to determine how much royalty
/// is owed and to whom.
/// @param _tokenId - the NFT asset queried for royalty information
/// @param _salePrice - the sale price of the NFT asset specified by _tokenId
/// @return receiver - address of who should be sent the royalty payment
/// @return royaltyAmount - the royalty payment amount for _salePrice
function royaltyInfo(
uint256 _tokenId,
uint256 _salePrice
) external
view
returns (
address receiver,
uint256 royaltyAmount
);
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: EIP-2981: NFT Royalty Standard, admin extension
* @author: Max Flow O2 -> @MaxFlowO2 on bird app/GitHub
* @notice: the ERC-165 identifier for this interface is unknown.
* @custom:source https://eips.ethereum.org/EIPS/eip-2981
* @custom:change-log MIT -> Apache-2.0
*
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright and related rights waived via CC0. *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "../../eip/2981/IERC2981.sol";
interface IERC2981Admin is IERC2981 {
/// @dev function (state storage) sets the royalty data for a token
/// @param tokenId uint256 for the token
/// @param receiver address for the royalty reciever for token
/// @param permille uint16 for the permille of royalties 20 -> 2.0%
function setRoyalties(
uint256 tokenId
, address receiver
, uint16 permille
) external;
/// @dev function (state storage) revokes the royalty data for a token
/// @param tokenId uint256 for the token
function revokeRoyalties(
uint256 tokenId
) external;
/// @dev function (state storage) sets the royalty data for a collection
/// @param receiver address for the royalty reciever for token
/// @param permille uint16 for the permille of royalties 20 -> 2.0%
function setRoyalties(
address receiver
, uint16 permille
) external;
/// @dev function (state storage) revokes the royalty data for a collection
function revokeRoyalties()
external;
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: ERC-721 Non-Fungible Token Standard
* @author: William Entriken, Dieter Shirley, Jacob Evans, Nastassia Sachs
* @notice: the ERC-165 identifier for this interface is 0x80ac58cd.
* @custom:source https://eips.ethereum.org/EIPS/eip-721
* @custom:change-log interface ERC721 * is ERC165 * -> interface IERC721
* @custom:change-log removed payable from IERC721
* @custom:change-log removed events from IERC721 (handled in Lib721)
* @custom:change-log readability enhanced
* @custom:change-log MIT -> Apache-2.0
* @custom:change-log TypeError: Data location must be "memory" or "calldata" for parameter (line 84)
*
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright and related rights waived via CC0. *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "../165/IERC165.sol";
interface IERC721 is IERC165 {
/// @dev This emits when ownership of any NFT changes by any mechanism.
/// This event emits when NFTs are created (`from` == 0) and destroyed
/// (`to` == 0). Exception: during contract creation, any number of NFTs
/// may be created and assigned without emitting Transfer. At the time of
/// any transfer, the approved address for that NFT (if any) is reset to none.
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
/// @dev This emits when the approved address for an NFT is changed or
/// reaffirmed. The zero address indicates there is no approved address.
/// When a Transfer event emits, this also indicates that the approved
/// address for that NFT (if any) is reset to none.
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
/// @dev This emits when an operator is enabled or disabled for an owner.
/// The operator can manage all NFTs of the owner.
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
/// @notice Count all NFTs assigned to an owner
/// @notice NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param _owner An address for whom to query the balance
/// @return The number of NFTs owned by `_owner`, possibly zero
function balanceOf(
address _owner
) external
view
returns (uint256);
/// @notice Find the owner of an NFT
/// @notice NFTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @param _tokenId The identifier for an NFT
/// @return The address of the owner of the NFT
function ownerOf(
uint256 _tokenId
) external
view
returns (address);
/// @notice Transfers the ownership of an NFT from one address to another address
/// @notice Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
/// `onERC721Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(
address _from
, address _to
, uint256 _tokenId
, bytes calldata data
) external;
/// @notice Transfers the ownership of an NFT from one address to another address
/// @notice This works identically to the other function with an extra data parameter,
/// except this function just sets data to "".
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function safeTransferFrom(
address _from
, address _to
, uint256 _tokenId
) external;
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST
/// @notice Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function transferFrom(
address _from
, address _to
, uint256 _tokenId
) external;
/// @notice Change or reaffirm the approved address for an NFT
/// @notice The zero address indicates there is no approved address.
/// Throws unless `msg.sender` is the current NFT owner, or an authorized
/// operator of the current owner.
/// @param _approved The new approved NFT controller
/// @param _tokenId The NFT to approve
function approve(
address _approved
, uint256 _tokenId
) external;
/// @notice Enable or disable approval for a third party ("operator") to manage
/// all of `msg.sender`'s assets
/// @notice Emits the ApprovalForAll event. The contract MUST allow
/// multiple operators per owner.
/// @param _operator Address to add to the set of authorized operators
/// @param _approved True if the operator is approved, false to revoke approval
function setApprovalForAll(
address _operator
, bool _approved
) external;
/// @notice Get the approved address for a single NFT
/// @notice Throws if `_tokenId` is not a valid NFT.
/// @param _tokenId The NFT to find the approved address for
/// @return The approved address for this NFT, or the zero address if there is none
function getApproved(
uint256 _tokenId
) external
view
returns (address);
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(
address _owner
, address _operator
) external
view
returns (bool);
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: ERC-721 Non-Fungible Token Standard, optional metadata extension
* @author: William Entriken, Dieter Shirley, Jacob Evans, Nastassia Sachs
* @notice: the ERC-165 identifier for this interface is 0x5b5e139f.
* @custom:source https://eips.ethereum.org/EIPS/eip-721
* @custom:change-log interface ERC721Metadata * is ERC721 * -> interface IERC721Metadata
* @custom:change-log readability enhanced
* @custom:change-log MIT -> Apache-2.0
* @custom:change-log Data location must be "memory" or "calldata" for return parameter (lines 48, 54, 64)
*
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright and related rights waived via CC0. *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "./IERC721.sol";
interface IERC721Metadata is IERC721 {
/// @notice A descriptive name for a collection of NFTs in this contract
function name()
external
view
returns (string memory _name);
/// @notice An abbreviated name for NFTs in this contract
function symbol()
external
view
returns (string memory _symbol);
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
/// @notice Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
/// 3986. The URI may point to a JSON file that conforms to the "ERC721
/// Metadata JSON Schema".
function tokenURI(
uint256 _tokenId
) external
view
returns (string memory);
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: ERC-721 Non-Fungible Token Standard, required wallet interface
* @author: William Entriken, Dieter Shirley, Jacob Evans, Nastassia Sachs
* @notice: the ERC-165 identifier for this interface is 0x150b7a02.
* @custom:source https://eips.ethereum.org/EIPS/eip-721
* @custom:change-log interface ERC721TokenReceiver -> interface IERC721TokenReceiver
* @custom:change-log readability enhanced
* @custom:change-log MIT -> Apache-2.0
* @custom:change-log TypeError: Data location must be "memory" or "calldata" for parameter (line 60)
*
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright and related rights waived via CC0. *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "../165/IERC165.sol";
interface IERC721TokenReceiver is IERC165 {
/// @notice Handle the receipt of an NFT
/// @notice The ERC721 smart contract calls this function on the recipient
/// after a `transfer`. This function MAY throw to revert and reject the
/// transfer. Return of other than the magic value MUST result in the
/// transaction being reverted.
/// Note: the contract address is always the message sender.
/// @param _operator The address which called `safeTransferFrom` function
/// @param _from The address which previously owned the token
/// @param _tokenId The NFT identifier which is being transferred
/// @param _data Additional data with no specified format
/// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
/// unless throwing
function onERC721Received(
address _operator
, address _from
, uint256 _tokenId
, bytes calldata _data
) external
returns(bytes4);
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: EIP-173: Contract Ownership Standard, MaxFlowO2's extension
* @author: Max Flow O2 -> @MaxFlowO2 on bird app/GitHub
* @notice: Interface for enhancing EIP-173
* @custom:change-log UUPS Upgradable
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "../../eip/173/IERC173.sol";
interface IMAX173 is IERC173 {
/// @dev This is the classic "EIP-173" method of renouncing onlyOwner()
function renounceOwnership()
external;
/// @dev This accepts the push-pull method of onlyOwner()
function acceptOwnership()
external;
/// @dev This declines the push-pull method of onlyOwner()
function declineOwnership()
external;
/// @dev This starts the push-pull method of onlyOwner()
/// @param newOwner: addres of new pending owner role
function pushOwnership(
address newOwner
) external;
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: [Not an EIP]: Contract Developer Standard
* @author: Max Flow O2 -> @MaxFlowO2 on bird app/GitHub
* @notice: Interface for onlyDev() role
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "../../eip/165/IERC165.sol";
interface IMAXDEV is IERC165 {
/// @dev Classic "EIP-173" but for onlyDev()
/// @return Developer of contract
function developer()
external
view
returns (address);
/// @dev This renounces your role as onlyDev()
function renounceDeveloper()
external;
/// @dev Classic "EIP-173" but for onlyDev()
/// @param newDeveloper: addres of new pending Developer role
function transferDeveloper(
address newDeveloper
) external;
/// @dev This accepts the push-pull method of onlyDev()
function acceptDeveloper()
external;
/// @dev This declines the push-pull method of onlyDev()
function declineDeveloper()
external;
/// @dev This starts the push-pull method of onlyDev()
/// @param newDeveloper: addres of new pending developer role
function pushDeveloper(
address newDeveloper
) external;
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: [Not an EIP]: Contract Roles Standard
* @author: Max Flow O2 -> @MaxFlowO2 on bird app/GitHub
* @notice: Interface for MaxAccess version of Roles
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "../../eip/165/IERC165.sol";
interface IRoles is IERC165 {
/// @dev Returns `true` if `account` has been granted `role`.
/// @param role: Bytes4 of a role
/// @param account: Address to check
/// @return bool true/false if account has role
function hasRole(
bytes4 role
, address account
) external
view
returns (bool);
/// @dev Returns the admin role that controls a role
/// @param role: Role to check
/// @return admin role
function getRoleAdmin(
bytes4 role
) external
view
returns (bytes4);
/// @dev Grants `role` to `account`
/// @param role: Bytes4 of a role
/// @param account: account to give role to
function grantRole(
bytes4 role
, address account
) external;
/// @dev Revokes `role` from `account`
/// @param role: Bytes4 of a role
/// @param account: account to revoke role from
function revokeRole(
bytes4 role
, address account
) external;
/// @dev Renounces `role` from `account`
/// @param role: Bytes4 of a role
function renounceRole(
bytes4 role
) external;
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: [Not an EIP] Payment Splitter, interface for ether payments
* @author: Max Flow O2 -> @MaxFlowO2 on bird app/GitHub
* @notice: Interface for Payment Splitter
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "../../eip/165/IERC165.sol";
interface ISplitter is IERC165 {
/// @dev returns total shares
/// @return uint256 of all shares on contract
function totalShares()
external
view
returns (uint256);
/// @dev returns shares of an address
/// @param payee address of payee to return
/// @return mapping(address => uint) of _shares
function shares(
address payee
) external
view
returns (uint256);
/// @dev returns total releases in "eth"
/// @return uint256 of all "eth" released in wei
function totalReleased()
external
view
returns (uint256);
/// @dev returns released "eth" of an payee
/// @param payee address of payee to look up
/// @return mapping(address => uint) of _released
function released(
address payee
) external
view
returns (uint256);
/// @dev returns amount of "eth" that can be released to payee
/// @param payee address of payee to look up
/// @return uint in wei of "eth" to release
function releasable(
address payee
) external
view
returns (uint256);
/// @dev returns index number of payee
/// @param payee number of index
/// @return address at _payees[index]
function payeeIndex(
address payee
) external
view
returns (uint256);
/// @dev this returns the array of payees[]
/// @return address[] payees
function payees()
external
view
returns (address[] memory);
/// @dev this claims all "eth" on contract for msg.sender
function claim()
external;
/// @dev This pays all payees
function payClaims()
external;
/// @dev This adds a payee
/// @param payee Address of payee
/// @param _shares Shares to send user
function addPayee(
address payee
, uint256 _shares
) external;
/// @dev This removes a payee
/// @param payee Address of payee to remove
/// @dev use payPayees() prior to use if anything is on the contract
function removePayee(
address payee
) external;
/// @dev This removes all payees
/// @dev use payPayees() prior to use if anything is on the contract
function clearPayees()
external;
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: [Not an EIP]: Access lists
* @author: @MaxFlowO2 on bird app/GitHub
* @notice: Formerly whitelists, now allowlist, or whatever it's called.
* @custom:change-log removed end variable/functions (un-needed)
* @custom:change-log variables renamed from lib whitelist
* @custom:change-log internal -> internal
* @custom:error-code A:1 "(user) is already whitelisted."
* @custom:error-code A:2 "(user) is not whitelisted."
* @custom:error-code A:3 "Whitelist already enabled."
* @custom:error-code A:4 "Whitelist already disabled."
* @custom:change-log added custom error codes
* @custom:change-log removed import "./Strings.sol"; (un-needed)
*
* Include with 'using Lists for Lists.Access;'
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "./CountersV2.sol";
library Lists {
using CountersV2 for CountersV2.Counter;
event ListChanged(bool _old, bool _new, address _address);
event ListStatus(bool _old, bool _new);
error MaxSplaining(string reason);
struct Access {
bool _status;
CountersV2.Counter added;
CountersV2.Counter removed;
mapping(address => bool) allowed;
}
function add(
Access storage list
, address user
) internal {
if (list.allowed[user]) {
revert MaxSplaining({
reason : "A:1"
});
}
// since now all previous values are false no need for another variable
// and add them to the list!
list.allowed[user] = true;
// increment counter
list.added.increment();
// emit event
emit ListChanged(false, list.allowed[user], user);
}
function remove(
Access storage list
, address user
) internal {
if (!list.allowed[user]) {
revert MaxSplaining({
reason : "A:2"
});
}
// since now all previous values are true no need for another variable
// and remove them from the list!
list.allowed[user] = false;
// increment counter
list.removed.increment();
// emit event
emit ListChanged(true, list.allowed[user], user);
}
function enable(
Access storage list
) internal {
if (list._status) {
revert MaxSplaining({
reason : "A:3"
});
}
list._status = true;
emit ListStatus(false, list._status);
}
function disable(
Access storage list
) internal {
if (!list._status) {
revert MaxSplaining({
reason : "A:4"
});
}
list._status = false;
emit ListStatus(true, list._status);
}
function status(
Access storage list
) internal
view
returns (bool) {
return list._status;
}
function totalAdded(
Access storage list
) internal
view
returns (uint) {
return list.added.current();
}
function totalRemoved(
Access storage list
) internal
view
returns (uint) {
return list.removed.current();
}
function onList(
Access storage list
, address user
) internal
view
returns (bool) {
return list.allowed[user];
}
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: [EIP721] Max-721 Implementation, using EIP 1822
* @author: Max Flow O2 -> @MaxFlowO2 on bird app/GitHub
* @notice ERC721 Implementation with:
* Enhanced EIP173 - Ownership via roles
* EIP2981 - NFT Royalties
* PsuedoRandom Engine - Expansion of BAYC engine
* TimeCop + Lists - For presales
* PaymentSplitter v2 - For "ETH" payments
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.17 <0.9.0;
import "./eip/721/IERC721TokenReceiver.sol";//
import "./eip/721/IERC721Metadata.sol";//
import "./eip/721/IERC721.sol";//
import "./modules/2981/IERC2981Admin.sol";//
import "./modules/access/MaxAccess.sol";//
import "./modules/splitter/ISplitter.sol";//
import "./lib/Address.sol";//
import "./lib/721.sol";//
import "./lib/Roles.sol";//
import "./lib/2981c.sol";//
import "./lib/Payments.sol";//
abstract contract Max721 is MaxAccess
, IERC721
, IERC721Metadata
, IERC721TokenReceiver
, IERC2981Admin
, ISplitter {
using Lib721 for Lib721.Token;
using Roles for Roles.Role;
using Lib2981c for Lib2981c.Royalties;
using Payments for Payments.GasTokens;
using Address for address;
// The Structs...
Lib721.Token internal token721;
Roles.Role internal contractRoles;
Lib2981c.Royalties internal royalties;
Payments.GasTokens internal splitter;
// The rest (got to have a few)
bytes4 constant internal DEVS = 0xca4b208b;
bytes4 constant internal PENDING_DEVS = 0xca4b208a; // DEVS - 1
bytes4 constant internal OWNERS = 0x8da5cb5b;
bytes4 constant internal PENDING_OWNERS = 0x8da5cb5a; // OWNERS - 1
bytes4 constant internal ADMIN = 0xf851a440;
uint256 startTime; // Set to opening (can +48h for secondary)
uint256 period; // Set to the period
uint256 maxCap; // Cpacity of minter
bytes32 internal admin;
mapping(uint256 => uint256) internal claimedAdmin;
bytes32 internal homies;
mapping(uint256 => uint256) internal claimedHomies;
bytes32 internal normies;
mapping(uint256 => uint256) internal claimedNormies;
string internal contractURL;
string internal image;
string internal description;
string internal animationURI;
uint256 public normiesCost = 0.069 ether;
uint256 public publicCost = 0.1 ether;
event PaymentReceived(address indexed _payee, uint256 _amount);
/// @dev this is Unauthorized(), basically a catch all, zero description
/// @notice 0x82b42900 bytes4 of this
error Unauthorized();
/// @dev this is MaxSplaining(), giving you a reason, aka require(param, "reason")
/// @param reason: Use the "Contract name: error"
/// @notice 0x0661b792 bytes4 of this
error MaxSplaining(
string reason
);
/// @dev this is TooSoonJunior(), using times
/// @param yourTime: should almost always be block.timestamp
/// @param hitTime: the time you should have started
/// @notice 0xf3f82ac5 bytes4 of this
error TooSoonJunior(
uint yourTime
, uint hitTime
);
/// @dev this is TooLateBoomer(), using times
/// @param yourTime: should almost always be block.timestamp
/// @param hitTime: the time you should have ended
/// @notice 0x43c540ef bytes4 of this
error TooLateBoomer(
uint yourTime
, uint hitTime
);
///////////////////////
/// MAX-721: Modifiers
///////////////////////
modifier onlyRole(bytes4 role) {
if (contractRoles.has(role, msg.sender) || contractRoles.has(ADMIN, msg.sender)) {
_;
} else {
revert Unauthorized();
}
}
modifier onlyOwner() {
if (contractRoles.has(OWNERS, msg.sender)) {
_;
} else {
revert Unauthorized();
}
}
modifier onlyDev() {
if (contractRoles.has(DEVS, msg.sender)) {
_;
} else {
revert Unauthorized();
}
}
///////////////////////
/// MAX-721: Internals
///////////////////////
function __Max721_init(
string memory _name
, string memory _symbol
, address _admin
, address _dev
, address _owner
) internal {
token721.setName(_name);
token721.setSymbol(_symbol);
contractRoles.add(ADMIN, _admin);
contractRoles.setAdmin(_admin);
contractRoles.add(DEVS, _dev);
contractRoles.setDeveloper(_dev);
contractRoles.add(OWNERS, _owner);
contractRoles.setOwner(_owner);
}
function safeHook(
address from,
address to,
uint256 tokenId,
bytes memory data
) internal
returns (bool) {
if (to.isContract()) {
try IERC721TokenReceiver(to).onERC721Received(msg.sender, from, tokenId, data)
returns (bytes4 retval) {
return retval == IERC721TokenReceiver.onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert Unauthorized();
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
/////////////////////////////////////////
/// EIP-173: Contract Ownership Standard
/////////////////////////////////////////
/// @notice Get the address of the owner
/// @return The address of the owner.
function owner()
view
external
returns(address) {
return contractRoles.getOwner();
}
/// @notice Set the address of the new owner of the contract
/// @dev Set _newOwner to address(0) to renounce any ownership.
/// @param _newOwner The address of the new owner of the contract
function transferOwnership(
address _newOwner
) external
onlyRole(OWNERS) {
contractRoles.add(OWNERS, _newOwner);
contractRoles.setOwner(_newOwner);
contractRoles.remove(OWNERS, msg.sender);
}
////////////////////////////////////////////////////////////////
/// EIP-173: Contract Ownership Standard, MaxFlowO2's extension
////////////////////////////////////////////////////////////////
/// @dev This is the classic "EIP-173" method of renouncing onlyOwner()
function renounceOwnership()
external
onlyRole(OWNERS) {
contractRoles.setOwner(address(0));
contractRoles.remove(OWNERS, msg.sender);
}
/// @dev This accepts the push-pull method of onlyOwner()
function acceptOwnership()
external
onlyRole(PENDING_OWNERS) {
contractRoles.add(OWNERS, msg.sender);
contractRoles.setOwner(msg.sender);
contractRoles.remove(PENDING_OWNERS, msg.sender);
}
/// @dev This declines the push-pull method of onlyOwner()
function declineOwnership()
external
onlyRole(PENDING_OWNERS) {
contractRoles.remove(PENDING_OWNERS, msg.sender);
}
/// @dev This starts the push-pull method of onlyOwner()
/// @param newOwner: addres of new pending owner role
function pushOwnership(
address newOwner
) external
onlyRole(OWNERS) {
contractRoles.add(PENDING_OWNERS, newOwner);
}
//////////////////////////////////////////////
/// [Not an EIP]: Contract Developer Standard
//////////////////////////////////////////////
/// @dev Classic "EIP-173" but for onlyDev()
/// @return Developer of contract
function developer()
external
view
returns (address) {
return contractRoles.getDeveloper();
}
/// @dev This renounces your role as onlyDev()
function renounceDeveloper()
external
onlyRole(DEVS) {
contractRoles.setDeveloper(address(0));
contractRoles.remove(DEVS, msg.sender);
}
/// @dev Classic "EIP-173" but for onlyDev()
/// @param newDeveloper: addres of new pending Developer role
function transferDeveloper(
address newDeveloper
) external
onlyRole(DEVS) {
contractRoles.add(DEVS, newDeveloper);
contractRoles.setDeveloper(newDeveloper);
contractRoles.remove(DEVS, msg.sender);
}
/// @dev This accepts the push-pull method of onlyDev()
function acceptDeveloper()
external
onlyRole(PENDING_DEVS) {
contractRoles.add(DEVS, msg.sender);
contractRoles.setDeveloper(msg.sender);
contractRoles.remove(PENDING_DEVS, msg.sender);
}
/// @dev This declines the push-pull method of onlyDev()
function declineDeveloper()
external
onlyRole(PENDING_DEVS) {
contractRoles.remove(PENDING_DEVS, msg.sender);
}
/// @dev This starts the push-pull method of onlyDev()
/// @param newDeveloper: addres of new pending developer role
function pushDeveloper(
address newDeveloper
) external
onlyRole(DEVS) {
contractRoles.add(PENDING_DEVS, newDeveloper);
}
//////////////////////////////////////////
/// [Not an EIP]: Contract Roles Standard
//////////////////////////////////////////
/// @dev Returns `true` if `account` has been granted `role`.
/// @param role: Bytes4 of a role
/// @param account: Address to check
/// @return bool true/false if account has role
function hasRole(
bytes4 role
, address account
) external
view
returns (bool) {
return contractRoles.has(role, account);
}
/// @dev Returns the admin role that controls a role
/// @param role: Role to check
/// @return admin role
function getRoleAdmin(
bytes4 role
) external
view
returns (bytes4) {
return ADMIN;
}
/// @dev Grants `role` to `account`
/// @param role: Bytes4 of a role
/// @param account: account to give role to
function grantRole(
bytes4 role
, address account
) external
onlyRole(role) {
if (role == PENDING_DEVS || role == PENDING_OWNERS) {
revert Unauthorized();
} else {
contractRoles.add(role, account);
}
}
/// @dev Revokes `role` from `account`
/// @param role: Bytes4 of a role
/// @param account: account to revoke role from
function revokeRole(
bytes4 role
, address account
) external
onlyRole(role) {
if (role == PENDING_DEVS || role == PENDING_OWNERS) {
if (account == msg.sender) {
contractRoles.remove(role, account);
} else {
revert Unauthorized();
}
} else {
contractRoles.remove(role, account);
}
}
/// @dev Renounces `role` from `account`
/// @param role: Bytes4 of a role
function renounceRole(
bytes4 role
) external
onlyRole(role) {
contractRoles.remove(role, msg.sender);
}
////////////////////////////////////////////////////////////////////////
/// ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev may be added, but not fully supported see ERC-165 below
////////////////////////////////////////////////////////////////////////
/// @notice Count NFTs tracked by this contract
/// @return A count of valid NFTs tracked by this contract, where each one of
/// them has an assigned and queryable owner not equal to the zero address
function totalSupply()
external
view
virtual
returns (uint256) {
return token721.getSupply();
}
/////////////////////////////////////////////////
/// ERC721 Metadata, optional metadata extension
/////////////////////////////////////////////////
/// @notice A descriptive name for a collection of NFTs in this contract
function name()
external
view
virtual
override
returns (string memory _name) {
return token721.getName();
}
/// @notice An abbreviated name for NFTs in this contract
function symbol()
external
view
virtual
override
returns (string memory _symbol) {
return token721.getSymbol();
}
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
/// @notice Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
/// 3986. The URI may point to a JSON file that conforms to the "ERC721
/// Metadata JSON Schema".
function tokenURI(
uint256 _tokenId
) external
view
virtual
override
returns (string memory) {
return token721.getTokenURI(_tokenId);
}
////////////////////////////////////////
/// ERC-721 Non-Fungible Token Standard
////////////////////////////////////////
/// @notice Count all NFTs assigned to an owner
/// @notice NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param _owner An address for whom to query the balance
/// @return The number of NFTs owned by `_owner`, possibly zero
function balanceOf(
address _owner
) external
view
virtual
override
returns (uint256) {
return token721.getBalanceOf(_owner);
}
/// @notice Find the owner of an NFT
/// @notice NFTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @param _tokenId The identifier for an NFT
/// @return The address of the owner of the NFT
function ownerOf(
uint256 _tokenId
) external
view
virtual
override
returns (address) {
return token721.getOwnerOf(_tokenId);
}
/// @notice Transfers the ownership of an NFT from one address to another address
/// @notice Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
/// `onERC721Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(
address _from
, address _to
, uint256 _tokenId
, bytes calldata data
) external
virtual
override {
token721.doTransferFrom(_from, _to, msg.sender, _tokenId);
safeHook(_from, _to, _tokenId, data);
emit Transfer(_from, _to, _tokenId);
}
/// @notice Transfers the ownership of an NFT from one address to another address
/// @notice This works identically to the other function with an extra data parameter,
/// except this function just sets data to "".
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function safeTransferFrom(
address _from
, address _to
, uint256 _tokenId
) external
virtual
override {
this.safeTransferFrom(_from, _to, _tokenId, "");
}
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST
/// @notice Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function transferFrom(
address _from
, address _to
, uint256 _tokenId
) external
virtual
override {
token721.doTransferFrom(_from, _to, msg.sender, _tokenId);
emit Transfer(_from, _to, _tokenId);
}
/// @notice Change or reaffirm the approved address for an NFT
/// @notice The zero address indicates there is no approved address.
/// Throws unless `msg.sender` is the current NFT owner, or an authorized
/// operator of the current owner.
/// @param _approved The new approved NFT controller
/// @param _tokenId The NFT to approve
function approve(
address _approved
, uint256 _tokenId
) external
virtual
override {
token721.setApprove(_approved, msg.sender, _tokenId);
emit Approval(msg.sender, _approved, _tokenId);
}
/// @notice Enable or disable approval for a third party ("operator") to manage
/// all of `msg.sender`'s assets
/// @notice Emits the ApprovalForAll event. The contract MUST allow
/// multiple operators per owner.
/// @param _operator Address to add to the set of authorized operators
/// @param _approved True if the operator is approved, false to revoke approval
function setApprovalForAll(
address _operator
, bool _approved
) external
virtual
override {
token721.setApprovalForAll(_operator, msg.sender, _approved);
emit ApprovalForAll(msg.sender, _operator, _approved);
}
/// @notice Get the approved address for a single NFT
/// @notice Throws if `_tokenId` is not a valid NFT.
/// @param _tokenId The NFT to find the approved address for
/// @return The approved address for this NFT, or the zero address if there is none
function getApproved(
uint256 _tokenId
) external
view
virtual
override
returns (address) {
return token721.getApproved(_tokenId);
}
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(
address _owner
, address _operator
) external
view
virtual
override
returns (bool) {
return token721.isApprovedForAll(_owner, _operator);
}
///////////////////////////////////////////////////////////////////
/// ERC-721 Non-Fungible Token Standard, required wallet interface
/// @dev This is to disable all safe transfers to this contract
///////////////////////////////////////////////////////////////////
/// @notice Handle the receipt of an NFT
/// @notice The ERC721 smart contract calls this function on the recipient
/// after a `transfer`. This function MAY throw to revert and reject the
/// transfer. Return of other than the magic value MUST result in the
/// transaction being reverted.
/// Note: the contract address is always the message sender.
/// @param _operator The address which called `safeTransferFrom` function
/// @param _from The address which previously owned the token
/// @param _tokenId The NFT identifier which is being transferred
/// @param _data Additional data with no specified format
/// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
/// unless throwing
function onERC721Received(
address _operator
, address _from
, uint256 _tokenId
, bytes calldata _data
) external
virtual
override
returns(bytes4) {
revert Unauthorized();
}
///////////////////////////////////
/// EIP-2981: NFT Royalty Standard
///////////////////////////////////
/// @notice Called with the sale price to determine how much royalty
/// is owed and to whom.
/// @param _tokenId - the NFT asset queried for royalty information
/// @param _salePrice - the sale price of the NFT asset specified by _tokenId
/// @return receiver - address of who should be sent the royalty payment
/// @return royaltyAmount - the royalty payment amount for _salePrice
function royaltyInfo(
uint256 _tokenId,
uint256 _salePrice
) external
view
virtual
override
returns (
address receiver,
uint256 royaltyAmount
) {
(receiver, royaltyAmount) = royalties.royaltyInfo(_tokenId,_salePrice);
}
////////////////////////////////////////////////////
/// EIP-2981: NFT Royalty Standard, admin extension
/// @dev Using the collection standard
////////////////////////////////////////////////////
/// @dev function (state storage) sets the royalty data for a token
/// @param tokenId uint256 for the token
/// @param receiver address for the royalty reciever for token
/// @param permille uint16 for the permille of royalties 20 -> 2.0%
function setRoyalties(
uint256 tokenId
, address receiver
, uint16 permille
) external
virtual
override {
revert Unauthorized();
}
/// @dev function (state storage) revokes the royalty data for a token
/// @param tokenId uint256 for the token
function revokeRoyalties(
uint256 tokenId
) external
virtual
override {
revert Unauthorized();
}
/// @dev function (state storage) sets the royalty data for a collection
/// @param receiver address for the royalty reciever for token
/// @param permille uint16 for the permille of royalties 20 -> 2.0%
function setRoyalties(
address receiver
, uint16 permille
) external
virtual
onlyOwner()
override {
royalties.setRoyalties(receiver, permille);
}
/// @dev function (state storage) revokes the royalty data for a collection
function revokeRoyalties()
external
virtual
onlyOwner()
override {
royalties.revokeRoyalties();
}
////////////////////////////////////////////////////////////////
/// [Not an EIP] Payment Splitter, interface for ether payments
////////////////////////////////////////////////////////////////
/// @dev returns total shares
/// @return uint256 of all shares on contract
function totalShares()
external
view
virtual
override
returns (uint256) {
return splitter.getTotalShares();
}
/// @dev returns shares of an address
/// @param payee address of payee to return
/// @return mapping(address => uint) of _shares
function shares(
address payee
) external
view
virtual
override
returns (uint256) {
return splitter.payeeShares(payee);
}
/// @dev returns total releases in "eth"
/// @return uint256 of all "eth" released in wei
function totalReleased()
external
view
virtual
override
returns (uint256) {
return splitter.getTotalReleased();
}
/// @dev returns released "eth" of an payee
/// @param payee address of payee to look up
/// @return mapping(address => uint) of _released
function released(
address payee
) external
view
virtual
override
returns (uint256) {
return splitter.payeeReleased(payee);
}
/// @dev returns amount of "eth" that can be released to payee
/// @param payee address of payee to look up
/// @return uint in wei of "eth" to release
function releasable(
address payee
) external
view
virtual
override
returns (uint256) {
uint totalReceived
= address(this).balance
+ this.totalReleased();
return
totalReceived
* this.shares(payee)
/ this.totalShares()
- this.released(payee);
}
/// @dev returns index number of payee
/// @param payee number of index
/// @return address at _payees[index]
function payeeIndex(
address payee
) external
view
virtual
override
returns (uint256) {
return splitter.payeeIndex(payee);
}
/// @dev this returns the array of payees[]
/// @return address[] payees
function payees()
external
view
virtual
override
returns (address[] memory) {
return splitter.getPayees();
}
/// @dev this claims all "eth" on contract for msg.sender
function claim()
external
virtual
override {
if (this.shares(msg.sender) == 0) {
revert Unauthorized();
}
uint256 payment = this.releasable(msg.sender);
if (payment == 0) {
revert Unauthorized();
}
splitter.processPayment(msg.sender, payment);
Address.sendValue(payable(msg.sender), payment);
}
/// @dev This pays all payees
function payClaims()
external
virtual
override {
address[] memory toPay = splitter.getPayees();
uint256 len = toPay.length;
for (uint x = 0 ; x < len ;) {
uint256 payment = this.releasable(toPay[x]);
splitter.processPayment(toPay[x], payment);
Address.sendValue(payable(toPay[x]), payment);
unchecked { ++x; }
}
}
/// @dev This adds a payee
/// @param payee Address of payee
/// @param _shares Shares to send user
function addPayee(
address payee
, uint256 _shares
) external
virtual
onlyDev()
override {
splitter.addPayee(payee, _shares);
}
/// @dev This removes a payee
/// @param payee Address of payee to remove
/// @notice use payPayees() prior to use if anything is on the contract
function removePayee(
address payee
) external
virtual
onlyDev()
override {
splitter.removePayee(payee);
}
/// @dev This removes all payees
/// @notice use payPayees() prior to use if anything is on the contract
function clearPayees()
external
virtual
onlyDev()
override {
splitter.clearPayees();
}
//////////////////////////////////////////
/// EIP-165: Standard Interface Detection
//////////////////////////////////////////
/// @dev Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @notice Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(
bytes4 interfaceID
) external
view
virtual
override
returns (bool) {
return (
interfaceID == type(IERC173).interfaceId ||
interfaceID == type(IERC721).interfaceId ||
interfaceID == type(IERC2981).interfaceId ||
interfaceID == type(IERC721Metadata).interfaceId
);
}
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: [Not an EIP]: MaxFlow's 173/Dev/Roles Interface
* @author: Max Flow O2 -> @MaxFlowO2 on bird app/GitHub
* @notice: Interface for MaxAccess
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
import "./IMAX173.sol";
import "./IMAXDEV.sol";
import "./IRoles.sol";
interface MaxAccess is IMAX173
, IMAXDEV
, IRoles {
///@dev this just imports all 3 and pushes to Implementation
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)
pragma solidity ^0.8.0;
/**
* @dev These functions deal with verification of Merkle Tree proofs.
*
* The tree and the proofs can be generated using our
* https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
* You will find a quickstart guide in the readme.
*
* WARNING: You should avoid using leaf values that are 64 bytes long prior to
* hashing, or use a hash function other than keccak256 for hashing leaves.
* This is because the concatenation of a sorted pair of internal nodes in
* the merkle tree could be reinterpreted as a leaf value.
* OpenZeppelin's JavaScript library generates merkle trees that are safe
* against this attack out of the box.
*/
library MerkleProof {
/**
* @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
* defined by `root`. For this, a `proof` must be provided, containing
* sibling hashes on the branch from the leaf to the root of the tree. Each
* pair of leaves and each pair of pre-images are assumed to be sorted.
*/
function verify(
bytes32[] memory proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
/**
* @dev Calldata version of {verify}
*
* _Available since v4.7._
*/
function verifyCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
/**
* @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
* from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
* hash matches the root of the tree. When processing the proof, the pairs
* of leafs & pre-images are assumed to be sorted.
*
* _Available since v4.4._
*/
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Calldata version of {processProof}
*
* _Available since v4.7._
*/
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
/**
* @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
* `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
*
* CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
*
* _Available since v4.7._
*/
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
/**
* @dev Calldata version of {multiProofVerify}
*
* CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
*
* _Available since v4.7._
*/
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
/**
* @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
* proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
* leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
* respectively.
*
* CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
* is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
* tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
*
* _Available since v4.7._
*/
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the merkle tree.
uint256 leavesLen = leaves.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
return hashes[totalHashes - 1];
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
/**
* @dev Calldata version of {processMultiProof}.
*
* CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
*
* _Available since v4.7._
*/
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
// This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
// consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
// `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
// the merkle tree.
uint256 leavesLen = leaves.length;
uint256 totalHashes = proofFlags.length;
// Check proof validity.
require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
// At each step, we compute the next hash using two values:
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
// get the next hash.
// - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
// `proof` array.
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
return hashes[totalHashes - 1];
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: [Not an EIP] Payment Splitter
* @author: Max Flow O2 -> @MaxFlowO2 on bird app/GitHub
* @notice: Library for two structs one with "ERC-20's" and one without
* @custom:error-code PS:1 No Shares for address
* @custom:error-code PS:2 No payment due for address
* @custom:error-code PS:3 Can not use address(0)
* @custom:error-code PS:4 Shares can not be 0
* @custom:error-code PS:5 User has shares already
* @custom:error-code PS:6 User not in payees
* @custom:change-log added custom error-codes above
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
library Payments {
struct GasTokens {
uint256 totalShares;
uint256 totalReleased;
mapping(address => uint256) shares;
mapping(address => uint256) released;
address[] payees;
}
event PayeeAdded(address account, uint256 _shares);
event PayeeRemoved(address account, uint256 _shares);
event PayeesReset();
event PaymentReleased(address to, uint256 amount);
error MaxSplaining(string reason);
function findIndex(
address[] memory array
, address query
) internal
pure
returns (bool found, uint256 index) {
uint256 len = array.length;
for (uint x = 0; x < len;) {
if (array[x] == query) {
found = true;
index = x;
}
unchecked { ++x; }
}
}
function getTotalReleased(
GasTokens storage gasTokens
) internal
view
returns (uint256) {
return gasTokens.totalReleased;
}
function getTotalShares(
GasTokens storage gasTokens
) internal
view
returns (uint256) {
return gasTokens.totalShares;
}
function payeeShares(
GasTokens storage gasTokens
, address payee
) internal
view
returns (uint256) {
return gasTokens.shares[payee];
}
function payeeReleased(
GasTokens storage gasTokens
, address payee
) internal
view
returns (uint256) {
return gasTokens.released[payee];
}
function payeeIndex(
GasTokens storage gasTokens
, address payee
) internal
view
returns (uint256) {
(bool found, uint256 index) = findIndex(gasTokens.payees, payee);
if (found) {
return index;
} else {
revert MaxSplaining({
reason: "PS:6"
});
}
}
function allPayees(
GasTokens storage gasTokens
) internal
view
returns (address[] memory) {
return gasTokens.payees;
}
function addPayee(
GasTokens storage gasTokens
, address payee
, uint256 _shares
) internal {
if (payee == address(0)) {
revert MaxSplaining({
reason: "PS:3"
});
} else if (_shares == 0) {
revert MaxSplaining({
reason: "PS:4"
});
} else if (gasTokens.shares[payee] > 0) {
revert MaxSplaining({
reason: "PS:5"
});
}
gasTokens.payees.push(payee);
gasTokens.shares[payee] = _shares;
gasTokens.totalShares += _shares;
emit PayeeAdded(payee, _shares);
}
function getPayees(
GasTokens storage gasTokens
) internal
view
returns (address[] memory) {
return gasTokens.payees;
}
function removePayee(
GasTokens storage gasTokens
, address payee
) internal {
if (payee == address(0)) {
revert MaxSplaining({
reason: "PS:3"
});
}
uint256 whacked = payeeIndex(gasTokens, payee);
address last = gasTokens.payees[gasTokens.payees.length -1];
gasTokens.payees[whacked] = last;
gasTokens.payees.pop();
uint256 whackedShares = gasTokens.shares[payee];
delete gasTokens.shares[payee];
gasTokens.totalShares -= whackedShares;
emit PayeeRemoved(payee, whackedShares);
}
function clearPayees(
GasTokens storage gasTokens
) internal {
uint256 len = gasTokens.payees.length;
for (uint x = 0; x < len;) {
address whacked = gasTokens.payees[x];
delete gasTokens.shares[whacked];
unchecked { ++x; }
}
delete gasTokens.totalShares;
delete gasTokens.payees;
emit PayeesReset();
}
function processPayment(
GasTokens storage gasTokens
, address payee
, uint256 payment
) internal {
gasTokens.totalReleased += payment;
gasTokens.released[payee] += payment;
emit PaymentReleased(payee, payment);
}
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: Roles.sol
* @author: Max Flow O2 -> @MaxFlowO2 on bird app/GitHub
* @notice: Library for MaxAcess.sol
* @custom:change-log cleaned up variables
*
* Include with 'using Roles for Roles.Role;'
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
library Roles {
bytes4 constant internal DEVS = 0xca4b208b;
bytes4 constant internal OWNERS = 0x8da5cb5b;
bytes4 constant internal ADMIN = 0xf851a440;
struct Role {
mapping(address => mapping(bytes4 => bool)) bearer;
address owner;
address developer;
address admin;
}
event RoleChanged(bytes4 _role, address _user, bool _status);
event AdminTransferred(address indexed previousAdmin, address indexed newAdmin);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event DeveloperTransferred(address indexed previousDeveloper, address indexed newDeveloper);
error Unauthorized();
function add(
Role storage role
, bytes4 userRole
, address account
) internal {
if (account == address(0)) {
revert Unauthorized();
} else if (has(role, userRole, account)) {
revert Unauthorized();
}
role.bearer[account][userRole] = true;
emit RoleChanged(userRole, account, true);
}
function remove(
Role storage role
, bytes4 userRole
, address account
) internal {
if (account == address(0)) {
revert Unauthorized();
} else if (!has(role, userRole, account)) {
revert Unauthorized();
}
role.bearer[account][userRole] = false;
emit RoleChanged(userRole, account, false);
}
function has(
Role storage role
, bytes4 userRole
, address account
) internal
view
returns (bool) {
if (account == address(0)) {
revert Unauthorized();
}
return role.bearer[account][userRole];
}
function setAdmin(
Role storage role
, address account
) internal {
if (has(role, ADMIN, account)) {
address old = role.admin;
role.admin = account;
emit AdminTransferred(old, role.admin);
} else if (account == address(0)) {
address old = role.admin;
role.admin = account;
emit AdminTransferred(old, role.admin);
} else {
revert Unauthorized();
}
}
function setDeveloper(
Role storage role
, address account
) internal {
if (has(role, DEVS, account)) {
address old = role.developer;
role.developer = account;
emit DeveloperTransferred(old, role.developer);
} else if (account == address(0)) {
address old = role.admin;
role.admin = account;
emit AdminTransferred(old, role.admin);
} else {
revert Unauthorized();
}
}
function setOwner(
Role storage role
, address account
) internal {
if (has(role, OWNERS, account)) {
address old = role.owner;
role.owner = account;
emit OwnershipTransferred(old, role.owner);
} else if (account == address(0)) {
address old = role.admin;
role.admin = account;
emit AdminTransferred(old, role.admin);
} else {
revert Unauthorized();
}
}
function getAdmin(
Role storage role
) internal
view
returns (address) {
return role.admin;
}
function getDeveloper(
Role storage role
) internal
view
returns (address) {
return role.developer;
}
function getOwner(
Role storage role
) internal
view
returns (address) {
return role.owner;
}
}
/* +%%#- ##. =+. .+#%#+: *%%#: .**+- =+
* .%@@*#*: @@: *%- #%*= .*@@=. =%. .%@@*%* +@@=+=% .%##
* .%@@- -=+ *@% :@@- #@=# -@@* +@- :@@@: ==* -%%. *** #@=*
* %@@: -.* :. +@@-.#@# =@%#. :. -@* :@@@. -:# .%. *@# *@#*
* *%@- +++ +@#.-- .*%*. .#@@*@# %@@%*#@@: .@@=-. -%- #%@: +*- =*@* -@%=:
* @@% =## +@@#-..%%:%.-@@=-@@+ .. +@% #@#*+@: .*= @@% =#* -*. +#. %@#+*@
* @@# +@* #@# +@@. -+@@+#*@% =#: #@= :@@-.%# -=. : @@# .*@* =@= :*@:=@@-:@+
* -#%+@#- :@#@@+%++@*@*:=%+..%%#= *@ *@++##. =%@%@%%#- =#%+@#- :*+**+=: %%++%*
*
* @title: Strings
* @author: OpenZeppelin
* @notice: Strings Library
* @custom:source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts-upgradeable/v4.7.3/contracts/utils/StringsUpgradeable.sol
* @custom:change-log Readable, External/Public, Removed code comments, MIT -> Apache-2.0
* @custom:change-log Added MaxSplaining
* @custom:error-code Str:1 "hex length insufficient"
*
* Include with 'using Strings for <insert type>'
*/
// SPDX-License-Identifier: Apache-2.0
/******************************************************************************
* Copyright 2022 Max Flow O2 *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
pragma solidity >=0.8.0 <0.9.0;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
error MaxSplaining(string reason);
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);
}
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);
}
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;
}
if (value != 0) {
revert MaxSplaining({
reason: "Str:1"
});
}
return string(buffer);
}
function toHexString(
address addr
) internal
pure
returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
{
"compilationTarget": {
"project:/contracts/Homies.sol": "HOW"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_dev","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_start","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"string","name":"reason","type":"string"}],"name":"MaxSplaining","type":"error"},{"inputs":[{"internalType":"string","name":"reason","type":"string"}],"name":"MaxSplaining","type":"error"},{"inputs":[{"internalType":"string","name":"reason","type":"string"}],"name":"MaxSplaining","type":"error"},{"inputs":[{"internalType":"string","name":"reason","type":"string"}],"name":"MaxSplaining","type":"error"},{"inputs":[{"internalType":"uint256","name":"yourTime","type":"uint256"},{"internalType":"uint256","name":"hitTime","type":"uint256"}],"name":"TooLateBoomer","type":"error"},{"inputs":[{"internalType":"uint256","name":"yourTime","type":"uint256"},{"internalType":"uint256","name":"hitTime","type":"uint256"}],"name":"TooSoonJunior","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"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":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"string","name":"what","type":"string"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_payee","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"PaymentReceived","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":[],"name":"acceptDeveloper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"payee","type":"address"},{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"addPayee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"adminMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_approved","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":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"clearPayees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"declineDeveloper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"declineOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"developer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"role","type":"bytes4"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"role","type":"bytes4"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"role","type":"bytes4"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"homiesMint","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":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"isClaimedAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"isClaimedHomies","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"isClaimedNormies","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"_name","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"normiesCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"normiesMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"payClaims","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"payee","type":"address"}],"name":"payeeIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"payees","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"quant","type":"uint256"}],"name":"publicMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newDeveloper","type":"address"}],"name":"pushDeveloper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"pushOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"payee","type":"address"}],"name":"releasable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"payee","type":"address"}],"name":"released","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"payee","type":"address"}],"name":"removePayee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceDeveloper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"role","type":"bytes4"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"role","type":"bytes4"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"revokeRoyalties","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeRoyalties","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","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":"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":"uint256","name":"_amount","type":"uint256"}],"name":"setCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newURI","type":"string"}],"name":"setContractURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_normies","type":"uint256"},{"internalType":"uint256","name":"_public","type":"uint256"}],"name":"setFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_description","type":"string"},{"internalType":"string","name":"_image","type":"string"},{"internalType":"string","name":"_animation","type":"string"}],"name":"setJSON","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_admin","type":"bytes32"},{"internalType":"bytes32","name":"_homies","type":"bytes32"},{"internalType":"bytes32","name":"_normies","type":"bytes32"}],"name":"setMerks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint16","name":"permille","type":"uint16"}],"name":"setRoyalties","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint16","name":"permille","type":"uint16"}],"name":"setRoyalties","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_start","type":"uint256"},{"internalType":"uint256","name":"_period","type":"uint256"}],"name":"setTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"payee","type":"address"}],"name":"shares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"showTimes","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":"_symbol","type":"string"}],"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":"totalReleased","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newDeveloper","type":"address"}],"name":"transferDeveloper","outputs":[],"stateMutability":"nonpayable","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":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]