반응형
더보기

 

 

TON (The Open Network)

일명 (분산형 개방형 인터넷 플랫폼)

TON 블록체인


Blockchain of Blockchains

Single Actor

  • Actor 는 스마트 컨트랙트 , Account 와 같이 블록체인 엔티티를 설명하는데 사용되는 말이다.

TON 에서는 address code data balance 같은 속성들이 있다. 이러한 객체들은 storage , behavior 를 가지고 있다. 이러한 behavior 는 다음과 같은 패턴을 보인다.

  1. 무슨일이 발생 (보통은 컨트랙트가 메시지를 가져올때)
  2. TON 버추얼 머신에서 code를 실행한 이벤트에 따라 컨트랙트가 작동한다.
  3. code data 와 같은 속성들을 컨트랙트가 변경한다.
  4. 컨트랙트는 선택적으로 출력 메시지를 발생시킨다.
  5. 컨트랙트는 다음 이벤트가 발생할 때까지 대기한다.

The Lowest Level : Account Chain

트랜잭션의 시퀀스 Tx1 -> Tx2 -> Tx3 -> ...체인 이라고 부른다.

AccountChain 은 트랜잭션의 단일 계정의 체인을 강조하기 위한 말이다.

트랜잭션을 처리하는 노드는 스마트 컨트랙트의 상태를 조정해야 하므로 다음과 같은 순서로 배치된다. [Tx1 -> Tx2] -> [Tx3 -> Tx4 -> Tx5] -> [] -> [Tx6]

이렇게 되는 배칭은 컨트랙트의 시퀀싱에는 영향을 미치지않는다. 각각의 트랜잭션은 오직 하나의 이전 트랜잭션과 최대 하나의 다음 트랜잭션을 가진다. 이러한 시퀀스는 Block 으로 잘리게 된다.

블록이 들어오거나 나가는 메시지를 담는 큐를 포함하는 것도 편리하다. 이러한 경우에는 블록에 블록에서의 스마트 컨트랙트에서 무슨 일이 생기는지에 대해 설명된 정보의 전부가 포함이 될것이다.

Many AccountChains: Shards

몇개의 AccountChains 를 ShardChain 라고 한다. ShardChain 을 또한 ShardBlocks 로 나눌 수 있다. SharBlocks 는 개개인의 AccountBlocks 를 포함하고 있다.

ShardChains 를 동적으로 분리하고 병합

ShardChain 은 구분하기 쉬운 AccountChains 로 구성되어있다. 따라서, 쉽게 분리가 가능하다. 예를 들어 1million 개의 계정에서 일어나는 이벤트를 처리하는 ShardChain 1개가 있고 초당 거래가 너무 많아 하나의 노드에서 처리하고 저장할 수 없다면 ShardChain 을 두 개로 나눌 수 있다.

또한, 일부 Shard 에서 사용되지 않는 부분이 생기면 하나의 더 큰 ShardChain 으로 병합할 수 있다.

각각의 계정은 메시지를 보내서 상호작용할 수 있다. 메시지 발신큐에서 수신큐로 옮기고 이러한 메시지를 broadcast 할 수 있는 특별한 라우팅 메커니즘이 있다.

  1. 모든 메시지는 전달되어진다.
  2. 모든 메시지는 연속적으로 전달된다. (순서가 보장된다.)

BlockChain

모든 계정을 담은 Shard 의 집합이 일련의 규칙을 따르는 것을 블록체인 이라고 한다.

TON 에서는 다양한 규칙이 있으며 많은 블록체인들은 동시에 작동되고, crosschain 들과 메시지를 보내면서 상호작용한다. 같은 방식으로 한 체인안에 계정들도 서로 의사소통할 수 있다.

Workchain : 당신만의 규칙을 가진 블록체인

Shardchain 들의 그룹을 커스텀하고 싶다면 Workchain 을 만들 수 있다. 솔리디티로 구현된 스마트 컨트랙트를 EVM에서 작동하는 것이 그 예이다.

