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

Constant contract method with arguments #547

Closed
mhoangvslev opened this issue Jun 19, 2019 · 15 comments
Closed

Constant contract method with arguments #547

mhoangvslev opened this issue Jun 19, 2019 · 15 comments
Labels
discussion Questions, feedback and general information.

Comments

@mhoangvslev
Copy link

mhoangvslev commented Jun 19, 2019

Hello,

I have been struggling trying to get returned results by contract's method with parameter. For example, let's try the method getKitty(_id: uint256) of CryptoKitties contract with _id = 1590560

I tried different ways but the reponse is always:
Could not retrive result of ContractCall: Error: invalid input argument (arg="_id", reason="invalid number value", value=[THE_VALUE], version=4.0.30) where THE_VALUE is:

  • Attempt 1: BigNumber object,
  • Attempt 2: BigNumber.toHexString()
  • Attempt 3: BigNumber.toNumber()
  • Attempt 4: BigNumber.toString()

Could you please help me?

@ricmoo
Copy link
Member

ricmoo commented Jun 19, 2019

Can you please include a code snippet? :)

@ricmoo ricmoo added the discussion Questions, feedback and general information. label Jun 19, 2019
@mhoangvslev
Copy link
Author

mhoangvslev commented Jun 19, 2019

I will resume the long script into:

// Given the sucessful initialisation 
// Given the event Birth
const logs = await this._provider.getLogs({
            fromBlock,
            toBlock,
            address: this._contractAddr,
            topics: [this._event.topic]
        }).catch(() => { });
const result = await Promise.all(logs.map((logEntry) => this.extractData(logEntry)))

// In extractData(logEntry)...
const data = this._event.decode(logEntry.data, logEntry.topics);
const kittyId = data["kittyId"]; // returns BigNumber

const formattedId = kittyId //Atttempt 1
// const formattedId = kittyId.toHexString() //Atttempt 2
// const formattedId = kittyId.toNumber() //Atttempt 3
// const formattedId = kittyId.toString() //Atttempt 4

const kittyInfo = await contract.functions.getKitty(formattedId);

// error

@ricmoo
Copy link
Member

ricmoo commented Jun 19, 2019

Can you console.log the logEntry, data and kittyId? Just to make sure they are all what is expected?

Also, can you throw in the line where you create the Contract? Just want to double check the ABI. :)

Because it otherwise looks fine, I think. And I’ve definitely used ethers with CryptoKitties (have you seen ethers-meow, the CLI for CK?). :)

@mhoangvslev
Copy link
Author

mhoangvslev commented Jun 19, 2019

@ricmoo

Can you console.log the logEntry, data and kittyId? Just to make sure they are all what is expected?

logEntry

{
address: "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d"
blockHash: "0x4fbf27b6123517d1bad96f4fab4e62406e733ffbf58ba3f8ac2e3e9b656a4a48"
blockNumber: 7925481
data: "0x0000000000000000000000007e8bd3fba239748c76673cd69332bfa4d06fc904000000000000000000000000000000000000000000000000000000000018ac610000000000000000000000000000000000000000000000000000000000000847000000000000000000000000000000000000000000000000000000000009b68a00004bd2929ce748dc17a52b28d8518c86410c302886298a74144b7a14c2b56f"
logIndex: 15
removed: false
topics: Array(1)
0: "0x0a5311bd2a6608f08a180df2ee7c5946819a649b204b554bb8e39825b2c50ad5"
length: 1
__proto__: Array(0)
transactionHash: "0x43eca3d0918c10658e989a183565c275c8486890db4db19d8073537871324c12"
transactionIndex: 32
__proto__: Object
}

data

{
0: "0x7e8bD3FbA239748c76673CD69332bFA4D06fC904"
1: t {_hex: "0x18ac61", _ethersType: "BigNumber"}
2: t {_hex: "0x0847", _ethersType: "BigNumber"}
3: t {_hex: "0x09b68a", _ethersType: "BigNumber"}
4: t {_hex: "0x4bd2929ce748dc17a52b28d8518c86410c302886298a74144b7a14c2b56f", _ethersType: "BigNumber"}
genes: t {_hex: "0x4bd2929ce748dc17a52b28d8518c86410c302886298a74144b7a14c2b56f", _ethersType: "BigNumber"}
kittyId: t {_hex: "0x18ac61", _ethersType: "BigNumber"}
length: 5
matronId: t {_hex: "0x0847", _ethersType: "BigNumber"}
owner: "0x7e8bD3FbA239748c76673CD69332bFA4D06fC904"
sireId: t {_hex: "0x09b68a", _ethersType: "BigNumber"}
_ethersType: "Description"
__proto__: v
}

