🦁 [PROJECT LION] NFT 블록체인 마켓 앱 만들기 with 그라운드X 3기 - 챕터 3-9 회고


-1. 카이카스(Kaikas) 지갑에 두개의 계정을 준비합니다.

바오밥(Baobab) 테스트넷 👆


-2. 두개의 계정을 연결 후 솔리디티 코드를 입력하고 배포(Deploy)

👉 전체 코드는 하단에 있습니다


-3. 배포하고 나온 mintWithTokenURI 함수로 토큰 발행 (mint)

    function mintWithTokenURI (address to, uint256 tokenId, string memory tokenURI) public returns (bool) {
        // 토큰을 발행 하는 함수
        tokenOwner[tokenId] = to; // to는 소유주 이기때문에 👉 msg.sender 으로도 쓸수 있다
                                       // 토큰 번호를 입력하면 토큰 소유주의 주소가 나온다
        tokenURIs[tokenId] = tokenURI; // 토큰 번호를 입력하면 토큰의 이름이 나온다

        _ownedTokens[to].push(tokenId); // 주소를 입력하면 소유중인 토큰 번호들이 나온다

        return true; // 리턴
    }

to 👉 나의 지갑 주소
tokenId 👉 임의의 토큰 번호
tokenURI 👉 임의의 토큰 별명

👉 입력 후 transcat


-4. 발행된 토큰의 조회 기능

tokenOwner[tokenId] = to; // to는 소유주 이기때문에 👉 msg.sender 으로도 쓸수 있다
// 토큰 번호를 입력하면 토큰 소유주의 주소가 나온다
tokenURIs[tokenId] = tokenURI; // 토큰 번호를 입력하면 토큰의 이름이 나온다

발행된 토큰의 번호를 입력하면

  • tokenOwner 👉 토큰을 소유하고 있는 지갑주소 조회
  • tokenURI 👉 토큰의 별명을 조회

👉트랜잭션을 발생시키지 않고 call( ) 한다


-5. 토큰을 추가 발행해보고 총 보유중인 토큰 조회

 function ownedTokens (address owner) public view returns (uint256[] memory) {
        // 내가 가지고 있는 모든 토큰들 조회
        return _ownedTokens[owner]; // 조회하고자 하는 주소를 입력하면 토큰들(배열)이 나온다
    }

토큰을 2번과 3번을 추가로 발행하고
ownedTokens 함수에 👉 지갑주소를 입력하여 통해 조회

👉 조회는 트랜잭션을 발생시키지 않고 call( ) 한다


-6. 보유중인 토큰을 다른 주소로 전송

function safeTransferFrom(address from, address to, uint256 tokenId) public {
        // 토큰을 전송하는 함수
        require(from == msg.sender, "Not from"); // 토큰을 보내는 사람이 소유주일때만 실행 👉 아니면 에러
        require(from == tokenOwner[tokenId], "NotTokenFrom"); // 토큰이 본인의 토큰일때만 실행 👉 아니면 에러

        _removeTokenFromList(from, tokenId);
        // 밑에서 정의한 함수를 여기서 사용 (토큰을 전송하면 내 목록에서 사라져야하기때문)
        _ownedTokens[to].push(tokenId);
        // 전송후에 상대방이 가지고 있는 토큰배열에 전송받은 토큰을 추가 해주기
        tokenOwner[tokenId] = to; // from은 내 주소(소유쥬) to는 받는사람 토큰의 번호를 입력한다
    }

👉 이 과정에서는 require 기능이 필요한데 ❗
조건에 맞으면 통과되고 아니면 에러메세지와 함께 실행이 중지된다
(에러핸들러 require)

from 👉 나의 지갑 주소
to 👉 토큰을 전송받을 주소
tokenId 👉 전송할 토큰의 번호

👉 입력 후 transcat


나의 지갑에 있던 토큰이 상대방의 지갑으로 전달되었으니
전달되는 과정에서 나의 지갑에 있던 토큰을
제거하는 작업을 해야한다 (스왑기능)

    function _removeTokenFromList(address from, uint256 tokenId) private {
        // [10, 15, 20, 23] 👈 20번을 삭제하고 싶을때
        // [10, 15, 23, 20] 👈 20번과 23번을 스왑하고
        // [10, 15, 23] 👈 배열의 마지막 제거
        uint256 lastTokenIndex = _ownedTokens[from].length -1;
        // 👆 배열의 마지막을 선택해주는 변수
        for (uint256 i=0; i< _ownedTokens[from].length; i++) {
            // 반복문실행 소유중인 토큰 번호(배열)
            if (tokenId == _ownedTokens[from][i]) {
                // 삭제하고싶은 토큰번호를 소유주의 배열에서 찾았다면
                // 스왑기능 실행
                _ownedTokens[from][i] = _ownedTokens[from][lastTokenIndex];
                // 배열과 = 배열의 마지막 위치는 같다 선언함으로써 위치 변경
                _ownedTokens[from][lastTokenIndex] = tokenId;
                // 배열의 마지막이 삭제하고 싶은 토큰번호로 스왑됨
                break;
            }
        }
        _ownedTokens[from].length--; // 👈 배열의 맨마지막 제거
    }