이론적으로, 커뮤니티 안의 모든 구성원은 자신만의 워크체인을 만들 수 있는데 사실 약간 복잡하다. 워크체인을 만들려면 돈도 많이들고 2/3 의 구성원들의 찬성이 필요하기 때문이다.

현재 TON 에는 2개의 워크체인이 있다. (MasterChain, BaseChain)

BaseChain

  • 값이 싸기에 모든 트랜잭션에서 거의 사용된다.

MasterChain

  • TON 의 중요한 함수들을 가지고 있다.

MasterChain : 블록체인들의 블록체인

메시지 라우팅과 트랜잭션 실행에 있어 동기화는 중요하다. 여러 체인들의 상태를 관리하고 고치는데 있어 노드들은 어떠한 방법이 필요하다.

따라서, TON 은 MasterChain 을 만들었다. 마스터 체인의 블록들은 시스템에서 모든 블록들의 정보를 가지고 있다. 그렇기에 observer 은 명확하게 멀티체인 시스템의 상태를 결정할 수 있다.

반응형
반응형

사용할 프레임 워크 : chakra UI

강의에서는 리액트 17 버전을 사용중이기 때문에 차크라 UI 는 1버전으로 다운로드 해줘야 정상 실행된다.

 

 

App.tsx

import React, { FC, useState, useEffect  } from "react";

import { BrowserRouter, Routes, Route} from "react-router-dom"; 
import Layout from "./components/Layout";
import Main from "./routes/main";
import MyAnimal from "./routes/my-animal";
import SaleAnimal from "./routes/sale-animal";

const App: FC = () => {
  const [account, setAccount] = useState<string>("");

  const getAccount = async () => {
    try{
      if(window.ethereum){
        const accounts = await window.ethereum.request({
          method:'eth_requestAccounts',
        })
        setAccount(accounts[0])
      } else{
        alert("Install Metamask");
      }
    }catch(err){
      console.log(err);
    }
  }

  useEffect(() => {
    getAccount();
    console.log(account)
  }, [account])
  

  return (
    <BrowserRouter>
      <Layout>
        <Routes>
          <Route path="/" element={<Main account={account}/>} />
          <Route path="my-animal" element={<MyAnimal account={account}/>} />
          <Route path="sale-animal" element={<SaleAnimal account={account}/>} />
        </Routes>
      </Layout>
      
    </BrowserRouter>
  )
};

export default App;
  • account 가 바뀔때마다 렌더링을 해주는 방식
  • Metamask 설치 유무또한 확인해 주어야한다.

main.tsx

import React , {FC, useState} from 'react';
import {Button, Box, Text, Flex} from '@chakra-ui/react';
import { mintAnimalTokenContract } from '../contracts';
import AnimalCard from '../components/AnimalCard';

interface MainProps {
    account :string;
}