kittyId

Since I formatted it so I can persist to my DB, it looks like this:
Please note that I used:

ethers.utils.bigNumberify(kittyId.toHexString()).toNumber()
{
blockheight: 7925481
eventTime: DateTime {year: 2019, month: 6, day: 9, hour: 17, minute: 16,}
genes: "523308004950315299488617992418535319508765580468225957764634777726661999"
kittyId: 1616993
matronId: 2119
owner: "0x7e8bD3FbA239748c76673CD69332bFA4D06fC904"
sireId: 636554
}

Also, can you throw in the line where you create the Contract? Just want to double check the ABI. :)

// Given the correct Provider, Address, and ABI thanks to copy-paste precision
this._contract = new ethers.Contract(copyPastedAddr, copyPastedABI, InfuraProvider);

have you seen ethers-meow, the CLI for CK

I just discovered it: it looks nice! I am doing network analysis with smart-contract events data. Highly experimental also :)

@mhoangvslev
Copy link
Author

mhoangvslev commented Jun 20, 2019

@ricmoo I tracked down the issue to the line let v = bigNumberify(value); in encode(value: BigNumberish): Uint8Array of abi-coder.ts

The value variable was:

[t]
0: t
_hex: "0x189993"
_ethersType: "BigNumber"
__proto__: Object
length: 1
__proto__: Array(0)

@mhoangvslev
Copy link
Author

I found the solution, it was my fault all along! I wanted to call the function name with an array of argument. The above mentioned function caught that.

Solution:

this._contract.functions[funcName].apply(null, args) as Promise<any>

@mhoangvslev
Copy link
Author

mhoangvslev commented Jun 26, 2019

@ricmoo I have another question. apply() takes the first argument as this. When I call a contract's method, what should be this? I believe the answer is contract itself.

@ricmoo
Copy link
Member

ricmoo commented Jun 26, 2019

In most cases that is correct (although it would be whatever you are calling the function on).

Since this is a meta-class, the functions are pre-bound with the contract passed in, so it matters less, as those functions do not use this. I think. Are you having problems with it?

@mhoangvslev
Copy link
Author

I get a lot of invalid json reponse error somehow. So you are saying that contractFunc.apply(null, args)and contractFunc(contract, args) yield the same result?

@ricmoo
Copy link
Member

ricmoo commented Jun 26, 2019

Possibly, but I’m not sure. I’ve never passed in null like that, but I think it should be fine. But invalid JSON responses shouldn’t be related. Can you add provider.on("debug", console.log) to see what the problems are?

@mhoangvslev
Copy link
Author

You are right, the result doesn't change from one to another. I think it's the JSON RPC Provider (ie Infura) that cracks if I ask too much from it. Here I extract Birth events from 1st June to 4th June: for each Birth event I want to call getKitty on the kittyId attribute. I have 1000-ish events at the end.

@ricmoo
Copy link
Member

ricmoo commented Jun 28, 2019

If you are hammering any of the backend services, they will certainty throttle you. If you need to mine a substantial amount of data, you can either run your own node, or use one of the various services that provide an instant node to query without throttling. Or you can add throttling yourself, not hitting the backend more than 3 times per second.

But like any web service, they will prevent you from DoS-ing. :p

@mhoangvslev
Copy link
Author

mhoangvslev commented Jun 28, 2019

use one of the various services that provide an instant node to query without throttling

Do you have any name in mind? I use Infura backed with DataSmith, the only free Provider as far as I know.

Or you can add throttling yourself, not hitting the backend more than 3 times per second.

There must be a good way to estimate the agony I rain down on Infura, i.e getLogs * nbConteactCalls.

@ricmoo
Copy link
Member

ricmoo commented Jun 28, 2019

Sure, check out QuikNode which spins up a node for you, or VIPNode, which provides access to incentivized parties to run full nodes in light mode.

I haven't used either myself yet, but both seem like very promising services, that are affordable, especially for projects that need intermittent access. I imagine these being used to spin up and back-fill historic data, and then a simple INFURA/Etherscan connection can be queried every block to keep the database up to date.

The new v5 provider is a bit smarter about load-balancing across the backends, but also queries multiple ones to ensure a quorum. I want to add a throttling option to it too, but haven't got around to it.

@ricmoo
Copy link
Member

ricmoo commented Jul 9, 2019

Closing this now, as I think the issue was caused by the third-party backends throttling you, but please feel free to re-open if you believe there is still and issue.

Thanks! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion Questions, feedback and general information.
Projects
None yet
Development

No branches or pull requests

2 participants