Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getTransactionReceipt error #69

Closed
jennazenk opened this issue Nov 17, 2017 · 17 comments
Closed

getTransactionReceipt error #69

jennazenk opened this issue Nov 17, 2017 · 17 comments
Assignees
Labels
bug Verified to be an issue. fixed/complete This Bug is fixed or Enhancement is complete and published.

Comments

@jennazenk
Copy link

jennazenk commented Nov 17, 2017

await provider.waitForTransaction(transaction.hash);
const receipt = await provider.getTransactionReceipt(transaction.hash);

When I log receipt from getTransactionReceipt, I get this error:

Error: invalid transaction receipt - exactly one of status and root should be present

What am I doing wrong?

My provider is a parity node running on Kovan (I used providers.JsonRpcProvider)

@ricmoo
Copy link
Member

ricmoo commented Nov 17, 2017

That is interesting. I should have time today/timorrow to look into the receipt issues.

That isn’t your fault, it means the node is returning a root (pre-Byzantium) and staus (post-Byzantium) parameters, which it isn’t supposed to do.

There is an easy fix, I will just allow both.

@ricmoo ricmoo self-assigned this Nov 17, 2017
@ricmoo ricmoo added the bug Verified to be an issue. label Nov 17, 2017
@ricmoo
Copy link
Member

ricmoo commented Nov 17, 2017

Fixed in e8b23c2.

Once Travis CI finishes running the tests cases, I will publish to NPM and close this ticket.

@ricmoo
Copy link
Member

ricmoo commented Nov 17, 2017

Published to npm.

Please make sure the version you sync to is ethers-providers@2.1.10. If you blow away your package-lock.json and node_modules/ and install, you will get it from the umbrella package.

Thanks again!

@ricmoo ricmoo closed this as completed Nov 17, 2017
@jennazenk
Copy link
Author

Awesome Richard! Thanks a lot!

@jennazenk
Copy link
Author

Hello @ricmoo ! Thanks for the quick fix! It indeed worked, I now get the transaction receipt, however for some reason I only get the first event ("Mined"), and I expect to have a second one after that. I can see on etherscan that my transaction went through as expected, so I think I should have the second event log on the receipt. Do you have any idea what I could be doing wrong here?

@ricmoo
Copy link
Member

ricmoo commented Nov 20, 2017

Heya @jennazenk,

Can you send a transaction hash? You mean the transaction on Etherscan has 2 event logs, but the receipt returned only has 1?

The type of all logs, I believe is “mined”; I haven’t noticed other values, but don’t usually pay attention to receipts. Do the topics match what you expect?

@jennazenk
Copy link
Author

Hey Richard! Thanks for coming back to me that fast! So after more investigation, I have only one event, which is called "FundUpdated", which I see on etherscan/parity ui/ and on this receipt. In the transaction receipt, I think i get the topic so the keccak256 of my event signature if Im not mistaking.

In web3, on the transaction receipt I used to be able to see the event name and data directly, as per below (eg. logs.event and logs.args) :

screenshot from 2017-11-20 17-39-48

So my question is, how do I look for a specific event (like "FundUpdated") in a receipt ? I think if I run keccak256 on the signature of my event and compare it with the topic, I can determine if thats the event I am looking for. Are you aware of a better way for me to do this ?

@ricmoo
Copy link
Member

ricmoo commented Nov 20, 2017

That is only possible because the web3 instance you are using above has the ABI. Otherwise, you are correct, there is no way to go from the keccak256(name(params)) => name(params) (the non-anonymous functions the first topic is this keccak256(name)).

You can do something like the following in ethers:

var contract = new ethers.Contract(contractAddress, contractInterface, provider);
contract.onfundupdated = function(someParam) {
    console.log(someParam);
    // 0x00000...000ca
}

But from just a receipt you would need a list of all possible events and parameters you expect.

Do you need to work against the receipt?

@jennazenk
Copy link
Author

Yes we work a lot against the receipts; for the topics/event name I have a list of all relevant events so I can do it on my end. However, receipt.data is also encoded on the receipt; would you be ok with a PR combining the functionality of Interface.decodeParams and getTransactionReceipt, on the Contract Instance?

@ricmoo
Copy link
Member

ricmoo commented Nov 20, 2017

The contract interface already has that functionality. :)

I’m off to a client right now, but will post the code needed to parse data and topics this afternoon. I’m doing a slight refactor of them at code right now as well as running it against a new dataset of 2000-ish test cases.

@jennazenk
Copy link
Author

Alright cool, will wait for your update!

The reason why we work w the receipt and not the event listener on the contract instance is because we need to make sure we get the event linked to a specific transaction (not sure if I'm being v clear here..); do you think that this use case will be addressed in a future refactoring?

Thanks a lot!

@ricmoo
Copy link
Member

ricmoo commented Nov 21, 2017

Here is an example of how to use the event part of the interface directly.

> var abi = [ { type: 'event', inputs: [ { type: 'uint256', name: 'something' } ], name: 'FundUpdated' } ];
> var i = new ethers.Interface(abi)
Interface {
  abi: [Getter],
  functions: {},
  events: { FundUpdated: { [Function: func] inputs: [Getter] } },
  deployFunction: { [Function: func] inputs: [Getter] } }
> var info = i.events.FundUpdated()
EventDescription {
  inputs: [ { type: 'uint256', name: 'something' } ],
  name: 'FundUpdated',
  signature: 'FundUpdated(uint256)',
  topics: 
   [ '0xe34c389652410c46bb438dd3b75c4e2665251c032d7cf198239862e556751e6a' ],
  parse: [Function] }
> var topics = [ '0xe34c389652410c46bb438dd3b75c4e2665251c032d7cf198239862e556751e6a' ];
> var data = '0x00000000000000000000000000000000000000000000000000000000000000ca'
> info.parse(topics, data)
Result {
  '0': BigNumber { _bn: <BN: ca> },
  something: BigNumber { _bn: <BN: ca> },
  length: 1 }

Right now it requires the topics and data, but soon it will support only data; in which case it will only return non-indexed parameters. But receipts provide both these, so I think it should do for your purposes.

As you can see, it also created the info.topics[0] you need to pay attention to for your event name.

Let me know if there is anything else you think should be there.

@jennazenk
Copy link
Author

Thanks again Richard! Indeed, it was possible for us to parse the interface for an event and get our data with a custom 'decodeReceiptForEvent' function. However, we were thinking, would it be interesting to have a method on the contract instance that directly decodes topics and associated data, and returns a readable receipt?

@ricmoo
Copy link
Member

ricmoo commented Nov 21, 2017

Do you mean that you want to add something like:

contract.parseTransactionReceipt(receipt);
/*
{
    event: "FundUpdated",
    args: {
       id: 202
    }   
}
*/

And it uses the topics[0] to look up the matching function and calls parse with the topics and data? That would be fairly simple to add; but might have ambiguous results in the case of anonymous events.

This might make more sense as a recipe in the cookbook.

@mrwillis
Copy link

Hi @ricmoo , is there any simpler way now to parse events from receipts other than using the interface directly as you illustrate in your previous post?

@naddison36
Copy link

@mrwillis this is how I parse events in the transactions receipt.

/**
 * Parses transaction events from the logs in a transaction receipt
 * @param {TransactionReceipt} receipt Transaction receipt containing the events in the logs
 * @returns {{[eventName: string]: TransactionEvent}} an object with each key-value pair being the event name and event object
 */
protected getTransactionEvents(receipt: TransactionReceipt): {[eventName: string]: TransactionEvent}
{
	const txEvents: {[eventName: string]: TransactionEvent}  = {}

	// do not get events if the contract has not been set yet. This will be true for deploy transactions which can emit events
	if (!this.contract) {
		return txEvents
	}

	// for each log in the transaction receipt
	for (const log of receipt.logs)
	{
		// for each event in the ABI
		for (const abiEvent of Object.values(this.contract.interface.events))
		{
			// if the hash of the ABI event equals the tx receipt log
			if (abiEvent.topics[0] == log.topics[0])
			{
				// Parse the event from the log topics and data
				txEvents[abiEvent.name] = abiEvent.parse(log.topics, log.data)

				// stop looping through the ABI events
				break
			}
		}
	}

	return txEvents
}

@ricmoo
Copy link
Member

ricmoo commented Jul 25, 2018

If you use v4, interface.parseLog will figure out which event it is and parse it for you, including the name, function signature, topic and parsed values.

There is also an interface.parseTransaction that will look up what function and what values the transaction represents.

So, you can directly access contract.interface.parseLog if you have a contract, or you can just instantiate an Interface if you are processing the blockchain in general.

@ricmoo ricmoo added the fixed/complete This Bug is fixed or Enhancement is complete and published. label Jan 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Verified to be an issue. fixed/complete This Bug is fixed or Enhancement is complete and published.
Projects
None yet
Development

No branches or pull requests

4 participants