Truffle: Document .request: Access RAW transaction data

Created on 25 Mar 2017  Â·  14Comments  Â·  Source: trufflesuite/truffle

  • [x] I've asked for help in the Truffle Gitter before filing this issue.

See: https://gitter.im/ConsenSys/truffle?at=58d4e2698fa16339540fa65e


(Further ignoring the template, which didn't seem fitting for a feature request)

Abstract

The Truffle contract API currently has no way of getting the raw transaction data of a given contract function call. The workaround is to call MyContract.contract.myFunction.getData(...args) but the MyContract.contract interface is not meant for this.

Use cases

I have two production use-cases, there may be more:

Executing arbitrary transactions from wallet contracts

The standard for forwarding contracts specifies the function

execute(address _to, uint _value, bytes _data) returns (bytes32 _id)

where _data is ABI encoded transaction call data. This API is used by most MultiSig wallets and allows MultiSig wallets to call contracts. This, for example, facilitates owning tokens in a MultiSig wallet. To send one token from multiSig we do the following:

const recipient = … // address
const amount = 1;
const innertx = token.contract.transfer.getData(recipient, amount);
const id = await multiSig.execute(token, 0, innertx);
// Announce id to other co-owners for approval. Once approved, innertx will be
// executed on tokenContract with msg.sender == multiSig.

Other MultiSig uses, such as owning other Ownable contracts requires similar code. In fact, the only thing you can do with a generic MultiSig wallet without using txdata is receiving and sending Ether.

Executing callbacks

In ERC20 and ERC23 callback mechanism are proposed where an opaque byte array can be passed to the called contract. At least [one example[4] uses this opaque byte array to encode a function call on the contract. Using it would be similar to the MultiSig example above.

Proposal

My ideal syntax would look like this:

const recipient = … // address
const amount = 1;
const id = await multiSig.execute(token, 0, token.transfer(recipient, amount));

To make this possible, I suggest making the promise returned by contract functions cancelable and adding a rawData() function to it. Then when a transaction promise is passed as an argument in a place where bytes is expected, it will be substituted by the rawData() and the transaction will be cancelled. (Supporting casting to bytes4 also makes sense as this indicates functions without arguments).

Or, easier, something similar to the raw contract API can de implemented:

const recipient = … // address
const amount = 1;
const id = await multiSig.execute(token, 0, token.transfer.data(recipient, amount));
Contract Docs bug priority6 💭

Most helpful comment

Hey @AusIV! I just faced the same issue and apparently, they indeed broke the .request() function in the new version. The workaround I found so far is to interact with an instance of web3.Contract that is stored within a Truffle contract instance (https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#methods-mymethod-encodeabi).

let raw_data = contract.contract.methods.getVal(1).encodeABI();

All 14 comments

If I did understand correctly it, web3 already does have this API!

The equivalent of your desired .getData is .request

token.transfer.request(recipient, amount).params[0].data will give you the data param that would be sent with that transaction.

@izqui is correct - truffle-contract exposes the .request option, which gives you the information you need.

Adding a TODO to document this.

I realize this is an old ticket to document the .request option, but when I upgraded from truffle 4.x to 5.0 contract.method.request(args) has started throwing:

TypeError: fn(...).request is not a function
    at execute.prepareCall.then.res (project/node_modules/truffle/build/webpack:/packages/truffle-contract/lib/execute.js:390:1)

Running:

Truffle v5.0.5 (core: 5.0.5)
Solidity - 0.5.3 (solc-js)
Node v8.9.4

@AusIV I think you need to do contract.request(args)...assuming request is a function found inside your contract. Or you could do contract.methods.request(args).

If I have a contract with a method "getVal(uint256)", I can call it with:

contract.getVal(1)

or

contract.methods["getVal(uint256)"](1)

My understanding it that the request option on these methods should give me access to the parameters of the request without actually executing it, so if I did:

let req = contract.getVal.request(1)

or

let req = contract.methods["getVal(uint256)"].request(1)

It should give me an object that represents the request. Then I should be able to do req.params[0].data to get the function call data.

In both of the above cases the .request value is a defined property, but when I call it I get

TypeError: fn(...).request is not a function
    at execute.prepareCall.then.res (project/node_modules/truffle/build/webpack:/packages/truffle-contract/lib/execute.js:390:1)

Note that the error is not coming directly from my call to request, but from this line: https://github.com/trufflesuite/truffle/blob/develop/packages/truffle-contract/lib/execute.js#L390

Oh my mistake @AusIV, yes you need to use Contract.methods[<signature>] to do that using methods.
I am unfamiliar with the request method you are trying to use. Is that a web3 feature? I can't seem to find it documented...do you have a link?

This ~2 year old ticket was originally about documenting that feature. I hijacked it to say "that feature isn't working anymore." It was working in truffle 4, but seemed to break in truffle 5.

contract.methods[<signature>] is still present and still a function, but internally it tries to call another object, also titled "request" which is not a function.

Hmm, perhaps something changed with the web3 interface. Truffle 4 uses [email protected] while Truffle 5 uses [email protected]. I'll do some more research and see what I can find.

Hey @AusIV! I just faced the same issue and apparently, they indeed broke the .request() function in the new version. The workaround I found so far is to interact with an instance of web3.Contract that is stored within a Truffle contract instance (https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#methods-mymethod-encodeabi).

let raw_data = contract.contract.methods.getVal(1).encodeABI();

@bashalex Do you happen to know if that is a feature that they plan on fixing and maintaining going forward?

@bashalex works!

But it would be great to have it fixed again. Its feel really bad to use myContract.contract....

I am also having the issue in the current version of truffle:

truffle(develop)> contract.myFunction.request()
TypeError: fn(...).request is not a function
    at process._tickCallback (internal/process/next_tick.js:68:7)
    at execute.prepareCall.then.res (/home/node/.config/yarn/global/node_modules/truffle/build/webpack:/packages/truffle-contract/lib/execute.js:394:1)

truffle(develop)> version
Truffle v5.0.18 (core: 5.0.18)
Solidity - 0.5.8 (solc-js)
Node v10.14.2
Web3.js v1.0.0-beta.37

The work aroung mentionned by @bashalex works fine..
This feature is really usefull when passing commands to contracts.
It would be also really great to document it correctly and add one testcase or more covering this feature.

Many thanks.

Looks like this is a bug now. The work to be done is to fix the .request() method that we provide in truffle-contract. Thanks for bearing with us!

OK, closing this now. The functionality is a little different than it used to be, but it is once again there and working!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

oed picture oed  Â·  3Comments

EmanHerawy picture EmanHerawy  Â·  3Comments

timothywangdev picture timothywangdev  Â·  3Comments

rjl493456442 picture rjl493456442  Â·  4Comments

TOMOAKI12345 picture TOMOAKI12345  Â·  3Comments