Subscribing to a Solidity event from frontend
Asked Answered
R

1

6

I am trying to subscribe to a PurchaseMade event defined in Solidty from the frontend. I am not getting the expected results and need help with what I'm doing wrong.

Environment:

  • ganache-cli, Truffle
  • web3.js, React.js

Initialing contract Instance:

export const getContractInstance = () => {
    let web3Provider

    if (typeof window.web3 !== 'undefined') {
        // if metamask is on, web3 is injected...
    web3Provider = web3.currentProvider
    } else {
        // otherwise, use ganache-cli...
    web3Provider = new Web3.providers.HttpProvider('http://localhost:8545')
    }

    web3 = new Web3(web3Provider)

    return new web3.eth.Contract(CryptoKpopAbi, CONTRACT_ADDRESS)
}

Subscribing to PurchaseMade event

onBuy = (obj) => {
    web3.eth.subscribe("PurchaseMade", {}, () => {
        debugger
    });

    this.ContractInstance.methods.buy(1).send({
        from: this.state.currentUserAddress,
        gas: GAS_LIMIT,
        value: web3.utils.toWei(price.toString(), "ether"),
    }).then((receipt) => {
        console.log(receipt)
    }).catch((err) => {
        console.log(err.message)
    })
}

I get this warning when I call web3.eth.subscribe:

Subscription "PurchaseMade" doesn't exist. Subscribing anyway.

I get this error on tx receipt (after send()` succeeds

Uncaught TypeError: Cannot read property 'subscriptionName' of undefined

I used this official doc to setup the subscription

http://web3js.readthedocs.io/en/1.0/web3-eth-subscribe.html

Thank you in advance!

UPDATE:

Event declaration in contract

event PurchaseMade(uint objId, uint oldPrice, uint newPrice, string objName, address prevOwner, address newOwner);

Event call in contract

function buy(uint _tokenId) payable public {
  address prevOwner = ownerOf(_tokenId);
  uint currentPrice = tokenIdToPrice[_tokenId];

  ...

  PurchaseMade(_tokenId, currentPrice, newPrice,
    tokens[_tokenId].name, prevOwner, msg.sender);
}
Rhyner answered 4/2, 2018 at 13:10 Comment(1)
can you add the contract itself? or at least show the declaration of PurchaseMade eventFilia
N
6

You're attempting to subscribe to the event itself. The API lets you subscribe to an event type and add filters. The valid event types are:

  • pendingTransactions: Receive a subset of new transactions sent to the blockchain (Primarily used for miners who want to be selective of the transactions they process)
  • newBlockHeaders: Receive notification when a new block has been added to the blockchain.
  • syncing: Receive notification when node syncing starts/stops
  • logs: Receive notification on log updates on the blockchain. These are the events you're interested in.

Look at the API documentation for examples on how to use subscribe("logs").

The subscribe API is usually used to listen to events occurring across the blockchain. An easier approach for listening to events for a specific contract is to use events for the deployed contract (documentation). It's not much different than using subscribe above, but it already has the contract address and topic filters.

this.ContractInstance.events.PurchaseMade({}, (error, data) => {
  if (error)
    console.log("Error: " + error);
  else 
    console.log("Log data: " + data);
});

There's one important note, though. With web3 1.0, listening to events is not supported using HttpProvider. You have to use Websockets or IPC.

EDIT - I forgot to mention you can also get the events from the transaction receipt:

contractInstance.events.eventName.returnValues;
Neall answered 4/2, 2018 at 23:2 Comment(4)
Thanks! This is very helpful. The last important note is kind of surprising. Does that mean I cannot use Metamask as a provider if I want to subscribe to events? How are other people doing it? Is it possible to have two providers (one websocket provider just to subscribe to events, and metamask for function calls)?Rhyner
Yes, Infura supports WS connections (github.com/INFURA/infura/issues/29#issuecomment-356366477). And, yes, I would use two web3 instances, one with WS for subscriptions and one with HTTP for the rest. I’m not sure if the WS limitation is permanent (remember, web3 1.0 is still beta)...hopefully not.Neall
thanks man. I also posted another question to which I think you can shed some light to ;)Rhyner
Thank you @AdamKipnis! Your edit (contractInstance.events.eventName.returnValues;) does the job elegantly in my scenario!Slop

© 2022 - 2024 — McMap. All rights reserved.