๐ KIP-17
๐ KIP-17์ ํน์ง
- NFT์ฉ ํ์ค API ์ ๊ณต (NFT์ถ์ , ์ ์ก ๋ฑ)
- ๋ชจ๋ ํ ํฐ์ transfer/ mint/ burn ์์
์ ์ด๋ฒคํธ ๋ก๊ทธ๋ณ ์ถ์ ์ ๊ฑฐ์ณ์ผ ํฉ๋๋ค.
์ฆ, ์ก๊ธ ์์ ์ ๋ฌด์กฐ๊ฑด transfer/ mint/ burn ๊ด๋ จ๋ ๋ชจ๋ ์์ ์์ ๋ฐ์๋์ด์ผ ํฉ๋๋ค. - ERC-721์ ์ง๊ฐ ์ธํฐํ์ด์ค๊ฐ ERC-721๊ณผ ํธํ๋๋๋ก ์ง์ํจ.
์ถ์ฒ : KIP-17 ๊ณต์ ๋ฌธ์
โ KIP17 ์ปจํธ๋ํธ๋ฅผ ์ฌ์ฉํด ๋ฐฐํฌ
KIP17Token.sol ์ ์ ์๋ ์ธํฐํ์ด์ค (๊ณต์๋ฌธ์)
โ KIP-17์ ์ฌ์ฉํด Bapp NFT Market contract ๋ง๋ค๊ธฐ
- ๋ฐํ, ์กฐํ
- ํ๋งค: Market์๊ฒ ์ ์ก
- ๊ตฌ๋งค: Market์์ buy์คํ
KIPcontract์์ ๋ค์ ์ฝ๋ ์ ๊ฑฐ (์ค์ต์ฉ์ผ๋ก ๊ฐ๋จํ ๊ธฐ๋ฅ๋ง ์ฌ์ฉํ ๊ฑฐ๋ผ)
KIP17Burnable,
KIP17Pausable
๊ด๋ จ ์ฝ๋ ์ญ์
KIP17Mintable,mintWithTokenURI์ onlyMinter ์ญ์
pragma solidity >=0.4.24 <=0.5.6;
contract NFTSimple {
string public name = 'KlayLion';
string public symbol = "DD";
mapping (uint256 => address) public tokenOwner;
mapping (uint256 => string) public tokenURIs;
// ์์ ํ ํ ํฐ ๋ฆฌ์คํธ
mapping(address => uint256[]) private _ownedTokens;
// onKIP17Received bytes value
bytes4 private constant _KIP17_RECEIVED = 0x6745782b;
// mint(tokenId, uri, owner)
// transferForm(form, to, tokenId) -> owner๊ฐ ๋ฐ๋๋ ๊ฒ(from -> to)
//๋ฐํ
function mintWithTokenURI(address to, uint256 tokenId, string memory tokenURI) public returns (bool) {
// to์๊ฒ tokenId(์ผ๋ จ๋ฒํธ)๋ฅผ ๋ฐํํ๊ฒ ๋ค.
// ์ ํ ๊ธ์๋ tokenURI
tokenOwner[tokenId] = to;
tokenURIs[tokenId] = tokenURI;
// add token to the list
_ownedTokens[to].push(tokenId);
return true;
}
// ์ ์ก
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
require(from == msg.sender, "from != msg.sender"); // ๋ณด๋ธ ์ฌ๋์ด fromํ๊ณ ๊ฐ์ ๋
require(from == tokenOwner[tokenId], "you are not the owner of the token"); // ๋ณด๋ธ ์ฌ๋์ด ํ ํฐ์ ์์ ์ฃผ์ผ ๋
// ํด๋น ํ ํฐ๋ง ์ง์ฐ๊ณ
_removeTokenFromList(from, tokenId);
// ์ ์ก ๋ฐ๋ ๊ณณ์ ํด๋นํ ํฐ ์ถ๊ฐ
_ownedTokens[to].push(tokenId);
// ํด๋น ํ ํฐ์ ์ ์ก
tokenOwner[tokenId] = to; // ์ด๊ฒ๋ง ์์ผ๋ฉด ์๋ฌด๋ ํ ํฐ์ ์ ์กํ ์ ์๋ค.
// if ์ ์ก ๋ฐ์ ์ค๋งํธ ์ปจํํ
ํธ์ ์คํํ ์ฝ๋๊ฐ ์์ผ๋ฉด! ๋ค์ ์ฝ๋ ์คํ
require(
_checkOnKIP17Received(from, to, tokenId, _data), "KIP17: transfer to non KIP17Receiver implementer"
);
}
// internal ์ ์ฌ์ฉ ํ์ฌ ๋ด๋ถ์์๋ง ํธ์ถํ ์ ์๋๋ก ํจ
function _checkOnKIP17Received(address from, address to, uint256 tokenId, bytes memory _data) internal returns (bool) {
bool success;
bytes memory returndata;
// ์ค๋งํธ ์ปจํธ๋ ํธ๊ฐ ์๋๋ฉด
if (!isContract(to)){
return true;
}
// ์ค๋งํธ ์ปจํธ๋ ํธ๋ผ๋ฉด ์คํํด๋ผ
(success, returndata) = to.call(
abi.encodeWithSelector(
_KIP17_RECEIVED, // ํด๋น ํจ์๋ฅผ ์คํํด์ ๋ค์ parameter๋ค์ ๋ฐ์์๋ผ
msg.sender,
from,
tokenId,
_data
)
);
// ๋ฆฌํด๊ฐ์ด _KIP17_RECEIVED์ ๊ฐ์ผ๋ฉด?
if(
returndata.length !=0 &&
abi.decode(returndata, (bytes4)) == _KIP17_RECEIVED
){
return true;
}
return false;
}
function isContract(address account) internal view returns (bool) {
uint256 size;
// extcodesize : ์ฝ๋๊ฐ ์กด์ฌ ํ๋์ง?
assembly { size :=extcodesize(account)}
return size > 0;
}
// ๊ธฐ์กด ํ ํฐ ์์ ์ฃผ์ ํ ํฐ ์ญ์
// ์ค๋งํธ ์ปจํธ๋ํธ ์์์๋ง ํธ์ถํ๊ณ ์ถ์ผ๋ฉด private
function _removeTokenFromList(address from, uint256 tokenId) private {
// [10, 15, 19, 20] -> 19๋ฒ ์ญ์ ํ๊ณ ์ถ๋ค
// [10, 15, 20, 19]
// [10, 15, 20]
uint256 lastTokenIndex = _ownedTokens[from].length - 1;
for(uint256 i=0 ; i<_ownedTokens[from].length; i++){
// Swap last token with deleting token.
if(tokenId == _ownedTokens[from][i]){
_ownedTokens[from][i] = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][lastTokenIndex] = tokenId;
break;
}
}
_ownedTokens[from].length--;
}
// ์์ ์ค์ธ ํ ํฐ
function ownerToken(address owner) public view returns (uint256[] memory){
return _ownedTokens[owner];
}
function setTokenUri(uint256 id, string memory uri) public{
tokenURIs[id] = uri;
}
}
// ์ค๋งํธ ์ปจํธ๋ํธ๋ ํ ํฐ์ ์์ ํ ์ ์์
contract NFTMarket {
mapping(uint256 => address) public seller;
//function buyNFT(uint256 tokenId, address NFTAddress, address to) public returns (bool){
// NFTAddress์์ NFTsimple์ ๊ฐ์ง ํจ์๋ฅผ ํธ์ถ ํ ์ ์์
// NFTSimple(NFTAddress).safeTransferFrom(address(this), to, tokenId);
// address(this) ๋ ์ค์ค๋ก์ ์ฃผ์
// KLAY๋ฅผ ์ ์กํ ์ ์๊ฒํ๋ ค๋ฉด, payble ์ฌ์ฉ
function buyNFT(uint256 tokenId, address NFTAddress) public payable returns (bool){
// ํ ํฐ ํ ์ฌ๋(seller)์ ์ฃผ์์ ๋ ๋ฐ์ ์ฌ๋(receiver)์ ์ฃผ์๋ฅผ ๋ฃ์ด์ค
address payable receiver = address(uint160(seller[tokenId]));
// Send 0.01 KLAY to receiver
// 10 ** 18 PEB = 1KALY
// 10 ** 16 PEB = 0.01KALY
receiver.transfer(10 ** 16);
// ๊ตฌ๋งค์์๊ฒ ํ ํฐ์ด ๊ฐ๋๋ก
NFTSimple(NFTAddress).safeTransferFrom(address(this), msg.sender, tokenId, '0x00');
return true;
}
// Market์ด token์ ๋ฐ์์ ๋ (ํ๋งค๋์ ์ฌ๋ผ๊ฐ์ ๋), ํ๋งค์๊ฐ ๋๊ตฐ์ง ๊ธฐ๋ก
function onKIP17Recevied(address operator, address from, uint256 tokenId, bytes memory data) public returns (bytes4) {
seller[tokenId] = from;
// ํ ํฐ์ ๋ฐ์์ ๋,
// "๊ตฌํํ๊ณ ์ถ์ ๊ธฐ๋ฅ์ด ์์ผ๋ฉด ~~ํ ๋ฌธ์๋ฅผ ๋ฆฌํดํด๋ผ."
return bytes4(keccak256("onKIP17Received(address,address,uint256,bytes)"));
}
}
๋ฐ์ํ
๋๊ธ