const Main : FC<MainProps> = ({account}) => {
    const [NewAnimalType, setNewAnimalType] = useState<string>("")

    const onClickMint = async () =>{
        try{
            if(!account) return;

            const response = await mintAnimalTokenContract.methods
                                    .mintAnimalToken()
                                    .send({from: account});

            console.log(response);
            if(response.status){
                const balanceLength = await mintAnimalTokenContract.methods
                                    .balanceOf(account)
                                    .call();

                const animalTokenId = await mintAnimalTokenContract.methods
                .tokenOfOwnerByIndex(account, parseInt(balanceLength.length, 10) -1)
                .call();

                const animalType = await mintAnimalTokenContract.methods
                .animalTypes(animalTokenId)
                .call();

                setNewAnimalType(animalType);
                console.log(NewAnimalType);
            }
        }catch(err){
            console.log(err);
        }
    }

    return <Flex w="full" h= "100vh" justifyContent="center" alignItems="center" direction="column">
        <Box>
            {NewAnimalType ? <AnimalCard animalType={NewAnimalType} /> : <Text>Let's mint Animal Card!</Text>}
        </Box>
        <Box>
            <Button mt={4} size="sm" colorScheme="blue" onClick={onClickMint}>Mint</Button>
        </Box>
    </Flex>
}
export default Main;

함수설명

  1. onClickMint
    • 컨트랙트 함수에서 mint 를 호출하여 토큰을 생성하고 토큰의 id, type 을 저장한다.

my-animal.tsx

import React , {FC, useState, useEffect} from "react";
import AnimalCard from "../components/AnimalCard";
import { mintAnimalTokenContract, saleAnimalTokenAddress, saleAnimalTokenContract } from "../contracts";
import {Grid, Box , Button, Text, Flex} from '@chakra-ui/react'
import MyAnimalCard, { IMyanimalCard } from "../components/MyAnimalCard";
interface MyAnimalProps {
    account : string;
}

const MyAnimal : FC<MyAnimalProps> = ({account}) =>{
    const [AnimalCardArray, setAnimalCardArray] = useState<IMyanimalCard[]>();
    const [SaleStatus, setSaleStatus] = useState<boolean>(false);

    const getAnimalTokens = async() => {
        try{
            
            const balanceLength = await mintAnimalTokenContract.methods
            .balanceOf(account)
            .call();

            if(balanceLength =="0") return ;

            const tempAnimalCardArray: IMyanimalCard[] = [];

            const response = await mintAnimalTokenContract.methods.getAnimalTokens(account).call();
            console.log(response)

            response.map((v: IMyanimalCard) => {
                tempAnimalCardArray.push({animalTokenId : v.animalTokenId, animalType : v.animalType, animalPrice : v.animalPrice})
            })
            
            setAnimalCardArray(tempAnimalCardArray);
        }catch(err){
            console.log(err);
        }
    };

    const getIsApprovedForAll = async () =>{
        try{
            const response = await mintAnimalTokenContract.methods
            .isApprovedForAll(account, saleAnimalTokenAddress)
            .call();
            if(response){
                setSaleStatus(response);
            }
            console.log(response);
        }catch(err){
            console.log(err)
        }
    }

    const onClickApproveToggle = async () =>{
        try{
            if(!account) return;

            const response = await mintAnimalTokenContract.methods
            .setApprovalForAll(saleAnimalTokenAddress, !SaleStatus)
            .send({from:account});

            if(response.status){
                setSaleStatus(!SaleStatus);
            }
        }catch(err){
            console.log(err);
        }
    }

    useEffect(() => {
        if(!account) return;
        getAnimalTokens();
        getIsApprovedForAll();
    }, [account])
    
    useEffect(() => {
        console.log(AnimalCardArray)
    }, [AnimalCardArray])

    return(
        <>
        <Flex alignItems="center">
            <Text display="inline-block">
                Sale Status : {SaleStatus ? "True" : "False"}
            </Text>
            <Button size="xs" ml={2} colorScheme={SaleStatus ? "red" : "blue"} onClick={onClickApproveToggle}>
                {SaleStatus ? "Cancel" : "Approve"}
            </Button>
        </Flex>
        <Grid templateColumns ="repeat(4,1fr)" gap={8} mt={2}>
            {
                AnimalCardArray && AnimalCardArray.map((v,i) =>{
                    return <MyAnimalCard key={i} animalTokenId={v.animalTokenId} animalType={v.animalType} animalPrice={v.animalPrice} saleStatus={SaleStatus} account={account}/>;
                }
            )}
        </Grid>
        </>
    );
}

export default MyAnimal

함수 설명

  1. getAnimalTokens
    • account 가 소유한 카드들을 불러와서 배열형태로 리턴한다.
  2. getIsApprovedAll
    • account 와 카드의 주인이 지닌 주소를 비교해 같지 않은지 확인 한다.
    • true 값이 반환되어야 판매등록이 가능하다.
  3. onClickApproveToggle
    • getIsApprovedAll 을 위한 버튼 함수

sale-animal.tsx

import React , {FC, useState, useEffect} from "react";
import {Grid} from "@chakra-ui/react"
import { IMyanimalCard } from "../components/MyAnimalCard";
import { mintAnimalTokenContract, saleAnimalTokenContract } from "../contracts";
import SaleAnimalCard from "../components/SaleAnimalCard";


interface SaleAnimalProps {
    account:string;
}

const SaleAnimal:FC<SaleAnimalProps> = ({account}) =>{
    const [saleAnimalCard, setSaleAnimalCard] = useState<IMyanimalCard[]>()

    const getOnSaleAnimalTokens = async () =>{
        try{
            const getOnSaleAnimalTokenArrayLength = await saleAnimalTokenContract.methods.getOnSaleAnimalTokenArrayLength().call();

            const tempOnSaleArray : IMyanimalCard[] = [];

            for(let i = 0;i<parseInt(getOnSaleAnimalTokenArrayLength, 10); i++){
                const animalTokenId = await saleAnimalTokenContract.methods.onSaleAnimalTokenArray(i).call();

                const animalType = await mintAnimalTokenContract.methods.animalTypes(animalTokenId).call();

                const animalPrice = await saleAnimalTokenContract.methods.animalTokenPrices(animalTokenId).call();

                tempOnSaleArray.push({animalTokenId, animalType, animalPrice})
            }

            setSaleAnimalCard(tempOnSaleArray);
        } catch(err){
            console.log(err);
        }
    }

    useEffect(() => {
      getOnSaleAnimalTokens();
    }, [])
    
  
    

    return(
        <Grid mt={4} templateColumns="repeat(4, 1fr)" gap={8}>
                {saleAnimalCard && saleAnimalCard.map((v,i) => {
                    return <SaleAnimalCard key={i} animalType={v.animalType} animalPrice={v.animalPrice} animalTokenId={v.animalTokenId} account={account} getOnSaleAnimalTokens={getOnSaleAnimalTokens}/>
                })}
        </Grid>
    );
}

export default SaleAnimal

함수 설명

  1. getOnSaleAnimalTokens
    • 판매중인 토큰들을 불러오는 함수
    • my-animal 과 같이 컨트랙트에서 배열을 받아오면 되지만 강의에서는 귀찮아서 for문으로 했다.
반응형

'BlockChain > Solidity' 카테고리의 다른 글

[DP/NFT] 3. SaleAnimalToken 작성  (0) 2022.09.05
[DP/NFT] 2. Minting contract 작성  (0) 2022.09.05
[DP/NFT] 1. Install Solidity & Metamask  (0) 2022.08.25
반응형
// 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;
    //FE 에서 사용할 배열. 판매중이 토큰 확인용

    function setForSaleAnimalToken(uint256 _animalTokenId, uint256 _price) public {
        //판매 등록 함수

        address animalTokenOwner = mintAnimalTokenAddress.ownerOf(_animalTokenId);

        require(animalTokenOwner == msg.sender, "Caller is not animal token owner");
        //함수 실행 사람이 토큰의 주인이 맞는지
        require(_price > 0 , "Price is zero or lower");

        require(animalTokenPrices[_animalTokenId] == 0, "This animal token is already on sale.");

        require(mintAnimalTokenAddress.isApprovedForAll(animalTokenOwner, address(this)), "Animal token owner did not approve token.");
        //스마트컨트랙트 판매권한 여부를 확인
        
        animalTokenPrices[_animalTokenId] = _price;

        onSaleAnimalTokenArray.push(_animalTokenId);
    }
}

 

