클레이튼 엠버서더에서 진행하는 Core dev 프로젝트 중 ‘주소 라벨링’을 진행하고 있다.
이더리움의 ENS(Ethereum Name Service)를 Klaytn으로 개발하여 KIP를 등록하는 것이 이번 프로젝트의 목표이다.
ENS 배포
1. 로컬 배포(완)
ollie님의 도움으로 hardhat 로컬 네트워크에는 컨트랙트 배포를 할 수 있었음.
버전에 따른 코드 파악이 부족했음.
ENS가 버전에 따라 쓰이는 코드가 다르다는 것을 뒤늦게 알았지만 어떻게 적용해야하는지 파악이 어려웠다. (하지만 ollie님은 단 번에 파악하심 ㅋㅋ..)
ensdomain 문서의
deps.sol
의 경우 모두 다 가져와서 한꺼번에 하게끔 되어있는데, 하나씩 해보면 된다는 말.ensdomain 컨트랙트가 버전업 되면서 코드가 약간 달라짐. 그래서 script/deploy.js를 고대로 가져다 쓰면 배포가 안됨.
결국은
@ensdomains-contract
모듈 내의 resolver 부분(이쪽이 변경됨)을 ollie님이 알려주신v0.0.8
버전으로 낮춰서 컴파일하고,deploy.js
를 살짝 변경해야 함.
그런데, 내가 hardhat을 잘 못해서 그런지 ollie님의 코드를 똑같이 쓰면 배포가 안됨.
.deployed()
함수가 말을 듣지 않아 다른 함수 사용해서 배포- 예)
(기존 코드)
await ens.deployed()
ens.address
// CA⇒ ens.deployed() is not a function
(수정)
await ens.waitForDeployment()
ens.target
// CA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
// deploy.js const { ethers } = require("hardhat"); const namehash = require("eth-ens-namehash"); const { makeAbi } = require("../makeABI"); const tld = "test"; const labelhash = (label) => ethers.keccak256(ethers.toUtf8Bytes(label)); const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; const ZERO_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000"; async function main() { const ENSRegistry = await ethers.getContractFactory("ENSRegistry"); const FIFSRegistrar = await ethers.getContractFactory("FIFSRegistrar"); const ReverseRegistrar = await ethers.getContractFactory("ReverseRegistrar"); const PublicResolver = await ethers.getContractFactory("PublicResolver"); const signers = await ethers.getSigners(); const accounts = signers.map((s) => s.address); // console.log(accounts); let ensAddress; const ens = await ENSRegistry.deploy().then((res) => { ensAddress = res.target; // 이전에는 deployed() 이후에 .address로 컨트랙트 주소를 알 수 있었지만 target으로 변경함 return res; }); await ens.waitForDeployment(); // deployed() 요거 왜 안되지?? ㅠㅠ => 결국에는 waitForDeployment() 씀 console.log("Registry at", ensAddress); await makeAbi(ensAddress, "ens"); let resolverAddress; const resolver = await PublicResolver.deploy(ensAddress, ZERO_ADDRESS).then( (res) => { resolverAddress = res.target; return res; } ); await resolver.waitForDeployment(); await setupResolver(ens, resolver, accounts[0]); console.log("Resolver at", resolverAddress); await makeAbi(resolverAddress, "resolver"); const registrar = await FIFSRegistrar.deploy(ens.target, namehash.hash(tld)); await registrar.waitForDeployment(); await setupRegistrar(ens, registrar); console.log("Registrar at", registrar.target); await makeAbi(registrar.target, "registrar"); const reverseRegistrar = await ReverseRegistrar.deploy( ens.target, resolver.target ); await reverseRegistrar.waitForDeployment(); await setupReverseRegistrar(ens, registrar, reverseRegistrar, accounts[0]); console.log("ReverseRegistrar at", reverseRegistrar.target); await makeAbi(reverseRegistrar.target, "reverseRegistrar"); } async function setupResolver(ens, resolver, account) { const resolverNode = namehash.hash("resolver"); const resolverLabel = labelhash("resolver"); await ens.setSubnodeOwner(ZERO_HASH, resolverLabel, account); await ens.setResolver(resolverNode, resolver.target); await resolver["setAddr(bytes32,address)"](resolverNode, resolver.target); } async function setupRegistrar(ens, registrar) { await ens.setSubnodeOwner(ZERO_HASH, labelhash(tld), registrar.target); } async function setupReverseRegistrar( ens, registrar, reverseRegistrar, account ) { await ens.setSubnodeOwner(ZERO_HASH, labelhash("reverse"), account); await ens.setSubnodeOwner( namehash.hash("reverse"), labelhash("addr"), reverseRegistrar.target ); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
- 예)
hardhat이 낯설다?…
이번 기회에 hardhat을 익혀보고 싶어서 해봤는데, 확실히 truffle과는 다른 너낌.. 배포하는데 꽤 애먹음.
2. 클레이튼 바오밥 배포(완)
이제 klaytn-baobab 에서 배포를 하려하니 다음과 같은 오류가 뜸.
1 2 3 4 5 6 7 8 9 10 11
ProviderError: evm: execution reverted at HttpProvider.request (/Users/instant/Desktop/ens_test/node_modules/hardhat/src/internal/core/providers/http.ts:88:21) at processTicksAndRejections (node:internal/process/task_queues:96:5) at HardhatEthersProvider.estimateGas (/Users/instant/Desktop/ens_test/node_modules/@nomicfoundation/hardhat-ethers/src/internal/hardhat-ethers-provider.ts:237:27) at /Users/instant/Desktop/ens_test/node_modules/@nomicfoundation/hardhat-ethers/src/signers.ts:235:35 at async Promise.all (index 0) at HardhatEthersSigner._sendUncheckedTransaction (/Users/instant/Desktop/ens_test/node_modules/@nomicfoundation/hardhat-ethers/src/signers.ts:256:7) at HardhatEthersSigner.sendTransaction (/Users/instant/Desktop/ens_test/node_modules/@nomicfoundation/hardhat-ethers/src/signers.ts:125:18) at send (/Users/instant/Desktop/ens_test/node_modules/ethers/src.ts/contract/contract.ts:299:20) at Proxy.setResolver (/Users/instant/Desktop/ens_test/node_modules/ethers/src.ts/contract/contract.ts:338:16) at setupResolver (/Users/instant/Desktop/ens_test/scripts/deploy.js:65:3)
시도 1) hardhat 설정을 내가 잘못했나?
deploy.js
에서 account를 불러올 때 truffle과 달리 다음과 같이 불러오는데 이게 로컬 네트워크에서만 적용되는 줄 알았는데, 검색해보니 config 설정을 하면 되는 거 였음.1 2 3 4 5
// deploy.js ... const signers = await ethers.getSigners(); const accounts = signers.map((s) => s.address); ...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// hardhat.config.js require("@nomicfoundation/hardhat-toolbox"); const path = require("path"); require("dotenv").config({ path: path.resolve(__dirname, "./.env") }); const { KLAYTN_URL, PRIVATE_KEY } = process.env; /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: "0.8.19", networks: { klaytn: { url: KLAYTN_URL || "", accounts: [PRIVATE_KEY], }, }, };
하지만 계속 되는 에러.
시도 2) PublicResolver 구조를 한번 더 파악해보자.
1 2 3 4 5 6 7 8 9 10 11
ProviderError: evm: execution reverted at HttpProvider.request (/Users/instant/Desktop/ens_test/node_modules/hardhat/src/internal/core/providers/http.ts:88:21) at processTicksAndRejections (node:internal/process/task_queues:96:5) at HardhatEthersProvider.estimateGas (/Users/instant/Desktop/ens_test/node_modules/@nomicfoundation/hardhat-ethers/src/internal/hardhat-ethers-provider.ts:237:27) at /Users/instant/Desktop/ens_test/node_modules/@nomicfoundation/hardhat-ethers/src/signers.ts:235:35 at async Promise.all (index 0) at HardhatEthersSigner._sendUncheckedTransaction (/Users/instant/Desktop/ens_test/node_modules/@nomicfoundation/hardhat-ethers/src/signers.ts:256:7) at HardhatEthersSigner.sendTransaction (/Users/instant/Desktop/ens_test/node_modules/@nomicfoundation/hardhat-ethers/src/signers.ts:125:18) at send (/Users/instant/Desktop/ens_test/node_modules/ethers/src.ts/contract/contract.ts:299:20) at Proxy.setResolver (/Users/instant/Desktop/ens_test/node_modules/ethers/src.ts/contract/contract.ts:338:16) at setupResolver (/Users/instant/Desktop/ens_test/scripts/deploy.js:65:3)
위의 에러를 보면
PublicResolver
를 배포할 때ENSRegistry
에 있는setResolver
함수에서 자꾸 거절함.1 2 3 4 5 6 7 8 9 10 11 12 13 14
// ENSRegistry.sol ... function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public virtual override authorised(node) returns(bytes32) { bytes32 subnode = keccak256(abi.encodePacked(node, label)); _setOwner(subnode, owner); emit NewOwner(node, label, owner); return subnode; } function setResolver(bytes32 node, address resolver) public virtual override authorised(node) { emit NewResolver(node, resolver); records[node].resolver = resolver; } ...
함수 내에
require
등의 제어가 없으니authorised
문제인가…?하지만 scope를 확인하면,
setSubnodeOwner
는 잘 된다.
시도 3) 네트워크 차이..?
‘당연히 로컬 네트워크는 빠를 수 밖에 없다. 배포 시에 셋팅이 문제라면 컨트랙트가 배포되는 속도와 TPS에 따른 차이가 존재하려나?’ 라는 생각을 하게 됐다.
아직까지 hardhat을 잘 쓰는게 아니기 때문에 배포 따로, 셋팅 따로 하는 로직으로 바꿔 버렸다.
결과는?
- ✅ 배포
배포 스크립트
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
const { ethers } = require("hardhat"); const { makeAbi } = require("../makeABI"); async function main() { const ENSRegistry = await ethers.getContractFactory("ENSRegistry"); const FIFSRegistrar = await ethers.getContractFactory("FIFSRegistrar"); const ReverseRegistrar = await ethers.getContractFactory("ReverseRegistrar"); const PublicResolver = await ethers.getContractFactory("PublicResolver"); const signers = await ethers.getSigners(); const accounts = signers.map((s) => s.address); let ensAddress; const ens = await ENSRegistry.deploy().then((res) => { ensAddress = res.target; // 이전에는 deployed() 이후에 .address로 컨트랙트 주소를 알 수 있었지만 target으로 변경됨 return res; }); await ens.waitForDeployment(); // deployed() 요거 왜 안되지?? ㅠㅠ => hardhat 버전이 업데이트 되면서 다른 것 써야함 console.log("Registry at", ensAddress); await makeAbi(ensAddress, "ens"); let resolverAddress; const resolver = await PublicResolver.deploy(ensAddress, ZERO_ADDRESS).then( (res) => { resolverAddress = res.target; return res; } ); await resolver.waitForDeployment(); console.log("Resolver at", resolverAddress); await makeAbi(resolverAddress, "resolver"); const registrar = await FIFSRegistrar.deploy(ens.target, namehash.hash(tld)); await registrar.waitForDeployment(); console.log("Registrar at", registrar.target); await makeAbi(registrar.target, "registrar"); const reverseRegistrar = await ReverseRegistrar.deploy( ens.target, resolver.target ); await reverseRegistrar.waitForDeployment(); console.log("ReverseRegistrar at", reverseRegistrar.target); await makeAbi(reverseRegistrar.target, "reverseRegistrar"); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
- ✅ 셋팅
셋팅 스크립트
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
const path = require("path"); require("dotenv").config({ path: path.resolve(__dirname, "./.env") }); const { KLAYTN_URL, PRIVATE_KEY, ADDRESS } = process.env; const Caver = require("caver-js"); const caver = new Caver(KLAYTN_URL); const { ethers } = require("hardhat"); const namehash = require("eth-ens-namehash"); const tld = "test"; const labelhash = (label) => ethers.keccak256(ethers.toUtf8Bytes(label)); const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"; const ZERO_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000"; // Contract const ensData = require("./abis/ens.json"); const ens = new caver.klay.Contract(ensData.abi, ensData.address).methods; const registrarData = require("./abis/registrar.json"); const registrar = new caver.klay.Contract( registrarData.abi, registrarData.address ).methods; const resolverData = require("./abis/resolver.json"); const resolver = new caver.klay.Contract(resolverData.abi, resolverData.address) .methods; const reverseRegistrarData = require("./abis/reverseRegistrar.json"); const reverseRegistrar = new caver.klay.Contract( reverseRegistrarData.abi, reverseRegistrarData.address ).methods; const setting = async () => { const resolverNode = namehash.hash("resolver"); const resolverLabel = labelhash("resolver"); console.log("setSubnodeOwner를 셋팅합니다."); const setSubnodeOwner = await ens .setSubnodeOwner(ZERO_HASH, resolverLabel, ADDRESS) .encodeABI(); await SendTransaction(setSubnodeOwner, ensData.address); console.log(" "); console.log("setResolver를 셋팅합니다."); const setResolver = await ens .setResolver(resolverNode, resolverData.address) .encodeABI(); await SendTransaction(setResolver, ensData.address); console.log(" "); console.log("setAddr를 셋팅합니다."); const setAddr = await resolver .setAddr(resolverNode, resolverData.address) .encodeABI(); await SendTransaction(setAddr, resolverData.address); console.log(" "); console.log("Registrar를 셋팅합니다."); const setupRegistrar = await ens .setSubnodeOwner(ZERO_HASH, labelhash(tld), registrarData.address) .encodeABI(); await SendTransaction(setupRegistrar, ensData.address); console.log(" "); console.log("ReverseRegistrar를 셋팅합니다."); const reverseSubnodeOwner = await ens .setSubnodeOwner(ZERO_HASH, labelhash("reverse"), ADDRESS) .encodeABI(); await SendTransaction(reverseSubnodeOwner, ensData.address); const reverseSubnodeOwnerCa = await ens .setSubnodeOwner( namehash.hash("reverse"), labelhash("addr"), reverseRegistrarData.address ) .encodeABI(); await SendTransaction(reverseSubnodeOwnerCa, ensData.address); }; const SendTransaction = async (data, to, estimate) => { await caver.klay.accounts .signTransaction( { from: ADDRESS, to: to, gasPrice: await caver.klay.getGasPrice(), gas: 500000, data: data, }, PRIVATE_KEY ) .then(async (Tx) => { await caver.klay .sendSignedTransaction(Tx.rawTransaction) .then((hash, err) => { if (err) { console.log(err); } else { console.log("success!"); } }); }); }; setting();
- ✅ 배포