📌 설치
- npm i @openzeppelin/contracts
- npm install -g remixd
- deprecated 으로 설치 안된다. 아래 명령어를 이용하자
- npm install -g @remix-project/remixd
📦 sample code - MintAnimalToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
contract MintAnimalToken is ERC721Enumerable {
constructor() ERC721("h662Animals","HAS"){}
function mintAnimalToken() public {
uint256 animalTokenId = totalSupply() + 1;
_mint(msg.sender, animalTokenId);
}
}
🔖 ERC721
NFT에 관한 프로토콜이 정의해놓은 인터페이스
- remix로 실행
- remixd -s . --remix-ide https://remix.ethereum.org
📦 remix ide 연결
- Auto Compile
- 체크해주면 알아서 컴파일 해준다.
- deploy된 contract 확인 가능
계정이 여러 개 있다.
mintAnimalToken() 함수를 호출하면 해당 계정이 등록되고, ownerOf를 통해 조회할 수 있다.
만약 Id에 해당하는 것이 없다면 에러가 뜬다.
📌 첫번째 스마트 컨트랙트 완성
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
contract MintAnimalToken is ERC721Enumerable {
constructor() ERC721("h662Animals","HAS"){}
mapping(uint256 => uint256) public animalTypes;
function mintAnimalToken() public {
uint256 animalTokenId = totalSupply() + 1;
uint256 animalType = uint256(keccak256(abi.encodePacked(block.timestamp,msg.sender,animalTokenId))) % 5 + 1;
animalTypes[animalTokenId] = animalType;
_mint(msg.sender, animalTokenId);
}
}
📌 판매 스마트 컨트렉트 작성
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "MintAnimalToken.sol";
contract SaleAnimalToken{
MintAnimalToken public mintAnimalTokenAddress;
constructor(address _mintAnimalTokenAddress){
mintAnimalTokenAddress = MintAnimalToken(_mintAnimalTokenAddress);
}
mapping (uint256 => uint256) public animalTokenPrices;
uint256[] public onSaleAnimalTokenArray;
function setForSaleAnimalToken(uint256 _animalTokenId, uint256 _price) public{
address animalTokenOwner = mintAnimalTokenAddress.ownerOf(_animalTokenId);
require(animalTokenOwner == msg.sender, "not owner");
require(_price > 0 , "lower price");
require(animalTokenPrices[_animalTokenId]==0, "already on sale");
require(mintAnimalTokenAddress.isApprovedForAll(animalTokenOwner,address(this)),"Animal token owner did not approve token");
animalTokenPrices[_animalTokenId] = _price;
onSaleAnimalTokenArray.push(_animalTokenId);
}
}
- mintAnimalToken.sol을 먼저 배포하고 나서, 해당 mintAnimalToken.sol의 주소값을 복사해준다.
- SaleAnimalToken.sol 은 주소값이 인자로 필요하므로, 해당 minanimalToken의 주소값을 넣어준다.
- transact를 누르면 deploy가 된다.
먼저 배포를 해주자.
그리고 해당 animalToken을 10원에 팔려고 하면 에러가 난다.
require(mintAnimalTokenAddress.isApprovedForAll(animalTokenOwner,address(this)),"Animal token owner did not approve token");
이는 4번째 SaleAnimalToekn.sol 에서 4번째 require에서 걸리기 때문이다.
이는 isApprovedForAll에서 owner -> 지갑 계정, operator -> SaleAnimalToken 배포된 주소 를 넣었을 때, false가 나오기 때문이다. true로 바꿔주어야 에러가 안난다.
해당 지갑의 계정으로 setApprovalForAll의 operator에 해당 스마트 컨트렉트의 주소를, approved에 true를 넣으면 true로 바뀐다.
isApprovedForAll을 다시 호출해보면 true로 바뀐 것을 볼 수 있다.
이제 다시 transact를 누르면 정상적으로 처리되는 것을 볼 수 있다.
📌 구매 함수 추가
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "MintAnimalToken.sol";
contract SaleAnimalToken{
MintAnimalToken public mintAnimalTokenAddress;
constructor(address _mintAnimalTokenAddress){
mintAnimalTokenAddress = MintAnimalToken(_mintAnimalTokenAddress);
}
mapping (uint256 => uint256) public animalTokenPrices;
uint256[] public onSaleAnimalTokenArray;
function setForSaleAnimalToken(uint256 _animalTokenId, uint256 _price) public{
address animalTokenOwner = mintAnimalTokenAddress.ownerOf(_animalTokenId);
require(animalTokenOwner == msg.sender, "not owner");
require(_price > 0 , "lower price");
require(animalTokenPrices[_animalTokenId]==0, "already on sale");
require(mintAnimalTokenAddress.isApprovedForAll(animalTokenOwner,address(this)),"Animal token owner did not approve token");
animalTokenPrices[_animalTokenId] = _price;
onSaleAnimalTokenArray.push(_animalTokenId);
}
function purchaseAnimalToken (uint256 _animalTokenId) public payable {
uint256 price = animalTokenPrices[_animalTokenId];
address animalTokenOwner = mintAnimalTokenAddress.ownerOf(_animalTokenId);
require(price > 0, "AnimalToken not sale");
require(price <= msg.value , "not money");
require(animalTokenOwner != msg.sender , "not owner");
payable(animalTokenOwner).transfer(msg.value);
mintAnimalTokenAddress.safeTransferFrom(animalTokenOwner,msg.sender, _animalTokenId);
animalTokenPrices[_animalTokenId] = 0;
for(uint256 i=0; i<onSaleAnimalTokenArray.length; i++){
if(animalTokenPrices[onSaleAnimalTokenArray[i]] == 0){
onSaleAnimalTokenArray[i] = onSaleAnimalTokenArray[onSaleAnimalTokenArray.length-1];
onSaleAnimalTokenArray.pop();
}
}
}
function getOnSaleAnimalTokenArrayLength() view public returns (uint256){
return onSaleAnimalTokenArray.length;
}
}
- purchaseAnimalToken 함수를 추가했다.
🔖 상품 등록 및 세팅
- 작업 순서
- mintAnimalToken.sol depoly
- saleAnimalToken.sol deploy ( mintAnimalToken 주소 기반으로 )
- minAnimalToken.sol 에서 mintAnimalToken 함수 호출
- 상품 등록이 등록됐다.
- setApprovalForAll에서 saleAnimalToken.sol 컨트렉트 등록
- apporved false -> true로 바뀔 수 있게 등록
- operator에 스마트 컨트렉트 주소, approved = true 주면 된다.
- saleAnimalToken.sol에서 setForSaleAnimalToken 함수에 animalTokenId, price등록
- 예제로 1번, price= 10 등록
🔖 상품 판매
animalTokenId 에 1을 입력하고 transact를 누르면 된다.
그런데, 현재 계정은 판매를 등록한 계정이므로 계정을 바꿔주자.
그런다음 transact를 누르면 거래가 완료됐다.
mintAnimalToken.sol의 ownerOf 함수에서 누가 주인인 지 확인할 수 있다.
- 이 블로그의 글은 아래 강좌를 수강하면서 정리한 글입니다.
- 디앱 프로젝트 ( NFT 생성, NFT 구매 및 판매 )
- ( https://www.inflearn.com/course/%EB%94%94%EC%95%B1-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8/dashboard )
'SSAFY 6기 > 📌blockchain' 카테고리의 다른 글
인프런 nft 강좌 #3 - react 개발 (0) | 2022.03.14 |
---|---|
인프런 nft 강좌 #2 - react 세팅 (0) | 2022.03.14 |
인프런 Lottery Dapp 강좌 #1 (0) | 2022.03.03 |
댓글