알고리즘

  • MintAnimalToken 장부를 먼저 Deploy 해야한다.
  • Deploy 된 장부의 주소값을 인자로 등록한다.
  • setForSaleAnimalToken 함수
    • 등록할 토큰의 ID 를 가진 주인의 주소를 불러온다.
    • 함수를 실행하는 사람이 msg.sender 가 주인이 맞는지 확인한다.
    • 가격이 0 초과인지 확인한다.
    • 판매등록이 되지 않은 상품인지 확인한다.
    • 해당 장부가 판매등록이 허가가 된 장부인지 확인한다.
    • 배열에 판매등록을 한다.

 

function purchaseAnimalToken(uint256 _animalTokenId) public payable {
        //payble 을 붙여야 matic 이 왔다갔다하는 함수를 실행할 수 있다.

        uint256 price = animalTokenPrices[_animalTokenId];
        address animalTokenOwner = mintAnimalTokenAddress.ownerOf(_animalTokenId);

        require(price > 0, "Animal Token not sale.");
        require(price <= msg.value, "Caller sent lower than price.");
        //함수를 실행할때 보내는 MATIC 의 양이 같거나 큰지 확인
        require(animalTokenOwner != msg.sender, "Caller is animal Token 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();
                //맨뒤랑 바꿔서 맨뒤 삭제
            }
        }
        
        
    }
    //읽기 전용 FE 함수
    //판매중인 리스트 확인용
    function getOnSaleAnimalTokenArrayLength() view public returns (uint256){
            return onSaleAnimalTokenArray.length;
    }

 

