ERC-20 컨트랙트 작성 with Hardhat, Openzeppelin
(기본 프로젝트 생성 참고)
Hardhat 튜토리얼 - 1) 시작
블록체인 스마트 컨트랙트를 작성하기 위해 Solidity라는 언어를 사용합니다. Solidity 언어를 이용해서 개발을 하려면 개발 도구가 필요합니다. 그 중에 가장 편하게 접근할 수 있는 것은 Remix 라는
infomauntain.tistory.com
Hardhat으로 ERC20을 작성하고 배포해봅니다.
Openzepplelin의 ERC20을 참고합니다.
ERC20 - OpenZeppelin Docs
Often, you’ll want to be able to divide your tokens into arbitrary amounts: say, if you own 5 GLD, you may want to send 1.5 GLD to a friend, and keep 3.5 GLD to yourself. Unfortunately, Solidity and the EVM do not support this behavior: only integer (who
docs.openzeppelin.com
먼저 프로젝트 폴더를 생성하고 Hardhat 프로젝트를 새로 생성합니다.
$ mkdir hardhat-erc20
$ cd hardhat-erc20
$ npx hardhat
888 888 888 888 888
888 888 888 888 888
888 888 888 888 888
8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
888 888 "88b 888P" d88" 888 888 "88b "88b 888
888 888 .d888888 888 888 888 888 888 .d888888 888
888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
👷 Welcome to Hardhat v2.17.1 👷
✔ What do you want to do? · Create a TypeScript project
✔ Hardhat project root: · /Users/beomjooncho/work/study/hardhat-erc20
✔ Do you want to add a .gitignore? (Y/n) · y
✔ Do you want to install this sample project's dependencies with npm (hardhat @nomicfoundation/hardhat-toolbox)? (Y/n) · y
npm install --save-dev hardhat@^2.17.1 @nomicfoundation/hardhat-toolbox@^3.0.0
npm WARN deprecated request-promise-native@1.0.9: request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm WARN deprecated fsevents@2.1.3: "Please update to latest v2.3 or v2.2"
npm WARN deprecated fsevents@2.1.3: "Please update to latest v2.3 or v2.2"
npm WARN deprecated debug@3.2.6: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)
npm WARN deprecated debug@3.2.6: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)
npm WARN deprecated uuid@2.0.1: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
npm WARN deprecated uuid@3.4.0: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
added 746 packages, and audited 747 packages in 1m
133 packages are looking for funding
run `npm fund` for details
12 vulnerabilities (5 moderate, 4 high, 3 critical)
To address issues that do not require attention, run:
npm audit fix
To address all issues (including breaking changes), run:
npm audit fix --force
Run `npm audit` for details.
✨ Project created ✨
See the README.md file for some example tasks you can run
Give Hardhat a star on Github if you're enjoying it! 💞✨
https://github.com/NomicFoundation/hardhat
추가로 openzeppelin/contracts를 설치해 줍니다.
$ npm install @openzeppelin/contracts
VSCode를 열고, contracts 폴더 아래에 아래와 같이 openzeppelin의 ERC20.sol을 상속 받는 컨트랙트를 작성해 줍니다.
파일명은 GLDToken.sol 입니다.
// contracts/GLDToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract GLDToken is ERC20 {
constructor(uint256 initialSupply) ERC20("Gold", "GLD") {
_mint(msg.sender, initialSupply);
}
}
scripts 폴더에 deploy.ts 파일을 아래와 같이 작성해줍니다.
import { ethers } from "hardhat";
async function main() {
const initialSupply = ethers.parseEther("10000000");
const gldToken = await ethers.deployContract("GLDToken", [initialSupply])
await gldToken.waitForDeployment();
console.log(`The token ${await gldToken.name()}(${await gldToken.symbol()}) is deployed`);
console.log(`GLDToken with deployed to ${gldToken.target}`);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
- 최초 발행 금액은 천만(10,000,000) 토큰(GLD)를 발행합니다. 배포 계정으로 들어가게 됩니다.
- gldToken.target 으로 배포된 후 컨트랙트 Address를 표시하도록 하였습니다.
배포를 진행해봅니다.
먼저, 터미널 한 곳에서 hardhat 노드를 실행해줍니다.
$ npx hardhat node
Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/
Accounts
========
WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.
Account #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
Account #1: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 (10000 ETH)
Private Key: 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
Account #2: 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC (10000 ETH)
Private Key: 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
Account #3: 0x90F79bf6EB2c4f870365E785982E1f101E93b906 (10000 ETH)
Private Key: 0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
Account #4: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 (10000 ETH)
Private Key: 0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a
Account #5: 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc (10000 ETH)
Private Key: 0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba
Account #6: 0x976EA74026E726554dB657fA54763abd0C3a0aa9 (10000 ETH)
Private Key: 0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e
Account #7: 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955 (10000 ETH)
Private Key: 0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356
Account #8: 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f (10000 ETH)
Private Key: 0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97
Account #9: 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720 (10000 ETH)
Private Key: 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6
Account #10: 0xBcd4042DE499D14e55001CcbB24a551F3b954096 (10000 ETH)
Private Key: 0xf214f2b2cd398c806f84e317254e0f0b801d0643303237d97a22a48e01628897
Account #11: 0x71bE63f3384f5fb98995898A86B02Fb2426c5788 (10000 ETH)
Private Key: 0x701b615bbdfb9de65240bc28bd21bbc0d996645a3dd57e7b12bc2bdf6f192c82
Account #12: 0xFABB0ac9d68B0B445fB7357272Ff202C5651694a (10000 ETH)
Private Key: 0xa267530f49f8280200edf313ee7af6b827f2a8bce2897751d06a843f644967b1
Account #13: 0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec (10000 ETH)
Private Key: 0x47c99abed3324a2707c28affff1267e45918ec8c3f20b8aa892e8b065d2942dd
Account #14: 0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097 (10000 ETH)
Private Key: 0xc526ee95bf44d8fc405a158bb884d9d1238d99f0612e9f33d006bb0789009aaa
Account #15: 0xcd3B766CCDd6AE721141F452C550Ca635964ce71 (10000 ETH)
Private Key: 0x8166f546bab6da521a8369cab06c5d2b9e46670292d85c875ee9ec20e84ffb61
Account #16: 0x2546BcD3c84621e976D8185a91A922aE77ECEc30 (10000 ETH)
Private Key: 0xea6c44ac03bff858b476bba40716402b03e41b8e97e276d1baec7c37d42484a0
Account #17: 0xbDA5747bFD65F08deb54cb465eB87D40e51B197E (10000 ETH)
Private Key: 0x689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b061667b5a93e037fd
Account #18: 0xdD2FD4581271e230360230F9337D5c0430Bf44C0 (10000 ETH)
Private Key: 0xde9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0
Account #19: 0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199 (10000 ETH)
Private Key: 0xdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e
WARNING: These accounts, and their private keys, are publicly known.
Any funds sent to them on Mainnet or any other live network WILL BE LOST.
- 하드햇 내장 노드는 항상 같은 Account를 출력해줍니다. 테스트하는데 사용할 수 있습니다.
- 배포할 때는 특별히 지정하지 않으면 Account #0 번이 사용됩니다. (0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266)
실행한 터미널은 그대로 두고, 다른 터미널을 하나 더 엽니다.
새로 실행한 터미널에서 배포를 진행합니다.
$ npx hardhat run scripts/deploy.ts --network localhost
The token Gold(GLD) is deployed
GLDToken with deployed to 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
배포가 정상적으로 진행되고 CA를 얻었습니다. (0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0)
이제 현재 터미널에서 npx hardhat console 명령으로 노드 콘솔에 접속합니다.
$ npx hardhat console --network localhost
Welcome to Node.js v16.16.0.
Type ".help" for more information.
>
접속된 콘솔에서 배포된 컨트랙트를 불러옵니다.
> const token = await ethers.getContractAt("GLDToken", "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0")
undefined
배포된 컨트랙트의 name, symbol, decimals, 최초 배포된 계정의 balanceOf 를 확인해봅니다.
> await token.name()
'Gold'
> await token.symbol()
'GLD'
> await token.decimals()
18n
> await token.balanceOf("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
10000000000000000000000000n
첫번째 계정에서 두번째 계정으로 transfer를 해보고 받아졌는지 확인해봅니다.
> await token.transfer("0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "2000000000000000000")
ContractTransactionResponse {
provider: HardhatEthersProvider {
_hardhatProvider: LazyInitializationProviderAdapter {
_providerFactory: [AsyncFunction (anonymous)],
_emitter: [EventEmitter],
_initializingPromise: [Promise],
provider: [BackwardsCompatibilityProviderAdapter]
},
_networkName: 'localhost',
_blockListeners: [],
_transactionHashListeners: Map(0) {},
_eventListeners: []
},
blockNumber: 6,
blockHash: '0x42942ff4d9ffa57b5423db7db92e8fbfe89b549b10f3ffcdc6a97e5f08faef2f',
index: undefined,
hash: '0xb981f331ba68591f2f5721452d8ba49c7dc5ba4a6f9fff38c5b339642cf3fe74',
type: 2,
to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
nonce: 5,
gasLimit: 30000000n,
gasPrice: 469401188n,
maxPriorityFeePerGas: 0n,
maxFeePerGas: 594085878n,
data: '0xa9059cbb00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000000de0b6b3a7640000',
value: 0n,
chainId: 31337n,
signature: Signature { r: "0x8d11d3e25917006ef7609a95b073d592365f1aa72e88f2ce230d19b8542a3d8e", s: "0x6c29dcd8e4cfe1a0e6efdcdce4a642eb3438a9455cd213250ed59f3e8b291394", yParity: 0, networkV: null },
accessList: []
}
> await token.balanceOf("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")
2000000000000000000n
>
> await toekn.balanceOf("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
9999998000000000000000000n
배포된 토큰 컨트랙트가 잘 동작하는 것을 확인할 수 있습니다.