반응형

1. Install ehthers zkSync

npm install ethers zkSync

2. zkSync 란?

zkSync 는 EVM 을 확장하기 위한 레이어 2 솔루션. ZKP 를 이용한 빠르고 저렴한 거래처리 가능

ZKP (Zero Knowledge Proof) 는 뭔데?

어떤 정보를 드러내지 않고 특정 주장이나 사실이 참임을 증명하는 기술

블록체인에서는 거래내역을 보여주지 않고 거래내역이 유효함을 증명할 수 있다.

zkSync 를 통해 빠르고 저렴한 거래처리를 적용할 수 있다!

Provider 생성

alice.js, bob.js 를 zkSync 로 연결하기 위해서는 util.js 에서 zkSync Provider를 생성해야한다.

// utils.js

async function getZkSyncProvider(zksync, networkName) {
    let zkSyncProvider;
    try {
        zkSyncProvider = await zksync.getDefaultProvider(networkName);
    } catch (error) {
        console.log('Unable to connect to zkSync.');
        console.error(error);
    }
    return zkSyncProvider;
}

3. zkSync 계정 만들기

zkSync 와 이더리움을 연결하려면 계정과 지갑에 대해 알아야한다. 따라서, zkSync 의 지갑 계정을 생성해 이더리움과 연결해보자.

async function getZkSyncProvider (zksync, networkName) {
  let zkSyncProvider
  try {
    zkSyncProvider = await zksync.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Unable to connect to zkSync.')
    console.log(error)
  }
  return zkSyncProvider
}

async function getEthereumProvider (ethers, networkName) {
  let ethersProvider
  try {
    ethersProvider = new ethers.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Could not connect to Rinkeby')
    console.log(error)
  }
  return ethersProvider
}

async function initAccount (rinkebyWallet, zkSyncProvider, zksync) {
  const zkSyncWallet = await zksync.Wallet.fromEthSigner(rinkebyWallet, zkSyncProvider)
  return zkSyncWallet
}

 

위 코드의 결과로 zkSyncWallet 을 생성할 수 있다.

4. zkSync 인증 확인하기

zkSync 계정을 생성했다면 인증 여부를 확인해야한다. 지갑의 key 가 존재한다면 key 가 singing key에 등록이 되어있는지 확인 후 changePubkey 에 저장한다.

async function registerAccount (wallet) {
  console.log(`Registering the ${wallet.address()} account on zkSync`)
  if(!await wallet.isSigningKeySet()){
    if(await wallet.getAccountId() === undefined){
        throw new Error('Unknown account')
    }
    const changePubkey = await wallet.setSigningKey()
    await changePubkey.awaitReceipt()
  }
}

5. zkSync 에 재화를 넣어보자

Priority Operations

  • 이더리움 네트워크에서 작동되는 작업들

Transactions

  • zkSync 에서 사용되는 활동들

zkSync에서 transaction 이 사용되는 원리

zkSync 에 트랜잭션을 보낼때 Promise 를 사용하게 된다. 트랜잭션의 작업을 믿는다면 따로 블록이 생성될 때까지 기다릴 필요없다.

zkSync 트랜잭션은 SNARK 증명 방식으로 검증하고, 이더리움 스마트 컨트랙트에 등록될 때까지 10분 소요된다.

async function depositToZkSync(zkSyncWallet, token, amountToDeposit, ethers){
    const deposit = await zkSyncWallet.depositToSyncFromEthereum({
        depositTo: zkSyncWallet.address(),
        token: token,
        amount: ethers.utils.parseEther(amountToDeposit)
    })
    try{
        await deposit.awaitReceipt()
    }catch(error){
        console.log('Error while awaiting confirmation from the zkSync operators.')
        console.log(error)
    }
}
  • deposit 객체를 만들기 위해 이더리움 wei 단위로 변경해야한다.
  • awaitReceipt 함수를 이용해 트랜잭션이 정상 작동한지 확인 한다.

6. zkSync 에서 애셋 옮기기

애셋을 옮기기 위해서는 두 가지 과정으로 나눌 수 있다.

  1. syncTransfer 함수를 통해 zkSync 지갑과 수취인의 주소, 애셋 정보 및 fee 를 전달할 수 있다.
  2. awaitReceipt 를 통해 정상 작동했는지 확인할 수 있다.