알고리즘

  • payable 을 사용해야 구입 판매 함수를 사용할 수 있다.
  • 구매할 토큰의 ID 와 토큰 주인의 주소를 불러온다.
  • 토큰의 판매여부를 확인
  • 구매자가 보낸 금액이 토큰 가격보다 크거나 같은지 확인
  • 구매자가 판매자와 동일한지 확인
  • payable 함수를 이용해 msg.value 에 저장된 금액을 판매자로 송금
  • 판매된 토큰을 배열에서 제거

후기

  • Solidity 언어를 많이 다룰줄 알았는데 아닌것같다. 그래도 대충 하는 법을 익힌듯 해서 얼른 만들어버리고 다른 강의 들어야겠다.

 

전체 알고리즘

MintAnimal Token Deploy
mint 주소를 이용해 Sale 장부 실행
Animal 민팅
Sale 장부 허가
판매 등록후 확인
다른 계정으로 구매후 구매확인

반응형

'BlockChain > Solidity' 카테고리의 다른 글

[DP/NFT] 4. 리액트 세팅  (0) 2022.09.05
[DP/NFT] 2. Minting contract 작성  (0) 2022.09.05
[DP/NFT] 1. Install Solidity & Metamask  (0) 2022.08.25
반응형
// 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;
    // animalTokenID 입력하면 animalType 이 나오게 맵핑

    function mintAnimalToken() public {
        uint256 animalTokenId = totalSupply() + 1;
        //nft 에 유일한 값을 부여

        //랜덤한 값을 생성
        //Keccak 알고리즘을 사용하기 위해 byte 값이 필요
        //abi.encodePacked 에 세가지 수를 사용하여 겹치지 않는 수 생성
        //5로 나눈 나머지에 1을 더하여 animal type 이 1~5 사이의 값이 생성되도록 함
        uint256 animalType = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, animalTokenId))) % 5 + 1;

        animalTypes[animalTokenId] = animalType;
        //mapping

        _mint(msg.sender, animalTokenId); // 명령어를 실행하는 사람, 토큰 ID 를 민팅
    }
}

 

알고리즘 요약

  • Keccak 알고리즘을 통해 1~5 랜덤값 생성
  • 맵핑을 통한 ID 별로 types 결정

디버그

  • 컨트랙트이름과 함수이름을 동일하게 짓는 실수는 하지말자
반응형

'BlockChain > Solidity' 카테고리의 다른 글

[DP/NFT] 4. 리액트 세팅  (0) 2022.09.05
[DP/NFT] 3. SaleAnimalToken 작성  (0) 2022.09.05
[DP/NFT] 1. Install Solidity & Metamask  (0) 2022.08.25

+ Recent posts