Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/helpers/Base64.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/// @title Base64
/// @author Brecht Devos - <brecht@loopring.org>
/// @notice Provides a function for encoding some bytes in base64
library Base64 {
string internal constant TABLE =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
function encode(bytes memory data) internal pure returns (string memory) {
if (data.length == 0) return "";
// load the table into memory
string memory table = TABLE;
// multiply by 4/3 rounded up
uint256 encodedLen = 4 * ((data.length + 2) / 3);
// add some extra buffer at the end required for the writing
string memory result = new string(encodedLen + 32);
assembly {
// set the actual output length
mstore(result, encodedLen)
// prepare the lookup table
let tablePtr := add(table, 1)
// input ptr
let dataPtr := data
let endPtr := add(dataPtr, mload(data))
// result ptr, jump over length
let resultPtr := add(result, 32)
// run over the input, 3 bytes at a time
for {
} lt(dataPtr, endPtr) {
} {
dataPtr := add(dataPtr, 3)
// read 3 bytes
let input := mload(dataPtr)
// write 4 characters
mstore(
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 '='
switch mod(mload(data), 3)
case 1 {
mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
}
case 2 {
mstore(sub(resultPtr, 1), shl(248, 0x3d))
}
}
return result;
}
}
"
},
"contracts/helpers/strings.sol": {
"content": "// SPDX-License-Identifier: MIT
/*
* @title String & slice utility library for Solidity contracts.
* @author Nick Johnson <arachnid@notdot.net>
*/
pragma solidity ^0.7.0;
library strings {
struct slice {
uint _len;
uint _ptr;
}
function memcpy(uint dest, uint src, uint len) private pure {
// Copy word-length chunks while possible
for (; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}
// Copy remaining bytes
uint 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.
*/
function toSlice(string memory self) internal pure returns (slice memory) {
uint ptr;
assembly {
ptr := add(self, 0x20)
}
return slice(bytes(self).length, 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.
*/
function concat(slice memory self, slice memory other) internal pure returns (string memory) {
string memory ret = new string(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;
}
}
"
},
"contracts/Metadata.sol": {
"content": "// SPDX-License-Identifier: MIT
/**
* Forked from folia-app/folia-contracts: https://github.com/folia-app/folia-contracts
* Many thanks to Billy Rennekamp <https://github.com/okwme> and Folia <https://www.folia.app/> ????
*/
pragma solidity ^0.7.0;
/**
* Metadata contract is upgradeable and returns metadata about Token
*/
import './helpers/strings.sol';
contract Metadata {
using strings for *;
function tokenURI(
uint256 _tokenId
) public pure returns (string memory _infoUrl) {
string memory base = 'https://seeder.mutant.garden/v1/tokens/';
string memory id = uint2str(_tokenId);
return base.toSlice().concat(id.toSlice());
}
function uint2str(uint256 i) internal pure returns (string memory) {
if (i == 0) return '0';
uint256 j = i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length - 1;
while (i != 0) {
uint256 _uint = 48 + (i % 10);
bstr[k--] = toBytes(_uint)[31];
i /= 10;
}
return string(bstr);
}
function toBytes(uint256 x) public pure returns (bytes memory b) {
b = new bytes(32);
assembly {
mstore(add(b, 32), x)
}
}
}
"
},
"contracts/MutationController.sol": {
"content": "// SPDX-License-Identifier: MIT
/**
* Forked from folia-app/folia-contracts: https://github.com/folia-app/folia-contracts
* Many thanks to Billy Rennekamp <https://github.com/okwme> and Folia <https://www.folia.app/> ????
*/
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import './MutationToken.sol';
import './Seeder.sol';
import 'openzeppelin-solidity/contracts/math/SafeMath.sol';
import 'openzeppelin-solidity/contracts/access/Ownable.sol';
import 'openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol';
import 'openzeppelin-solidity/contracts/cryptography/ECDSA.sol';
contract MutationController is Ownable, ReentrancyGuard {
event MutationBought(uint256 tokenId, address recipient, uint256 paid);
using SafeMath for uint256;
uint256 public adminPriceShare = 0.035 ether;
bool public paused = false;
address payable public admin;
MutationToken public mutationToken;
MutationMetadata public mutationMetadata;
Seeder public seeder;
uint256 private nextTokenId = 1;
address public authorizedSigner;
// map mutant tokenIds (birthBlock) to a boolean
mapping(uint256 => bool) private allowPublicMint;
// map mutant TokenIds to public minting price share
mapping(uint256 => uint256) private publicMintHolderShare;
// map mutant TokenIds to payout wallet addresses
mapping(uint256 => address) private publicMintingPayoutWallet;
modifier notPaused() {
require(!paused, 'MutationController: is paused');
_;
}
modifier onlyAdmin() {
require((msg.sender == admin), 'MutationController: You are not the admin');
_;
}
modifier onlyMutantHolderOrAdmin(uint256 _mutantTokenId) {
require(
seeder.ownerOf(_mutantTokenId) == msg.sender || msg.sender == admin,
'MutationController: you do not own that mutant or you are not admin'
);
_;
}
constructor(
MutationToken _mutationToken,
MutationMetadata _mutationMetadata,
Seeder _seeder,
address payable _admin,
address payable _authorizedSigner
) {
require(
_admin != address(0),
'MutationController: admin cannot be zero address'
);
require(
_authorizedSigner != address(0),
'MutationController: signer cannot be zero address'
);
require(
address(_mutationToken) != address(0),
'MutationController: token cannot be zero address'
);
require(
address(_mutationMetadata) != address(0),
'MutationController: metadata cannot be zero address'
);
require(
address(_seeder) != address(0),
'MutationController: seeder cannot be zero address'
);
mutationToken = _mutationToken;
mutationMetadata = _mutationMetadata;
seeder = _seeder;
admin = _admin;
authorizedSigner = _authorizedSigner;
}
function updatePaused(bool _paused) public onlyOwner {
paused = _paused;
}
function updateAdminPriceShare(uint256 _price) public onlyOwner {
adminPriceShare = _price;
}
function updateAuthorizedSigner(address _authorizedSigner) public onlyOwner {
authorizedSigner = _authorizedSigner;
}
function setHolderPublicMintPayoutWallet(
uint256 _mutantTokenId,
address _payoutWalletAddress
) public onlyMutantHolderOrAdmin(_mutantTokenId) {
require(
_payoutWalletAddress != address(0),
'MutationController: Payout wallet cannot be zero address'
);
publicMintingPayoutWallet[_mutantTokenId] = _payoutWalletAddress;
}
function setPublicMintHolderShareAndPayoutAddressAndEnable(
uint256 _mutantTokenId,
uint256 _publicPrice,
address _payoutWalletAddress
) public {
updatePublicPrice(_mutantTokenId, _publicPrice);
setHolderPublicMintPayoutWallet(_mutantTokenId, _payoutWalletAddress);
setPublicMintEnabled(_mutantTokenId, true);
}
function updatePublicPrice(
uint256 _mutantTokenId,
uint256 _publicPrice
) public onlyMutantHolderOrAdmin(_mutantTokenId) {
publicMintHolderShare[_mutantTokenId] = _publicPrice;
}
function getPublicHolderShare(
uint256 _tokenId
) public view returns (uint256 _price) {
return publicMintHolderShare[_tokenId];
}
function mint(
address _recipient,
string memory _mutantName,
Mutation memory _mutation
) private returns (uint256 _mutationTokenId) {
uint256 tokenId = nextTokenId++;
mutationMetadata.storeMutation(tokenId, _mutantName, _mutation);
mutationToken.mint(_recipient, tokenId);
return tokenId;
}
function adminMint(
address _recipient,
string memory _mutantName,
Mutation memory _mutation
) public onlyAdmin returns (uint256 _mutationTokenId) {
return mint(_recipient, _mutantName, _mutation);
}
function setPublicMintEnabled(
uint256 _mutantTokenId,
bool _enabled
) public onlyMutantHolderOrAdmin(_mutantTokenId) {
allowPublicMint[_mutantTokenId] = _enabled;
}
function getPublicMintEnabled(
uint256 _mutantTokenId
) public view returns (bool) {
return allowPublicMint[_mutantTokenId];
}
function buy(
address _recipient,
string memory _mutantName,
Mutation memory _mutation,
bytes memory _signature
) public payable notPaused nonReentrant returns (uint256 _mutationTokenId) {
require(
allowPublicMint[_mutation.birthBlock],
'MutationController: Public mint needs to be allowed for this mutant'
);
uint256 holderShare = publicMintHolderShare[_mutation.birthBlock];
require(
msg.value >= holderShare + adminPriceShare,
'MutationController: You did not send enough ether'
);
address holderShareWallet = publicMintingPayoutWallet[_mutation.birthBlock];
require(
holderShareWallet != address(0),
'MutationController: No receiving mutant holder payout wallet set'
);
verifySignature(_mutantName, _mutation, _signature);
uint256 mutationTokenId = mint(_recipient, _mutantName, _mutation);
// uint256 adminReceives = msg.value - holderShare;
// uint256 holderReceives = holderShare;
uint256 adminReceives = msg.value.sub(holderShare);
bool success;
(success, ) = admin.call{value: adminReceives}('');
require(success, 'MutationController: Admin failed to receive');
(success, ) = holderShareWallet.call{value: holderShare}('');
require(success, 'MutationController: Holder failed to receive');
emit MutationBought(mutationTokenId, _recipient, msg.value);
return mutationTokenId;
}
function buyAsHolder(
address _recipient,
string memory _mutantName,
Mutation memory _mutation,
bytes memory _signature
)
public
payable
notPaused
nonReentrant
onlyMutantHolderOrAdmin(_mutation.birthBlock)
{
require(
msg.value >= adminPriceShare,
'MutationController: You did not send enough ether'
);
verifySignature(_mutantName, _mutation, _signature);
uint256 tokenId = mint(_recipient, _mutantName, _mutation);
bool success;
(success, ) = admin.call{value: msg.value}('');
require(success, 'MutationController: Payout address failed to receive');
emit MutationBought(tokenId, _recipient, msg.value);
}
function updateAdmin(address payable _admin) public onlyOwner {
admin = _admin;
}
function verifySignature(
string memory _mutantName,
Mutation memory _mutation,
bytes memory _signature
) private view {
bytes32 messageHash = keccak256(
abi.encodePacked(
msg.sender,
_mutation.birthBlock,
_mutation.arweave,
_mutation.mutationBlock,
_mutation.mutationIndex,
_mutantName
)
);
bytes32 ethSignedMessageHash = ECDSA.toEthSignedMessageHash(messageHash);
address recoveredSigner = ECDSA.recover(ethSignedMessageHash, _signature);
require(
recoveredSigner == authorizedSigner,
'MutationController: Invalid signature'
);
}
function updateMutationToken(MutationToken _mutationToken) public onlyOwner {
mutationToken = _mutationToken;
}
function updateMutationMetadata(
MutationMetadata _mutationMetadata
) public onlyOwner {
mutationMetadata = _mutationMetadata;
}
}
"
},
"contracts/MutationMetadata.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import './helpers/strings.sol';
import './helpers/Base64.sol';
import './MutationController.sol';
import './MutationMetadataDB.sol';
contract MutationMetadata {
using strings for *;
string private constant JSON_PROTOCOL_URI = 'data:application/json;base64,';
address public controller;
address public admin;
string public titleSeparator = ' / ';
string public descriptionText = 'Mutant Garden Seeder Mutation';
MutationMetadataDB public db;
function updateController(address _controller) public onlyAdminOrController {
controller = _controller;
}
function updateAdmin(address _admin) public onlyAdminOrController {
admin = _admin;
}
modifier onlyAdminOrController() {
require(
(msg.sender == controller || msg.sender == admin),
'MutationMetadata: You are not an admin or controller'
);
_;
}
constructor(address _admin) {
admin = _admin;
}
function setTitleSeparator(
string memory _titleSeparator
) public onlyAdminOrController {
titleSeparator = _titleSeparator;
}
function setDescriptionText(
string memory _descriptionText
) public onlyAdminOrController {
descriptionText = _descriptionText;
}
function setMutationMetadataDB(
address _mutationMetadataDBAddress
) public onlyAdminOrController {
db = MutationMetadataDB(_mutationMetadataDBAddress);
}
function storeMutation(
uint256 _tokenId,
string memory _mutantName,
Mutation memory _mutation
) public onlyAdminOrController {
require(
!isMutantMutationAlreadyStored(
_mutation.birthBlock,
_mutation.mutationBlock
),
'mutation already exists'
);
bytes memory n = bytes(db.getMutantNameByBirthBlock(_mutation.birthBlock));
if (n.length == 0) {
db.setMutantNameByBirthBlock(_mutation.birthBlock, _mutantName);
}
db.setMutation(_tokenId, _mutation);
db.setTokenIdByMutantBirthAndMutation(
_mutation.birthBlock,
_mutation.mutationBlock,
_tokenId
);
}
function getTokenIdByMutantMutation(
uint256 _birthBlock,
uint256 _mutationBlock
) public view returns (uint256) {
return db.getTokenIdByMutantBirthAndMutation(_birthBlock, _mutationBlock); // ;;//tokenIdByMutantMutation[_birthBlock][_mutationBlock];
}
function getMutationByTokenId(
uint256 _tokenId
) public view returns (Mutation memory) {
Mutation memory mutation = db.getMutation(_tokenId);
require(mutation.birthBlock != 0, 'Mutation does not exist');
return mutation;
}
function getTokenData(
uint256 _tokenId
) public view returns (string memory, Mutation memory) {
Mutation memory mutation = db.getMutation(_tokenId); //mutations[_tokenId];
require(mutation.birthBlock != 0, 'Mutation does not exist');
string memory mutantName = db.getMutantNameByBirthBlock(
mutation.birthBlock
);
return (mutantName, mutation);
}
function getMutantNameByBirthBlock(
uint256 _birthBlock
) public view returns (string memory) {
return db.getMutantNameByBirthBlock(_birthBlock);
}
function isMutantMutationAlreadyStored(
uint256 _birthBlock,
uint256 _mutationBlock
) public view returns (bool) {
uint256 tokenId = db.getTokenIdByMutantBirthAndMutation(
_birthBlock,
_mutationBlock
);
return tokenId != 0;
}
function tokenURI(
uint256 _tokenId
) public view returns (string memory _infoUrl) {
Mutation memory mutation = db.getMutation(_tokenId);
require(mutation.birthBlock != 0, 'Mutation does not exist');
string memory mutantName = db.getMutantNameByBirthBlock(
mutation.birthBlock
);
string memory mutationString = mutation.mutationIndex == 0
? 'birth'
: uint2str(mutation.mutationIndex);
bytes memory json = abi.encodePacked(
'{"name":"',
mutantName,
titleSeparator,
mutationString,
'",',
'"description":"',
descriptionText,
'",',
'"attributes":[{"trait_type":"Name","value":"',
mutantName,
'"},{"trait_type":"Birth Block","value":"',
uint2str(mutation.birthBlock),
'"},{"trait_type":"Mutation Block","value":"',
uint2str(mutation.mutationBlock),
'"},{"trait_type":"Mutation","value":"',
mutationString,
'"}],',
'"image":"ar://',
mutation.arweave,
'"}'
);
return string(abi.encodePacked(JSON_PROTOCOL_URI, Base64.encode(json)));
}
function uint2str(uint256 i) internal pure returns (string memory) {
if (i == 0) return '0';
uint256 j = i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length - 1;
while (i != 0) {
uint256 _uint = 48 + (i % 10);
bstr[k--] = toBytes(_uint)[31];
i /= 10;
}
return string(bstr);
}
function toBytes(uint256 x) internal pure returns (bytes memory b) {
b = new bytes(32);
assembly {
mstore(add(b, 32), x)
}
}
}
"
},
"contracts/MutationMetadataDB.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import 'openzeppelin-solidity/contracts/access/Ownable.sol';
struct Mutation {
uint256 birthBlock;
uint256 mutationBlock;
uint256 mutationIndex; // 0 = birth
string arweave;
}
contract MutationMetadataDB is Ownable {
// maps birthBlock to fixed data
address public mutationMetadata;
mapping(uint256 => string) private mutantNames;
// maps tokenId to mutation data
mapping(uint256 => Mutation) private mutations;
// birthBlock => mutationBlock => tokenId
mapping(uint256 => mapping(uint256 => uint256))
private tokenIdByMutantMutation;
modifier onlyMetadata() {
require(
(msg.sender == mutationMetadata || msg.sender == owner()),
'MutationMetadataDB: You are not an owner or metadata contract'
);
_;
}
function updateMutationMetadata(address _mutationMetadata) public onlyOwner {
mutationMetadata = _mutationMetadata;
}
function getMutantNameByBirthBlock(
uint256 _birthBlock
) public view returns (string memory) {
return mutantNames[_birthBlock];
}
function getTokenIdByMutantBirthAndMutation(
uint256 _birthBlock,
uint256 _mutationBlock
) public view returns (uint256 _tokenId) {
return tokenIdByMutantMutation[_birthBlock][_mutationBlock];
}
function getMutation(uint256 _tokenId) public view returns (Mutation memory) {
return mutations[_tokenId];
}
function setMutantNameByBirthBlock(
uint256 _birthBlock,
string memory _mutantName
) public onlyMetadata {
mutantNames[_birthBlock] = _mutantName;
}
function setTokenIdByMutantBirthAndMutation(
uint256 _birthBlock,
uint256 _mutationBlock,
uint256 _tokenId
) public onlyMetadata {
tokenIdByMutantMutation[_birthBlock][_mutationBlock] = _tokenId;
}
function setMutation(
uint256 _tokenId,
Mutation memory _mutation
) public onlyMetadata {
mutations[_tokenId] = _mutation;
}
}
"
},
"contracts/MutationToken.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import 'openzeppelin-solidity/contracts/token/ERC721/ERC721.sol';
import 'openzeppelin-solidity/contracts/token/ERC20/IERC20.sol';
import 'openzeppelin-solidity/contracts/access/Ownable.sol';
import './MutationMetadata.sol';
contract MutationToken is ERC721, Ownable {
address public metadata;
address public controller;
address public admin;
modifier onlyAdminOrController() {
require(
(msg.sender == controller || msg.sender == admin),
'MutationToken: You are not an admin or controller'
);
_;
}
constructor(
string memory name,
string memory symbol,
address _metadata,
address _admin
) ERC721(name, symbol) {
metadata = _metadata;
admin = _admin;
}
function mint(
address recipient,
uint256 tokenId
) public onlyAdminOrController {
_safeMint(recipient, tokenId);
}
function burn(uint256 tokenId) public onlyAdminOrController {
_burn(tokenId);
}
function updateMetadata(address _metadata) public onlyAdminOrController {
metadata = _metadata;
}
function updateController(address _controller) public onlyAdminOrController {
controller = _controller;
}
function updateAdmin(address _admin) public onlyOwner {
admin = _admin;
}
function tokenURI(
uint256 _tokenId
) public view virtual override returns (string memory _infoUrl) {
return MutationMetadata(metadata).tokenURI(_tokenId);
}
/**
* @dev Moves Token to a certain address.
* @param _to The address to receive the Token.
* @param _amount The amount of Token to be transferred.
* @param _token The address of the Token to be transferred.
*/
function recoverERC20(
address _to,
uint256 _amount,
address _token
) public onlyOwner returns (bool) {
require(_amount <= IERC20(_token).balanceOf(address(this)));
return IERC20(_token).transfer(_to, _amount);
}
}
"
},
"contracts/Seeder.sol": {
"content": "// SPDX-License-Identifier: MIT
/**
* Forked from folia-app/folia-contracts: https://github.com/folia-app/folia-contracts
* Many thanks to Billy Rennekamp <https://github.com/okwme> and Folia <https://www.folia.app/> ????
*/
pragma solidity ^0.7.0;
import 'openzeppelin-solidity/contracts/token/ERC721/ERC721.sol';
import 'openzeppelin-solidity/contracts/token/ERC20/IERC20.sol';
import 'openzeppelin-solidity/contracts/access/Ownable.sol';
// import "openzeppelin-solidity/contracts/access/AccessControl.sol";
import './Metadata.sol';
contract Seeder is ERC721, Ownable {
// using Roles for Roles.Role;
// Roles.Role private _admins;
// uint8 admins;
address public metadata;
address public controller;
modifier onlyAdminOrController() {
// require(
// (_admins.has(msg.sender) || msg.sender == controller),
// "You are not an admin or controller"
// );
_;
}
constructor(
string memory name,
string memory symbol,
address _metadata
) ERC721(name, symbol) {
metadata = _metadata;
// _admins.add(msg.sender);
// admins += 1;
}
function mint(
address recipient,
uint256 tokenId
) public onlyAdminOrController returns (uint256) {
_mint(recipient, tokenId);
}
function burn(uint256 tokenId) public onlyAdminOrController {
// _burn(ownerOf(tokenId), tokenId);
}
function updateMetadata(address _metadata) public onlyAdminOrController {
metadata = _metadata;
}
function updateController(address _controller) public onlyAdminOrController {
controller = _controller;
}
function addAdmin(address _admin) public onlyOwner {
// _admins.add(_admin);
// admins += 1;
}
function removeAdmin(address _admin) public onlyOwner {
// require(admins > 1, "Cannot remove the last admin");
// _admins.remove(_admin);
// admins -= 1;
}
function tokenURI(
uint256 _tokenId
) public view virtual override returns (string memory _infoUrl) {
return Metadata(metadata).tokenURI(_tokenId);
}
/**
* @dev Moves Token to a certain address.
* @param _to The address to receive the Token.
* @param _amount The amount of Token to be transferred.
* @param _token The address of the Token to be transferred.
*/
function moveToken(
address _to,
uint256 _amount,
address _token
) public onlyAdminOrController returns (bool) {
require(_amount <= IERC20(_token).balanceOf(address(this)));
return IERC20(_token).transfer(_to, _amount);
}
}
"
},
"openzeppelin-solidity/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <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.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
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.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
"
},
"openzeppelin-solidity/contracts/cryptography/ECDSA.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
// Check the signature length
if (signature.length != 65) {
revert("ECDSA: invalid signature length");
}
// Divide the signature in r, s and v variables
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return recover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
require(signer != address(0), "ECDSA: invalid signature");
return signer;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* replicates the behavior of the
* https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
* JSON-RPC method.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\
32", hash));
}
}
"
},
"openzeppelin-solidity/contracts/introspection/ERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts may inherit from this and call {_registerInterface} to declare
* their support of an interface.
*/
abstract contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () internal {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See {IERC165-supportsInterface}.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See {IERC165-supportsInterface}.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal virtual {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
"
},
"openzeppelin-solidity/contracts/introspection/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <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}.
*/
interface IERC165 {
/**
* @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.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
},
"openzeppelin-solidity/contracts/math/SafeMath.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
"
},
"openzeppelin-solidity/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
"
},
"openzeppelin-solidity/contracts/token/ERC721/ERC721.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../../utils/Context.sol";
import "./IERC721.sol";
import "./IERC721Metadata.sol";
import "./IERC721Enumerable.sol";
import "./IERC721Receiver.sol";
import "../../introspection/ERC165.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
import "../../utils/EnumerableSet.sol";
import "../../utils/EnumerableMap.sol";
import "../../utils/Strings.sol";
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {
using SafeMath for uint256;
using Address for address;
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableMap for EnumerableMap.UintToAddressMap;
using Strings for uint256;
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
// which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
// Mapping from holder address to their (enumerable) set of owned tokens
mapping (address => EnumerableSet.UintSet) private _holderTokens;
// Enumerable mapping from token ids to their owners
EnumerableMap.UintToAddressMap private _tokenOwners;
// Mapping from token ID to approved address
mapping (uint256 => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) private _operatorApprovals;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Optional mapping for token URIs
mapping (uint256 => string) private _tokenURIs;
// Base URI
string private _baseURI;
/*
* bytes4(keccak256('balanceOf(address)')) == 0x70a08231
* bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
* bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
* bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
* bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
* bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
* bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
*
* => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
* 0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
*/
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
/*
* bytes4(keccak256('name()')) == 0x06fdde03
* bytes4(keccak256('symbol()')) == 0x95d89b41
* bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
*
* => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
*/
bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
/*
* bytes4(keccak256('totalSupply()')) == 0x18160ddd
* bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
* bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
*
* => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
*/
bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor (string memory name_, string memory symbol_) public {
_name = name_;
_symbol = symbol_;
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721);
_registerInterface(_INTERFACE_ID_ERC721_METADATA);
_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: balance query for the zero address");
return _holderTokens[owner].length();
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(base, tokenId.toString()));
}
/**
* @dev Returns the base URI set via {_setBaseURI}. This will be
* automatically added as a prefix in {tokenURI} to each token's URI, or
* to the token ID if no specific URI is set for that token ID.
*/
function baseURI() public view virtual returns (string memory) {
return _baseURI;
}
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
return _holderTokens[owner].at(index);
}
/**
* @dev See {IERC721Enumerable-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
// _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
return _tokenOwners.length();
}
/**
* @dev See {IERC721Enumerable-tokenByIndex}.
*/
function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
(uint256 tokenId, ) = _tokenOwners.at(index);
return tokenId;
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(_msgSender() == owner || ERC721.isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not owner nor approved for all"
);
_approve(to, tokenId);
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual override returns (address) {
require(_exists(tokenId), "ERC721: approved query for nonexistent token");
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
require(operator != _msgSender(), "ERC721: approve to caller");
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual override {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
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(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
_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) internal view virtual returns (bool) {
return _tokenOwners.contains(tokenId);
}
/**
* @dev Returns whether `spender` is allowed to manage `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
require(_exists(tokenId), "ERC721: operator query for nonexistent token");
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || ERC721.isApprovedForAll(owner, spender));
}
/**
* @dev Safely mints `tokenId` and transfers it to `to`.
*
* Requirements:
d*
* - `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) internal virtual {
_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, bytes memory _data) internal virtual {
_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) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId);
_holderTokens[to].add(tokenId);
_tokenOwners.set(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) internal virtual {
address owner = ERC721.ownerOf(tokenId); // internal owner
_beforeTokenTransfer(owner, address(0), tokenId);
// Clear approvals
_approve(address(0), tokenId);
// Clear metadata (if any)
if (bytes(_tokenURIs[tokenId]).length != 0) {
delete _tokenURIs[tokenId];
}
_holderTokens[owner].remove(tokenId);
_tokenOwners.remove(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(address from, address to, uint256 tokenId) internal virtual {
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); // internal owner
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
// Clear approvals from the previous owner
_approve(address(0), tokenId);
_holderTokens[from].remove(tokenId);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
emit Transfer(from, to, tokenId);
}
/**
* @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
/**
* @dev Internal function to set the base URI for all token IDs. It is
* automatically added as a prefix to the value returned in {tokenURI},
* or to the token ID if {tokenURI} is empty.
*/
function _setBaseURI(string memory baseURI_) internal virtual {
_baseURI = baseURI_;
}
/**
* @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(address from, address to, uint256 tokenId, bytes memory _data)
private returns (bool)
{
if (!to.isContract()) {
return true;
}
bytes memory returndata = to.functionCall(abi.encodeWithSelector(
IERC721Receiver(to).onERC721Received.selector,
_msgSender(),
from,
tokenId,
_data
), "ERC721: transfer to non ERC721Receiver implementer");
bytes4 retval = abi.decode(returndata, (bytes4));
return (retval == _ERC721_RECEIVED);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits an {Approval} event.
*/
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId); // internal owner
}
/**
* @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` cannot be the zero address.
* - `to` cannot be the zero address.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
}
"
},
"openzeppelin-solidity/contracts/token/ERC721/IERC721.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
import "../../introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
Submitted on: 2025-10-25 12:11:58
Comments
Log in to comment.
No comments yet.