如何订阅“日志事件”并在 ThunderCore 上获取通知?

Posted

技术标签:

【中文标题】如何订阅“日志事件”并在 ThunderCore 上获取通知?【英文标题】:How do I subscribe to `log events` and get notifications on ThunderCore? 【发布时间】:2020-08-31 21:10:34 【问题描述】:

我想订阅log 事件并在 ThunderCore 上接收通知时遇到一些问题。

据我所知,我似乎应该使用 websocket 和eth_subscribe。有例子吗??

或者,是否有其他解决方案可以实现我的目标?

【问题讨论】:

【参考方案1】:

通过以下方式获取有关 ThunderCore 上新合约事件的通知:

    使用 Websocket 连接到 RPC 节点,例如,https://testnet-rpc.thundercore.com 在logs 事件上调用eth_subscribe RPC 方法

独立示例

SimpleRecord.sol

pragma solidity ^0.4.25;

contract SimpleRecord 
    event Record(
        address indexed _from,
        uint _value
    );

    function write() payable public 
        emit Record(msg.sender, msg.value);
    

要生成Record 事件,请运行simple-record-write 以查看运行simple-record-log-subscribe 的通知:


const process = require('process')
const path = require('path')
const fs = require('fs')
const Web3 = require('web3')
const Accounts = require('web3-eth-accounts')
const util = require('util')

// truffle migrate --reset --network thunder-mainnet
const thunderWsUrl = 'wss://mainnet-ws.thundercore.com'
// truffle migrate --reset --network thunder-testnet
//const thunderWsUrl = 'wss://testnet-ws.thundercore.com'

const programName = () => 
    return path.basename(process.argv[1])


const web3Url = () => 
    let u = process.env['WEB3_PROVIDER_URI']
    if (u === undefined) 
        u = thunderWsUrl
    
    return u


const signTx = async (fromAccount, tx) => 
    const signedTx = await fromAccount.signTransaction(tx)
    return signedTx.rawTransaction // hex string


const setup = async () => 
    const privateKeys = fs.readFileSync(path.join(__dirname, '..', '.private-keys'), encoding: 'ascii').split('\n').filter(x => x.length > 0)
    const accounts = new Accounts()
    const account = accounts.privateKeyToAccount('0x' + privateKeys[0])
    const jsonBuf = fs.readFileSync(path.join(__dirname, '..', 'build', 'contracts', 'SimpleRecord.json'))
    const contractData = JSON.parse(jsonBuf)
    const contractAbi = contractData['abi']
    const web3ProviderUrl = web3Url()
    const web3 = new Web3(web3ProviderUrl)
    const networkId = await web3.eth.net.getId()

    let deployedNetwork, contractAddress
    try 
        deployedNetwork = contractData['networks'][networkId]
        contractAddress = deployedNetwork['address']
     catch (err) 
        msg = `error getting deployedNetwork: $err`
        throw new Error(msg)
    
    const contract = new web3.eth.Contract(contractAbi, contractAddress)
    return [ web3ProviderUrl, web3, networkId, contractAddress, contract, account ]


const prettyPrint = (o) => 
   return util.inspect(o, showHidden: false, depth: null, colors: true)


const recordWrite = async () => 
    const [web3ProviderUrl, web3, chainId, contractAddress, contract, fromAccount] = await setup()
    console.log('web3ProviderUrl:', web3ProviderUrl)
    const txnData = contract.methods.write().encodeABI()
    console.log('account.address:', fromAccount.address)
    const promiseResults = await Promise.all([
        web3.eth.getTransactionCount(fromAccount.address),
        web3.eth.getGasPrice(),
    ])
    const nonce = promiseResults[0]
    const gasPrice = promiseResults[1]
    const tx = 
        'gasLimit': 0,
        'chainId': chainId,
        'gasPrice': gasPrice,
        'nonce': Web3.utils.toHex(nonce),
        'from': fromAccount.address,
        'to': contractAddress,
        'value': 0xbeef,
        'data': txnData,
    
    const gasMultiple = 2.0
    tx.gasLimit = (await web3.eth.estimateGas(tx)) * gasMultiple
    console.log('tx:', prettyPrint(tx))
    const rawTxStr = await signTx(fromAccount, tx)
    const r = await web3.eth.sendSignedTransaction(rawTxStr)
    console.log('sendTransaction: receipt:', prettyPrint(r))
    return 0


