Skip to content

feat: Multiplex + MetaTransaction integration and MetaTransaction Multi-Fee Support [RFQ-795] [LIT-870] #665

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

Merged
merged 22 commits into from
Apr 19, 2023

Conversation

patrick-dowell
Copy link
Contributor

@patrick-dowell patrick-dowell commented Feb 21, 2023

Description

There are four big changes included in this PR:

  1. Allow executeMetaTransaction to pay out multiple fees by modifying the data structure MetaTransactionData. This results in the creation of a v2 MetaTransactions feature contract and MetaTransactionV2Data struct.

  2. Inclusion of four new selectors in MetaTransactionsFeatureV2:

  • _executeMultiplexBatchSellTokenForTokenCall
  • _executeMultiplexBatchSellTokenForEthCall
  • _executeMultiplexMultiHopSellTokenForTokenCall
  • _executeMultiplexMultiHopSellTokenForEthCall
  1. Modifications to Multiplex to account for the possibility that the MultiplexFeature is entered via MetaTransactionFeatureV2. Specifically, we refactor calls to msg.sender into BatchSellParams.payer and MultiHopSellParams.payer

  2. Add a call to multiHopBatchSellOtc to MultiplexFeature and add a conditional for MultiplexSubcall.OTC in Multiplex's _computeHopTarget. To accomplish this, changes were cherrypicked from Feat/multi hop sell otc [LIT-870] #667 at f32e834.

Testing instructions

Run forge test from contracts/zero-ex. There are numerous new local tests and a new forked test that cover the new functionality and contract, located at MetaTransactionV2Test.t.sol, MultiplexMetaTransactionsV2.t.sol, and MultiplexRfqTest.t.sol.

Types of changes

  • A new feature contract MetaTransactionsFeatureV2 was created, copying logic from MetaTransactionsFeature.
  • New functionality was added to MetaTransactionsFeatureV2
  • New functionality was added to MultiplexFeature
  • forge tests were added for the new feature contract and to test all new functionality.

Checklist:

  • Add tests to cover changes as needed.
  • Update documentation as needed.
  • Add new entries to the relevant CHANGELOG.jsons.

Comment on lines 49 to 50
// The sender of the transaction.
address msgSender;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the beneficiary of the metatransaction, or the relayer? We should invent new terminology here so that it's clear.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This naming is used elsewhere, such as openzeppelin's _msgSender(). msg.sender for regular transactions, and the end-user for GSN relayed calls.

So upon as long as this holds everywhere, the naming is ok.

