1

First off, my setup.

  • System: ArchLinux

  • Ganache: v2.0.0 (correction, v1.3.1)

  • Solidity: 0.5.8

  • web3: 1.0.0-beta.37 (anything higher than 37, contract wont deploy)

  • Node: 'latest'


Some background and context info

I have a simple smart contract as shown below:

pragma solidity ^0.5.7;

contract CommerceChain {

    // ------------------------------------------------------
    // generic object events
    event onNewCategory(uint productId);

    // ------------------------------------------------------
    // Blockchain variables
    address owner;

    mapping (uint => Category) categoryMapping;
    uint[] categoryIndex;


    struct Category {
        uint id;
        uint name;
        uint description;
    }


    // ------------------------------------------------------
    // Function modifier
    modifier isOwner(){
        require(msg.sender == owner, "You are not the owner");
        _;
    }

    modifier notExistsCategory(uint id) {
        require(categoryMapping[id].id != id, "Category already exists");
        _;

    }

    // ------------------------------------------------------
    // Default methods
    constructor() public payable {
        owner = msg.sender;
    }

    function() external payable {

    }


    function addCategory(uint id, uint  _name, uint _description) public isOwner notExistsCategory(id) {
        categoryMapping[id] = Category(id, _name, _description);
        categoryIndex.push(id);
        emit onNewCategory(id);
    }
}

this is part of a larger contract

1. To start off, I get the contract instance by:

const Web3 = require('web3);
const web3 = new Web3(new Web3.providers.WebsocketProvider(_config.provider.url));

//assuming the contract already exists
let contract = new web3.eth.Contract(abi, address, {
    data: '0x' + byteCode,
    gas: gasLimit,
    gasPrice: gasPrice
});

2. then I subscribe to the contract event by:

(I have up till not tested 3 ways of subscribing to the event)

Method 1 from eth event

contract.events.onNewCategory({
    fromBlock: 0
},(error, event) => {
    if (error) {
        console.log(error);
    }
    if (event) {
        console.log(event);
    }
}).on('data', (event) => {
    console.log(event); // same results as the optional callback above
}).on('changed', (event) => {
    console.log(event);
}).on('error', (error) => {
    console.error(error);
});

Method 2 from all events

contract.events.allEvents({
    fromBlock: 0
}, (error, event) => {
    if (error) {
        console.error(error);
    }
    if (event) {
        console.log(event);
    }
});

Method 3 from eth subscriptions

const web3 = _web3Helper.getWeb3Instance();
let eventName = eventType.toString();
const eventJsonInterface = web3.utils._.find(contract._jsonInterface, o => o.name === eventName && o.type === 'event');
subscribedEvents[eventName] = web3.eth.subscribe('logs', {
    address: config.wallet.address
}, (error, result) => {
    if (!error) {
        const eventObj = web3.eth.abi.decodeLog(eventJsonInterface.inputs, result.data, result.topics.slice(1));
        callback(eventObj);
    }
}).on("data", (log) => {
    console.log(log);
}).on("changed", (log) => {
    console.log(log);
});

3. then I call:

contract.methods.addCategory(1, 2, 3).send({from: walletAddress}, callback)

to add a category to the contract.


Problem:

TL;DR

function addCategory(uint id, uint name, uint description);
event onNewCategory(uint id);

I can see the transaction is processed, in remix.ethereum.org and when testing with localhost Ganache RPC, and I do get a transaction receipt.

However, the event for the transaction never fires, ever! No error gets thrown from event callbacks.

Why does my event never fire?


Update

I tested with npm web3 versions. Please note that for each transaction/send call, (and where it is available), I implement all event handlers, calls and promises excluding the callback as a parameter. Reason being, when this is implemented, further calls to .on() are never triggered.

Thus I will have a format like:

 contract.deploy({
    data: '0x' + byteCode
}).send({
        from: fromAddress
    }).on('error', (error) => {
        if (error) {
            console.error(error)
        }
    }).on('transactionHash', (transactionHash) => {
        if (transactionHash) {
            console.log("Contract deployment completed [" + transactionHash + "]")
        }
    }).on('receipt', (receipt) => {
        if (receipt) {
            console.log(receipt);
        }
    }).on('confirmation', (confirmationNumber, receipt) => {
        if (confirmationNumber) {
            console.log(confirmationNumber);
        }
        if (receipt) {
            console.log(receipt);
        }
    }).then(_newContract => {
        if (_newContract) {
            callback(_newContract);
        }
    }, reason => {
        if (reason) {
            console.error(reason);
        }
    });
  • 1.0.0-beta.37

The output for the above function is Contract deployment completed [...] including the transactionReceipt. Ganache reflects the transaction.

All callbacks, events are triggered as expected.

  • 1.0.0-beta.54

The output for the above function is Contract deployment completed [...]. No further output is recieved.

However, Ganache reflects the transaction (or contract creation):

[11:18:32 PM] eth_sendTransaction

[11:18:32 PM] Transaction: 0x5bdcb28761e82dec84cec1b11faae8baef9a7595a4b537fd5a333de396c56ec5

[11:18:32 PM] Gas usage: 21000

[11:18:32 PM] Block Number: 1

[11:18:32 PM] Block Time: Tue May 07 2019 23:18:32 GMT+0200 (South Africa Standard Time)

and also performing a transaction between 2 wallets, either an error or transaction hash is caught, no other even fires.

Maybe 54 is broken? I experienced the same issues with 50 - 53 where transationHashes or errors are received, and events do not fire.

6
  • Could you show transaction receipt from Remix? May 7, 2019 at 20:57
  • I'd try updating ganache and/or running your code with geth's dev chain.
    – Ismael
    May 7, 2019 at 21:11
  • @MikhailVladimirov see update
    – CybeX
    May 7, 2019 at 21:23
  • @Ismael I may be mistaken but isn't this an error with Web3 specifically?
    – CybeX
    May 7, 2019 at 21:24
  • @Cybex I've had strange interactions between web3 and ganache and sometimes I'd have to downgrade one of them or both. But web3 v1 is broken for since beta 34-37 so I'd stick with that versions. Other options are to try etherjs or go back to web3 v0.20. Also you might want to try geth devchain which should be closer the real blockchain.
    – Ismael
    May 12, 2019 at 4:17

2 Answers 2

1

I'll refer just to the first part of your code, which already seems to be erroneous (I would have posted it as a comment to your question, but it wouldn't be very clear that way):

