// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)pragmasolidity ^0.8.0;/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @dev 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
* ====
*/functionisContract(address account) internalviewreturns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in// construction, since the code is only stored at the end of the// constructor execution.uint256 size;
assembly {
size :=extcodesize(account)
}
return size >0;
}
/**
* @dev 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://diligence.consensys.net/posts/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].
*/functionsendValue(addresspayable 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");
}
/**
* @dev 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._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev 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._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(address(this).balance>= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytesmemory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytesmemory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytesmemory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/functionverifyCallResult(bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalpurereturns (bytesmemory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
Contract Source Code
File 2 of 15: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 3 of 15: ERC165.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)pragmasolidity ^0.8.0;import"./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/abstractcontractERC165isIERC165{
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {
return interfaceId ==type(IERC165).interfaceId;
}
}
Contract Source Code
File 4 of 15: ERC721.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC721/ERC721.sol)pragmasolidity ^0.8.0;import"./IERC721.sol";
import"./IERC721Receiver.sol";
import"./extensions/IERC721Metadata.sol";
import"../../utils/Address.sol";
import"../../utils/Context.sol";
import"../../utils/Strings.sol";
import"../../utils/introspection/ERC165.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, but not including the Enumerable extension, which is available separately as
* {ERC721Enumerable}.
*/contractERC721isContext, ERC165, IERC721, IERC721Metadata{
usingAddressforaddress;
usingStringsforuint256;
// Token namestringprivate _name;
// Token symbolstringprivate _symbol;
// Mapping from token ID to owner addressmapping(uint256=>address) private _owners;
// Mapping owner address to token countmapping(address=>uint256) private _balances;
// Mapping from token ID to approved addressmapping(uint256=>address) private _tokenApprovals;
// Mapping from owner to operator approvalsmapping(address=>mapping(address=>bool)) private _operatorApprovals;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/constructor(stringmemory name_, stringmemory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverride(ERC165, IERC165) returns (bool) {
return
interfaceId ==type(IERC721).interfaceId||
interfaceId ==type(IERC721Metadata).interfaceId||super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/functionbalanceOf(address owner) publicviewvirtualoverridereturns (uint256) {
require(owner !=address(0), "ERC721: balance query for the zero address");
return _balances[owner];
}
/**
* @dev See {IERC721-ownerOf}.
*/functionownerOf(uint256 tokenId) publicviewvirtualoverridereturns (address) {
address owner = _owners[tokenId];
require(owner !=address(0), "ERC721: owner query for nonexistent token");
return owner;
}
/**
* @dev See {IERC721Metadata-name}.
*/functionname() publicviewvirtualoverridereturns (stringmemory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/functionsymbol() publicviewvirtualoverridereturns (stringmemory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/functiontokenURI(uint256 tokenId) publicviewvirtualoverridereturns (stringmemory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
stringmemory baseURI = _baseURI();
returnbytes(baseURI).length>0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overriden in child contracts.
*/function_baseURI() internalviewvirtualreturns (stringmemory) {
return"";
}
/**
* @dev See {IERC721-approve}.
*/functionapprove(address to, uint256 tokenId) publicvirtualoverride{
address owner = ERC721.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(
_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not owner nor approved for all"
);
_approve(to, tokenId);
}
/**
* @dev See {IERC721-getApproved}.
*/functiongetApproved(uint256 tokenId) publicviewvirtualoverridereturns (address) {
require(_exists(tokenId), "ERC721: approved query for nonexistent token");
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/functionsetApprovalForAll(address operator, bool approved) publicvirtualoverride{
_setApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/functionisApprovedForAll(address owner, address operator) publicviewvirtualoverridereturns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/functiontransferFrom(addressfrom,
address to,
uint256 tokenId
) publicvirtualoverride{
//solhint-disable-next-line max-line-lengthrequire(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 tokenId
) publicvirtualoverride{
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 tokenId,
bytesmemory _data
) publicvirtualoverride{
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_safeTransfer(from, to, tokenId, _data);
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* `_data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/function_safeTransfer(addressfrom,
address to,
uint256 tokenId,
bytesmemory _data
) internalvirtual{
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
* and stop existing when they are burned (`_burn`).
*/function_exists(uint256 tokenId) internalviewvirtualreturns (bool) {
return _owners[tokenId] !=address(0);
}
/**
* @dev Returns whether `spender` is allowed to manage `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/function_isApprovedOrOwner(address spender, uint256 tokenId) internalviewvirtualreturns (bool) {
require(_exists(tokenId), "ERC721: operator query for nonexistent token");
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
}
/**
* @dev Safely mints `tokenId` and transfers it to `to`.
*
* Requirements:
*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/function_safeMint(address to, uint256 tokenId) internalvirtual{
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/function_safeMint(address to,
uint256 tokenId,
bytesmemory _data
) internalvirtual{
_mint(to, tokenId);
require(
_checkOnERC721Received(address(0), to, tokenId, _data),
"ERC721: transfer to non ERC721Receiver implementer"
);
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/function_mint(address to, uint256 tokenId) internalvirtual{
require(to !=address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId);
_balances[to] +=1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/function_burn(uint256 tokenId) internalvirtual{
address owner = ERC721.ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId);
// Clear approvals
_approve(address(0), tokenId);
_balances[owner] -=1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/function_transfer(addressfrom,
address to,
uint256 tokenId
) internalvirtual{
require(ERC721.ownerOf(tokenId) ==from, "ERC721: transfer of token that is not own");
require(to !=address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
// Clear approvals from the previous owner
_approve(address(0), tokenId);
_balances[from] -=1;
_balances[to] +=1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits a {Approval} event.
*/function_approve(address to, uint256 tokenId) internalvirtual{
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
}
/**
* @dev Approve `operator` to operate on all of `owner` tokens
*
* Emits a {ApprovalForAll} event.
*/function_setApprovalForAll(address owner,
address operator,
bool approved
) internalvirtual{
require(owner != operator, "ERC721: approve to caller");
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
* The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/function_checkOnERC721Received(addressfrom,
address to,
uint256 tokenId,
bytesmemory _data
) privatereturns (bool) {
if (to.isContract()) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
return retval == IERC721Receiver.onERC721Received.selector;
} catch (bytesmemory reason) {
if (reason.length==0) {
revert("ERC721: transfer to non ERC721Receiver implementer");
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
returntrue;
}
}
/**
* @dev Hook that is called before any token transfer. This includes minting
* and burning.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, ``from``'s `tokenId` will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/function_beforeTokenTransfer(addressfrom,
address to,
uint256 tokenId
) internalvirtual{}
}
Contract Source Code
File 5 of 15: IERC165.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/interfaceIERC165{
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}
Contract Source Code
File 6 of 15: IERC721.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)pragmasolidity ^0.8.0;import"../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/interfaceIERC721isIERC165{
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/eventApproval(addressindexed owner, addressindexed approved, uint256indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/eventApprovalForAll(addressindexed owner, addressindexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/functionbalanceOf(address owner) externalviewreturns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/functionownerOf(uint256 tokenId) externalviewreturns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 tokenId
) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom,
address to,
uint256 tokenId
) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/functionapprove(address to, uint256 tokenId) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/functiongetApproved(uint256 tokenId) externalviewreturns (address operator);
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/functionsetApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/functionisApprovedForAll(address owner, address operator) externalviewreturns (bool);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/functionsafeTransferFrom(addressfrom,
address to,
uint256 tokenId,
bytescalldata data
) external;
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)pragmasolidity ^0.8.0;/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/interfaceIERC721Receiver{
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
*/functiononERC721Received(address operator,
addressfrom,
uint256 tokenId,
bytescalldata data
) externalreturns (bytes4);
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)pragmasolidity ^0.8.0;import"../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableisContext{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Contract Source Code
File 11 of 15: PunkTLD.sol
// SPDX-License-Identifier: GPL-3.0-or-laterpragmasolidity ^0.8.4;import"./interfaces/IPunkTLDFactory.sol";
import"./lib/strings.sol";
import"@openzeppelin/contracts/token/ERC721/ERC721.sol";
import"@openzeppelin/contracts/access/Ownable.sol";
import"@openzeppelin/contracts/security/ReentrancyGuard.sol";
import"base64-sol/base64.sol";
/// @title Punk Domains TLD contract (v2)/// @author Tempe Techie/// @notice Dynamically generated NFT contract which represents a top-level domaincontractPunkTLDisERC721, Ownable, ReentrancyGuard{
usingstringsforstring;
uint256public price; // domain priceboolpublic buyingEnabled; // buying domains enabledaddresspublic factoryAddress; // PunkTLDFactory addressuint256public royalty; // share of each domain purchase (in bips) that goes to Punk Domainsuint256public referral =1000; // share of each domain purchase (in bips) that goes to the referrer (referral fee)uint256public totalSupply;
uint256public nameMaxLength =140; // max length of a domain namestringpublic description ="Punk Domains digital identity. Visit https://punk.domains/";
structDomain {
string name; // domain name that goes before the TLD name; example: "tempetechie" in "tempetechie.web3"uint256 tokenId;
address holder;
string data; // stringified JSON object, example: {"description": "Some text", "twitter": "@techie1239", "friends": ["0x123..."], "url": "https://punk.domains"}
}
mapping (string=> Domain) public domains; // mapping (domain name => Domain struct)mapping (uint256=>string) public domainIdsNames; // mapping (tokenId => domain name)mapping (address=>string) public defaultNames; // user's default domaineventDomainCreated(addressindexed user, addressindexed owner, string fullDomainName);
eventDefaultDomainChanged(addressindexed user, string defaultDomain);
eventDataChanged(addressindexed user);
eventTldPriceChanged(addressindexed user, uint256 tldPrice);
eventReferralFeeChanged(addressindexed user, uint256 referralFee);
eventTldRoyaltyChanged(addressindexed user, uint256 tldRoyalty);
eventDomainBuyingToggle(addressindexed user, bool domainBuyingToggle);
constructor(stringmemory _name,
stringmemory _symbol,
address _tldOwner,
uint256 _domainPrice,
bool _buyingEnabled,
uint256 _royalty,
address _factoryAddress
) ERC721(_name, _symbol) {
price = _domainPrice;
buyingEnabled = _buyingEnabled;
royalty = _royalty;
factoryAddress = _factoryAddress;
transferOwnership(_tldOwner);
}
// READ// Domain getters - you can also get all Domain data by calling the auto-generated domains(domainName) methodfunctiongetDomainHolder(stringcalldata _domainName) publicviewreturns(address) {
return domains[strings.lower(_domainName)].holder;
}
functiongetDomainData(stringcalldata _domainName) publicviewreturns(stringmemory) {
return domains[strings.lower(_domainName)].data; // should be a JSON object
}
functiongetFactoryOwner() publicviewreturns(address) {
Ownable factory = Ownable(factoryAddress);
return factory.owner();
}
functiontokenURI(uint256 _tokenId) publicviewoverridereturns (stringmemory) {
IPunkTLDFactory factory = IPunkTLDFactory(factoryAddress);
stringmemory fullDomainName =string(abi.encodePacked(domains[domainIdsNames[_tokenId]].name, name()));
returnstring(
abi.encodePacked("data:application/json;base64,",Base64.encode(bytes(abi.encodePacked(
'{"name": "', fullDomainName, '", ',
'"description": "', description, '", ',
'"image": "', _getImage(fullDomainName, factory.projectName()), '"}'))))
);
}
function_getImage(stringmemory _fullDomainName, stringmemory _projectName) internalpurereturns (stringmemory) {
stringmemory svgBase64Encoded = Base64.encode(bytes(string(abi.encodePacked(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" width="500" height="500"><defs><linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" style="stop-color:rgb(58,17,116);stop-opacity:1" /><stop offset="100%" style="stop-color:rgb(116,25,17);stop-opacity:1" /></linearGradient></defs><rect x="0" y="0" width="500" height="500" fill="url(#grad)"/><text x="50%" y="50%" dominant-baseline="middle" fill="white" text-anchor="middle" font-size="x-large">',
_fullDomainName,'</text><text x="50%" y="70%" dominant-baseline="middle" fill="white" text-anchor="middle">',
_projectName,'</text>',
'</svg>'
))));
returnstring(abi.encodePacked("data:image/svg+xml;base64,",svgBase64Encoded));
}
// WRITEfunctioneditDefaultDomain(stringcalldata _domainName) external{
require(domains[_domainName].holder ==msg.sender, "You do not own the selected domain");
defaultNames[msg.sender] = _domainName;
emit DefaultDomainChanged(msg.sender, _domainName);
}
/// @notice Edit domain custom data. Make sure to not accidentally delete previous data. Fetch previous data first./// @param _domainName Only domain name, no TLD/extension./// @param _data Custom data needs to be in a JSON object format.functioneditData(stringcalldata _domainName, stringcalldata _data) external{
require(domains[_domainName].holder ==msg.sender, "Only domain holder can edit their data");
domains[_domainName].data = _data;
emit DataChanged(msg.sender);
}
/// @notice Mint a new domain name as NFT (no dots and spaces allowed)./// @param _domainName Enter domain name without TLD and make sure letters are in lowercase form./// @return token IDfunctionmint(stringmemory _domainName,
address _domainHolder,
address _referrer
) externalpayablenonReentrantreturns(uint256) {
require(buyingEnabled ||msg.sender== owner(), "Buying TLDs disabled");
require(msg.value>= price, "Value below price");
_sendPayment(msg.value, _referrer);
return _mintDomain(_domainName, _domainHolder, "");
}
function_mintDomain(stringmemory _domainNameRaw,
address _domainHolder,
stringmemory _data
) internalreturns(uint256) {
// convert domain name to lowercase (only works for ascii, clients should enforce ascii domains only)stringmemory _domainName = strings.lower(_domainNameRaw);
require(strings.len(strings.toSlice(_domainName)) >1, "Domain must be longer than 1 char");
require(bytes(_domainName).length< nameMaxLength, "Domain name is too long");
require(strings.count(strings.toSlice(_domainName), strings.toSlice(".")) ==0, "There should be no dots in the name");
require(strings.count(strings.toSlice(_domainName), strings.toSlice(" ")) ==0, "There should be no spaces in the name");
require(domains[_domainName].holder ==address(0), "Domain with this name already exists");
_safeMint(_domainHolder, totalSupply);
Domain memory newDomain;
// store data in Domain struct
newDomain.name= _domainName;
newDomain.tokenId = totalSupply;
newDomain.holder = _domainHolder;
newDomain.data = _data;
// add to both mappings
domains[_domainName] = newDomain;
domainIdsNames[totalSupply] = _domainName;
if (bytes(defaultNames[_domainHolder]).length==0) {
defaultNames[_domainHolder] = _domainName; // if default domain name is not set for that holder, set it now
}
emit DomainCreated(msg.sender, _domainHolder, string(abi.encodePacked(_domainName, name())));
++totalSupply;
return totalSupply-1;
}
function_sendPayment(uint256 _paymentAmount, address _referrer) internal{
if (royalty >0&& royalty <5000) {
// send royalty - must be less than 50% (5000 bips)
(bool sentRoyalty, ) =payable(getFactoryOwner()).call{value: ((_paymentAmount * royalty) /10000)}("");
require(sentRoyalty, "Failed to send royalty to factory owner");
}
if (_referrer !=address(0) && referral >0&& referral <5000) {
// send referral fee - must be less than 50% (5000 bips)
(bool sentReferralFee, ) =payable(_referrer).call{value: ((_paymentAmount * referral) /10000)}("");
require(sentReferralFee, "Failed to send referral fee");
}
// send the rest to TLD owner
(bool sent, ) =payable(owner()).call{value: address(this).balance}("");
require(sent, "Failed to send domain payment to TLD owner");
}
///@dev Hook that is called before any token transfer. This includes minting and burning.function_beforeTokenTransfer(addressfrom,address to,uint256 tokenId) internaloverridevirtual{
if (from!=address(0)) { // run on every transfer but not on mint
domains[domainIdsNames[tokenId]].holder = to; // change holder address in Domain struct
domains[domainIdsNames[tokenId]].data =""; // reset custom dataif (bytes(defaultNames[to]).length==0) {
defaultNames[to] = domains[domainIdsNames[tokenId]].name; // if default domain name is not set for that holder, set it now
}
if (strings.equals(strings.toSlice(domains[domainIdsNames[tokenId]].name), strings.toSlice(defaultNames[from]))) {
defaultNames[from] =""; // if previous owner had this domain name as default, unset it as default
}
}
}
// OWNER/// @notice Only TLD contract owner can call this function.functionchangeDescription(stringcalldata _description) externalonlyOwner{
description = _description;
}
/// @notice Only TLD contract owner can call this function.functionchangeNameMaxLength(uint256 _maxLength) externalonlyOwner{
nameMaxLength = _maxLength;
}
/// @notice Only TLD contract owner can call this function.functionchangePrice(uint256 _price) externalonlyOwner{
price = _price;
emit TldPriceChanged(msg.sender, _price);
}
/// @notice Only TLD contract owner can call this function.functionchangeReferralFee(uint256 _referral) externalonlyOwner{
require(_referral <5000, "Referral fee cannot be 50% or higher");
referral = _referral; // referral must be in bipsemit ReferralFeeChanged(msg.sender, _referral);
}
/// @notice Only TLD contract owner can call this function.functiontoggleBuyingDomains() externalonlyOwner{
buyingEnabled =!buyingEnabled;
emit DomainBuyingToggle(msg.sender, buyingEnabled);
}
// FACTORY OWNER (current owner address of PunkTLDFactory)/// @notice Only Factory contract owner can call this function.functionchangeRoyalty(uint256 _royalty) external{
require(getFactoryOwner() ==msg.sender, "Sender not factory owner");
require(_royalty <5000, "Royalty cannot be 50% or higher");
royalty = _royalty; // royalty is in bipsemit TldRoyaltyChanged(msg.sender, _royalty);
}
}
Contract Source Code
File 12 of 15: ReentrancyGuard.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)pragmasolidity ^0.8.0;/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/abstractcontractReentrancyGuard{
// Booleans are more expensive than uint256 or any type that takes up a full// word because each write operation emits an extra SLOAD to first read the// slot's contents, replace the bits taken up by the boolean, and then write// back. This is the compiler's defense against contract upgrades and// pointer aliasing, and it cannot be disabled.// The values being non-zero value makes deployment a bit more expensive,// but in exchange the refund on every call to nonReentrant will be lower in// amount. Since refunds are capped to a percentage of the total// transaction's gas, it is best to keep them low in cases like this one, to// increase the likelihood of the full refund coming into effect.uint256privateconstant _NOT_ENTERED =1;
uint256privateconstant _ENTERED =2;
uint256private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/modifiernonReentrant() {
// On the first call to nonReentrant, _notEntered will be truerequire(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
Contract Source Code
File 13 of 15: Strings.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)pragmasolidity ^0.8.0;/**
* @dev String operations.
*/libraryStrings{
bytes16privateconstant _HEX_SYMBOLS ="0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/functiontoString(uint256 value) internalpurereturns (stringmemory) {
// Inspired by OraclizeAPI's implementation - MIT licence// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.solif (value ==0) {
return"0";
}
uint256 temp = value;
uint256 digits;
while (temp !=0) {
digits++;
temp /=10;
}
bytesmemory buffer =newbytes(digits);
while (value !=0) {
digits -=1;
buffer[digits] =bytes1(uint8(48+uint256(value %10)));
value /=10;
}
returnstring(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/functiontoHexString(uint256 value) internalpurereturns (stringmemory) {
if (value ==0) {
return"0x00";
}
uint256 temp = value;
uint256 length =0;
while (temp !=0) {
length++;
temp >>=8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/functiontoHexString(uint256 value, uint256 length) internalpurereturns (stringmemory) {
bytesmemory buffer =newbytes(2* length +2);
buffer[0] ="0";
buffer[1] ="x";
for (uint256 i =2* length +1; i >1; --i) {
buffer[i] = _HEX_SYMBOLS[value &0xf];
value >>=4;
}
require(value ==0, "Strings: hex length insufficient");
returnstring(buffer);
}
}
Contract Source Code
File 14 of 15: base64.sol
// SPDX-License-Identifier: MIT/// @title Base64/// @author Brecht Devos - <brecht@loopring.org>/// @notice Provides a function for encoding some bytes in base64libraryBase64{
stringinternalconstant TABLE ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
functionencode(bytesmemory data) internalpurereturns (stringmemory) {
if (data.length==0) return'';
// load the table into memorystringmemory table = TABLE;
// multiply by 4/3 rounded upuint256 encodedLen =4* ((data.length+2) /3);
// add some extra buffer at the end required for the writingstringmemory result =newstring(encodedLen +32);
assembly {
// set the actual output lengthmstore(result, encodedLen)
// prepare the lookup tablelet tablePtr :=add(table, 1)
// input ptrlet dataPtr := data
let endPtr :=add(dataPtr, mload(data))
// result ptr, jump over lengthlet resultPtr :=add(result, 32)
// run over the input, 3 bytes at a timefor {} lt(dataPtr, endPtr) {}
{
dataPtr :=add(dataPtr, 3)
// read 3 byteslet input :=mload(dataPtr)
// write 4 charactersmstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(18, input), 0x3F)))))
resultPtr :=add(resultPtr, 1)
mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(12, input), 0x3F)))))
resultPtr :=add(resultPtr, 1)
mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr( 6, input), 0x3F)))))
resultPtr :=add(resultPtr, 1)
mstore(resultPtr, shl(248, mload(add(tablePtr, and( input, 0x3F)))))
resultPtr :=add(resultPtr, 1)
}
// padding with '='switchmod(mload(data), 3)
case1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) }
case2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) }
}
return result;
}
}
Contract Source Code
File 15 of 15: strings.sol
// SPDX-License-Identifier: Apache-2.0/*
* @title String & slice utility library for Solidity contracts.
* @author Nick Johnson <arachnid@notdot.net>
*/pragmasolidity ^0.8.0;librarystrings{
structslice {
uint _len;
uint _ptr;
}
functionmemcpy(uint dest, uint src, uint _len) privatepure{
// Copy word-length chunks while possiblefor(; _len >=32; _len -=32) {
assembly {
mstore(dest, mload(src))
}
dest +=32;
src +=32;
}
// Copy remaining bytesuint mask =type(uint).max;
if (_len >0) {
mask =256** (32- _len) -1;
}
assembly {
let srcpart :=and(mload(src), not(mask))
let destpart :=and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
/*
* @dev Returns a slice containing the entire string.
* @param self The string to make a slice from.
* @return A newly allocated slice containing the entire string.
*/functiontoSlice(stringmemoryself) internalpurereturns (slice memory) {
uint ptr;
assembly {
ptr :=add(self, 0x20)
}
return slice(bytes(self).length, ptr);
}
/*
* @dev Returns the length of a null-terminated bytes32 string.
* @param self The value to find the length of.
* @return The length of the string, from 0 to 32.
*/functionlen(bytes32self) internalpurereturns (uint) {
uint ret;
if (self==0)
return0;
if (uint(self) &type(uint128).max==0) {
ret +=16;
self=bytes32(uint(self) /0x100000000000000000000000000000000);
}
if (uint(self) &type(uint64).max==0) {
ret +=8;
self=bytes32(uint(self) /0x10000000000000000);
}
if (uint(self) &type(uint32).max==0) {
ret +=4;
self=bytes32(uint(self) /0x100000000);
}
if (uint(self) &type(uint16).max==0) {
ret +=2;
self=bytes32(uint(self) /0x10000);
}
if (uint(self) &type(uint8).max==0) {
ret +=1;
}
return32- ret;
}
/*
* @dev Returns a slice containing the entire bytes32, interpreted as a
* null-terminated utf-8 string.
* @param self The bytes32 value to convert to a slice.
* @return A new slice containing the value of the input argument up to the
* first null.
*/functiontoSliceB32(bytes32self) internalpurereturns (slice memory ret) {
// Allocate space for `self` in memory, copy it there, and point ret at itassembly {
let ptr :=mload(0x40)
mstore(0x40, add(ptr, 0x20))
mstore(ptr, self)
mstore(add(ret, 0x20), ptr)
}
ret._len = len(self);
}
/*
* @dev Returns a new slice containing the same data as the current slice.
* @param self The slice to copy.
* @return A new slice containing the same data as `self`.
*/functioncopy(slice memoryself) internalpurereturns (slice memory) {
return slice(self._len, self._ptr);
}
/*
* @dev Copies a slice to a new string.
* @param self The slice to copy.
* @return A newly allocated string containing the slice's text.
*/functiontoString(slice memoryself) internalpurereturns (stringmemory) {
stringmemory ret =newstring(self._len);
uint retptr;
assembly { retptr :=add(ret, 32) }
memcpy(retptr, self._ptr, self._len);
return ret;
}
/*
* @dev Returns the length in runes of the slice. Note that this operation
* takes time proportional to the length of the slice; avoid using it
* in loops, and call `slice.empty()` if you only need to know whether
* the slice is empty or not.
* @param self The slice to operate on.
* @return The length of the slice in runes.
*/functionlen(slice memoryself) internalpurereturns (uint l) {
// Starting at ptr-31 means the LSB will be the byte we care aboutuint ptr =self._ptr -31;
uint end = ptr +self._len;
for (l =0; ptr < end; l++) {
uint8 b;
assembly { b :=and(mload(ptr), 0xFF) }
if (b <0x80) {
ptr +=1;
} elseif(b <0xE0) {
ptr +=2;
} elseif(b <0xF0) {
ptr +=3;
} elseif(b <0xF8) {
ptr +=4;
} elseif(b <0xFC) {
ptr +=5;
} else {
ptr +=6;
}
}
}
/*
* @dev Returns true if the slice is empty (has a length of 0).
* @param self The slice to operate on.
* @return True if the slice is empty, False otherwise.
*/functionempty(slice memoryself) internalpurereturns (bool) {
returnself._len ==0;
}
/*
* @dev Returns a positive number if `other` comes lexicographically after
* `self`, a negative number if it comes before, or zero if the
* contents of the two slices are equal. Comparison is done per-rune,
* on unicode codepoints.
* @param self The first slice to compare.
* @param other The second slice to compare.
* @return The result of the comparison.
*/functioncompare(slice memoryself, slice memory other) internalpurereturns (int) {
uint shortest =self._len;
if (other._len <self._len)
shortest = other._len;
uint selfptr =self._ptr;
uint otherptr = other._ptr;
for (uint idx =0; idx < shortest; idx +=32) {
uint a;
uint b;
assembly {
a :=mload(selfptr)
b :=mload(otherptr)
}
if (a != b) {
// Mask out irrelevant bytes and check againuint mask =type(uint).max; // 0xffff...if(shortest <32) {
mask =~(2** (8* (32- shortest + idx)) -1);
}
unchecked {
uint diff = (a & mask) - (b & mask);
if (diff !=0)
returnint(diff);
}
}
selfptr +=32;
otherptr +=32;
}
returnint(self._len) -int(other._len);
}
/*
* @dev Returns true if the two slices contain the same text.
* @param self The first slice to compare.
* @param self The second slice to compare.
* @return True if the slices are equal, false otherwise.
*/functionequals(slice memoryself, slice memory other) internalpurereturns (bool) {
return compare(self, other) ==0;
}
/*
* @dev Extracts the first rune in the slice into `rune`, advancing the
* slice to point to the next rune and returning `self`.
* @param self The slice to operate on.
* @param rune The slice that will contain the first rune.
* @return `rune`.
*/functionnextRune(slice memoryself, slice memory rune) internalpurereturns (slice memory) {
rune._ptr =self._ptr;
if (self._len ==0) {
rune._len =0;
return rune;
}
uint l;
uint b;
// Load the first byte of the rune into the LSBs of bassembly { b :=and(mload(sub(mload(add(self, 32)), 31)), 0xFF) }
if (b <0x80) {
l =1;
} elseif(b <0xE0) {
l =2;
} elseif(b <0xF0) {
l =3;
} else {
l =4;
}
// Check for truncated codepointsif (l >self._len) {
rune._len =self._len;
self._ptr +=self._len;
self._len =0;
return rune;
}
self._ptr += l;
self._len -= l;
rune._len = l;
return rune;
}
/*
* @dev Returns the first rune in the slice, advancing the slice to point
* to the next rune.
* @param self The slice to operate on.
* @return A slice containing only the first rune from `self`.
*/functionnextRune(slice memoryself) internalpurereturns (slice memory ret) {
nextRune(self, ret);
}
/*
* @dev Returns the number of the first codepoint in the slice.
* @param self The slice to operate on.
* @return The number of the first codepoint in the slice.
*/functionord(slice memoryself) internalpurereturns (uint ret) {
if (self._len ==0) {
return0;
}
uint word;
uint length;
uint divisor =2**248;
// Load the rune into the MSBs of bassembly { word:=mload(mload(add(self, 32))) }
uint b = word / divisor;
if (b <0x80) {
ret = b;
length =1;
} elseif(b <0xE0) {
ret = b &0x1F;
length =2;
} elseif(b <0xF0) {
ret = b &0x0F;
length =3;
} else {
ret = b &0x07;
length =4;
}
// Check for truncated codepointsif (length >self._len) {
return0;
}
for (uint i =1; i < length; i++) {
divisor = divisor /256;
b = (word / divisor) &0xFF;
if (b &0xC0!=0x80) {
// Invalid UTF-8 sequencereturn0;
}
ret = (ret *64) | (b &0x3F);
}
return ret;
}
/*
* @dev Returns the keccak-256 hash of the slice.
* @param self The slice to hash.
* @return The hash of the slice.
*/functionkeccak(slice memoryself) internalpurereturns (bytes32 ret) {
assembly {
ret :=keccak256(mload(add(self, 32)), mload(self))
}
}
/*
* @dev Returns true if `self` starts with `needle`.
* @param self The slice to operate on.
* @param needle The slice to search for.
* @return True if the slice starts with the provided text, false otherwise.
*/functionstartsWith(slice memoryself, slice memory needle) internalpurereturns (bool) {
if (self._len < needle._len) {
returnfalse;
}
if (self._ptr == needle._ptr) {
returntrue;
}
bool equal;
assembly {
let length :=mload(needle)
let selfptr :=mload(add(self, 0x20))
let needleptr :=mload(add(needle, 0x20))
equal :=eq(keccak256(selfptr, length), keccak256(needleptr, length))
}
return equal;
}
/*
* @dev If `self` starts with `needle`, `needle` is removed from the
* beginning of `self`. Otherwise, `self` is unmodified.
* @param self The slice to operate on.
* @param needle The slice to search for.
* @return `self`
*/functionbeyond(slice memoryself, slice memory needle) internalpurereturns (slice memory) {
if (self._len < needle._len) {
returnself;
}
bool equal =true;
if (self._ptr != needle._ptr) {
assembly {
let length :=mload(needle)
let selfptr :=mload(add(self, 0x20))
let needleptr :=mload(add(needle, 0x20))
equal :=eq(keccak256(selfptr, length), keccak256(needleptr, length))
}
}
if (equal) {
self._len -= needle._len;
self._ptr += needle._len;
}
returnself;
}
/*
* @dev Returns true if the slice ends with `needle`.
* @param self The slice to operate on.
* @param needle The slice to search for.
* @return True if the slice starts with the provided text, false otherwise.
*/functionendsWith(slice memoryself, slice memory needle) internalpurereturns (bool) {
if (self._len < needle._len) {
returnfalse;
}
uint selfptr =self._ptr +self._len - needle._len;
if (selfptr == needle._ptr) {
returntrue;
}
bool equal;
assembly {
let length :=mload(needle)
let needleptr :=mload(add(needle, 0x20))
equal :=eq(keccak256(selfptr, length), keccak256(needleptr, length))
}
return equal;
}
/*
* @dev If `self` ends with `needle`, `needle` is removed from the
* end of `self`. Otherwise, `self` is unmodified.
* @param self The slice to operate on.
* @param needle The slice to search for.
* @return `self`
*/functionuntil(slice memoryself, slice memory needle) internalpurereturns (slice memory) {
if (self._len < needle._len) {
returnself;
}
uint selfptr =self._ptr +self._len - needle._len;
bool equal =true;
if (selfptr != needle._ptr) {
assembly {
let length :=mload(needle)
let needleptr :=mload(add(needle, 0x20))
equal :=eq(keccak256(selfptr, length), keccak256(needleptr, length))
}
}
if (equal) {
self._len -= needle._len;
}
returnself;
}
// Returns the memory address of the first byte of the first occurrence of// `needle` in `self`, or the first byte after `self` if not found.functionfindPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) privatepurereturns (uint) {
uint ptr = selfptr;
uint idx;
if (needlelen <= selflen) {
if (needlelen <=32) {
bytes32 mask;
if (needlelen >0) {
mask =bytes32(~(2** (8* (32- needlelen)) -1));
}
bytes32 needledata;
assembly { needledata :=and(mload(needleptr), mask) }
uint end = selfptr + selflen - needlelen;
bytes32 ptrdata;
assembly { ptrdata :=and(mload(ptr), mask) }
while (ptrdata != needledata) {
if (ptr >= end)
return selfptr + selflen;
ptr++;
assembly { ptrdata :=and(mload(ptr), mask) }
}
return ptr;
} else {
// For long needles, use hashingbytes32 hash;
assembly { hash :=keccak256(needleptr, needlelen) }
for (idx =0; idx <= selflen - needlelen; idx++) {
bytes32 testHash;
assembly { testHash :=keccak256(ptr, needlelen) }
if (hash == testHash)
return ptr;
ptr +=1;
}
}
}
return selfptr + selflen;
}
// Returns the memory address of the first byte after the last occurrence of// `needle` in `self`, or the address of `self` if not found.functionrfindPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) privatepurereturns (uint) {
uint ptr;
if (needlelen <= selflen) {
if (needlelen <=32) {
bytes32 mask;
if (needlelen >0) {
mask =bytes32(~(2** (8* (32- needlelen)) -1));
}
bytes32 needledata;
assembly { needledata :=and(mload(needleptr), mask) }
ptr = selfptr + selflen - needlelen;
bytes32 ptrdata;
assembly { ptrdata :=and(mload(ptr), mask) }
while (ptrdata != needledata) {
if (ptr <= selfptr)
return selfptr;
ptr--;
assembly { ptrdata :=and(mload(ptr), mask) }
}
return ptr + needlelen;
} else {
// For long needles, use hashingbytes32 hash;
assembly { hash :=keccak256(needleptr, needlelen) }
ptr = selfptr + (selflen - needlelen);
while (ptr >= selfptr) {
bytes32 testHash;
assembly { testHash :=keccak256(ptr, needlelen) }
if (hash == testHash)
return ptr + needlelen;
ptr -=1;
}
}
}
return selfptr;
}
/*
* @dev Modifies `self` to contain everything from the first occurrence of
* `needle` to the end of the slice. `self` is set to the empty slice
* if `needle` is not found.
* @param self The slice to search and modify.
* @param needle The text to search for.
* @return `self`.
*/functionfind(slice memoryself, slice memory needle) internalpurereturns (slice memory) {
uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr);
self._len -= ptr -self._ptr;
self._ptr = ptr;
returnself;
}
/*
* @dev Modifies `self` to contain the part of the string from the start of
* `self` to the end of the first occurrence of `needle`. If `needle`
* is not found, `self` is set to the empty slice.
* @param self The slice to search and modify.
* @param needle The text to search for.
* @return `self`.
*/functionrfind(slice memoryself, slice memory needle) internalpurereturns (slice memory) {
uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr);
self._len = ptr -self._ptr;
returnself;
}
/*
* @dev Splits the slice, setting `self` to everything after the first
* occurrence of `needle`, and `token` to everything before it. If
* `needle` does not occur in `self`, `self` is set to the empty slice,
* and `token` is set to the entirety of `self`.
* @param self The slice to split.
* @param needle The text to search for in `self`.
* @param token An output parameter to which the first token is written.
* @return `token`.
*/functionsplit(slice memoryself, slice memory needle, slice memory token) internalpurereturns (slice memory) {
uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr);
token._ptr =self._ptr;
token._len = ptr -self._ptr;
if (ptr ==self._ptr +self._len) {
// Not foundself._len =0;
} else {
self._len -= token._len + needle._len;
self._ptr = ptr + needle._len;
}
return token;
}
/*
* @dev Splits the slice, setting `self` to everything after the first
* occurrence of `needle`, and returning everything before it. If
* `needle` does not occur in `self`, `self` is set to the empty slice,
* and the entirety of `self` is returned.
* @param self The slice to split.
* @param needle The text to search for in `self`.
* @return The part of `self` up to the first occurrence of `delim`.
*/functionsplit(slice memoryself, slice memory needle) internalpurereturns (slice memory token) {
split(self, needle, token);
}
/*
* @dev Splits the slice, setting `self` to everything before the last
* occurrence of `needle`, and `token` to everything after it. If
* `needle` does not occur in `self`, `self` is set to the empty slice,
* and `token` is set to the entirety of `self`.
* @param self The slice to split.
* @param needle The text to search for in `self`.
* @param token An output parameter to which the first token is written.
* @return `token`.
*/functionrsplit(slice memoryself, slice memory needle, slice memory token) internalpurereturns (slice memory) {
uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr);
token._ptr = ptr;
token._len =self._len - (ptr -self._ptr);
if (ptr ==self._ptr) {
// Not foundself._len =0;
} else {
self._len -= token._len + needle._len;
}
return token;
}
/*
* @dev Splits the slice, setting `self` to everything before the last
* occurrence of `needle`, and returning everything after it. If
* `needle` does not occur in `self`, `self` is set to the empty slice,
* and the entirety of `self` is returned.
* @param self The slice to split.
* @param needle The text to search for in `self`.
* @return The part of `self` after the last occurrence of `delim`.
*/functionrsplit(slice memoryself, slice memory needle) internalpurereturns (slice memory token) {
rsplit(self, needle, token);
}
/*
* @dev Counts the number of nonoverlapping occurrences of `needle` in `self`.
* @param self The slice to search.
* @param needle The text to search for in `self`.
* @return The number of occurrences of `needle` found in `self`.
*/functioncount(slice memoryself, slice memory needle) internalpurereturns (uint cnt) {
uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len;
while (ptr <=self._ptr +self._len) {
cnt++;
ptr = findPtr(self._len - (ptr -self._ptr), ptr, needle._len, needle._ptr) + needle._len;
}
}
/*
* @dev Returns True if `self` contains `needle`.
* @param self The slice to search.
* @param needle The text to search for in `self`.
* @return True if `needle` is found in `self`, false otherwise.
*/functioncontains(slice memoryself, slice memory needle) internalpurereturns (bool) {
return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) !=self._ptr;
}
/*
* @dev Returns a newly allocated string containing the concatenation of
* `self` and `other`.
* @param self The first slice to concatenate.
* @param other The second slice to concatenate.
* @return The concatenation of the two strings.
*/functionconcat(slice memoryself, slice memory other) internalpurereturns (stringmemory) {
stringmemory ret =newstring(self._len + other._len);
uint retptr;
assembly { retptr :=add(ret, 32) }
memcpy(retptr, self._ptr, self._len);
memcpy(retptr +self._len, other._ptr, other._len);
return ret;
}
/*
* @dev Joins an array of slices, using `self` as a delimiter, returning a
* newly allocated string.
* @param self The delimiter to use.
* @param parts A list of slices to join.
* @return A newly allocated string containing all the slices in `parts`,
* joined with `self`.
*/functionjoin(slice memoryself, slice[] memory parts) internalpurereturns (stringmemory) {
if (parts.length==0)
return"";
uint length =self._len * (parts.length-1);
for(uint i =0; i < parts.length; i++)
length += parts[i]._len;
stringmemory ret =newstring(length);
uint retptr;
assembly { retptr :=add(ret, 32) }
for(uint i =0; i < parts.length; i++) {
memcpy(retptr, parts[i]._ptr, parts[i]._len);
retptr += parts[i]._len;
if (i < parts.length-1) {
memcpy(retptr, self._ptr, self._len);
retptr +=self._len;
}
}
return ret;
}
/**
* Lower
*
* Converts all the values of a string to their corresponding lower case
* value.
*
* @param _base When being used for a data type this is the extended object
* otherwise this is the string base to convert to lower case
* @return string
*/functionlower(stringmemory _base)
internalpurereturns (stringmemory) {
bytesmemory _baseBytes =bytes(_base);
for (uint i =0; i < _baseBytes.length; i++) {
_baseBytes[i] = _lower(_baseBytes[i]);
}
returnstring(_baseBytes);
}
/**
* Lower
*
* Convert an alphabetic character to lower case and return the original
* value when not alphabetic
*
* @param _b1 The byte to be converted to lower case
* @return bytes1 The converted value if the passed value was alphabetic
* and in a upper case otherwise returns the original value
*/function_lower(bytes1 _b1)
privatepurereturns (bytes1) {
if (_b1 >=0x41&& _b1 <=0x5A) {
returnbytes1(uint8(_b1) +32);
}
return _b1;
}
}