NFT는 ERC-721로 구성되어 있으며 NWT_Token으로 구매가 가능하다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../node_modules/@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "../node_modules/@openzeppelin/contracts/utils/Counters.sol";
import "../node_modules/@openzeppelin/contracts/access/Ownable.sol";
import "../node_modules/@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract WATTONFT is ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
IERC20 public token;
uint256 public nftPrice;
event NewNft(address owner, uint256 tokenId, string tokenUri);
mapping (string => uint256) public getTokenId;
mapping (uint256 => uint256) public tokenPrice;
constructor() ERC721("WATTONFTs", "WTNFT") {}
function setToken (address tokenAddress) public onlyAuthorized returns (bool) {
require(tokenAddress != address(0x0));
token = IERC20(tokenAddress);
return true;
}
function mintNFT(string memory tokenURI) public onlyAuthorized returns (uint256) {
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(msg.sender, newItemId);
_setTokenURI(newItemId, tokenURI);
emit NewNft(msg.sender,newItemId,tokenURI);
return newItemId;
}
function approveSale(address receipent) onlyAuthorized public {
_setApprovalForAll(receipent, msg.sender, true);
}
function setForSale(uint256 _tokenId, uint256 _price) public onlyAuthorized {
address tokenOwner = ownerOf(_tokenId);
require(tokenOwner != address(0x0));
if(tokenOwner == msg.sender){
require(_price > 0,'price is zero or lower');
tokenPrice[_tokenId] = _price;
}else{
require(_price > 0,'price is zero or lower');
require(isApprovedForAll(tokenOwner, msg.sender),'token owner did not approve');
tokenPrice[_tokenId] = _price;
}
}
function purchaseToken(uint256 _tokenId,address buyer) public onlyAuthorized {
uint bal = token.balanceOf(buyer);
uint256 price = tokenPrice[_tokenId];
address tokenSeller = ownerOf(_tokenId);
require(buyer != address(0x0));
require(tokenSeller != address(0x0));
require(bal >= price,"buyer is not enough money");
require(tokenSeller != buyer,"owner not buy itself");
token.transferFrom(buyer,tokenSeller,price);
safeTransferFrom(tokenSeller, buyer, _tokenId);
}
event Start();
event Bid(address indexed sender, uint amount);
event Withdraw(address indexed bidder, uint amount);
event End(address winner, uint amount);
event Endedat(uint a);
struct Auction {
bool started;
address owner;
uint nftId;
bool status;
uint endAt;
address highestBidder;
uint highestBid;
bool ended;
}
mapping(uint => Auction) auction;
mapping(uint => mapping(address => uint)) public bids;
function startAuction(uint nftId, address owner, uint _startingBid) public {
auction[nftId].nftId = nftId;
auction[nftId].owner = owner;
auction[nftId].started = true;
auction[nftId].highestBid = _startingBid;
transferFrom(owner, address(this), nftId);
auction[nftId].endAt = block.timestamp + 1 days;
emit Endedat(auction[nftId].endAt);
}
function bid(uint nftId, address buyer, uint amount) public {
require(auction[nftId].started, "not started");
require(block.timestamp < auction[nftId].endAt, "ended");
require(amount > auction[nftId].highestBid, "value < highest");
if (auction[nftId].highestBidder != address(0)) {
bids[nftId][auction[nftId].highestBidder] += auction[nftId].highestBid;
}
token.transferFrom(buyer, address(this), amount);
auction[nftId].highestBidder = buyer;
auction[nftId].highestBid = amount;
}
function withdraw(address addr, uint nftId) public {
uint bal = bids[nftId][addr];
bids[nftId][addr] = 0;
token.transferFrom(address(this), addr , bal);
emit Withdraw(addr, bal);
}
function end(uint nftId) public {
require(auction[nftId].started, "not started");
require(block.timestamp >= auction[nftId].endAt, "not ended");
require(!auction[nftId].ended, "ended");
auction[nftId].ended = true;
if (auction[nftId].highestBidder != address(0)) {
safeTransferFrom(address(this), auction[nftId].highestBidder, nftId);
token.transferFrom(address(this), auction[nftId].owner, auction[nftId].highestBid);
} else {
safeTransferFrom(address(this), auction[nftId].owner, nftId);
}
emit End(auction[nftId].highestBidder, auction[nftId].highestBid);
}
}
해석
using Counters for Counters.Counter;
- Counters 는 라이브러리
- using Conters for Counters.Counter : current() 또는 increment()와 같은 카운터 라이브러리 내부의 모든 기능을 카운터 구조에 할당하라는 의미
- 이러한 함수들을 호출할 때, 그 함수들의 첫 번째 매개변수는 카운터 구조인 유형 그 자체
-
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) pragma solidity ^0.8.0; /** * @title Counters * @author Matt Condon (@shrugs) * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number * of elements in a mapping, issuing ERC721 ids, or counting request ids. * * Include with `using Counters for Counters.Counter;` */ library Counters { struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { unchecked { counter._value += 1; } } function decrement(Counter storage counter) internal { uint256 value = counter._value; require(value > 0, "Counter: decrement overflow"); unchecked { counter._value = value - 1; } } function reset(Counter storage counter) internal { counter._value = 0; } }
Counters.Counter private _tokenIds;
- Counters.Counter 구조체의 _value = _tokenIds;
IERC20 public token;
- IERC20 함수를 사용한다. 위함이다 setToken을 먼저 해줄 수 있도록
uint256 public nftPrice;
- nft 가격을 정하기위해서 만들어논 상태변수
event NewNft(address owner, uint256 tokenId, string tokenUri);
mapping (string => uint256) public getTokenId;
- tokenUri를 key값으로하는 tokenId를 얻기위한 맵핑 tokenId가 재대로반환이 되지않아서 만들어 놓았다.
mapping (uint256 => uint256) public tokenPrice;
- 토큰아이디를 통해서 해당 nft의 가격을 알 수 있게 만들어 놓은 맵핑
function setToken
function setToken (address tokenAddress) public onlyAuthorized returns (bool) {
require(tokenAddress != address(0x0));
token = IERC20(tokenAddress);
return true;
}
function mintNFT
- NFT 민팅
function mintNFT(string memory tokenURI) public onlyAuthorized returns (uint256) {
_tokenIds.increment();
- Counter 라이브러리 increment : counter._tokenIds += 1;
uint256 newItemId = _tokenIds.current();
- Counter 라이브러리 current : counter._tokenIds; => newItemId
_mint(msg.sender, newItemId);
- 우리 플랫폼은 서버가 호출한다 msg.sender = serverAddress, newItemId = 현재 Id 값
_setTokenURI(newItemId, tokenURI);
- ERC721URIStorage 에 _setTokenURI로 newItemId, tokenURI를 넘겨준다
-
abstract contract ERC721URIStorage is ERC721 { using Strings for uint256; mapping(uint256 => string) private _tokenURIs; function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token"); _tokenURIs[tokenId] = _tokenURI; } }
-
mapping(uint256 => string) private _tokenURIs => _tokenUIRs[uint256] = string =>_tokenURIs[tokenId] = _tokenURI;
emit NewNft(msg.sender,newItemId,tokenURI);
- event NewNft에 기록
return newItemId;
}
- mintNFT erc721에 있는 _mint함수를 이용해서 유일한 토큰을 생성하는 함수
- 밑에 nft발행방식은 누군가에게 발헹해주는 방식이 아닌 서버계정 자체에 nft를 발행해주는 함수.
function approveSale
- approveSale함수를 사용해서 소유자가 msg.seder에게 대납소유권을 허락해주는 방식. 그래야 safeTransferFrom을 사용할 수 가 있다.
function approveSale(address receipent) onlyAuthorized public {
_setApprovalForAll(receipent, msg.sender, true);
- ERC-721 : _setApprovalForAll
-
function _setApprovalForAll(address owner,address operator,bool approved) internal virtual {require(owner != operator, "ERC721: approve to caller");_operatorApprovals[owner][operator] = approved;emit ApprovalForAll(owner, operator, approved);}
-
}
function setForSale
- setForSale함수를 이용해서 소유자가 tokenId에 가격을 지정
function setForSale(uint256 _tokenId, uint256 _price) public onlyAuthorized {
- 토큰의 소유자 계정만 판매하도록 만드는 함수
address tokenOwner = ownerOf(_tokenId);
- ERC-721 ownerOf
-
function ownerOf(uint256 tokenId) public view virtual override returns (address) { address owner = _owners[tokenId]; require(owner != address(0), "ERC721: owner query for nonexistent token"); return owner; }
require(tokenOwner != address(0x0));
if(tokenOwner == msg.sender){
- tokenOwner 와 msg.sender가 일치하다면
require(_price > 0,'price is zero or lower');
tokenPrice[_tokenId] = _price;
- _tokenId를 통해 가격을 알 수 있고 인자로 받은 _price 값 지정
}else{
require(_price > 0,'price is zero or lower');
require(isApprovedForAll(tokenOwner, msg.sender),'token owner did not approve');
- approveSale에서 했던
- _operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
- 대납 소유권 확인 한다
- _operatorApprovals[owner][operator] = approved;
tokenPrice[_tokenId] = _price;
- 확인이 되었으면 값 지정 판매
}
}
function purchaseToken
- purchaseToken함수를 사용해서 seller 와 buyer간에 교환
function purchaseToken(uint256 _tokenId,address buyer) public onlyAuthorized {
uint bal = token.balanceOf(buyer);
- 구매자의 토큰 밸런스 확인
uint256 price = tokenPrice[_tokenId];
- setForSale에서 지정한 값을 price로
address tokenSeller = ownerOf(_tokenId);
- 토큰 소유자만 판매하게 하는 함수 ownerOf 주소값을 tokenSeller 지정
require(buyer != address(0x0));
require(tokenSeller != address(0x0));
require(bal >= price,"buyer is not enough money");
require(tokenSeller != buyer,"owner not buy itself");
- 본인은 구매를 못함
token.transferFrom(buyer,tokenSeller,price);
- 구매자가 판매자게에 erc20토큰을 보내는 함수
safeTransferFrom(tokenSeller, buyer, _tokenId);
- 판매자가 구매자에게 tokenId를 넘기는 함수
}
'WATTO 프로젝트 > 컨트렉트' 카테고리의 다른 글
Ethereum 기반 컨트렉트 klaytn 환경으로 변경 (0) | 2022.02.14 |
---|---|
NFT 경매 (0) | 2022.02.13 |
Token_Swap (0) | 2022.02.13 |
WT_Token (0) | 2022.02.12 |
NWT_Token (0) | 2022.02.12 |
댓글