BlockChain
(BlockChain) NFT 거래 컨트랙트
JJeongHyun
2023. 3. 14. 17:13
반응형
https://developerjjh.tistory.com/177
SaleToken 컨트랙트
- 사용자 간 NFT 판매 및 구매에 관한 컨트랙트
토큰 정보에 대한 구조체
struct TokenInfo {
uint tokenId;
uint Rank;
uint Type;
uint price;
}
- price : 가격, 0일 때 판매 중이 아닌 걸로 정의
NFT 가격 매핑
mapping(uint => uint) public tokenPrices;
- tokenId => price
판매중인 NFT의 tokenId 목록
uint[] public SaleTokenList;
판매 등록 함수
function SalesToken(uint _tokenId, uint _price) public {
address tokenOwner = Token.ownerOf(_tokenId);
require(tokenOwner == msg.sender);
require(_price > 0);
require(Token.isApprovedForAll(msg.sender, address(this)));
tokenPrices[_tokenId] = _price;
SaleTokenList.push(_tokenId);
}
- NFT의 소유자를 찾아 저장
- NFT 소유자가 판매 등록을 했는지, 소유자만 판매 등록가능
- 판매 가격이 0보다 큰지, 판매 중인지 확인
- NFT에 대한 권한이 현재 컨트랙트에 있는지 확인
- OpenSea를 기준으로 했을 때 setApprovedForAll 메서드가 이미 존재
- 메타마스크에 연결, 로그인이 됐을 때 그 계정에 대한 권한을 위임받는다
- owner가 판매 컨트랙트에게 모든 토큰을 위임했는지 확인
- msg.sender : 판매하는 사람 (토큰 소유자)
- 두 번째 매개변수 : 대리인 (OpenSea 계정)
- 가격을 등록, 매핑한다
- 판매 목록에 추가
토큰을 구매하는 함수
function PurchaseToken(uint _tokenId) public payable {
address tokenOwner = Token.ownerOf(_tokenId);
require(tokenOwner != msg.sender);
require(tokenPrices[_tokenId] > 0);
require(tokenPrices[_tokenId] <= msg.value);
payable(tokenOwner).transfer(msg.value);
Token.transferFrom(tokenOwner, msg.sender, _tokenId);
tokenPrices[_tokenId] = 0;
popSaleToken(_tokenId);
}
- 토큰 ID에 대한 소유자를 저장
- 구매하려고 하는 계정이 토큰의 소유자이면 실행 중지
- 토큰 ID의 가격이 0보다 큰지, 즉 판매 중인지 확인
- 구매자가 토큰을 구매하기 위해 충분한 Ether를 전송했는지 가격을 확인
- 현재 컨트랙트가 NFT 소유자에게 구매자로부터 받은 Ether를 전송
- NFT 소유자로부터 구매자에게 NFT 전송
- 가격 0, 판매를 중지한다는 뜻으로 재정의
- 판매 목록에서 제외, 제거
구매 취소 함수
function cancelSaleToken(uint _tokenId) public {
address tokenOwner = Token.ownerOf(_tokenId);
require(tokenOwner == msg.sender);
require(tokenPrices[_tokenId] > 0);
tokenPrices[_tokenId] = 0;
popSaleToken(_tokenId);
}
전달받은 토큰을 SaleTokenList에서 삭제하는 함수
function popSaleToken(uint _tokenId) private returns (bool) {
for (uint i = 0; i < SaleTokenList.length; i++) {
if (SaleTokenList[i] == _tokenId) {
SaleTokenList[i] = SaleTokenList[SaleTokenList.length - 1];
SaleTokenList.pop();
return true;
}
}
return false;
}
- 삭제할 원소를 찾아서 목록 제일 마지막 원소로 덮어주고 그 마지막 원소를 제거해줌으로써 원하는 원소를 덮는다
판매 중인 전체 NFT 목록 가져오는 함수
function getSaleTokenList() public view returns (TokenInfo[] memory) {
require(SaleTokenList.length > 0);
TokenInfo[] memory list = new TokenInfo[](SaleTokenList.length);
for (uint i = 0; i < SaleTokenList.length; i++) {
uint tokenId = SaleTokenList[i];
uint Rank = Token.getTokenRank(tokenId);
uint Type = Token.getTokenType(tokenId);
uint price = tokenPrices[tokenId];
list[i] = TokenInfo(tokenId, Rank, Type, price);
}
return list;
}
- TokenInfo 형식으로 반환
- [{tokenId : 1, Type : 1, Rank : 2, price :....},...]
- 판매 중인 목록이 없는지 확인
- 등록된, 판매중인 NFT 개수의 크기로 NFT 정보 배열을 생성
- JS 문법 > let list : new Array(SaleTokenList.length)
- NFT의 정보를 생성해서 list에 저장
NFT 소유자를 기준으로 가지고 있는 NFT 목록 가져오는 함수
function getOwnerTokens(
address _tokenOwner
) public view returns (TokenInfo[] memory) {
uint balance = Token.balanceOf(_tokenOwner);
require(balance > 0);
TokenInfo[] memory list = new TokenInfo[](balance);
for (uint i = 0; i < balance; i++) {
uint tokenId = Token.tokenOfOwnerByIndex(_tokenOwner, i);
uint Rank = Token.getTokenRank(tokenId);
uint Type = Token.getTokenType(tokenId);
uint price = tokenPrices[tokenId];
list[i] = TokenInfo(tokenId, Rank, Type, price);
}
return list;
}
- 등록한 NFT 개수를 저장
- NFT의 개수가 있는지 확인
- ERC721Enumerable 컨트랙트에 존재하는 메서드로 소유자의 NFT 목록 중 i번째 ID를 가져온다
minting 직후에 소유하고 있는 마지막 NFT 가져오는 함수
function getLatestToken(
address _tokenOwner
) public view returns (TokenInfo memory) {
uint balance = Token.balanceOf(_tokenOwner);
uint tokenId = Token.tokenOfOwnerByIndex(_tokenOwner, balance - 1);
uint Rank = Token.getTokenRank(tokenId);
uint Type = Token.getTokenType(tokenId);
uint price = tokenPrices[tokenId];
return TokenInfo(tokenId, Rank, Type, price);
}
전체 코드
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "./NftToken.sol";
contract SaleToken {
NftToken public Token;
constructor(address _tokenAddress) {
Token = NftToken(_tokenAddress);
}
struct TokenInfo {
uint tokenId;
uint Rank;
uint Type;
uint price;
}
mapping(uint => uint) public tokenPrices;
uint[] public SaleTokenList;
function SalesToken(uint _tokenId, uint _price) public {
address tokenOwner = Token.ownerOf(_tokenId);
require(tokenOwner == msg.sender);
require(_price > 0);
require(Token.isApprovedForAll(msg.sender, address(this)));
tokenPrices[_tokenId] = _price;
SaleTokenList.push(_tokenId);
}
function PurchaseToken(uint _tokenId) public payable {
address tokenOwner = Token.ownerOf(_tokenId);
require(tokenOwner != msg.sender);
require(tokenPrices[_tokenId] > 0);
require(tokenPrices[_tokenId] <= msg.value);
payable(tokenOwner).transfer(msg.value);
Token.transferFrom(tokenOwner, msg.sender, _tokenId);
tokenPrices[_tokenId] = 0;
popSaleToken(_tokenId);
}
function cancelSaleToken(uint _tokenId) public {
address tokenOwner = Token.ownerOf(_tokenId);
require(tokenOwner == msg.sender);
require(tokenPrices[_tokenId] > 0);
tokenPrices[_tokenId] = 0;
popSaleToken(_tokenId);
}
function popSaleToken(uint _tokenId) private returns (bool) {
for (uint i = 0; i < SaleTokenList.length; i++) {
if (SaleTokenList[i] == _tokenId) {
SaleTokenList[i] = SaleTokenList[SaleTokenList.length - 1];
SaleTokenList.pop();
return true;
}
}
return false;
}
function getSaleTokenList() public view returns (TokenInfo[] memory) {
require(SaleTokenList.length > 0);
TokenInfo[] memory list = new TokenInfo[](SaleTokenList.length);
for (uint i = 0; i < SaleTokenList.length; i++) {
uint tokenId = SaleTokenList[i];
uint Rank = Token.getTokenRank(tokenId);
uint Type = Token.getTokenType(tokenId);
uint price = tokenPrices[tokenId];
list[i] = TokenInfo(tokenId, Rank, Type, price);
}
return list;
}
function getOwnerTokens(
address _tokenOwner
) public view returns (TokenInfo[] memory) {
uint balance = Token.balanceOf(_tokenOwner);
require(balance > 0);
TokenInfo[] memory list = new TokenInfo[](balance);
for (uint i = 0; i < balance; i++) {
uint tokenId = Token.tokenOfOwnerByIndex(_tokenOwner, i);
uint Rank = Token.getTokenRank(tokenId);
uint Type = Token.getTokenType(tokenId);
uint price = tokenPrices[tokenId];
list[i] = TokenInfo(tokenId, Rank, Type, price);
}
return list;
}
function getLatestToken(
address _tokenOwner
) public view returns (TokenInfo memory) {
uint balance = Token.balanceOf(_tokenOwner);
uint tokenId = Token.tokenOfOwnerByIndex(_tokenOwner, balance - 1);
uint Rank = Token.getTokenRank(tokenId);
uint Type = Token.getTokenType(tokenId);
uint price = tokenPrices[tokenId];
return TokenInfo(tokenId, Rank, Type, price);
}
}