-
Notifications
You must be signed in to change notification settings - Fork 465
Decode Calldata + Return Values in Contract Wrappers #2018
Decode Calldata + Return Values in Contract Wrappers #2018
Conversation
|
dc30e10
to
a90783c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh man, this is great! Thanks for implementing this!
The main change I would request is to move the tests from contracts/utils
to abi-gen
. It looks like they are testing the getAbiDecodedTransactionData
and getAbiDecodedReturnData
methods whose source code is in abi-gen-templates
. We're planning on combining abi-gen-templates
and abi-gen
soon - for now, we are trying to build up a test suite in abi-gen
. I suggest moving the test cases in TestAbi.sol
to packages/abi-gen/test-cli/fixtures/contracts/AbiGenDummy.sol
, and adding the unit tests to packages/abi-gen/test-cli/test_typescript/test/abi_gen_dummy_test.ts
.
and a huge thank you for adding test coverage!
a5f7152
to
118818b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a few questions about the Dummy
smart contract, but otherwise this looks really nice 🍻 !
/// This allows us to test `getABIDecodedTransactionData` and `getABIDecodedReturnData` that is | ||
/// include in contract wrappers. | ||
// solhint-disable no-complex-fallback | ||
function () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this function just loop forever? If the calldata hits the fallback function, then the fallback will call back into this contract using the same calldata, which would intuitively also make it hit the fallback function, creating an infinite loop. Please let me know if I'm wrong about this, but I've done a bit of testing locally and it seems to loop forever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't this never get called if used as intended because the functions it's forwarding to are defined in this contract?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yeah you're right this can be removed now that all tests are in the same contract. 🙏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dorothy-zbornak Yeah, I think that is the case. I was just saying that it would loop infinitely if it was called for some reason.
payable | ||
{ | ||
address addr = address(this); | ||
assembly { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason why you're using assembly here? It seems like you could use high-level solidity for this.
const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately. | ||
const defaultEncodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately. | ||
const defaultDecodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: false }; | ||
const runTest = <T>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is 🔥
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New to this package, but looks pretty good. Just some Qs.
@@ -44,3 +44,21 @@ getABIEncodedTransactionData( | |||
const abiEncodedTransactionData = self._strictEncodeArguments('{{this.functionSignature}}', [{{> normalized_params inputs=inputs}}]); | |||
return abiEncodedTransactionData; | |||
}, | |||
getABIDecodedTransactionData( | |||
returnData: string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this called returnData
instead of callData
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo, thank you 🙏
@@ -34,6 +34,19 @@ export class MethodDataType extends AbstractSetDataType { | |||
return value; | |||
} | |||
|
|||
public strictDecode<T>(calldata: string, rules?: DecodingRules): T { | |||
const value = super.decode(calldata, rules, this._methodSelector); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't find any tests where we check that decode()
with a mismatched method selector throws. Do we have one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great catch -- test added!
/// This allows us to test `getABIDecodedTransactionData` and `getABIDecodedReturnData` that is | ||
/// include in contract wrappers. | ||
// solhint-disable no-complex-fallback | ||
function () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't this never get called if used as intended because the functions it's forwarding to are defined in this contract?
} | ||
|
||
/// @dev The fallback function calls into this contract and executes one of the above functions. | ||
/// This allows us to test `getABIDecodedTransactionData` and `getABIDecodedReturnData` that is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really sure how this can be used to test getABIDecodedTransactionData()
. Where are the tests that use this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See below 🙏
… contract wrappers + test cases
26d2dcd
to
6787629
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one note about using comments to organise AbiGenDummy.sol. Feel free to change or not. Looks good to me!
@@ -138,4 +138,90 @@ contract AbiGenDummy | |||
uint someState; | |||
function nonPureMethod() public returns(uint) { return someState += 1; } | |||
function nonPureMethodThatReturnsNothing() public { someState += 1; } | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've been keeping all the test cases in one contract to speed up test run times. I'm thinking about how we could keep it somewhat organised, since we're working towards having full coverage of Solidity language features. We don't have this convention yet, but what about prefixing a section of test cases with the methods it's meant to test?
e.g.
// begin tests for `decodeTransactionData`, `decodeReturnData`
...
// end tests for `decodeTransactionData`, `decodeReturnData`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just use inheritance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xianny I've added comments for now for consistency, although I agree w Dorothy - it could be good to split this into multiple contracts alongside the refactor of abi-gen
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya, let's keep that in mind. I'm a little concerned that inheritance might complicate testing, especially when we add the EVM simulator. As in we would have to test whatever case we want PLUS inheritance.
@@ -28,6 +28,20 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); | |||
|
|||
describe('AbiGenDummy Contract', () => { | |||
let abiGenDummy: AbiGenDummyContract; | |||
const runTestAsync = async (contractMethod: any, input: any, output: any) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
love this setup! 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
╰(▔∀▔)╯
Description
This adds ABI decoding of transaction calldata and return values to contract wrappers. These are particularly useful for meta-transactions and the delegate-call proxy pattern.
This PR includes the following changes:
getABIDecodedTransactionData
to contract wrappers, which decodes calldata.getABIDecodedReturnData
to contract wrappers, which decodes return values.strictDecode
to the abi encoder, which decodes to the same format as the function's arguments.Testing instructions
Types of changes
Checklist:
[WIP]
if necessary.