터미널에서 web3.js 를 이용해 지갑을 설정하고 컨트랙트의 함수를 실행시켜 보았다.
먼저, 간단한 컨트랙트를 작성하고 테스트 넷에 배포한다.
contract AAA {
uint public a = 100;
function setA(uint _a) public {
a = _a;
}
function add(uint _a, uint _b) public pure returns(uint) {
return _a+_b;
}
}
그 다음, 터미널에서 web3.js 가 설치된 폴더로 이동한 후 아래의 코드를 순차적으로 실행한다.
var {Web3} = require('web3')
var web3 = new Web3('INFURA API')
var privateKey = '0x PRIVATE KEY'
=> 개인 키 설정. 0x 로 시작해야 함.
var account = web3.eth.accounts.privateKeyToAccount(privateKey)
=> 개인 키를 이용해 지갑을 만들고 account 라는 변수로 설정.
web3.eth.accounts.wallet.add(account)
=> 지갑 관리.
var abi = [ABI]
=> CA 의 ABI 를 변수로 설정.
var c_address = 'CA'
=> CA 를 변수로 설정.
var contract = new web3.eth.Contract(abi, c_address)
=> ABI, CA 를 이용해 컨트랙트 변수를 설정.
contract.methods.a().call().then(console.log)
=> 위에서 불러온 컨트랙트의 a() 함수를 call. 상태변수 확인.
var tx = {from : account.address, to : c_address, gas : 300000, gasPrice : 3000000, data : contract.methods.setA(123456789).encodeABI()}
=> 함수를 실행 할 트랜잭션을 설정. setA(123456789) 로 실행.
var signPromise = web3.eth.accounts.signTransaction(tx, account.privateKey)
=> 위에서 설정한 트랜잭션과 개인키를 이용해 서명.
signPromise.then((signedTx)=>{var sentTx = web3.eth.sendSignedTransaction(signedTx.raw || signedTx.rawTransaction); sentTx.on("receipt",receipt=>{console.log(receipt)})})
=> 서명을 테스트 넷으로 보냄.
contract.methods.a().call().then(console.log)
=> 위에서 보낸 서명으로 상태변수 a가 바뀌었는지 확인.
이더리움의 논스는 각 계정에서 보내는 트랜잭션 마다 할당된 번호이다. 트랜잭션마다 논스는 1씩 증가한다.
왜 논스가 필요할까?
논스는 중복되지 않으며 순차적으로 증가하는데, 만약 같은 논스에 여러 트랜잭션을 받았을 경우, 해당 논스 중 가장 가스비를 많이 지불한 트랜잭션의 거래만을 처리한다.
이더리움에서는 이러한 방식으로 이중 지불 문제를 해결했다.
위에서 실습한 내용에서도 논스 에러를 마주쳤다.
이더리움에는 현재 내 컨트랙트가 논스 값이 1. 다음 논스 값은 2이어야 하지만 서명이 바뀌지 않았기에, 트랜잭션을 일으키면 too low nonce 라는 에러가 뜬다.
이더리움의 event 는 기록을 남기는 것으로, 이 기록을 읽거나 추적할 수 있다.
emit event명
형식으로 함수가 실행될 때, 이벤트를 기록할 수 있다.
이렇게 기록되는 이벤트들은 추적 할 수 있다. (listen 또는 monitoring..)
아래 컨트랙트를 작성하고 실험해보았다.
contract event_emit_two {
event ADD(string add, uint result);
event SUB(string sub, uint result);
event MUL(string mul, uint result);
event DIV(string div, uint result);
function add(uint a, uint b) public returns(uint c){
c = a + b;
emit ADD("Added", c);
// pure , view 는 emit 불가능.
}
function add2(uint _a, uint _b) public pure returns(uint){
return _a +_b;
}
function sub(uint a, uint b) public returns(uint c){
c = a + b;
emit SUB("sub", c);
// pure , view 는 emit 불가능.
}
function mul(uint a, uint b) public returns(uint c){
c = a + b;
emit MUL("mul", c);
// pure , view 는 emit 불가능.
}
function div(uint a, uint b) public returns(uint c){
c = a + b;
emit DIV("div", c);
// pure , view 는 emit 불가능.
}
}
먼저, event emit 을 기록하지 않는 함수 add2 를 실행했다.
아래 사진과 같이 함수가 call 되었지만 가스비는 소모하지 않았으며, logs 는 비어있다.
두 번쨰로, event emit 을 기록하는 함수 add 를 실행했다.
아래 사진과 같이 함수가 실행되며 EVM 을 통하므로 가스비가 소모된다. 또한, logs 에 event : ADD 라는 기록이 남게된다.
event 를 emit 할때는 pure 또는 view 는 사용할 수 없다.
EVM 을 통해 가스비를 소모하며 체인에 기록하기 때문이다.
web3.js 를 이용하여 event 를 추적하기.(monitoring 또는 listen..)
위에서 web3.js 를 이용하여 함수를 실행하고 상태변수의 값을 받아왔다.
이걸 응용해서 emit 을 하고, event 의 값을 변경이 있을 때마다 받아올 수 있다.
저번 게시물에서 infura 의 Https API 발급을 받았는데,
이번에는 WebSockets API 를 받는다.
위의 event emit 컨트랙트를 테스트넷에 배포한 후,
배포한 컨트랙트를 이용해 contract 변수 설정 까지.
(첫 번째 터미널 : https API) (두번 째 터미널 : websocket API)
contract.events.ADD({}, {fromBlock : 0 , toBlock : 'latest'}).on('data', function(event) {console.log(event);})
tx 를 add(1,3) 으로 설정.
var tx = {from : account.address, to : c_address, gas : 300000, gasPrice : 3000000, data : contract.methods.add(3,1).encodeABI()}
signPromise 로 서명.
var signPromise = web3.eth.accounts.signTransaction(tx, account.privateKey)
함수 실행.
signPromise.then((signedTx)=>{var sentTx = web3.eth.sendSignedTransaction(signedTx.raw || signedTx.rawTransaction); sentTx.on("receipt",receipt=>{console.log(receipt)})})
실행을 하면 아래와 같은 pending 이 뜬다. 전송이 끝날 때까지 기다린다.
전송이 완료되면 아래와 같은 사진이 뜬다.
tx 해시를 보자. (0x486f...)
그리고 두번 째 터미널을 확인하면, tx 해시가 같은 것을 볼 수 있고,
add(1,3) 함수의 값이 4n 으로 표시되어있다.
vscode 의 터미널에서 실행했다면 두번째 터미널에서는 자동으로 갱신이 되는데,
이렇게 파워쉘을 각각 열어서 실행했다면, 키보드의 아래화살표를 한번 눌러주면 갱신이 된다. (첫 번째 터미널의 pending 이 끝난 후)