ERC-20 토큰이란 무엇일까요? ERC-20은 이더리움 블록체인에서 토큰 구현을 위한 스마트 컨트랙트의 기술 표준입니다.
NeoX 블록체인에 자체 ERC-20 토큰을 배포하거나 작동 방식을 이해하고 싶다면 이 가이드가 도움이 될 것입니다.
이전 튜토리얼인 Hello World 스마트 컨트랙트 배포 또는 스마트 컨트랙트 검증 을 따라하셨다면, 동일한 폴더로 이동하시면 됩니다.
이 부분을 건너뛰고 싶으시다면 이 저장소 를 클론하시면 됩니다.
이 경우 Step 5로 바로 이동하여 처음 몇 단계를 건너뛸 수 있습니다. 그렇지 않다면 처음부터 프로젝트를 설정하는 방법을 살펴보겠습니다.
NeoX 테스트넷 체인과 상호작용하기 위해 개인키가 필요합니다. MetaMask를 사용하고 계시다면 매우 간단합니다.
MetaMask 지갑에서 계정 세부정보로 이동하세요. 
비밀번호를 입력하고 개인키 표시를 클릭하세요. 
NeoX 테스트넷 토큰을 받는 방법은 두 가지가 있습니다:
#dev-resources 채널에서 faucet 봇과 상호작용할 수 있습니다.NeoX 테스트넷 익스플로러에 연결하여 잔액을 확인할 수 있습니다.
먼저 프로젝트를 위한 폴더를 생성해야 합니다. 명령줄에서 다음 명령어를 입력하세요.
mkdir my-token
cd my-token
프로젝트 폴더 안에 있으니 npm init을 사용하여 프로젝트를 초기화하겠습니다.
아직 npm이 설치되어 있지 않다면, Node.js와 npm 설치 방법을 따라하세요.
이제 프로젝트를 초기화할 준비가 되었습니다. npm init을 입력하고 정보를 입력하세요. 테스트를 위해 다음과 같이 선택했습니다:
package name: (Token)
version: (1.0.0)
description: 내 ERC-20 컨트랙트
entry point: (index.js)
test command:
git repository:
keywords:
author: NeoDashboard
license: (ISC)
이제 package.json 파일이 생성되었습니다. 다음 단계로 넘어가겠습니다.
NeoX 스마트 컨트랙트를 컴파일, 배포, 테스트 및 디버깅하기 위해 Hardhat을 사용할 것입니다. my-token 프로젝트 내에서 다음 명령어를 입력하세요: npm install --save-dev hardhat
설치가 완료되면 npx hardhat 명령어를 실행하여 Hardhat 프로젝트를 생성합니다 (이 튜토리얼에서는 create an empty hardhat.config.js 옵션을 선택하세요).
그리고 아래와 같이 hardhat.config.js 파일을 수정합니다:
require("dotenv").config();
require("@nomicfoundation/hardhat-toolbox");
const { NEOX_PK } = process.env;
module.exports = {
solidity: {
version: "0.8.26",
settings: {
optimizer: {
enabled: true,
runs: 200,
details: {
yul: false,
},
},
}
},
networks: {
'neox-t4': {
url: 'https://neoxt4seed1.ngd.network',
accounts: [`${NEOX_PK}`],
gasPrice: 40e9,
gas: 50e6,
},
},
etherscan: {
apiKey: {
'neox-t4': 'empty'
},
customChains: [
{
network: 'neox-t4',
chainId: 12227332,
urls: {
apiURL: 'https://xt4scan.ngd.network/api',
browserURL: 'https://neoxt4scan.ngd.network'
}
}
]
}
};
지갑에서 보내는 모든 트랜잭션은 고유한 개인키(첫 번째 단계에서 가져온 것)를 사용하여 서명이 필요합니다. 프로그램에 이 권한을 제공하기 위해 개인키를 환경 파일에 안전하게 저장할 수 있습니다.
프로젝트 디렉토리에 dotenv 패키지를 설치하여 .env 파일에서 환경 변수를 사용할 수 있게 합니다:
npm install dotenv --save
그리고 NEOX_PK라는 이름의 개인키 변수가 있는 .env 파일을 생성합니다. 이 파일은 다음과 같습니다:
PRIVATE_KEY = "your-metamask-private-key"
이제 Hardhat 추천 플러그인을 설치하겠습니다:
npm install --save-dev @nomicfoundation/hardhat-toolbox
이제 OpenZeppelin의 ERC-20 구현을 사용하여 우리만의 토큰을 만들겠습니다. 먼저 OpenZeppelin 컨트랙트를 설치해야 합니다:
npm install @openzeppelin/contracts
그런 다음 contracts 폴더를 만들고 그 안에 MyToken.sol 파일을 생성합니다. 이 파일에 다음 코드를 작성합니다:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
constructor() ERC20("MyToken", "MTK") {
_mint(msg.sender, 1000000 * 10 ** decimals());
}
}
이 컨트랙트는 다음과 같은 기능을 합니다:
스마트 컨트랙트 파일명은 토큰명과 맞추는 것이 일반적입니다. 예를 들어 Token이라는 이름과 TOK라는 심볼을 가진 토큰을 만든다고 가정해보겠습니다.
따라서 contracts 폴더에 Token.sol 파일을 추가합니다.
OpenZeppelin은 이미 ERC-20 컨트랙트를 위한 모든 기능을 제공합니다. 따라서 코드는 매우 간단하며 다음과 같습니다:
// contracts/FunToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract Token is ERC20 {
// 토큰 공급량: 100,000,000
uint256 constant initialSupply = 100000000 * (10**18);
// 컨트랙트 생성 시 호출되는 생성자
constructor() ERC20("Token", "TOK") {
_mint(msg.sender, initialSupply);
}
}
이렇게 하면 Token이라는 이름과 TOK라는 심볼을 가진 토큰이 생성되며, 배포에 사용된 주소로 1억 개의 토큰이 발행됩니다. 이는 생성자에서 _mint 명령어로 처리됩니다.
initialSupply를 정의하는 줄에서 기본 소수점 자릿수(18)를 사용하고 있음을 알 수 있습니다. 만약 11자리 소수점 토큰을 만들고 싶다면 (10**11)로 변경하고 아래 함수를 추가하세요:
function decimals() public view override returns (uint8) {
return 11;
}
환경이 준비되었다면 아래 명령어 한 줄로 컨트랙트를 컴파일할 수 있습니다:
npx hardhat compile
튜토리얼을 잘 따라왔다면 아래와 비슷한 결과가 나올 것입니다.
Compiled 6 Solidity files successfully (evm target: paris).
파일을 추가하거나 삭제했다면 숫자는 달라질 수 있습니다.
컨트랙트 배포를 위해 scripts 폴더에 deploy_erc20.js라는 배포 스크립트를 만들고 아래와 같이 작성합니다:
async function main() {
const MyToken = await ethers.getContractFactory("Token")
// 배포 시작, 컨트랙트 객체를 반환
const my_token = await MyToken.deploy()
console.log(my_token)
console.log("컨트랙트가 배포된 주소:", my_token.target)
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
})
이제 아래 명령어로 배포 스크립트를 실행할 수 있습니다.
npx hardhat run --network neox-t4 scripts/deploy_erc20.js
아래와 비슷한 결과가 출력됩니다.
Contract deployed to address: 0x94Be4bc282Cb35945763cA060dd4C6c1A2Fb07f7
이제 컨트랙트가 배포되었으며, 익스플로러에서 배포된 해시로 검색해 확인할 수 있습니다.
이 튜토리얼을 따라했다면 컨트랙트 검증이 매우 간단하다는 것을 이미 아실 겁니다. Step 5에서 받은 해시로 아래 명령어를 실행하세요:
npx hardhat verify --network neox-t4 0x94Be4bc282Cb35945763cA060dd4C6c1A2Fb07f7
아래와 같이 검증이 성공했다는 메시지가 출력됩니다.
Successfully submitted source code for contract
contracts/Token.sol:Token at 0x94Be4bc282Cb35945763cA060dd4C6c1A2Fb07f7
for verification on the block explorer. Waiting for verification result...
Successfully verified contract Token on the block explorer.
https://neoxt4scan.ngd.network/address/0x94Be4bc282Cb35945763cA060dd4C6c1A2Fb07f7#code
검증 튜토리얼과 Hello World 튜토리얼에서 컨트랙트와 상호작용하는 방법을 이미 살펴보았습니다. 여기서는 새로운 예시로 스크립트를 작성해보겠습니다.
scripts 폴더에 get_balance.js 파일을 아래와 같이 추가합니다.
async function main() {
const contractAddress = "0x94Be4bc282Cb35945763cA060dd4C6c1A2Fb07f7";
const address = "0x2614D8Fc06dcd0B073d889A498546FF464193551"
const TokenContract = await hre.ethers.getContractAt("Token", contractAddress);
let balance = await TokenContract.balanceOf(address);
console.log(`${address}의 잔액은 ${hre.ethers.formatUnits(balance, 18)}`);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
});
이 스크립트에서 contractAddress는 Step 5에서 받은 해시로, address는 잔액을 조회하고 싶은 주소로 바꿔서 아래 명령어로 실행하세요.
npx hardhat run --network neox-t4 scripts/get_balance.js
아래와 같은 결과가 출력됩니다.
0x2614D8Fc06dcd0B073d889A498546FF464193551의 잔액은 100000000.0
이제 balanceOf 호출을 ERC-20의 다른 함수로 바꿔서 원하는 정보를 조회할 수 있습니다. 예를 들어, 토큰을 전송하고 싶다면 아래와 같이 100 $TOK를 receiverAddress로 보내는 스크립트를 사용할 수 있습니다:
async function main() {
const contractAddress = "0x94Be4bc282Cb35945763cA060dd4C6c1A2Fb07f7";
const receiverAddress = "0x762daCF582172E69662613A035C8D73Fa2720755";
const amount = BigInt(100 * (10 ** 18), 18);
const TokenContract = await hre.ethers.getContractAt("Token", contractAddress);
let tx = await TokenContract.transfer(receiverAddress, amount);
await tx.wait()
console.log(`${hre.ethers.formatUnits(amount, 18)} $TOK이 ${receiverAddress} 주소로 전송되었습니다. 트랜잭션 해시: ${tx.hash}`);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
축하합니다! NeoX 테스트넷 체인에 자체 ERC-20 컨트랙트를 성공적으로 생성했습니다. 배운 내용을 요약하면 다음과 같습니다: