pragma solidity >=0.4.24 <=0.5.6;
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// Note: the ERC-165 identifier for this interface is 0x80ac58cd.
interface ERC721 /* is ERC165 */ {
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
function balanceOf(address _owner) external view returns (uint256);
function ownerOf(uint256 _tokenId) external view returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external ;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external ;
function transferFrom(address _from, address _to, uint256 _tokenId) external ;
function approve(address _approved, uint256 _tokenId) external ;
function setApprovalForAll(address _operator, bool _approved) external;
function getApproved(uint256 _tokenId) external view returns (address);
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
interface ERC721TokenReceiver {
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns(bytes4);
}
contract ERC721Implementation is ERC721 {
// 토큰의 아이디를 키값으로 해당 주소를 리턴
mapping (uint256 => address) tokenOwner;
// 계정 주소에 해당하는 토큰 수를 리턴
mapping (address => uint256) owendTokensCount;
mapping (uint256 => address) tokenApprovals;
// 누가 누구에게 권한 부여를 했는가에 대한 맵핑, 1:N으로 권한을 부여할 수 있음.
mapping (address => mapping (address => bool)) operatorApprovals;
// 토큰 발행
function mint(
address _to, // 발행된 토큰의 소유자
uint _tokenId // 몇 번째 토큰인지
) public {
tokenOwner[_tokenId] = _to;
owendTokensCount[_to] += 1; // 토큰 보유 개수 1 증가시킴
}
// 특정 계정의 토큰 수를 리턴
function balanceOf(address _owner) public view returns (uint256) {
return owendTokensCount[_owner];
}
// 토큰의 주인 주소를 리턴
function ownerOf(uint256 _tokenId) public view returns (address) {
return tokenOwner[_tokenId];
}
// 토큰 전송
function transferFrom(address _from, address _to, uint256 _tokenId) public {
// 이 함수를 호출한 계정이 _tokenId의 소유자인지 검사
address owner = ownerOf(_tokenId);
// 1.토큰의 소유자 계정이거나 2.특정 토큰에 권한이 있는 계정이 접근했을때 3.소유자의 전체 토큰의 전송 권한을 가진 계정이 호출했을때
require(msg.sender == owner || getApproved(_tokenId) == msg.sender || isApprovedForAll(owner, msg.sender));
// 빈 인자 검사
require(_from != address(0)); // address(0)는 비어있다는 의미
require(_to != address(0));
// 보내는 측의 토큰 보유 수량을 1차감
owendTokensCount[_from] -= 1;
// 기존 토큰 보유자의 토큰 소유 정보를 제거
tokenOwner[_tokenId] = address(0);
// 전달받는 측의 토큰 보유 수량을 1증가
owendTokensCount[_to] += 1;
// 전달받는 측에 토큰 소유정보 추가
tokenOwner[_tokenId] = _to;
}
// 토큰 안전 정송
function safeTransferFrom(address _from, address _to, uint256 _tokenId) public {
transferFrom(_from, _to, _tokenId);
if(isContract(_to)){
bytes4 returnValue = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, '');
require(returnValue == 0x150b7a02);
}
}
// 토큰 안전 전송2
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) public {
transferFrom(_from, _to, _tokenId);
if(isContract(_to)){
bytes4 returnValue = ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _tokenId, data);
require(returnValue == 0x150b7a02);
}
}
function approve(address _approved, uint256 _tokenId) public {
// 권한을 받게되는 계정은 토큰 오너가 아니여야 한다.
address owner = ownerOf(_tokenId);
require(_approved != owner);
// 토큰 주인이 호출해야함
require(msg.sender == owner);
tokenApprovals[_tokenId] = _approved;
}
function getApproved(uint256 _tokenId) public view returns (address) {
return tokenApprovals[_tokenId];
}
function setApprovalForAll(address _operator, bool _approved) external {
// _operator: 계정이 소유한 모든 토큰들을 대신 운영할 계정
// _approved: 권한 부여 여부
require(_operator != msg.sender);
operatorApprovals[msg.sender][_operator] = _approved;
}
function isApprovedForAll(address _owner, address _operator) public view returns (bool) {
return operatorApprovals[_owner][_operator];
}
function isContract(address _addr) private view returns (bool) {
uint256 size;
assembly { size:= extcodesize(_addr) }
return size > 0;
}
}
contract Auction is ERC721TokenReceiver {
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes memory _data) public returns(bytes4) {
return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
contract ERC721 {
string private _name;
string private _symbol;
mapping(uint256 => string) private _tokenInfo;
mapping(uint256 => address) private _owners;
mapping(address => uint256) private _balances;
mapping(uint256 => address) private _tokenApprovals;
mapping(address => mapping(address=>bool)) private _operatorApprovals;
uint private _totalSupply;
event Transfer(address indexed from, address indexed to, uint tokenId);
event Approval(address indexed from, address indexed to, uint tokenId);
event ApprovalForAll(address from, address operator, bool approval);
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function balanceOf(address owner) public view returns (uint256) {
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view returns (address) {
return _owners[tokenId];
}
function name() public view returns (string memory) {
return _name;
}
function symbol() public view returns (string memory) {
return _symbol;
}
function tokenURI(uint tokenId) public view returns (string memory) {
return _tokenInfo[tokenId];
}
function getApproved(uint256 tokenId) public view returns (address) {
return _tokenApprovals[tokenId];
}
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
// 사용자 a가 사용자 b의 계좌에서 나에게 전송해달라고 요청하는 함수, 받는 사람이 호출하는 구조
function transferFrom(
address from,
address to,
uint256 tokenId
) public {
address owner = _owners[tokenId];
require(from == owner || isApprovedForAll(owner, msg.sender) || getApproved(tokenId) == msg.sender, "Not Approved");
_balances[msg.sender] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
function mint(address to, uint256 tokenId, string memory url ) public {
_balances[to] += 1;
_owners[tokenId] = to;
_tokenInfo[tokenId] = url;
_totalSupply += 1;
emit Transfer(address(0), to, tokenId);
}
function burn(uint256 tokenId) public {
address owner = _owners[tokenId];
delete _tokenApprovals[tokenId];
_balances[owner] -= 1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
}
function transfer(address to, uint256 tokenId) public {
require(_owners[tokenId] == msg.sender, "Incorrect Owner");
delete _tokenApprovals[tokenId];
_balances[msg.sender] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(msg.sender, to, tokenId);
}
function approve(address to, uint256 tokenId) public {
require(_owners[tokenId] == msg.sender, "Incorrect Owner");
_tokenApprovals[tokenId] = to;
emit Approval(_owners[tokenId], to, tokenId);
}
function setApprovalForAll(address owner, address operator, bool approved) public {
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
contract ERC721 {
string private _name;
string private _symbol;
mapping(uint256 => uint) private _tokenInfo;
mapping(uint256 => address) private _owners;
mapping(address => uint256) private _balances;
mapping(uint256 => address) private _tokenApprovals;
mapping(address => mapping(address=>bool)) private _operatorApprovals;
uint private _totalSupply = 10000;
event Transfer(address indexed from, address indexed to, uint tokenId);
event Approval(address indexed from, address indexed to, uint tokenId);
event ApprovalForAll(address from, address operator, bool approval);
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function balanceOf(address owner) public view returns (uint256) {
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view returns (address) {
return _owners[tokenId];
}
function name() public view returns (string memory) {
return _name;
}
function symbol() public view returns (string memory) {
return _symbol;
}
function tokenURI(uint tokenId) public view returns (uint) {
return _tokenInfo[tokenId];
}
function getApproved(uint256 tokenId) public view returns (address) {
return _tokenApprovals[tokenId];
}
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
// 사용자 a가 사용자 b의 계좌에서 나에게 전송해달라고 요청하는 함수, 받는 사람이 호출하는 구조
function transferFrom(
address from,
address to,
uint256 tokenId
) public {
address owner = _owners[tokenId];
require(from == owner, "Not Approved");
require(isApprovedForAll(owner, msg.sender), "Not Approved");
require(getApproved(tokenId) == msg.sender, "Not Approved");
_balances[msg.sender] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
function mint(address to, uint256 tokenId) public {
_balances[to] += 1;
_owners[tokenId] = to;
_tokenInfo[tokenId] = randomNft(_totalSupply);
emit Transfer(address(0), to, tokenId);
}
function randomNft(uint maxValue) internal view returns(uint) {
return uint(keccak256(abi.encodePacked(msg.sender, block.timestamp, _totalSupply))) % maxValue;
}
function burn(uint256 tokenId) public {
address owner = _owners[tokenId];
delete _tokenApprovals[tokenId];
_balances[owner] -= 1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
}
function transfer(address to, uint256 tokenId) public {
require(_owners[tokenId] == msg.sender, "Incorrect Owner");
delete _tokenApprovals[tokenId];
_balances[msg.sender] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(msg.sender, to, tokenId);
}
function approve(address to, uint256 tokenId) public {
require(_owners[tokenId] == msg.sender, "Incorrect Owner");
_tokenApprovals[tokenId] = to;
emit Approval(_owners[tokenId], to, tokenId);
}
function setApprovalForAll(address owner, address operator, bool approved) public {
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
}
The enumeration extension is OPTIONAL for ERC-721 smart contracts (see "caveats", below). This allows your contract to publish its full list of NFTs and make them discoverable.
/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// Note: the ERC-165 identifier for this interface is 0x780e9d63.
interface ERC721Enumerable /* is ERC721 */ {
/// @notice Count NFTs tracked by this contract
/// @return A count of valid NFTs tracked by this contract, where each one of
/// them has an assigned and queryable owner not equal to the zero address
function totalSupply() external view returns (uint256);
/// @notice Enumerate valid NFTs
/// @dev Throws if `_index` >= `totalSupply()`.
/// @param _index A counter less than `totalSupply()`
/// @return The token identifier for the `_index`th NFT,
/// (sort order not specified)
function tokenByIndex(uint256 _index) external view returns (uint256);
/// @notice Enumerate NFTs assigned to an owner
/// @dev Throws if `_index` >= `balanceOf(_owner)` or if
/// `_owner` is the zero address, representing invalid NFTs.
/// @param _owner An address where we are interested in NFTs owned by them
/// @param _index A counter less than `balanceOf(_owner)`
/// @return The token identifier for the `_index`th NFT assigned to `_owner`,
/// (sort order not specified)
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}
계정마다 소유한 토큰을 불러오거나 계정들을 통해 발행된 모든 토큰들을 불러온다.
The metadata extension
is OPTIONAL for ERC-721 smart contracts (see "caveats", below). This allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent.
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// Note: the ERC-165 identifier for this interface is 0x5b5e139f.
interface ERC721Metadata /* is ERC721 */ {
/// @notice A descriptive name for a collection of NFTs in this contract
function name() external view returns (string _name);
/// @notice An abbreviated name for NFTs in this contract
function symbol() external view returns (string _symbol);
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
/// @dev 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);
}
tokenURI()는 토큰의 정보를 저장한 IPFS 주소를 의미함.
IPFS에 저장된 토큰 정보의 주소를 불러오는데 쓰인다.