์ฐ์ ๊ธฐ๋ก ๐ช
[Solidity+hardhat+JavaScript] ์ค๋งํธ์ปจํธ๋ ๋ฐฐํฌ ์ค์ต ๋ณธ๋ฌธ
์ด ๊ธ์ ์๋ ์์์ ๋ณด๊ณ ๊ณต๋ถํ ๋ด์ฉ์ ๊ธฐ๋กํ ๊ธ์ ๋๋ค.
๋ชฉํ
ํ๋ฉ ํ๋ก๊ทธ๋จ์ ๊ด์ฅํ๋ ์ค๋งํธ ์ปจํธ๋ ๋ง๋ค๊ธฐ
- ํ๋ก์ ํธ์ ์ค๋, ๋ชฉํ ๊ธ์ก, ํ๋ก์ ํธ์ ๊ธฐ๋ถํ ์ฌ๋๋ค์ ๋ชฉ๋ก, ๊ทธ๋ค์ ๊ตฌ๋งค๊ธ์ก, ๊ธฐ๋ถ๊ธ์ ๋์ ์ด์ก, ๋ฐ๋๋ผ์ธ
- ๋ฐ๋๋ผ์ธ ๋๋ฌ ์ ๊ธฐ๋ถ๊ธ ์ด์ก>=๋ชฉํ๊ธ์ก์ผ ๊ฒฝ์ฐ ๋์ ํ๋ก์ ํธ ์ค๋์๊ฒ ์ ๋ฌ -๊ธฐ๋ถ๊ธ ์ด์ก<๋ชฉํ๊ธ์ก์ผ ๊ฒฝ์ฐ ๋์ ๊ธฐ๋ถ์๋ค์๊ฒ ํ๋ถ
Contract ๋ฐฐํฌ
1. ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น
npm install -D hardhat
npm install -D ethers
npm install -D --legacy-peer-deps @nomiclabs/hardhat-ethers
npm install -D --legacy-peer-deps @nomiclabs/hardhat-waffle
2. hardhat ํ๋ก์ ํธ ์์ฑ
npx hardhat
์ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ๊ณ create an empty hardhat.config.js๋ฅผ ์ ํํ ํ hardhat.config.jsํ์ผ์ solidity๋ฒ์ ์ Fundraisign.sol ์๋จ pragma solidity ๋ฒ์ ์ ๋ง์ถฐ์ค์ผ ํ๋ค. ์ดํ hardhat.config.js ํ์ผ์ require("@nomiclabs/hardhat-waffle") ์ถ๊ฐํ๋ฉด ๋๋ค.
- hardhat.config.js ํ์ผ์ require("@nomiclabs/hardhat-waffle") ์ถ
- create an empty hardhat.config.js ์ ํ
- hardhat.config.js ํ์ผ์ solidity ๋ฒ์ ์ Fundraising.sol ์๋จ pragma solidity ๋ฒ์ ์ ๋ง์ถ๊ธฐ
- hardhat.config.js ํ์ผ์ require("@nomiclabs/hardhat-waffle") ์ถ๊ฐ
3. ๋ธ๋ก์ฒด์ธ ๊ณ์ ์์ฑ
์ด์ ์ปจํธ๋์ ๋ฐฐํฌํ๊ณ ๊ธฐ๋ถ๋ ๋์ ์๋ นํ๊ธฐ ์ํ ๋ธ๋ก์ฒด์ธ ๊ณ์ ์ ๋ง๋ค์ด๋ณด์. ์๋ ๋งํฌ๋ฅผ ๋๋ฌ์ ๋ฉํ๋ง์คํฌ ์ต์คํ ์ ์ ์ถ๊ฐํ๋๋ก ํ์.
https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn
๊ทธ๋ฐ ๋ค์ ์ ์ง๊ฐ ์์ฑ์ ๋๋ฌ ์ง๊ฐ์ ์์ฑํ๊ณ , ์ค์ ์ ๋ค์ด๊ฐ ํ ์คํธ๋ท์ด ๋ณด์ด๋๋ก ์ค์ ์ ํด์ฃผ์.
์ง๊ฐ์ ๋น๋ฐ๋ฒํธ์ ๋๋ชจ๋์ฝ๋๋ ์ ์ ์ฅํด๋์ด์ผ ํ๋ค. ์ ๋ ์์ด๋ฒ๋ ค์๋ ์๋๋ ์ ๋ณด์ด๋ค.
3. ํ ์คํธ ์ด๋ ๋ฐ๊ธฐ
์์์์๋ All that Node๋ก ์งํํ์ง๋ง ์ง๊ธ์ ๋๋ฌด ๋ง์ ์์ฒญ์ผ๋ก ์ธํด ๋งํ์ ์๋ ์ฌ์ดํธ์์ ํ ์คํธ ์ด๋๋ฅผ ์๋ นํ๋ค.
4. All that Node ํ๋ก์ ํธ ์์ฑ
All that Node ์๋จ์ dashboard - Create New Project๋ฅผ ํด๋ฆญํ์.
๊ธฐ๋ณธ ์ต์ ์ผ๋ก ์งํํ ๊ฒ์ด๋ค. checkout์ ๋๋ฅด๊ณ create project๋ฒํผ์ ๋๋ฅด์.
์ดํ add protocol์ ๋๋ฅด๊ณ Ethereum์ ์ ํํ๋๋ก ํ์.
์ด์ Goerli RPC ๋ณต์ฌํด hardhat.config.js์ ์ถ๊ฐํ ๊ฒ์ด๋ค. ์๋์ ๊ฐ์ ํ์์ผ๋ก url์ Goerli RPC/API Key ํ์์ผ๋ก ์ถ๊ฐํ๋ฉด ๋๋ค.
networks: {
goerli: {
url: "Goerli RPC ๋งํฌ/API Key",
},
},
5. HardHat์์ ์ง๊ฐ ๊ด๋ฆฌํ๋๋ก ํ๊ธฐ
ํ์ฌ๋ MetaMask์์ ์ง๊ฐ์ ๊ด๋ฆฌํ๊ณ ์๋ค. HardHat์์๋ ์ง๊ฐ์ ๊ด๋ฆฌํ ์ ์๋๋ก ํด์ฃผ์.
MetaMask์์ ๊ณ์ ์ธ๋ถ์ ๋ณด - ๋น๊ณต๊ฐ ํค ๋ด๋ณด๋ด๊ธฐ - ๋น๋ฐ๋ฒํธ ์ ๋ ฅ ๊ณผ์ ์ ๊ฑฐ์ณ private key๋ฅผ ์์๋ด๋๋ก ํ์.
์ดํ accounts๋ถ๋ถ์ ์์๋ธ private key๋ฅผ ์ถ๊ฐํด ์ฃผ๋ฉด ๋๋ค. ๋ค๋ง ์ด๊ฒ์ ๋ณด์์ ์ข์ง ์์ ๋ฐฉ๋ฒ์ด๋ .env์ ๊ฐ์ ์ ์ฅํด์ ๋ถ๋ฌ์ค๋ ๊ฒ์ ์ถ์ฒํ๋ค.
networks: {
goerli: {
url: "Goerli RPC ๋งํฌ/API Key",
accounts: ["์์๋ธ private key"]
},
},
6. ๋ฐฐํฌ ์ฝ๋ ์์ฑ
์ด์ ์์ฑํ ์ปจํธ๋ ์ฝ๋๋ฅผ ๋ฐฐํฌํด๋ณด์. scriptsํด๋๋ฅผ ๋ง๋ค๊ณ ์์ deploy.js ์์ฑํ ๋ค ์๋ ์ฝ๋๋ฅผ ์์ฑํด์ฃผ๋๋ก ํ์.
const { ethers } = require("hardhat");
async function main() {
const Fundraising = await ethers.getContractFactory("Fundraising"); //Fundraising ์ปจํธ๋์ ๊ฐ์ ธ์์ ์ปดํ์ผ ํ๋ค.
const contract = await Fundraising.deploy(100); //100์ด๋ผ๋ ์ธ์์ ํจ๊ป ๋ฐฐํฌํ๋ค. ํด๋น ์ธ์๋ Fundraising ์ปจํธ๋์ ์ ๋ฌ๋๋ค. ์ด ํ๋ก์ ํธ์์๋ ๋ชฉํ๊ธ์ก์ ์๋ฏธํ๋ค.
console.log("Contract address is: ", contract.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
๊ทธ๋ฐ ๋ค์ ์๋ ์ฝ๋๋ฅผ ์ปค๋งจ๋์ฐฝ์ ์ ๋ ฅํด์ฃผ๋๋ก ํ์.
npx hardhat run scripts/deploy.js --network goerli
์ฑ๊ณตํ ๊ฒฝ์ฐ ์ฝ์ ์ฐฝ์ contract address๊ฐ ์ถ๋ ฅ๋๋ค. ์๋ฌ๊ฐ ๋ฌ์ ๊ฒฝ์ฐ ์๋ ERRORS ๋ถ๋ถ์ ์ฐธ๊ณ ํ๋๋ก ํ์. ์ด์ ๋ฉํ๋ง์คํฌ๋ฅผ ํตํด Ether Scan์ ๋ค์ด๊ฐ๋ฉด ๋ฐฐํฌ๋ ์ปจํธ๋์ ํ์ธํ ์ ์๋ค.
7. ๋ฐฐํฌ๋ ์ปจํธ๋๊ณผ ์ํธ์์ฉ ํ๊ธฐ
Ether Scan์์ Contract Creation์ ๋๋ฌ ํด๋น ์ปจํธ๋ ์์ธ ๋ณด๊ธฐ ํ์ด์ง๋ก ์ ๊ทผํ ๋ค, ํด๋น ํ์ด์ง ์๋จ์ ์ปจํธ๋ ์ฃผ์๋ฅผ ๋ณต์ฌํ์.
๊ทธ๋ฆฌ๊ณ ๋ฉํ๋ง์คํฌ๋ฅผ ํตํด ๋ณต์ฌํ ์ปจํธ๋ ์ฃผ์๋ก 0.001Ether๋ฅผ ๋ณด๋ด์ฃผ๋๋ก ํ์. ์ฐธ๊ณ ๋ก 1 Ether = 1,000,000,000,000,000,000 WEI ์ด๋ค.
๋ค์ ์ปจํธ๋ ์์ธ ํ์ด์ง๋ฅผ ๋ณด๋ฉด 0.001Ether๊ฐ ์ ๋ค์ด๊ฐ ๊ฒ์ ํ์ธํ ์ ์๋ค.
8. ์ปจํธ๋ ํจ์ ์คํํ๊ธฐ
hardhat.config.js์ ์๋ ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ค. abi๊ฐ์ artifacts/contracts/Fundraising/Fundraising.json์์ ์ฐพ์ ์ ์๋ค.
task("check", "Check contract amounts", async () => {
const [developer] = await ethers.getSigners();
const contract = process.env.CONTRACT_ADDRESS;
const abi = [
'artifacts/contracts/Fundraising/Fundraising.json์ "abi":[]ํ๊ณ ๋์์๋ ๋ถ๋ถ',
];
const fundraising = new ethers.Contract(contract, abi, developer);
console.log(
await fundraising.targetAmount(),
await fundraising.raisedAmount()
);
});
๊ทธ๋ฆฌ๊ณ ์๋ ๋ช ๋ น์ด๋ฅผ ํตํด ๋ฐฉ๊ธ ์์ฑํ task๋ฅผ ์คํํด๋ณด์. 10์ง์๋ก ์ถ๋ ฅํ๊ณ ์ถ๋ค๋ฉด ์๋์ ๊ฐ์ด parseInt๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
npx hardhat check --network goerli
task("check", "Check contract amounts", async () => {
const [developer] = await ethers.getSigners();
const contract = process.env.CONTRACT_ADDRESS;
const abi = [
'artifacts/contracts/Fundraising/Fundraising.json์ "abi":[]ํ๊ณ ๋์์๋ ๋ถ๋ถ',
];
const fundraising = new ethers.Contract(contract, abi, developer);
//์ฌ๊ธฐ์๋ถํฐ ์์ ๋ ์ฝ๋
targetAmount = await fundraising.targetAmount();
raisedAmount = await fundraising.raisedAmount();
console.log(parseInt(targetAmount), parseInt(raisedAmount));
});
ERRORS
- Error: insufficient funds for intrinsic transaction cost
์ปจํธ๋์ ๋ฐฐํฌํ๊ธฐ์ ํ ์คํธ ์ด๋๊ฐ ๋ถ์กฑํด์ ๋ฐ์ํ๋ ์๋ฌ. ์๋ ์ฌ์ดํธ์์ ํ ์คํธ์ด๋๋ฅผ ๋ชจ์ ์ ์๋ค.
https://goerli-faucet.pk910.de/ - Error: overflow [ See: https://links.ethers.org/v5-errors-NUMERIC_FAULT-overflow ]
์์์ ๋ณด๊ณ 100000000000000000000๋ผ๋ ์ซ์์ ํจ๊ป ๋ฐฐํฌํ๋๋ ์๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค. 100์ผ๋ก ๊ณ ์ณ์ฃผ์๋ค. - ProviderError: Too Many Requests error received from ethereum-goerli-rpc.allthatnode.com
All that Node ์์ ํ์ฌ too many request๋ผ๋ฉฐ ํ ์คํธ ์ด๋๋ ๋ฐ์ ์ ์๋ ์ํ์ธ๋ฐ ๋ง ๊ทธ๋๋ก ๋๋ฌด ๋ง์ ์์ฒญ์ด ์์ ๊ทธ๋ฐ ๊ฒ ๊ฐ๋ค. ์ ์ ๋ค ์คํํ๋ฉด ์ฑ๊ณตํ๋ค. ์ ์ ๋ฌธ์ ๊ฐ ์๋๋ค. - TypeError: Cannot read properties of undefined (reading 'JsonRpcProvider')
์ด๋๋ฆฌ์ ๋ฒ์ ์ 5.4์ดํ๋ก ๋ฎ์ถ๋ฉด ํด๊ฒฐ๋๋ ์ค๋ฅ์ด๋ค. package.json์์ ์ด๋๋ฆฌ์ ๋ฒ์ ์ 5.4๋ก ์ ๋ ฅํ๊ณ npm install์ ํ ๋ค ๋ค์ ์๋ํด๋ณด์.
https://ethereum.stackexchange.com/questions/144451/typeerror-cannot-read-properties-of-undefined-reading-jsonrpcprovider
References
- Solidity types https://docs.soliditylang.org/en/v0.8.19/types.html#types
- Solidity iteration https://docs.soliditylang.org/en/v0.8.19/types.html#iterable-mappings