I believe this is needed to disambiguate in the cases of receiving WETH and the user wanting ETH. In which case we need to be the recipient (to unwrap), but things need to be verified for the real user, such as the OTC/RFQ order was for that user.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a discussion we had earlier today, @duncancmt and @abls and I felt that the best naming would be payer (// the sender of the input token), which kinda mirrors what's going on with recipient - the receiver of the output token.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dekz I see what you mean. I think this gets a little confusing with OTC/RFQ when the flow of value is bidirectional (equivalently: when the operation being executed is effectively a metatransaction with 2 signers). I concede that this isn't a big deal for the present PR, though.

Comment on lines +28 to +24
address private constant ZERO_ADDRESS = 0x0000000000000000000000000000000000000000;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use address(0)?

Comment on lines 292 to 293
// TODO: Remove this field from SignatureValidationError
// when rich reverts are part of the protocol repo.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open TODO

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is sadly copy/pasted from the original metatransaction v1. Happy to open a TODO if this is something we actually want to fix.

Comment on lines 340 to 360
ExternalTransformERC20Args memory args;
{
bytes memory encodedStructArgs = new bytes(state.mtx.callData.length - 4 + 32);
// Copy the args data from the original, after the new struct offset prefix.
bytes memory fromCallData = state.mtx.callData;
assert(fromCallData.length >= 160);
uint256 fromMem;
uint256 toMem;
assembly {
// Prefix the calldata with a struct offset,
// which points to just one word over.
mstore(add(encodedStructArgs, 32), 32)
// Copy everything after the selector.
fromMem := add(fromCallData, 36)
// Start copying after the struct offset.
toMem := add(encodedStructArgs, 64)
}
LibBytesV06.memCopy(toMem, fromMem, fromCallData.length - 4);
// Decode call args for `ITransformERC20Feature.transformERC20()` as a struct.
args = abi.decode(encodedStructArgs, (ExternalTransformERC20Args));
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think this would be cleaner with the destructive version:

{
    bytes memory data = state.mtx.callData;
    assert(data.length > 4);
    assembly {
        let newLen := sub(mload(data), 4)
        data := add(data, 4)
        mstore(data, newLen)
    }
    state.mtx.callData = data;
}
args = abi.decode(state.mtx.callData, ExternalTransformERC20Args);

@dekz do you have an opinion?

DeployZeroEx.ZeroExDeployed zeroExDeployed;
address private constant ZERO_ADDRESS = 0x0000000000000000000000000000000000000000;
address private constant USER_ADDRESS = 0x6dc3a54FeAE57B65d185A7B159c5d3FA7fD7FD0F;
uint256 private constant USER_KEY = 0x1fc1630343b31e60b7a197a53149ca571ed9d9791e2833337bbd8110c30710ec;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While USER_KEY and address constants were useful when thrown together for that reference script, we should probably have a better system for this in the test utils. A lot of things from that script are not good practice because that script was intended for a single use within an entirely different context.

Regarding improving our utils, a single getSigner function that can return only one signer isn't enough. It is common in our tests that we need multiple signers. Also, maybe we should be able to sign for any of the accounts in BaseTest. Unsure exactly the best solution, but imo all the people writing tests should have a discussion about organization of the shared test utils at some point.

@elenadimitrova elenadimitrova removed their request for review February 22, 2023 04:33
@abls abls mentioned this pull request Feb 22, 2023
3 tasks
Comment on lines 49 to 50
// The sender of the transaction.
address msgSender;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This naming is used elsewhere, such as openzeppelin's _msgSender(). msg.sender for regular transactions, and the end-user for GSN relayed calls.

So upon as long as this holds everywhere, the naming is ok.

I believe this is needed to disambiguate in the cases of receiving WETH and the user wanting ETH. In which case we need to be the recipient (to unwrap), but things need to be verified for the real user, such as the OTC/RFQ order was for that user.

Copy link
Collaborator

@duncancmt duncancmt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As instructed by @patrick-dowell , I re-reviewed this by diffing MetaTransactionsFeatureV2.sol against MetaTransactionsFeature.sol. Seems pretty straightforward. I left a comment about reentrancy; I haven't gone deep on whether this is a real concern, but it's something to think about. Please add a comment addressing this concern before merging.

import "@0x/contracts-erc20/src/IEtherToken.sol";

contract MetaTransactionTest is LocalTest {
address private constant ZERO_ADDRESS = address(0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a named constant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just copy/pasted from previous test code that we wrote. I can get rid of it.

@patrick-dowell patrick-dowell marked this pull request as ready for review February 24, 2023 07:49
@dextracker dextracker changed the title feat: Multiplex + MetaTransaction integration and MetaTransaction Multi-Fee Support [RFQ-795] feat: Multiplex + MetaTransaction integration and MetaTransaction Multi-Fee Support [RFQ-795] [LIT-870] Feb 24, 2023

_executeMetaTransaction(
abi.encodeWithSelector(
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken.selector,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abls this should be multiplexMultiHopSellTokenForEth.selector

@duncancmt duncancmt merged commit ff104e7 into development Apr 19, 2023
@duncancmt duncancmt deleted the feat/MetaTransactionV2 branch April 19, 2023 22:20
@abls abls mentioned this pull request Apr 20, 2023
@duncancmt duncancmt mentioned this pull request Apr 24, 2023
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants