본문 바로가기
SSAFY 6기/📌blockchain

인프런 nft 강좌 #1 스마트 컨트랙트 작성

by IMSfromSeoul 2022. 3. 10.

📌 설치

  • 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 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 함수에서 누가 주인인 지 확인할 수 있다.

 


 

'SSAFY 6기 > 📌blockchain' 카테고리의 다른 글

인프런 nft 강좌 #3 - react 개발  (0) 2022.03.14
인프런 nft 강좌 #2 - react 세팅  (0) 2022.03.14
인프런 Lottery Dapp 강좌 #1  (0) 2022.03.03

댓글