항상 블록체인 관련 개발은 기존의 웹개발과 다르다는 생각을 하며 접근하고 있다. 그리고 이번에도 스마트컨트랙트와 디앱을 연결하면서 맞딱뜨린 개념, Provider가 있었다. Provider가 있어야만 비로서 작동하는 디앱과 배포된 스마트 컨트랙트를 연결시켜 상호작용 할 수 있다. 그게 아니라면 사실상 내가 이더리움의 클라이언트가 되서 스마트 컨트랙트를 연동하는 방법이 있는 것 같은데, 이번에는 가볍게 발걸음 떼는 방법부터 배우려고 한다.
EIP-1193에서 소개하는 Provider 의 개념은 다음과 같다.
A JavaScript object made available to a consumer, that provides access to Ethereum by means of a Client.
직역: 클라이언트가 사용하는 툴들을 이용하여 컨슈머가 이더리움에 접근가능하게 만드는 자바스크립트 객체
이더리움 EIP-1193 문서 중
이더리움 스마트 컨트랙트를 이용한 웹개발을 하게 되면, 클라이언트
라는 개념이 이더리움 생태계에서 하나, 웹개발 구조에서 하나 존재하여 동명이념(?)으로 존재한다. 그래서 클라이언트라는 말이 나왔을 때, 이것이 블록체인 쪽의 개념인지, 웹서비스 쪽의 개념인지 구분할 필요가 있다. 그렇다면 위의 Provider 개념에서 등장하는 클라이언트는 체인 쪽의 개념이라 이해할 수 있다.
- 웹서비스의 클라이언트 : 서비스를 제공받는 유저 혹은 유저가 사용하고 있는 단말기 혹은 단말기에 띄어져 있는 화면 구성
- 블록체인의 클라이언트 : 블록체인 참여자로서, 체인에 대한 Remote Procedure Call(RPC)가 요청되면 체인에서 작업을 한 이후에 결과를 반환하는 노드
EIP-1193 상에서 지적하는 바는 다음과 같다.
현재 대부분의 dApp은 키 관리 소프트웨어(이를테면 메타마스크 같은 '지갑')이 제공하는 API를 사용하는 경우가 많다고 한다. 하지만 이더리움만 해도 지갑의 종류가 다양하기 때문에, 각각의 지갑이 제공하는 인터페이스와 기능들이 서로 충돌이 나는 경우가 있다.
지갑끼리도 서로 제공기능을 공유할 수 있게, 그리고 개발자들의 편의를 돕기 위해 표준을 제공하려고 하는 듯 하다.
사실 나는 크롬 콘솔창에서 ethereum을 치면 무언가 객체가 나오기에, "벌써 브라우저도 web3를 받아들일 준비하고 있구나"라고 착각하게 되었다. 사실 크롬 익스텐션으로 설치되어 있는 메타마스크가 제공하는 인터페이스 였다는 것을 안 것은 최근이다.
이 ethereum 객체가 제공하는 request 메서드를 이용하면 기존 web3가 제공하는 기능들을 지갑 소프트웨어를 거쳐 이용할 수 있다.
특히 스마트 컨트랙트의 함수를 호출하는 트랜잭션은 data 필드만 필요한 값을 넣어 보내면 되기 때문에, 사실상
eth_sendTransaction
만 요청할 수 있으면 모든 스마트 컨트랙트와의 상호작용을 Provider를 통해서 가능하게 된다.
아래는 배포한 스마트 컨트랙트를 web3 라이브러리와 메타마스크를 이용하여 함수를 호출하는 이벤트 핸들러다. 스마트 컨트랙트의 함수와 인자값을 bytecode로 변경하고, 메타마스크 provider로 트랜잭션에 값을 담아 전송한다.
[스마트 컨트랙트 함수 호출 이벤트핸들러]
// Web3 생성 - 테스트 네트워크
const web3 = new Web3("ws://localhost:8545");
// 컨트랙트 생성
const Contract = new web3.eth.Contract(contractABI, contractAddress);
// 핸들러
const clickHandler = async ()=>{
// data 는 최종적으로 bytecode로 바뀐 함수 호출 명령을 갖는다.
const data = Contract.methods.methodName(param1,params2).encodeABI();
// 트랜잭션 옵션 초기화
const tx = [{
from: window.selectedAddress,
to: Contract._address,
value : willToPay,
data,
}]
// 메타마스크 provider로 컨트랙트 호출 트랜잭션 전송하기
const result = await window.ethereum.request({method:'eth_sendTransaction', params: tx})
.catch(err=>{
if (err.code === 4001) console("user denied");
else return err;
});
// 잘 전송되었을 시, 트랜잭션 주소가 반환된다.
console.log(result);
}