Contract Source Code:
File 1 of 1 : Wrapped721
// SPDX-License-Identifier: MIT
pragma solidity <0.9.0 >=0.8.4 ^0.8.1 ^0.8.2;
// node_modules/@lambdalf-dev/ethereum-contracts/contracts/interfaces/IERC165.sol
/**
* @dev Required interface of an ERC165 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
* Note: the ERC-165 identifier for this interface is 0x01ffc9a7.
*/
interface IERC165 {
// **************************************
// ***** VIEW *****
// **************************************
/// @dev Returns if a contract implements an interface.
/// Interface identification is specified in ERC-165. This function uses less than 30,000 gas.
function supportsInterface(bytes4 interfaceId_) external view returns (bool);
// **************************************
}
// node_modules/@lambdalf-dev/ethereum-contracts/contracts/interfaces/IERC173.sol
// import "./IERC165.sol";
/**
* @dev Required interface of an ERC173 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-173[EIP].
* Note: the ERC-165 identifier for this interface is 0x7f5828d0.
*/
interface IERC173 /* is IERC165 */ {
// **************************************
// ***** ERRORS *****
// **************************************
/// @dev Thrown when `operator` is not the contract owner.
///
/// @param operator address trying to use a function reserved to contract owner without authorization
error IERC173_NOT_OWNER(address operator);
// **************************************
// **************************************
// ***** EVENTS *****
// **************************************
/// @dev This emits when ownership of a contract changes.
///
/// @param previousOwner the previous contract owner
/// @param newOwner the new contract owner
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
// **************************************
// **************************************
// ***** CONTRACT_OWNER *****
// **************************************
/// @dev Set the address of the new owner of the contract.
/// Set `newOwner_` to address(0) to renounce any ownership.
function transferOwnership(address newOwner_) external;
// **************************************
// **************************************
// ***** VIEW *****
// **************************************
/// @dev Returns the address of the owner.
function owner() external view returns(address);
// **************************************
}
// node_modules/@lambdalf-dev/ethereum-contracts/contracts/interfaces/IERC2981.sol
// import "./IERC165.sol";
/**
* @dev Required interface of an ERC2981 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-2981[EIP].
* Note: the ERC-165 identifier for this interface is 0x2a55205a.
*/
interface IERC2981 /* is IERC165 */ {
// **************************************
// ***** ERRORS *****
// **************************************
/// @dev Thrown when the desired royalty rate is higher than 10,000
error IERC2981_INVALID_ROYALTIES();
// **************************************
// **************************************
// ***** VIEW *****
// **************************************
/// @dev Called with the sale price to determine how much royalty is owed and to whom.
function royaltyInfo(uint256 tokenId_, uint256 salePrice_)
external view returns (address receiver, uint256 royaltyAmount);
// **************************************
}
// node_modules/@lambdalf-dev/ethereum-contracts/contracts/interfaces/IERC721.sol
// import "./IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-721[EIP].
* Note: the ERC-165 identifier for this interface is 0x80ac58cd.
*/
interface IERC721 /* is IERC165 */ {
// **************************************
// ***** ERRORS *****
// **************************************
/// @dev Thrown when `operator` is not allowed to manage `tokenId`.
///
/// @param operator address trying to manage the token
/// @param tokenId identifier of the NFT being referenced
error IERC721_CALLER_NOT_APPROVED(address operator, uint256 tokenId);
/// @dev Thrown when user tries to approve themselves for managing a token they own.
error IERC721_INVALID_APPROVAL();
/// @dev Thrown when a token is being transferred to a contract unable to handle it or the zero address.
///
/// @param receiver address unable to receive the token
error IERC721_INVALID_RECEIVER(address receiver);
/// @dev Thrown when checking ownership of the wrong token owner.
error IERC721_INVALID_TOKEN_OWNER();
/// @dev Thrown when the requested token doesn"t exist.
///
/// @param tokenId identifier of the NFT being referenced
error IERC721_NONEXISTANT_TOKEN(uint256 tokenId);
// **************************************
// **************************************
// ***** EVENTS *****
// **************************************
/// @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.
///
/// @param owner address that owns the token
/// @param approved address that is allowed to manage the token
/// @param tokenId identifier of the token being approved
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.
///
/// @param owner address that owns the tokens
/// @param operator address that is allowed or not to manage the tokens
/// @param approved whether the operator is allowed or not
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/// @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.
///
/// @param from address the token is being transferred from
/// @param to address the token is being transferred to
/// @param tokenId identifier of the token being transferred
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
// **************************************
// **************************************
// ***** PUBLIC *****
// **************************************
/// @notice Change or reaffirm the approved address for an NFT
/// @dev 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.
function approve(address approved_, uint256 tokenId_) external;
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev 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)"))`.
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
/// @dev This works identically to the other function with an extra data parameter,
/// except this function just sets data to "".
function safeTransferFrom(address from_, address to_, uint256 tokenId_) external;
/// @notice Enable or disable approval for a third party ("operator") to manage all of `msg.sender`'s assets.
/// @dev Emits the ApprovalForAll event. The contract MUST allow multiple operators per owner.
function setApprovalForAll(address operator_, bool approved_) 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
/// @dev 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.
function transferFrom(address from_, address to_, uint256 tokenId_) external;
// **************************************
// **************************************
// ***** VIEW *****
// **************************************
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid. Throws for queries about the zero address.
function balanceOf(address owner_) external view returns (uint256);
/// @notice Get the approved address for a single NFT
/// @dev Throws if `tokenId_` is not a valid NFT.
function getApproved(uint256 tokenId_) external view returns (address);
/// @notice Query if an address is an authorized operator for another address
function isApprovedForAll(address owner_, address operator_) external view returns (bool);
/// @notice Find the owner of an NFT
/// @dev NFTs assigned to zero address are considered invalid, and queries
/// about them do throw.
function ownerOf(uint256 tokenId_) external view returns (address);
// **************************************
}
// node_modules/@lambdalf-dev/ethereum-contracts/contracts/interfaces/IERC721Enumerable.sol
// import "./IERC721.sol";
/**
* @dev Required interface of an ERC721 compliant contract, optional enumeration extension, as defined in the
* https://eips.ethereum.org/EIPS/eip-721[EIP].
* Note: the ERC-165 identifier for this interface is 0x780e9d63.
*/
interface IERC721Enumerable /* is IERC721 */ {
// **************************************
// ***** ERRORS *****
// **************************************
/// @dev Thrown when trying to get the token at an index that doesn"t exist.
///
/// @param index the inexistant index
error IERC721Enumerable_INDEX_OUT_OF_BOUNDS(uint256 index);
/// @dev Thrown when trying to get the token owned by `tokenOwner` at an index that doesn"t exist.
///
/// @param index the inexistant index
error IERC721Enumerable_OWNER_INDEX_OUT_OF_BOUNDS(uint256 index);
// **************************************
// **************************************
// ***** VIEW *****
// **************************************
/// @dev Enumerate valid NFTs
/// Throws if `index_` >= {totalSupply()}.
function tokenByIndex(uint256 index_) external view returns (uint256);
/// @dev Enumerate NFTs assigned to an owner
/// Throws if `index_` >= {balanceOf(owner_)} or if `owner_` is the zero address, representing invalid NFTs.
function tokenOfOwnerByIndex(address owner_, uint256 index_) external view returns (uint256);
/// @dev Count NFTs tracked by this contract
function totalSupply() external view returns (uint256);
// **************************************
}
// node_modules/@lambdalf-dev/ethereum-contracts/contracts/interfaces/IERC721Metadata.sol
// import "./IERC721.sol";
/**
* @dev Required interface of an ERC721 compliant contract, optional metadata extension, as defined in the
* https://eips.ethereum.org/EIPS/eip-721[EIP].
* Note: the ERC-165 identifier for this interface is 0x5b5e139f.
*/
interface IERC721Metadata /* is IERC721 */ {
// **************************************
// ***** VIEW *****
// **************************************
/// @dev A descriptive name for a collection of NFTs in this contract
function name() external view returns (string memory);
/// @dev An abbreviated name for NFTs in this contract
function symbol() external view returns (string memory);
/// @dev A distinct Uniform Resource Identifier (URI) for a given asset.
/// 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);
// **************************************
}
// node_modules/@lambdalf-dev/ethereum-contracts/contracts/interfaces/IERC721Receiver.sol
/**
* @dev Required interface of an ERC721 receiver compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-721[EIP].
* Note: the ERC-165 identifier for this interface is 0x150b7a02.
*/
interface IERC721Receiver {
// **************************************
// ***** VIEW *****
// **************************************
/// @dev Handle the receipt of an NFT
/// 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.
function onERC721Received(
address operator_,
address from_,
uint256 tokenId_,
bytes calldata data_
) external returns(bytes4);
// **************************************
}
// node_modules/@openzeppelin/contracts/utils/Address.sol
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [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;
}
/**
* @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://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.8.0/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");
}
/**
* @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._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "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._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
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._
*/
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");
}
/**
* @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._
*/
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);
}
/**
* @dev 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");
}
/**
* @dev 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);
}
/**
* @dev 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");
}
/**
* @dev 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);
}
/**
* @dev 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);
}
}
/**
* @dev 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);
}
}
}
// node_modules/@openzeppelin/contracts/proxy/utils/Initializable.sol
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}
// src/interfaces/IAsset.sol
interface IAsset is IERC721, IERC721Metadata {}
// src/ERC173Initializable.sol
/**
* Author: Lambdalf the White
*/
abstract contract ERC173Initializable is IERC173, Initializable {
// **************************************
// ***** STORAGE VARIABLES *****
// **************************************
/// @dev The current contract owner.
address private _owner;
// **************************************
function _init_ERC173(address owner_) internal onlyInitializing {
_owner = owner_;
}
// **************************************
// ***** MODIFIERS *****
// **************************************
/// @dev Throws if called by any account other than the owner.
modifier onlyOwner() {
if (owner() != msg.sender) {
revert IERC173_NOT_OWNER(msg.sender);
}
_;
}
// **************************************
// **************************************
// ***** CONTRACT_OWNER *****
// **************************************
/// @dev Transfers ownership of the contract to `newOwner_`.
///
/// @param newOwner_ address of the new contract owner
///
/// Requirements:
///
/// - Caller must be the contract owner.
function transferOwnership(address newOwner_) public virtual override onlyOwner {
address _oldOwner_ = _owner;
_owner = newOwner_;
emit OwnershipTransferred(_oldOwner_, newOwner_);
}
// **************************************
// **************************************
// ***** VIEW *****
// **************************************
/// @dev Returns the address of the current contract owner.
///
/// @return contractOwner the current contract owner
function owner() public view virtual override returns (address contractOwner) {
return _owner;
}
// **************************************
}
// src/ERC2981Initializable.sol
/**
* Author: Lambdalf the White
*/
abstract contract ERC2981Initializable is IERC2981, Initializable {
// **************************************
// ***** DATA TYPES *****
// **************************************
/// @dev A structure representing royalties
struct RoyaltyData {
address recipient;
uint96 rate;
}
// **************************************
// **************************************
// ***** BYTECODE VARIABLES *****
// **************************************
/// @dev Royalty rate is stored out of 10,000 instead of a percentage
/// to allow for up to two digits below the unit such as 2.5% or 1.25%.
uint256 public constant ROYALTY_BASE = 10_000;
// **************************************
// **************************************
// ***** STORAGE VARIABLES *****
// **************************************
/// @dev Represents the royalties on each sale on secondary markets.
/// Set rate to 0 to have no royalties.
RoyaltyData private _royaltyData;
// **************************************
function _init_ERC2981(address royaltyRecipient_, uint96 royaltyRate_) internal onlyInitializing {
_setRoyaltyInfo(royaltyRecipient_, royaltyRate_);
}
// **************************************
// ***** VIEW *****
// **************************************
/// @dev Called with the sale price to determine how much royalty is owed and to whom.
///
/// @param tokenId_ identifier of the NFT being referenced
/// @param salePrice_ the sale price of the token sold
///
/// @return receiver the address receiving the royalties
/// @return royaltyAmount the royalty payment amount
/* solhint-disable no-unused-vars */
function royaltyInfo(
uint256 tokenId_,
uint256 salePrice_
) public view virtual override returns (address receiver, uint256 royaltyAmount) {
RoyaltyData memory _data_ = _royaltyData;
if (salePrice_ == 0 || _data_.rate == 0 || _data_.recipient == address(0)) {
return (address(0), 0);
}
uint256 _royaltyAmount_ = _data_.rate * salePrice_ / ROYALTY_BASE;
return (_data_.recipient, _royaltyAmount_);
}
/* solhint-enable no-unused-vars */
// **************************************
// **************************************
// ***** INTERNAL *****
// **************************************
/// @dev Sets the royalty rate to `newRoyaltyRate_` and the royalty recipient to `newRoyaltyRecipient_`.
///
/// @param newRoyaltyRecipient_ the address that will receive royalty payments
/// @param newRoyaltyRate_ the percentage of the sale price that will be taken off as royalties,
/// expressed in Basis Points (100 BP = 1%)
///
/// Requirements:
///
/// - `newRoyaltyRate_` cannot be higher than {ROYALTY_BASE};
function _setRoyaltyInfo(address newRoyaltyRecipient_, uint96 newRoyaltyRate_) internal virtual {
if (newRoyaltyRate_ > ROYALTY_BASE) {
revert IERC2981_INVALID_ROYALTIES();
}
_royaltyData = RoyaltyData(newRoyaltyRecipient_, newRoyaltyRate_);
}
// **************************************
}
// src/Wrapped721.sol
/**
* Author: Lambdalf the White
*/
contract Wrapped721 is IERC165, ERC173Initializable, ERC2981Initializable, IERC721, IERC721Metadata, IERC721Enumerable {
// **************************************
// ***** ERRORS *****
// **************************************
/// @dev Thrown when trying to withdraw from the contract with no balance.
error ETHER_NO_BALANCE();
/// @dev Thrown when contract fails to send ether to recipient.
///
/// @param to the recipient of the ether
/// @param amount the amount of ether being sent
error ETHER_TRANSFER_FAIL(address to, uint256 amount);
// **************************************
// **************************************
// ***** STORAGE VARIABLES *****
// **************************************
IAsset public underlyingAsset;
// ***********
// * IERC721 *
// ***********
/// @dev Number of NFT tracked by this contract
uint256 public totalSupply;
/// @dev Token ID mapped to approved address
mapping(uint256 => address) private _approvals;
/// @dev Token owner mapped to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
/// @dev List of owner addresses
mapping(uint256 => address) private _owners;
/// @dev List of owner balances
mapping(address => uint256) private _balances;
// ***********
// *******************
// * IERC721Metadata *
// *******************
/// @dev The token's base URI.
string private _baseUri;
// *******************
// **************************************
constructor() {
initialize(address(0), address(0), address(0), 0, "");
}
function initialize(
address admin_,
address asset_,
address royaltyRecipient_,
uint96 royaltyRate_,
string memory baseUri_
) public initializer {
underlyingAsset = IAsset(asset_);
_setBaseUri(baseUri_);
_init_ERC173(admin_);
_init_ERC2981(royaltyRecipient_, royaltyRate_);
}
// **************************************
// ***** FALLBACK *****
// **************************************
fallback() external payable {} // solhint-disable no-empty-blocks
receive() external payable {} // solhint-disable no-empty-blocks
// **************************************
// **************************************
// ***** MODIFIER *****
// **************************************
// ***********
// * IERC721 *
// ***********
/// @dev Throws if `tokenId_` doesn't exist.
/// A token exists if it has been minted and is not owned by the zero address.
///
/// @param tokenId_ identifier of the NFT being referenced
modifier exists(uint256 tokenId_) {
if (_owners[tokenId_] == address(0)) {
revert IERC721_NONEXISTANT_TOKEN(tokenId_);
}
_;
}
// ***********
// **************************************
// **************************************
// ***** PUBLIC *****
// **************************************
/// @dev Wraps a token from the underlying collection and transfers it to `toAddress_`.
///
/// @param tokenIds_ list of identifiers of the NFT being referenced
///
/// Requirements:
///
/// - The token number `tokenId_` must exist in the underlying collection.
/// - The caller must own the token in the underlying collection.
/// - This contract must be allowed to transfer `tokenId_` from the underlying collection on behalf of the caller.
function wrap(uint256[] calldata tokenIds_) public {
unchecked {
totalSupply += tokenIds_.length;
_balances[msg.sender] += tokenIds_.length;
}
for (uint256 i; i < tokenIds_.length; ++i) {
_owners[tokenIds_[i]] = msg.sender;
emit Transfer(address(0), msg.sender, tokenIds_[i]);
underlyingAsset.transferFrom(msg.sender, address(this), tokenIds_[i]);
}
}
/// @dev Unwraps a token from the underlying collection and transfers it to `toAddress_`.
///
/// @param tokenIds_ list of identifiers of the NFT being referenced
///
/// Requirements:
///
/// - The token number `tokenId_` must exist in the underlying collection.
/// - The caller must own the token or be an approved operator.
function unwrap(uint256[] calldata tokenIds_) public {
unchecked {
totalSupply -= tokenIds_.length;
_balances[msg.sender] -= tokenIds_.length;
}
for (uint256 i; i < tokenIds_.length; ++i) {
if (_owners[tokenIds_[i]] != msg.sender) {
revert IERC721_CALLER_NOT_APPROVED(msg.sender, tokenIds_[i]);
}
_owners[tokenIds_[i]] = address(0);
emit Transfer(msg.sender, address(0), tokenIds_[i]);
underlyingAsset.transferFrom(address(this), msg.sender, tokenIds_[i]);
}
}
// ***********
// * IERC721 *
// ***********
/// @dev Gives permission to `to_` to transfer the token number `tokenId_` on behalf of its owner.
/// 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.
///
/// @param to_ The new approved NFT controller
/// @param tokenId_ The NFT to approve
///
/// Requirements:
///
/// - The token number `tokenId_` must exist.
/// - The caller must own the token or be an approved operator.
/// - Must emit an {Approval} event.
function approve(address to_, uint256 tokenId_) public virtual override {
address _tokenOwner_ = ownerOf(tokenId_);
if (to_ == _tokenOwner_) {
revert IERC721_INVALID_APPROVAL();
}
bool _isApproved_ = _isApprovedOrOwner(_tokenOwner_, msg.sender, tokenId_);
if (!_isApproved_) {
revert IERC721_CALLER_NOT_APPROVED(msg.sender, tokenId_);
}
_approvals[tokenId_] = to_;
emit Approval(_tokenOwner_, to_, tokenId_);
}
/// @dev Transfers the token number `tokenId_` from `from_` to `to_`.
///
/// @param from_ The current owner of the NFT
/// @param to_ The new owner
/// @param tokenId_ identifier of the NFT being referenced
///
/// Requirements:
///
/// - The token number `tokenId_` must exist.
/// - `from_` must be the token owner.
/// - The caller must own the token or be an approved operator.
/// - `to_` must not be the zero address.
/// - If `to_` is a contract, it must implement {IERC721Receiver-onERC721Received} with a return value of `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`,
/// - Must emit a {Transfer} event.
function safeTransferFrom(address from_, address to_, uint256 tokenId_) public virtual override {
safeTransferFrom(from_, to_, tokenId_, "");
}
/// @dev Transfers the token number `tokenId_` from `from_` to `to_`.
///
/// @param from_ The current owner of the NFT
/// @param to_ The new owner
/// @param tokenId_ identifier of the NFT being referenced
/// @param data_ Additional data with no specified format, sent in call to `to_`
///
/// Requirements:
///
/// - The token number `tokenId_` must exist.
/// - `from_` must be the token owner.
/// - The caller must own the token or be an approved operator.
/// - `to_` must not be the zero address.
/// - If `to_` is a contract, it must implement {IERC721Receiver-onERC721Received} with a return value of `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`,
/// - Must emit a {Transfer} event.
function safeTransferFrom(address from_, address to_, uint256 tokenId_, bytes memory data_) public virtual override {
transferFrom(from_, to_, tokenId_);
if (!_checkOnERC721Received(from_, to_, tokenId_, data_)) {
revert IERC721_INVALID_RECEIVER(to_);
}
}
/// @dev Allows or disallows `operator_` to manage the caller's tokens on their behalf.
///
/// @param operator_ Address to add to the set of authorized operators
/// @param approved_ True if the operator is approved, false to revoke approval
///
/// Requirements:
///
/// - Must emit an {ApprovalForAll} event.
function setApprovalForAll(address operator_, bool approved_) public virtual override {
if (operator_ == msg.sender) {
revert IERC721_INVALID_APPROVAL();
}
_operatorApprovals[msg.sender][operator_] = approved_;
emit ApprovalForAll(msg.sender, operator_, approved_);
}
/// @dev Transfers the token number `tokenId_` from `from_` to `to_`.
///
/// @param from_ the current owner of the NFT
/// @param to_ the new owner
/// @param tokenId_ identifier of the NFT being referenced
///
/// Requirements:
///
/// - The token number `tokenId_` must exist.
/// - `from_` must be the token owner.
/// - The caller must own the token or be an approved operator.
/// - `to_` must not be the zero address.
/// - Must emit a {Transfer} event.
function transferFrom(address from_, address to_, uint256 tokenId_) public virtual override {
if (to_ == address(0)) {
revert IERC721_INVALID_RECEIVER(to_);
}
address _tokenOwner_ = ownerOf(tokenId_);
if (from_ != _tokenOwner_) {
revert IERC721_INVALID_TOKEN_OWNER();
}
if (!_isApprovedOrOwner(_tokenOwner_, msg.sender, tokenId_)) {
revert IERC721_CALLER_NOT_APPROVED(msg.sender, tokenId_);
}
unchecked {
--_balances[from_];
++_balances[to_];
}
_transfer(from_, to_, tokenId_);
}
// ***********
// ***********************
// * ERC721BatchBurnable *
// ***********************
/// @dev Burns `tokenId_`.
///
/// Requirements:
///
/// - `tokenId_` must exist
/// - The caller must own `tokenId_` or be an approved operator
function burn(uint256 tokenId_) public {
address _tokenOwner_ = ownerOf(tokenId_);
if (!_isApprovedOrOwner(_tokenOwner_, msg.sender, tokenId_)) {
revert IERC721_CALLER_NOT_APPROVED(msg.sender, tokenId_);
}
unchecked {
--totalSupply;
--_balances[msg.sender];
}
_transfer(_tokenOwner_, address(0), tokenId_);
}
// ***********************
// **************************************
// **************************************
// ***** CONTRACT_OWNER *****
// **************************************
/// @notice Withdraws all the money stored in the contract and sends it to the treasury.
///
/// Requirements:
///
/// - Caller must be the contract owner.
/// - Contract must have a positive balance.
/// - Caller must be able to receive the funds.
function withdraw() public onlyOwner {
uint256 _balance_ = address(this).balance;
if (_balance_ == 0) {
revert ETHER_NO_BALANCE();
}
// solhint-disable-next-line
(bool _success_,) = payable(msg.sender).call{value: _balance_}("");
if (!_success_) {
revert ETHER_TRANSFER_FAIL(msg.sender, _balance_);
}
}
// *******************
// * IERC721Metadata *
// *******************
/// @notice Updates the baseUri for the tokens.
///
/// @param newBaseUri_ the new baseUri for the tokens
///
/// Requirements:
///
/// - Caller must be the contract owner.
function setBaseUri(string memory newBaseUri_) public onlyOwner {
_setBaseUri(newBaseUri_);
}
// *******************
// ************
// * IERC2981 *
// ************
/// @dev Sets the royalty rate to `newRoyaltyRate_` and the royalty recipient to `newRoyaltyRecipient_`.
///
/// @param newRoyaltyRecipient_ the address that will receive royalty payments
/// @param newRoyaltyRate_ the percentage of the sale price that will be taken off as royalties,
/// expressed in Basis Points (100 BP = 1%)
///
/// Requirements:
///
/// - Caller must be the contract owner.
/// - `newRoyaltyRate_` cannot be higher than {ROYALTY_BASE};
function setRoyaltyInfo(address newRoyaltyRecipient_, uint96 newRoyaltyRate_) public onlyOwner {
_setRoyaltyInfo(newRoyaltyRecipient_, newRoyaltyRate_);
}
// ************
// **************************************
// **************************************
// ***** VIEW *****
// **************************************
// ***********
// * IERC721 *
// ***********
/// @dev Returns the number of tokens in `tokenOwner_`'s account.
///
/// @param tokenOwner_ address that owns tokens
///
/// @return ownerBalance the nomber of tokens owned by `tokenOwner_`
///
/// Requirements:
///
/// - `tokenOwner_` must not be the zero address
function balanceOf(address tokenOwner_) public view virtual override returns (uint256 ownerBalance) {
if (tokenOwner_ == address(0)) {
revert IERC721_INVALID_TOKEN_OWNER();
}
ownerBalance = _balances[tokenOwner_];
}
/// @dev Returns the address that has been specifically allowed to manage `tokenId_` on behalf of its owner.
///
/// @param tokenId_ the NFT that has been approved
///
/// @return approved the address allowed to manage `tokenId_`
///
/// Requirements:
///
/// - `tokenId_` must exist.
///
/// Note: See {Approve}
function getApproved(uint256 tokenId_) public view virtual override returns (address approved) {
if (_owners[tokenId_] == address(0)) {
revert IERC721_NONEXISTANT_TOKEN(tokenId_);
}
return _approvals[tokenId_];
}
/// @dev Returns whether `operator_` is allowed to manage tokens on behalf of `tokenOwner_`.
///
/// @param tokenOwner_ address that owns tokens
/// @param operator_ address that tries to manage tokens
///
/// @return isApproved whether `operator_` is allowed to handle `tokenOwner`'s tokens
///
/// Note: See {setApprovalForAll}
function isApprovedForAll(
address tokenOwner_,
address operator_
) public view virtual override returns (bool isApproved) {
return _operatorApprovals[tokenOwner_][operator_];
}
/// @dev Returns the owner of the token number `tokenId_`.
///
/// @param tokenId_ the NFT to verify ownership of
///
/// @return tokenOwner the owner of token number `tokenId_`
///
/// Requirements:
///
/// - `tokenId_` must exist.
function ownerOf(uint256 tokenId_) public view virtual override returns (address tokenOwner) {
tokenOwner = _owners[tokenId_];
if (tokenOwner == address(0)) {
revert IERC721_NONEXISTANT_TOKEN(tokenId_);
}
}
// ***********
// *********************
// * IERC721Enumerable *
// *********************
/// @dev Enumerate valid NFTs
///
/// @param index_ the index requested
///
/// @return tokenId the identifier of the token at the specified index
///
/// Requirements:
///
/// - `index_` must be less than {totalSupply}
function tokenByIndex(uint256 index_) public view virtual override returns (uint256) {
if (index_ >= totalSupply) {
revert IERC721Enumerable_INDEX_OUT_OF_BOUNDS(index_);
}
uint256 _count_;
uint256 _id_;
while (_count_ <= index_) {
if (_owners[_id_] != address(0)) {
if (index_ == _count_) {
return _id_;
}
unchecked {
++_count_;
}
}
unchecked {
++_id_;
}
}
}
/// @dev Enumerate NFTs assigned to an owner
///
/// @param tokenOwner_ the address requested
/// @param index_ the index requested
///
/// @return tokenId the identifier of the token at the specified index
///
/// Requirements:
///
/// - `index_` must be less than {balanceOf(tokenOwner_)}
/// - `tokenOwner_` must not be the zero address
function tokenOfOwnerByIndex(address tokenOwner_, uint256 index_) public view virtual override returns (uint256) {
if (tokenOwner_ == address(0)) {
revert IERC721_INVALID_TOKEN_OWNER();
}
if (index_ >= _balances[tokenOwner_]) {
revert IERC721Enumerable_OWNER_INDEX_OUT_OF_BOUNDS(index_);
}
uint256 _count_;
uint256 _id_;
while (_count_ <= index_) {
if (_owners[_id_] == tokenOwner_) {
if (index_ == _count_) {
return _id_;
}
unchecked {
++_count_;
}
}
unchecked {
++_id_;
}
}
}
// *********************
// *******************
// * IERC721Metadata *
// *******************
/// @dev A descriptive name for a collection of NFTs in this contract
///
/// @return tokenName The descriptive name of the NFTs
function name() public view virtual override returns (string memory tokenName) {
tokenName = underlyingAsset.name();
}
/// @dev An abbreviated name for NFTs in this contract
///
/// @return tokenSymbol The abbreviated name of the NFTs
function symbol() public view virtual override returns (string memory tokenSymbol) {
tokenSymbol = underlyingAsset.symbol();
}
/// @dev A distinct Uniform Resource Identifier (URI) for a given asset.
///
/// @param tokenId_ the NFT that has been approved
///
/// @return uri the URI of the token
///
/// Requirements:
///
/// - `tokenId_` must exist.
function tokenURI(uint256 tokenId_) public view virtual override returns (string memory uri) {
if (_owners[tokenId_] == address(0)) {
revert IERC721_NONEXISTANT_TOKEN(tokenId_);
}
return bytes(_baseUri).length > 0 ? string(abi.encodePacked(_baseUri, _toString(tokenId_))) : _toString(tokenId_);
}
// *******************
// ***********
// * IERC165 *
// ***********
/// @dev Query if a contract implements an interface.
/// @dev see https://eips.ethereum.org/EIPS/eip-165
///
/// @param interfaceId_ the interface identifier, as specified in ERC-165
///
/// @return bool true if the contract implements the specified interface, false otherwise
///
/// Requirements:
///
/// - This function must use less than 30,000 gas.
function supportsInterface(bytes4 interfaceId_) public pure override returns (bool) {
return interfaceId_ == type(IERC721).interfaceId || interfaceId_ == type(IERC721Enumerable).interfaceId
|| interfaceId_ == type(IERC721Metadata).interfaceId || interfaceId_ == type(IERC173).interfaceId
|| interfaceId_ == type(IERC165).interfaceId || interfaceId_ == type(IERC2981).interfaceId;
}
// ***********
// **************************************
// **************************************
// ***** INTERNAL *****
// **************************************
// ***********
// * IERC721 *
// ***********
/// @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 owning the token being transferred
/// @param to_ address the token is being transferred to
/// @param tokenId_ identifier of the NFT being referenced
/// @param data_ optional data to send along with the call
///
/// @return isValidReceiver whether the call correctly returned the expected value
function _checkOnERC721Received(
address from_,
address to_,
uint256 tokenId_,
bytes memory data_
) internal virtual returns (bool isValidReceiver) {
// 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.
//
// IMPORTANT
// It is unsafe to assume that an address not flagged by this method
// is an externally-owned account (EOA) and not a contract.
//
// Among others, the following types of addresses will not be flagged:
//
// - 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
uint256 _size_;
assembly {
_size_ := extcodesize(to_)
}
// If address is a contract, check that it is aware of how to handle ERC721 tokens
if (_size_ > 0) {
try IERC721Receiver(to_).onERC721Received(msg.sender, from_, tokenId_, data_) returns (bytes4 retval) {
return retval == IERC721Receiver.onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert IERC721_INVALID_RECEIVER(to_);
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
/// @dev Internal function returning whether `operator_` is allowed to handle `tokenId_`
///
/// Note: To avoid multiple checks for the same data, it is assumed
/// that existence of `tokenId_` has been verified prior via {_exists}
/// If it hasn't been verified, this function might panic
///
/// @param operator_ address that tries to handle the token
/// @param tokenId_ identifier of the NFT being referenced
///
/// @return isApproved whether `operator_` is allowed to manage the token
function _isApprovedOrOwner(
address tokenOwner_,
address operator_,
uint256 tokenId_
) internal view virtual returns (bool isApproved) {
return operator_ == tokenOwner_ || operator_ == getApproved(tokenId_) || _operatorApprovals[tokenOwner_][operator_];
}
/// @dev Transfers `tokenId_` from `fromAddress_` to `toAddress_`.
///
/// Emits a {Transfer} event.
///
/// @param fromAddress_ the current owner of the NFT
/// @param toAddress_ the new owner
/// @param tokenId_ identifier of the NFT being referenced
function _transfer(address fromAddress_, address toAddress_, uint256 tokenId_) internal virtual {
_approvals[tokenId_] = address(0);
_owners[tokenId_] = toAddress_;
emit Transfer(fromAddress_, toAddress_, tokenId_);
}
// ***********
// *******************
// * IERC721Metadata *
// *******************
/// @notice Updates the baseUri for the tokens.
///
/// @param newBaseUri_ the new baseUri for the tokens
///
/// Requirements:
///
/// - Caller must be the contract owner.
function _setBaseUri(string memory newBaseUri_) internal virtual {
_baseUri = newBaseUri_;
}
/// @dev Converts a `uint256` to its ASCII `string` decimal representation.
///
/// @param value_ the value to convert to string.
///
/// @return str the string representation of `value_`
function _toString(uint256 value_) internal pure virtual returns (string memory str) {
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
let m := add(mload(0x40), 0xa0)
// Update the free memory pointer to allocate.
mstore(0x40, m)
// Assign the `str` to the end.
str := sub(m, 0x20)
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// prettier-ignore
for { let temp := value_ } 1 {} {
// solhint-disable-line
str := sub(str, 1)
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp := div(temp, 10)
// prettier-ignore
if iszero(temp) { break }
}
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 0x20)
// Store the length.
mstore(str, length)
}
}
// *******************
// **************************************
}