zkSync에서 전송 작업의 정밀도는 제한되어 있으므로 전송량은 5바이트 길이의 부동 소수점

지불한 수수료는 2바이트 길이의 부동 소수점 표현 으로 패키징해야한다.

async function transfer (from, toAddress, amountToTransfer, transferFee, token, zksync, ethers) {
    const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(ethers.utils.parseEther(amountToTransfer))
    const closestPackableFee = zksync.utils.closestPackableTransactionFee(ethers.utils.parseEther(transferFee))
    const transfer = await from.syncTransfer(
        {
            to:toAddress,
            token: token,
            amount : closestPackableAmount,
            fee:closestPackableFee
        }
    )
    const transferReceipt = await transfer.awaitReceipt()
    console.log('Got transfer receipt.')
    console.log(transferReceipt)
}

7. Transfer Fee

Transfer fee 를 계산하는 방법을 배우자

fee 의 종류

  1. off-chain fee
    • 계산 및 저장 비용을 뜻하며, 불가변성이다.
  2. on-chain fee
    • 이더리움에서 SNARK 를 검증하는데 드는 비용이다. 가스 가격에 따라 달라지기에 가변적이다.
async function getFee(transactionType, address, token, zkSyncProvider, ethers){
    const feeInWei = await zkSyncProvider.getTransactionFee(transactionType, address, token)
    return ethers.utils.formatEther(feeInWei.totalFee.toString())
}
  • getTransactionFee 로 구한 wei 는 매우 길기 때문에 formatEther 함수로 처리해준다.

8. Withdraw to Ethereum

세 가지 과정으로 Withdraw 할 수 있다.

  1. closestPackableAmount 를 계산
  2. wallet.withdrawFromSyncToEthereum 으로 withdraw를 계산
  3. Receipt 검증 완료
async function withdrawToEthereum (wallet, amountToWithdraw, withdrawalFee, token, zksync, ethers) {
    const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(ethers.utils.parseEther(amountToWithdraw))
    const closestPackableFee = zksync.utils.closestPackableTransactionFee(ethers.utils.parseEther(withdrawalFee))
    const withdraw = await wallet.withdrawFromSyncToEthereum({
        ethAddress : wallet.address(),
        token : token,
        amount : closestPackableAmount,
        fee : closestPackableFee
    })
    await withdraw.awaitVerifyReceipt()
  console.log('ZKP verification is complete')
}

9. Account Balances

계정의 Balance 에는 두가지가 존재한다.

  1. commit balance
    1. 거래 내역을 zkSync 의 스마트 컨트랙트에 포함시킨다.
    2. 즉시 사용 가능
    3. 이더리움 메인넷에 기록되지만 검증되지 않았다.
  2. verify balance
    1. 블록을 최종 검증 상태로 확정
    2. SNARK 로 블록을 검증
    3. 최종적으로 보장된 verify 금액
async function displayZkSyncBalance (wallet, ethers){
    const state = await wallet.getAccountState()
    if(state.committed.balances.ETH){
        console.log(`Commited ETH balance for ${wallet.address()}: ${ethers.utils.formatEther(state.committed.balances.ETH)}`)
    }else{
        console.log(`Commited ETH balance for ${wallet.address()}: 0`)
    }
    if(state.verified.balances.ETH){
        console.log(`Verified ETH balance for ${wallet.address()}: ${ethers.utils.formatEther(state.verified.balances.ETH)}`)
    }else{
        console.log(`Verified ETH balance for ${wallet.address()}: 0`)
    }
}

10. 가게주인 밥 - 블록체인과 연결

위에서 만든 utils.js 를 이용해 클라이언트와 연결해보자

(async () => {
  const ethers = require('ethers')
  const zksync = require('zksync')
  const utils = require('./utils')

  // Start here
  const zkSyncProvider = await utils.getZkSyncProvider(zksync, process.env.NETWORK_NAME)
  const ethersProvider = await utils.getEthereumProvider(ethers, process.env.NETWORK_NAME)
})()
  • zkSyncProvider, ethersProvider 을 util.js 에서 불러와야 한다.

11. 가게주인 밥 - 밥의 지갑 주소를 가져오다

