MetaMask
가 설치되어 있고 실행중이라면 브라우저에서window.ethereum
을 찍어보면MetaMask
가 만들어둔ethereum
객체가 생성되 있는것을 확인할 수 있습니다.
이것을 가지고Front
서버에서MetaMask
가 설치되어있는지 확인 할 수 있습니다.
- 지난번에는
Front
에서 바로Ethereum network
로 다이렉트로 요청을 보냈다면 오늘은 브라우저에서MetaMask
(지갑)으로JSON RPC
요청을 보내면MetaMask
에서Ethereum network
로 요청을 보내줄것입니다.- 지난번과 같은 방식으로
Back
서버에서 다이렉트로Transaction
을 발생시킨다면 어찌됐던Back
서버에 개인키를 보관하고 있다는 뜻이니 보안적으로 굉장히 취약합니다. 하지만,MetaMask
를 사용하게 되면MetaMask
에서 개인키를 각각 사용자의 로컬디렉토리에 개인키를 저장하기 때문에 비교적으로 안전하게 개인키를 보관할 수 있습니다.
테스트할것이니 터미널에서
Ganache
를 실행해주도록 하고chainId
는 임의로 1213으로 설정해줍니다.
npx create-react-app [디렉토리명]
npm install web3
로web3
라이브러리를 설치한후import
해올때는
import Web3 from 'web3/dist/web3.min.js'
이런식으로Front
에서 사용할 최소한의web3
만import
해오지 않으면 아래와 같이Error
가 발생합니다.
/* App.js */
import useWeb3 from './hook/userWeb3.js';
import { useState, useEffect } from 'react';
function App() {
const [account, web3] = useWeb3();
const [isLogin, setIsLogin] = useState(false);
const [balance, setBalance] = useState(0);
const handleSubmit = async (e) => {
e.preventDefault();
await web3.eth.sendTransaction({
from: account,
to: e.target.recived.value,
value: web3.utils.toWei(e.target.amount.value, 'ether'),
});
};
useEffect(() => {
const init = async () => {
const balance = await web3?.eth.getBalance(account);
setBalance(balance / 10 ** 18);
};
init();
if (account) setIsLogin(true);
}, [account]);
if (!isLogin) return <div>MetaMask 로그인후 사용해주세요.</div>;
return (
<div>
<span>
<h2>{account}님 환영합니다.</h2>
<div>Balance : {balance} ETH</div>
</span>
<div>
<form onSubmit={handleSubmit}>
<input type='text' id='recived' placeholder='받을계정' />
<input type='number' id='amount' placeholder='보낼 금액' />
<input type='submit' value='전송' />
</form>
</div>
</div>
);
}
export default App;
import { useEffect, useState } from 'react';
import Web3 from 'web3';
const useWeb3 = () => {
const [account, setAccount] = useState(null);
const [web3, setWeb3] = useState(null);
// MetaMask에서 생성해준 메서드로 ChainId를 가져옵니다.
const getChainId = async () => {
const chainId = await window.ethereum.request({
method: 'eth_chainId',
});
return chainId;
};
// MetaMask에서 생성해준 메서드로 지갑 주소를 가져옵니다.
// MetaMask에서 Front에 연결된 지갑이 없을경우 지갑을 연결해달라고 합니다.
const getRequestAccounts = async () => {
const account = await window.ethereum.request({
method: 'eth_requestAccounts',
});
return account;
};
// 네트워크 추가 함수
const addNetwork = async (chainId) => {
const network = {
chainId: chainId,
chainName: 'suhwanGanache',
rpcUrls: ['http://127.0.0.1:8545'],
nativeCurrency: {
name: 'Ethereum', // 통화 이름
symbol: 'ETH', // 통화 기호
decimals: 18, // 통화 소수점 자리
},
};
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [network],
});
};
useEffect(() => {
const init = async () => {
try {
const targetChainId = '0x4bd'; // 내가 설정한 Ganache ChainId
const chainId = await getChainId(); // 사용중인 MetaMask Network의 chainId
if (targetChainId !== chainId) {
addNetwork(targetChainId);
}
const [account] = await getRequestAccounts();
const web3 = new Web3(window.ethereum);
setWeb3(web3);
setAccount(account);
} catch (e) {
console.error(e.message);
}
};
// MetaMask 설치함
if (window.ethereum) {
init();
}
}, []);
return [account, web3];
};
export default useWeb3;
👉 window.ethereum을 확인하여 MetaMask가 설치되어 있는지 확인
if (window.ethereum) {
init();
}
- 페이지 접속시 브라우저에
MetaMask
가 설치되어있는지userWeb3.js Custom Hook
에서window.ethereum
으로 먼저 확인하고MetaMask
가 있다면init
함수를 실행합니다.
👉 브라우저와 연결된 지갑이 없으면 보여줄 화면
if (!isLogin) return <div>MetaMask 로그인후 사용해주세요.</div>;
MetaMask
가 설치되어 있다면 지갑이 연결되어있는지 확인후 안되있을경우App.js
에isLogin
이false
이므로MetaMask 로그인후 사용해주세요.
가 표시되고MetaMask
의 지갑 계정 중 연결할 계정을 선택하게 합니다.
- 지갑을 연결시켰다면
useWeb3.js
의const [account] = await getRequestAccounts()
로account
값을 가져오고 그 값을App.js
에게return
해주기에App.js
의account
값에 지갑 주소가 들어가고account
값이 변경되었으니useEffect
의 내용이 실행되어init
함수를 실행시켜 해당 지갑 주소의Balance
를 가져온후setIsLogin
을true
로 변경해주게 되고 아래의 내용을return
해주게 됩니다.
👉 App.js - useEffect()
useEffect(() => {
const init = async () => {
const balance = await web3?.eth.getBalance(account);
setBalance(balance / 10 ** 18);
};
init();
if (account) setIsLogin(true);
}, [account]);
👉 App.js - return
return (
<div>
<span>
<h2>{account}님 환영합니다.</h2>
<div>Balance : {balance} ETH</div>
</span>
<div>
<form onSubmit={handleSubmit}>
<input type='text' id='recived' placeholder='받을계정' />
<input type='number' id='amount' placeholder='보낼 금액' />
<input type='submit' value='전송' />
</form>
</div>
</div>
);
userWeb3.js
에서 받아온account
값과init
함수로 구해온 해당 지갑의balance
값을 가져와 화면을 리렌더링 해줍니다.
MetaMask
의 지갑 계정을 해당 페이지에 연결하면Ganache
네트워크의chainId
값과 현재 사용중인MetaMask
의 네트워크를 가져와 해당Ganache
네트워크chainId
가MetaMask
클라이언트에 이미 있는지 확인하고 없을경우에는 가져온Ganache
네트워크의chainId
로 새로운Network
를 추가하도록 요청합니다.
👉 Ganache 네트워크가 MetaMask에서 현재 사용중인지 확인
const targetChainId = '0x4bd'; // 내가 설정한 Ganache ChainId
const chainId = await getChainId(); // 사용중인 MetaMask Network의 chainId
if (targetChainId !== chainId) {
addNetwork(targetChainId);
}
👉 네트워크 추가 함수
const addNetwork = async (chainId) => {
const network = {
chainId: chainId,
chainName: 'suhwanGanache',
rpcUrls: ['http://127.0.0.1:8545'],
nativeCurrency: {
name: 'Ethereum', // 통화 이름
symbol: 'ETH', // 통화 기호
decimals: 18, // 통화 소수점 자리
},
};
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [network],
});
};
MetaMask
의 지갑 계정도 연결하고 네트워크도 추가했으면Ganache
의Test PrivateKey
를 가지고 임의의 지갑 2개를 추가해줍니다.MetaMask
에 지갑 추가하는 방법은 지난번 블로그 있습니다. 지갑을 추가하고 해당 페이지에 지갑들을 연결시켜주셔야 합니다.
- 임의의 지갑들을 추가했다면
Account2번
지갑에서Account3번
지갑으로ETH
를 송금하여Transaction
을 보내봅니다.Account3번
의 지갑 주소를 복사하고Account2번
으로 연결하고 화면 새로고침후 지갑 주소와 보낼 금액을 입력하고 전송 버튼을 클릭합니다.
👉 전송버튼 클릭시 Transaction을 발생시킬 함수
const handleSubmit = async (e) => {
e.preventDefault();
await web3.eth.sendTransaction({
from: account,
to: e.target.recived.value,
value: web3.utils.toWei(e.target.amount.value, 'ether'),
});
};
- 지난번
Transaction
을 보낼때처럼 다이렉트로 이더리움 네트워크로 보내느것이 아니라MetaMask
를 거쳐서 이더리움 네트워크로 요청을 보내기 때문에from
,to
,value
만 넣어주면 됩니다.
- 가스 수수료와 보내는 금액을 확인하고 아래의 확인 버튼을 클릭합니다.
- 지난번 일일이 가스
limit
과 가스price
와signature
등등을 다 구해서sendTransaction
을 했었는데MetaMask
를 이용하니 정말 간단합니다.
MetaMask
에Account2번
(발신자 지갑)을 확인해보면 보낸 금액30ETH
과 일정 가스비가 같이 빠져나간것을 확인할 수 있고Account3번
(수신자 지갑)은Account2번
지갑이 보낸30ETH
가 추가되어 있는것을 확인할 수 있습니다.