TIL

TIL) 스마트 컨트랙트 실습 with 노마드 코더

Whatisblockchain 2022. 6. 24. 20:12

본 컨텐츠는 노마드 코더 유튜브를 참고하여 작성하였습니다.

 

과정

1) repository 생성

2) 프로젝트 폴더 생성(npm init)

3) 필요한 패키지, 플러그인 다운로드

4) smart contract 작성

5) all that node 통해서 이더리움 테스트넷과 연결

6) deploy -- artifacts, cache 폴더 생성

7) contract와 communication 하는 함수 작성

 

배운내용

- npm init 활용

- 터미널에서 plug in 여러개 동시에 다운받는 법

- smart contract 작성 방법

- all that node 통해서 이더리움 테스트넷과 연결하는 방법

- smart contract deploy하는 방법

 

더 공부할 내용

- hardhat.config 관련 js 문법: task(), ethers.Contract, ethers.getSigners

- hardhat-waffle 플러그인

- hardhat-ethers 플러그인

- solidity 문법: constructor, contract, receive() external, payable

 

의문점

- 왜 process.env로 account private key 갖고오는게 안될까?

 


 

1) git repository 생성

 

2) 프로젝트 폴더 생성(npm init)

3) 필요한 패키지, 플러그인 다운로드

4) smart contract 작성

폴더트리

- contracts - Fundraising.sol

- scripts - deploy.js

- .env

- hardhat.config.js

- package-lock.json

- package.json

 


#contracts - Fundraising.sol

pragma solidity ^0.8.0;

contract Fundraising {
    uint256 public targetAmount;
    address public owner;
    mapping(address => uint256) public donations;

    uint256 public raisedAmount = 0;
    uint256 public finishTime = block.timestamp + 2 weeks; // 블록이 생성되는 기간

    constructor(uint256 _targetAmount) {
        targetAmount = _targetAmount;
        owner = msg.sender;
    }

    receive() external payable {
        require(block.timestamp < finishTime, 'This campaign is over'); // require 함수 통해서 조건분기. false일 때 text 표시
        donations[msg.sender] += msg.value;
        raisedAmount += msg.value;
    }
    function withdrawDonations() external {
        require(msg.sender == owner, 'Fund will only be released to the owner');
        require(raisedAmount >= targetAmount, 'The campaign reached the goal.');
        require(block.timestamp > finishTime, ' The campaign is not over yet.');
        payable(owner).transfer(raisedAmount);
    }
    function refund() external {
        require(block.timestamp > finishTime, 'The campaign is not over yet.');
        require(raisedAmount < targetAmount, 'The campaign reached the goal.');
        require(donations[msg.sender] > 0, 'You did not donate to this campaign.');
        uint256 toRefund = donations[msg.sender]; // refund 처리
        donations[msg.sender] = 0; // 잔여금액 0 처리
        payable(msg.sender).transfer(toRefund);
    }
}

 

5) all that node 통해서 이더리움 테스트넷과 연결 : api key 및 end point 가져오기

 

 

#hardhat.config.js

url, api key로 테스트넷과 연결

module.exports = {
  solidity: "0.8.0",
  networks: {
    ropsten: {
      url: 'https://ethereum-ropsten-rpc.allthatnode.com/7WYWGGeO0NUsKgFCvhrXzltMY72qZxuo', // URL+API Key
      accounts: ['account private key 입력']
    }
  }
};

 

6) deploy -- artifacts, cache 폴더 생성

deploy하기 위한 script 파일 작성

const { ethers } = require("hardhat");

async function main() {
    const Fundraising = await ethers.getContractFactory("Fundraising"); // Fundraising 컨트랙트 코드를 가져와서 compile
    const contract = await Fundraising.deploy(ethers.utils.parseEther('100.0')); // argument(본 실습에서는 target amount of money)와 함께 deploy
    console.log('Contract address is:', contract.address)
}

main().then(() => process.exit(0)).catch((error) => {
    console.error(error);
    process.exit(1);
})

 

terminal에서 npx 통해서 deploy

생성한 contract address etherscan에서 확인

 

 

테스트로 0.01 eth 보내보니 정상적으로 작동함

deploy하니, 패키지에 artifacts 폴더와 cache 폴더 자동생성

 

 

7) contract와 communication 하는 함수 작성

abi 기본 틀을 artifacts-constracts-Fundraising.sol에서 가져와서 contract의 목표금액, 잔액 등 상태를 확인할 수 있는 구문 추가 작성

require('@nomiclabs/hardhat-waffle');

task('check', 'check contract amounts', async () => {
  const [deployer] = await ethers.getSigners();
  const contract = '0x21B97BeF9B04F91cb9C174B41f5E4D5b31255Dc6';
  const abi = [
    {
      input: [
        {
          internalType: "uint256",
          name: "_targetAmount",
          type: "uint256"
        }
      ],
      stateMutability: "nonpayable",
      type: "constructor"
    },
    {
      input: [
        {
          internalType: "address",
          name: "",
          type: "address"
        }
      ],
      name: "donations",
      outputs: [
        {
          internalType: "uint256",
          name: "",
          type: "uint256"
        }
      ],
      stateMutability: "view",
      type: "function"
    },
    {
      input: [],
      name: "finishTime",
      outputs: [
        {
          internalType: "uint256",
          name: "",
          type: "uint256"
        }
      ],
      stateMutability: "view",
      type: "function"
    },
    {
      input: [],
      name: "owner",
      outputs: [
        {
          internalType: "address",
          name: "",
          type: "address"
        }
      ],
      stateMutability: "view",
      type: "function"
    },
    {
      input: [],
      name: "raisedAmount",
      outputs: [
        {
          internalType: "uint256",
          name: "",
          type: "uint256"
        }
      ],
      stateMutability: "view",
      type: "function"
    },
    {
      input: [],
      name: "refund",
      outputs: [],
      stateMutability: "nonpayable",
      type: "function"
    },
    {
      input: [],
      name: "targetAmount",
      outputs: [
        {
          internalType: "uint256",
          name: "",
          type: "uint256"
        }
      ],
      stateMutability: "view",
      type: "function"
    },
    {
      input: [],
      name: "withdrawDonations",
      outputs: [],
      stateMutability: "nonpayable",
      type: "function"
    },
    {
      stateMutability: "payable",
      type: "receive"
    }
  ];
  const fundraising = new ethers.Contract(contract, abi, deployer) // abi를 통해 contract를 조작하고 deploy
  console.log(
    await fundraising.targetAmount(),
    await fundraising.raisedAmount()
  );
})

 

터미널에서 check하여 정상적으로 작동함을 확인