const bobRinkebyWallet = new ethers.Wallet(process.env.BOB_PRIVATE_KEY, ethersProvider)
console.log(`Bob's Rinkeby address is: ${bobRinkebyWallet.address}`)
console.log(`Bob's initial balance on Rinkeby is: ${ethers.utils.formatEther(await bobRinkebyWallet.getBalance())}`)
const bobZkSyncWallet = await utils.initAccount(bobRinkebyWallet, zkSyncProvider, zksync) 
  • 밥에게 필요한 지갑주소와 zkSync 지갑 주소를 가져온다.

12. 잔액 업데이트

zkSync 에는 잔액이 업데이트 됐음을 알리는 메커니즘이 없기 때문에 setInterval 을 통해 잔액을 주기적으로 최신화해야한다.

  process.on('SIGINT', () => {
    console.log('Disconnecting')
    // Disconnect
    process.exit()
  })
  setInterval(async () => {
    // Call the `utils.displayZkSyncBalance` function
    await utils.displayZkSyncBalance(bobZkSyncWallet, ethers);
    console.log('---')
  }, SLEEP_INTERVAL)

13. 계좌 등록

상점을 사용할 앨리스의 계좌를 등록해야한다.

앨리스가 상점을 방문한 이후의 시나리오는 다음과 같다.

  1. 앨리스는 zkSync 계좌를 이용해 이더리움을 배포하려고한다.
  2. 퍼블릭 키를 등록해 zkSync 로 거래할 수 있게 하려고 한다.
  3. zkSync 를 이용해 밥과 거래를 할 것 이다.
  4. 이더리움 코인으로 zkSync 에서 이더리움으로 옮겨 남은 금액을 확인해야한다.
(async () => {
  const ethers = require('ethers')
  const zksync = require('zksync')
  const utils = require('./utils')
  const token = 'ETH'
  const amountToDeposit = '0.05'
  const amountToTransfer = '0.02'
  const amountToWithdraw = '0.002'

  const zkSyncProvider = await utils.getZkSyncProvider(zksync, process.env.NETWORK_NAME)
  const ethersProvider = await utils.getEthereumProvider(ethers, process.env.NETWORK_NAME)
  console.log('Creating a new Rinkeby wallet for Alice')
  const aliceRinkebyWallet = new ethers.Wallet(process.env.ALICE_PRIVATE_KEY, ethersProvider) // Account #78
  console.log(`Alice's Rinkeby address is: ${aliceRinkebyWallet.address}`)
  const aliceInitialRinkebyBalance = await aliceRinkebyWallet.getBalance()
  console.log(`Alice's initial balance on Rinkeby is: ${ethers.utils.formatEther(aliceInitialRinkebyBalance)}`)

  console.log('Creating a zkSync wallet for Alice')
  const aliceZkSyncWallet = await utils.initAccount(aliceRinkebyWallet, zkSyncProvider, zksync)

  console.log('Depositing')
  // Start here
    await utils.depositToZkSync(aliceZkSyncWallet, token, amountToDeposit, ethers)
    await utils.displayZkSyncBalance(aliceZkSyncWallet, ethers)
    await utils.registerAccount(aliceZkSyncWallet)
})()

14. zkSync 로 결제해보자

utils.getFee, utils.transfer 함수를 이용해 결제 시나리오를 만들어보자.

const transferFee = await utils.getFee('Transfer', aliceRinkebyWallet.address, token, zkSyncProvider, ethers)
await utils.transfer(aliceZkSyncWallet, process.env.BOB_ADDRESS, amountToTransfer, transferFee,token,zksync,ethers)

15. withdraw

결제와 마찬가지로 withdraw 도 구현할 수 있다.

const withdrawalFee = await utils.getFee('Withdraw', aliceRinkebyWallet.address, token,zkSyncProvider, ethers)
await utils.withdrawToEthereum(aliceZkSyncWallet, amountToWithdraw, withdrawalFee, token,zksync,ethers)
반응형

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

[DP/NFT] 4. 리액트 세팅  (0) 2022.09.05
[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
반응형

사용할 프레임 워크 : 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' 카테고리의 다른 글

[zkSync] 간단하게 다뤄보기  (0) 2025.01.11
[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' 카테고리의 다른 글

[zkSync] 간단하게 다뤄보기  (0) 2025.01.11
[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' 카테고리의 다른 글

[zkSync] 간단하게 다뤄보기  (0) 2025.01.11
[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