-7. 토큰 전송 후 각 지갑의 토큰보유 조회

ownedTokens 함수에 👉 각각 지갑주소를 입력하여 통해 조회

원래 가지고 있던 토큰 1, 2, 3 번

👉 전송 후 지갑에 남은 토큰 2, 3번이 조회되고

👉 전송 받은 다른지갑에는 1번이 조회되는걸 확인 할 수 있다


Caver.js 를 이용하여 웹페이지 UI를 만들어서 실제 기능을 구현해볼 계획입니다 ! 🦁


전체코드

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.4.24 <=0.5.6;

contract NFTSimple {
    string public name = "KlayLion"; // 토큰이름
    string public symbol = "KL"; // 토큰심볼

    mapping (uint256 => address) public tokenOwner; // 숫자를 넣으면 주소로 반환
    mapping (uint256 => string) public tokenURIs; // 숫자를 넣으면 문자로 반환
	  mapping (address => uint256[]) private _ownedTokens; // 주소를 넣으면 숫자 배열로 반환

    function mintWithTokenURI (address to, uint256 tokenId, string memory tokenURI) public returns (bool) {
        // 토큰을 발행 하는 함수
        tokenOwner[tokenId] = to; // to는 소유주 이기때문에 👉 msg.sender 으로도 쓸수 있다
                                       // 토큰 번호를 입력하면 토큰 소유주의 주소가 나온다
        tokenURIs[tokenId] = tokenURI; // 토큰 번호를 입력하면 토큰의 이름이 나온다

        _ownedTokens[to].push(tokenId); // 주소를 입력하면 소유중인 토큰 번호들이 나온다

        return true; // 리턴
    }

    function safeTransferFrom(address from, address to, uint256 tokenId) public {
        // 토큰을 전송하는 함수
        require(from == msg.sender, "Not from"); // 토큰을 보내는 사람이 소유주일때만 실행 👉 아니면 에러
        require(from == tokenOwner[tokenId], "NotTokenFrom"); // 토큰이 본인의 토큰일때만 실행 👉 아니면 에러

        _removeTokenFromList(from, tokenId);
        // 밑에서 정의한 함수를 여기서 사용 (토큰을 전송하면 내 목록에서 사라져야하기때문)
        _ownedTokens[to].push(tokenId);
        // 전송후에 상대방이 가지고 있는 토큰배열에 전송받은 토큰을 추가 해주기
        tokenOwner[tokenId] = to; // from은 내 주소(소유쥬) to는 받는사람 토큰의 번호를 입력한다
    }

    function _removeTokenFromList(address from, uint256 tokenId) private {
        // [10, 15, 20, 23] 👈 20번을 삭제하고 싶을때
        // [10, 15, 23, 20] 👈 20번과 23번을 스왑하고
        // [10, 15, 23] 👈 배열의 마지막 제거
        uint256 lastTokenIndex = _ownedTokens[from].length -1;
        // 👆 배열의 마지막을 선택해주는 변수
        for (uint256 i=0; i< _ownedTokens[from].length; i++) {
            // 반복문실행 소유중인 토큰 번호(배열)
            if (tokenId == _ownedTokens[from][i]) {
                // 삭제하고싶은 토큰번호를 소유주의 배열에서 찾았다면
                // 스왑기능 실행
                _ownedTokens[from][i] = _ownedTokens[from][lastTokenIndex];
                // 배열과 = 배열의 마지막 위치는 같다 선언함으로써 위치 변경
                _ownedTokens[from][lastTokenIndex] = tokenId;
                // 배열의 마지막이 삭제하고 싶은 토큰번호로 스왑됨
                break;
            }
        }
        _ownedTokens[from].length--; // 👈 배열의 맨마지막 제거
    }

    function ownedTokens (address owner) public view returns (uint256[] memory) {
        // 내가 가지고 있는 토큰들 조회
        return _ownedTokens[owner]; // 조회하고자 하는 주소를 입력하면 토큰들(배열)이 나온다
    }

    function setTokenURI (uint256 id, string memory uri) public {
        // 토큰의 이름 바꾸기 함수
        tokenURIs[id] = uri; // 바꾸고자 하는 토큰번호을 입력하고 바꾸고자 하는 토큰명을 입력한다
    }
}

---