//assuming the contract already exists
let contract = new web3.eth.Contract(abi, address, {
    data: '0x' + byteCode,
    gas: gasLimit,
    gasPrice: gasPrice
});

Everything past the address argument is redundant. In fact, even if the contract does not exist and you wish to deploy it, the gas and gasPrice parameters are not passed correctly.

So here are a pair of functions at your disposal (tested with beta34, but hopefully compatible with beta37 as well):

async function deploy(contractName, contractArgs) { # contractArgs should be an array
    const abi = fs.readFileSync(YOUR_ARTIFACTS_PATH + "/" + contractName + ".abi");
    const bin = fs.readFileSync(YOUR_ARTIFACTS_PATH + "/" + contractName + ".bin");
    const contract = new web3.eth.Contract(JSON.parse(abi));
    const options = {data: "0x" + bin, arguments: contractArgs};
    const transaction = contract.deploy(options);
    const handle = await send(transaction);
    return new web3.eth.Contract(JSON.parse(abi), handle.contractAddress);
}

function deployed(contractName, contractAddr) {
    const abi = fs.readFileSync(YOUR_ARTIFACTS_PATH + "/" + contractName + ".abi");
    return new web3.eth.Contract(JSON.parse(abi), contractAddr);
}

If the contract is indeed already deployed, then all you need is function deployed above.

If you need to deploy it, then in order for function deploy to work, you'll need this function as well:

async function send(transaction) {
    const options = {
        to  : transaction._parent._address,
        data: transaction.encodeABI(),
        gas : (await web3.eth.getBlock("latest")).gasLimit
    };
    const signedTransaction  = await web3.eth.accounts.signTransaction(options, YOUR_PRIVATE_KEY);
    const transactionReceipt = await web3.eth.sendSignedTransaction(signedTransaction.rawTransaction);
    return transactionReceipt;
}

Note that in beta34 (and possibly also in beta37), function signTransaction modifies the contents of the options parameter.

So be sure not to call it more than once in this context (e.g., within a while loop which retries until completed successfully).

I haven't used gasPrice at all, which means that the current gas-price (i.e., web3.eth.getGasPrice()) will be applied; feel free to add it into your scheme...

2
  • thank you for your advice. I will implement it. Notably, I have not found any tutorial or post that has something similar to yours, thus conclude that these posts/tutorials are aimed at strictly beginners (incl me ;) ). That being said, regarding the signTransaction() function, is this what I am lacking i.e. the reason why my events aren't firing?
    – CybeX
    May 6, 2019 at 15:10
  • @Cybex: Since you haven't called this function anywhere in your code, I doubt that it is has anything to do with your issue. I just noted that, should you (or anyone else reading this) choose to use it. May 6, 2019 at 15:13
1

My mistake was a simple overlooked incorrect address.

In method 3 (which I ended up using, appears to be the most suitable solution for having multiple events), you will see the following snippet:

const web3 = _web3Helper.getWeb3Instance();
let eventName = eventType.toString();
const eventJsonInterface = web3.utils._.find(contract._jsonInterface, o => o.name === eventName && o.type === 'event');
subscribedEvents[eventName] = web3.eth.subscribe('logs', {
    address: config.wallet.address
}, (error, result) => {
    if (!error) {
        const eventObj = web3.eth.abi.decodeLog(eventJsonInterface.inputs, result.data, result.topics.slice(1));
        callback(eventObj);
    }
}).on("data", (log) => {
    console.log(log);
}).on("changed", (log) => {
    console.log(log);
});

The issue is at address: config.wallet.address.

The reason for the error is I am listening on my wallet's address for events. Thus, no events will be emitted as no events are linked to my wallet address.

What should be done is I should listen on the contract's address for the events.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.