Compare commits

...

1 Commits

Author SHA1 Message Date
abls
929d35c30c add mtx fees as array of structs 2022-11-28 07:49:56 -08:00
3 changed files with 141 additions and 10 deletions

View File

@@ -91,8 +91,12 @@ contract MetaTransactionsFeature is
"uint256 salt,"
"bytes callData,"
"uint256 value,"
"address feeToken,"
"uint256 feeAmount"
"MetaTransactionFeeData[] fees"
")"
"MetaTransactionFeeData("
"address recipient,"
"address token,"
"uint256 amount"
")"
);
@@ -218,8 +222,7 @@ contract MetaTransactionsFeature is
mtx.salt,
keccak256(mtx.callData),
mtx.value,
mtx.feeToken,
mtx.feeAmount
keccak256(abi.encode(mtx.fees))
)
)
);
@@ -238,8 +241,8 @@ contract MetaTransactionsFeature is
LibMetaTransactionsStorage.getStorage().mtxHashToExecutedBlockNumber[state.hash] = block.number;
// Pay the fee to the sender.
if (state.mtx.feeAmount > 0) {
_transferERC20TokensFrom(state.mtx.feeToken, state.mtx.signer, state.sender, state.mtx.feeAmount);
for (uint256 i = 0; i < state.mtx.fees.length; ++i) {
_transferERC20TokensFrom(state.mtx.fees[i].token, state.mtx.signer, state.mtx.fees[i].recipient, state.mtx.fees[i].amount);
}
// Execute the call based on the selector.

View File

@@ -25,6 +25,15 @@ import "../libs/LibSignature.sol";
/// @dev Meta-transactions feature.
interface IMetaTransactionsFeature {
struct MetaTransactionFeeData {
// ERC20 fee recipient
address recipient;
// ERC20 fee `signer` pays `recipient`
IERC20TokenV06 token;
// ERC20 fee amount
uint256 amount;
}
/// @dev Describes an exchange proxy meta transaction.
struct MetaTransactionData {
// Signer of meta-transaction. On whose behalf to execute the MTX.
@@ -43,10 +52,8 @@ interface IMetaTransactionsFeature {
bytes callData;
// Amount of ETH to attach to the call.
uint256 value;
// ERC20 fee `signer` pays `sender`.
IERC20TokenV06 feeToken;
// ERC20 fee amount.
uint256 feeAmount;
// ERC20 fees
MetaTransactionFeeData[] fees;
}
/// @dev Emitted whenever a meta-transaction is executed via

View File

@@ -0,0 +1,121 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "forge-std/Script.sol";
import "src/IZeroEx.sol";
import "src/features/MetaTransactionsFeature.sol";
import "src/features/interfaces/IMetaTransactionsFeature.sol";
import "src/features/libs/LibSignature.sol";
import "src/transformers/FillQuoteTransformer.sol";
import "src/transformers/PayTakerTransformer.sol";
contract MetaTxScript is Script {
IZeroEx private constant exchangeProxy = IZeroEx(0xDef1C0ded9bec7F1a1670819833240f027b25EfF);
IERC20TokenV06 private constant wethToken = IERC20TokenV06(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IERC20TokenV06 private constant usdcToken = IERC20TokenV06(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
uint256 private constant oneEth = 1e18;
address private constant ZERO_ADDRESS = 0x0000000000000000000000000000000000000000;
address private constant USER_ADDRESS = 0x6dc3a54FeAE57B65d185A7B159c5d3FA7fD7FD0F;
uint256 private constant USER_KEY = 0x1fc1630343b31e60b7a197a53149ca571ed9d9791e2833337bbd8110c30710ec;
uint32 private constant PAYTAKER_TFM_NONCE = 7;
uint32 private constant FILLQUOTE_TFM_NONCE = 25;
function mtxCall(bytes memory callData) private returns (bytes memory) {
IMetaTransactionsFeature.MetaTransactionFeeData[] memory fees = new IMetaTransactionsFeature.MetaTransactionFeeData[](1);
fees[0] = IMetaTransactionsFeature.MetaTransactionFeeData({
recipient: address(123),
token: wethToken,
amount: oneEth
});
IMetaTransactionsFeature.MetaTransactionData memory mtx = IMetaTransactionsFeature.MetaTransactionData({
signer: payable(USER_ADDRESS),
sender: ZERO_ADDRESS,
minGasPrice: 0,
maxGasPrice: 100000000000,
expirationTimeSeconds: block.timestamp + 600,
salt: 123,
callData: callData,
value: 0,
fees: fees
});
bytes32 mtxHash = exchangeProxy.getMetaTransactionHash(mtx);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(USER_KEY, mtxHash);
LibSignature.Signature memory sig = LibSignature.Signature(LibSignature.SignatureType.EIP712, v, r, s);
return abi.encodeWithSelector(
exchangeProxy.executeMetaTransaction.selector,
mtx,
sig
);
}
function transformERC20Call() private pure returns (bytes memory) {
FillQuoteTransformer.OrderType[] memory fillSequence = new FillQuoteTransformer.OrderType[](1);
fillSequence[0] = FillQuoteTransformer.OrderType.Bridge;
FillQuoteTransformer.TransformData memory fillQuoteTransformData = FillQuoteTransformer.TransformData({
side: FillQuoteTransformer.Side.Sell,
sellToken: wethToken,
buyToken: usdcToken,
bridgeOrders: new IBridgeAdapter.BridgeOrder[](0),
limitOrders: new FillQuoteTransformer.LimitOrderInfo[](0),
rfqOrders: new FillQuoteTransformer.RfqOrderInfo[](0),
fillSequence: new FillQuoteTransformer.OrderType[](0),
fillAmount: 0,
refundReceiver: address(0),
otcOrders: new FillQuoteTransformer.OtcOrderInfo[](0)
});
IERC20TokenV06[] memory payTakerTokens = new IERC20TokenV06[](1);
payTakerTokens[0] = wethToken;
PayTakerTransformer.TransformData memory payTakerTransformData = PayTakerTransformer.TransformData(
payTakerTokens,
new uint256[](0)
);
ITransformERC20Feature.Transformation[] memory transformations = new ITransformERC20Feature.Transformation[](2);
transformations[0] = ITransformERC20Feature.Transformation(
FILLQUOTE_TFM_NONCE,
abi.encode(fillQuoteTransformData)
);
transformations[1] = ITransformERC20Feature.Transformation(
PAYTAKER_TFM_NONCE,
abi.encode(payTakerTransformData)
);
return abi.encodeWithSelector(
exchangeProxy.transformERC20.selector,
wethToken,
usdcToken,
0,
0,
transformations
);
}
function deploy() private {
address mtxFeature = address(new MetaTransactionsFeature(address(exchangeProxy)));
address owner = exchangeProxy.owner();
vm.prank(owner);
exchangeProxy.migrate(mtxFeature, abi.encodeWithSignature("migrate()"), owner);
}
function run() public {
deploy();
bytes memory transformCalldata = transformERC20Call();
bytes memory mtxCalldata = mtxCall(transformCalldata);
vm.prank(ZERO_ADDRESS);
wethToken.transfer(USER_ADDRESS, oneEth);
vm.prank(USER_ADDRESS);
wethToken.approve(address(exchangeProxy), oneEth);
vm.prank(USER_ADDRESS);
(address(exchangeProxy).call(mtxCalldata));
}
}