Event - (23/05/25)

nazzzo·2023년 5월 26일
0
	// solidity
    function increment() public returns(uint256){
        value += 1;
        return value;
    }

	// javascript
	// getTransaction
    const increment = async () => {
        const newTx = await deployed.methods.increment().send({ from: account })
        console.log(`getTransaction:`, newTx)
    
    // getTransactionReceipt  
    const decrement = async () => {
        await deployed.methods.decrement().send({ from: account }).then(console.log)

지난번에 만든 카운터 기능을 약간 고치고 싶어서 컨트랙트의 카운터 증감 함수에 리턴값을 추가해봤는데,
콘솔로는 이 리턴값을 확인할 수 없었습니다


트랜잭션 객체의 데이터 속성에 리턴값이 들어있을 줄 알았지만..
알고보니 send()로 트랜잭션을 전송했을 경우에
getTransaction으로는 함수의 실행 결과를 바로 반환받을 수 없는게 당연했습니다
트랜잭션은 비동기적으로 처리되고, 블록체인에서 트랜잭션이 포함된 블록을 기다려야 하기 때문입니다

반면then을 사용하면 블록체인에 포함된 객체를 리턴하는데,
이 경우에도 제가 지정한 리턴값은 담겨있지 않았습니다

그렇다고 해서 call 메서드만으로 상태 업데이트를 처리하려면
앱을 새로고침(call())하지 않는 이상 모두가 최신 상태를 실시간으로 공유할 수 없다는 문제가 생기겠죠
어떠한 다른 해결법을 구해야 했습니다

Event


이더리움에서 말하는 이벤트는 블록체인 네트워크의 블록에 특정값을 기록하는 기법을 말합니다
(트랜잭션 실행과 관련된 데이터, 상태변화 등)

이더리움에서 트랜잭션이 발생하면, 해당 트랜잭션이 성공과 실패 여부에 상관없이 트랜잭션 영수증(receipt)을 발행합니다
그리고 코드 실행 중에 이벤트가 발생했을 경우에 그 내용이 컨트랙트 내에서 정의한 방식대로
트랜잭션 영수증 객체에 포함되어 저장됩니다
이벤트는 로그들을 객체화한 형태로 영수증 객체의 logs 속성에 담기게 됩니다


이제 이벤트가 발생할 수 있도록 컨트랙트 코드를 수정해보겠습니다

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Counter {
    uint256 value;
    event Count(uint256 count);

    constructor(){}

    function get() public view returns(uint256) {
        return value;
    }
    function increment() public {
        value += 1;
      	emit Count(value);
    }
    function decrement() public {
        value -= 1;
        emit Count(value);
    }
}

Count라는 이벤트를 등록하고, incrementdecrement 함수가 호출될 때 이벤트가 발생하도록 했습니다
이벤트가 발생하면 업데이트된 value를 전송(emit)합니다

이제 이 value가 영수증 객체에 담겨있는지를 확인해볼 차례입니다


트러플 콘솔을 통해서 영수증에 담긴 이벤트 데이터 확인하기

// 콘솔 열기
npx truffle console --network goerli

// 영수증 출력
new web3.eth.getTransactionReceipt('0xc782e00bf256c69e5e08ee97ae8e51c70b1630546e97778044560704d9db9c46')


//
{
  blockHash: '0xe8141dccc1d0a7f49143c695d356b3f979b0c324d94a904c3a1ff2e017e10099',
  blockNumber: 9060079,
  contractAddress: null,
  cumulativeGasUsed: 3472955,
  effectiveGasPrice: 2726300271,
  from: '0xfa2e0fbbe4...',
  gasUsed: '0x6c4f',
  // 이벤트 데이터(최신 상태의 value)는 logs > data 속성에 16진수 형태로 담겨있습니다  
  logs: [
    {
      address: '0xD518c24...',
      blockHash: '0xe8141dccc1d0a7f49143c695d356b3f979b0c324d94a904c3a1ff2e017e10099',
      blockNumber: 9060079,
      data: '0x0000000000000000000000000000000000000000000000000000000000000003',
      logIndex: 12,
      removed: false,
      topics: [Array],
      transactionHash: '0xc782e00bf256c69e5e08ee97ae8e51c70b1630546e97778044560704d9db9c46',
      transactionIndex: 9,
      id: 'log_1a886bfe'
    }
  ],
  logsBloom: '0x00000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000010000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  status: true,
  to: '0xd518c24c0170f4138e38f6b50568542e12a02a1b',
  transactionHash: '0xc782e00bf256c69e5e08ee97ae8e51c70b1630546e97778044560704d9db9c46',
  transactionIndex: 9,
  type: '0x2'
}



그리고 클라이언트 측에서도 이벤트를 구독(subscribe())해서 카운터 값의 변화를 실시간으로 수신할 수 있습니다

web3.eth.subscribe(type [, options] [, callback]);

subscribe() 함수는 이더리움 네트워크에서 발생하는 이벤트를 구독하는 데 사용됩니다
다음과 같은 인자를 받을 수 있습니다

  • type: 구독할 이벤트의 종류를 지정하는 매개변수입니다. 가장 일반적인 값으로는 "logs"를 사용합니다. 이는 스마트 컨트랙트의 이벤트 로그를 구독하고자 할 때 사용됩니다.

  • options (선택적): 구독 옵션을 지정하는 객체입니다
    - address: 구독할 이벤트의 발생 주소를 지정합니다.
    특정 스마트 컨트랙트의 이벤트만 구독하고자 할 때 사용됩니다

  • callback (선택적): 이벤트가 발생할 때 실행되는 콜백 함수입니다
    콜백 함수는 구독된 이벤트에 대한 정보를 인자로 받습니다


import CounterContract from "../contracts/Counter.json"
...
    const [count, setCount] = useState(0);
    const [deployed, setDeployed] = useState(null);
    const [loading, setLoading] = useState(true);
...

    useEffect(() => {
        if (!web3 || !account) return;
        
        const contractAddress = "0xD518c24C0170f4..."
        const Deployed = new web3.eth.Contract(CounterContract.abi, contractAddress);
        setDeployed(Deployed);
      
        // subscribe() : 구독할 이벤트의 종류는 "logs", 옵션으로 이벤트의 발생 주소를 지정합니다
        // on() :  이벤트가 발생할 때마다 콜백함수를 실행합니다
        web3.eth.subscribe('logs', { address: contractAddress })
          .on("data", log => {
            const params = [
              {
                indexed: false,
                internalType: "uint256",
                name: "count",
                type: "uint256"
              }
            ];
      
            const value = web3.eth.abi.decodeLog(params, log.data);
            console.log(value);
            setCount(value.count);
          });
      
        Deployed.methods.get().call().then(value => {
          setLoading(false);
          setCount(value);
        });
      }, []);

+) then함수로도 이벤트가 담긴 영수증 객체를 반환받을 수 있었습니다
꽁꽁 쌓여있긴 하지만 returnValues 안에 10진수 변환이 끝난 형태로 담겨있네요

    const handleClick = async (type) => {
        if (deployed === null) return console.log("no deployed")
        await deployed.methods[type]().send({ from: account }).then(({events}) => {
            // console.log(events)
            setCount(events.Count.returnValues.count)
        })
    }

0개의 댓글