const logSubscribe = () => 
    return new Promise((resolve, reject) => 
        setup().then(([web3ProviderUrl, web3, chainId, contractAddress, contract, account]) => 
            let eventCount = 0
            console.log('web3ProviderUrl:', web3ProviderUrl)
            console.log('contractAddress:', contractAddress)
            console.log('contract.options.jsonInterface:', prettyPrint(contract.options.jsonInterface))
            const eventAbis = contract.options.jsonInterface.filter((abiObj) => abiObj.type === 'event')
            web3.eth.subscribe('logs',  address: contractAddress , (err, log) => 
                console.log('eth.subscribe("logs") callback')
                if (err) 
                    console.log('logs callback, err:', err)
                    reject(err)
                    return
                
                eventCount++
                console.log(`log[$eventCount]:`, log)
                const eventSig = log.topics[0]
                for (let abi of eventAbis) 
                    if (eventSig === abi.signature) 
                        const decoded = web3.eth.abi.decodeLog(abi.inputs, log.data, log.topics.slice(1))
                        console.log('Decoded Event:', prettyPrint(abi), '\n', prettyPrint(decoded))
                        resolve(0)
                        return
                    
                
            )
        )
    )


(async () => 
    if (programName().endsWith('-write')) 
        process.exit(await recordWrite())
     else if (programName().endsWith('-log-subscribe')) 
        process.exit(await logSubscribe())
     else 
        console.error(`unsupported program name: "$programName()"`)
        process.exit(2)
    
)()

field-support repo 的subscribe-to-logs 分支中查看完整的项目here。

【讨论】:

【参考方案2】:

listen_to_thundercore_mainnet.js

/**
 * Connect to Thundercore mainnet and listen for tips to a specific smart contract address.
 */

const Web3 = require('web3');
var provider = 'wss://mainnet-ws.thundercore.com';
var web3 = new Web3(provider);


let listenOnce = () => 
  // Create a connection to Thundercore mainet.

  const smartContractAddress = '0xb7d82e5B73e01bB4c1cFB1448f1215bf165929a2'

  var subscription = web3.eth.subscribe('logs', 
    address: smartContractAddress,
  , (error, result) => 
    if (error) 
      console.log(error)
      console.log('Reconnecting to Thundercore mainnet.')    
      setTimeout( () => 
        // Re-instantiating web3 is needed in case the connection is dropped.
        web3 = new Web3(provider);
        _listen()
      , 5000);
    
  )
  .on('connected', (subscriptionId) => 
    console.log('connected')
  )
  .on('data', (log) => 
    console.log(log);
  )
  .on("changed", (log) => 
    console.log('changed')
  )
  
  return subscription


const _listen = async () => 
  let tips = listenOnce();
  console.log('Connected to Thundercore mainnet.')

  // web3 subscription times out at 60 secs. Close and reopen at 50 secs.
  setInterval( () => 
    tips.unsubscribe( (error, success) => 
      if(error) 
        console.log('Failed to disconnect from Thundercore mainnet!');
      
      if(success) 
        console.log('disconnected');
      
    );
    tips = listenOnce();
  , (50 * 1000));



_listen()

使用 NodeJS 运行

node listen_to_thundercore_mainnet.js

Thundercore 会在 60 秒后断开您的连接。所以我们在此之前故意断开连接并立即重新连接。 这种方式在跟踪流量大的合约时会漏掉一些交易。

【讨论】:

以上是关于如何订阅“日志事件”并在 ThunderCore 上获取通知?的主要内容,如果未能解决你的问题,请参考以下文章

如何仅在输入模糊时触发反应表单 valueChanges 订阅并在 Angular 2 中输入键

在我当前订阅结束之前升级到更高的订阅时,订阅如何工作?

如何制作响应 ubuntu 14.04 和 16.04 上的 syslog 事件的 c# 程序?

如何取消 Stripe 计划中的所有订阅?

html 使用AJAX订阅Campaign Monitor列表,并在成功时订阅真正基本的动画

如何立即触发 apollo graphql 订阅?