Migrate the multiplex tests to foundry (#655)

* add some tests for multiplex using foundry

* remove try/catch from multiplex foundry tests

* refactor multiplex forge tests

* fix broken import, remove dead code

---------

Co-authored-by: Patrick Dowell <patrick.dowell@gmail.com>
This commit is contained in:
Andy 2023-02-22 19:16:48 -05:00 committed by GitHub
parent 459fb3ee28
commit 38665ffc86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1116 additions and 20 deletions

View File

@ -0,0 +1,615 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2023 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import {LibNativeOrder} from "src/features/libs/LibNativeOrder.sol";
import {IMultiplexFeature} from "src/features/interfaces/IMultiplexFeature.sol";
import {LocalTest} from "utils/LocalTest.sol";
import {MultiplexUtils} from "utils/MultiplexUtils.sol";
contract Multiplex is LocalTest, MultiplexUtils {
event RfqOrderFilled(
bytes32 orderHash,
address maker,
address taker,
address makerToken,
address takerToken,
uint128 takerTokenFilledAmount,
uint128 makerTokenFilledAmount,
bytes32 pool
);
event OtcOrderFilled(
bytes32 orderHash,
address maker,
address taker,
address makerToken,
address takerToken,
uint128 makerTokenFilledAmount,
uint128 takerTokenFilledAmount
);
event ExpiredRfqOrder(bytes32 orderHash, address maker, uint64 expiry);
event ExpiredOtcOrder(bytes32 orderHash, address maker, uint64 expiry);
event Transfer(address token, address from, address to, uint256 value);
event MintTransform(
address context,
address caller,
address sender,
address taker,
bytes data,
uint256 inputTokenBalance,
uint256 ethBalance
);
//// batch sells
// reverts if minBuyAmount is not satisfied
function test_multiplexBatchSellTokenForToken_revertsIfMinBuyAmountIsNotSatisfied() public {
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder();
IMultiplexFeature.BatchSellSubcall memory rfqSubcall = _makeRfqSubcall(rfqOrder);
_mintTo(address(rfqOrder.takerToken), rfqOrder.taker, rfqOrder.takerAmount);
vm.expectRevert("MultiplexFeature::_multiplexBatchSell/UNDERBOUGHT");
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken(
dai,
zrx,
_makeArray(rfqSubcall),
rfqOrder.takerAmount,
rfqOrder.makerAmount + 1
);
}
// reverts if given an invalid subcall type
function test_multiplexBatchSellTokenForToken_revertsIfGivenAnInvalidSubcallType() public {
uint256 sellAmount = 1e18;
vm.expectRevert("MultiplexFeature::_executeBatchSell/INVALID_SUBCALL");
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken(
dai,
zrx,
_makeArray(
IMultiplexFeature.BatchSellSubcall({
id: IMultiplexFeature.MultiplexSubcall.Invalid,
sellAmount: sellAmount,
data: hex""
})
),
sellAmount,
0
);
}
// reverts if the full sell amount is not sold
function test_multiplexBatchSellTokenForToken_revertsIfTheFullSellAmountIsNotSold() public {
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder();
IMultiplexFeature.BatchSellSubcall memory rfqSubcall = _makeRfqSubcall(rfqOrder);
_mintTo(address(rfqOrder.takerToken), rfqOrder.taker, rfqOrder.takerAmount);
vm.expectRevert("MultiplexFeature::_executeBatchSell/INCORRECT_AMOUNT_SOLD");
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken(
rfqOrder.takerToken,
rfqOrder.makerToken,
_makeArray(rfqSubcall),
rfqOrder.takerAmount + 1,
rfqOrder.makerAmount
);
}
// RFQ, fallback(UniswapV2)
function test_multiplexBatchSellTokenForToken_rfqFallbackUniswapV2() public {
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder();
_createUniswapV2Pool(uniV2Factory, dai, zrx, 10e18, 10e18);
_mintTo(address(rfqOrder.takerToken), rfqOrder.taker, rfqOrder.takerAmount);
vm.expectEmit(true, true, true, true);
emit RfqOrderFilled(
zeroExDeployed.features.nativeOrdersFeature.getRfqOrderHash(rfqOrder),
rfqOrder.maker,
rfqOrder.taker,
address(rfqOrder.makerToken),
address(rfqOrder.takerToken),
rfqOrder.takerAmount,
rfqOrder.makerAmount,
rfqOrder.pool
);
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken(
rfqOrder.takerToken,
zrx,
_makeArray(
_makeRfqSubcall(rfqOrder),
_makeUniswapV2BatchSubcall(_makeArray(address(dai), address(zrx)), rfqOrder.takerAmount, false)
),
rfqOrder.takerAmount,
0
);
}
// OTC, fallback(UniswapV2)
function test_multiplexBatchSellTokenForToken_otcFallbackUniswapV2() public {
LibNativeOrder.OtcOrder memory otcOrder = _makeTestOtcOrder();
_createUniswapV2Pool(uniV2Factory, dai, zrx, 10e18, 10e18);
_mintTo(address(otcOrder.takerToken), otcOrder.taker, otcOrder.takerAmount);
vm.expectEmit(true, true, true, true);
emit OtcOrderFilled(
zeroExDeployed.features.otcOrdersFeature.getOtcOrderHash(otcOrder),
otcOrder.maker,
otcOrder.taker,
address(otcOrder.makerToken),
address(otcOrder.takerToken),
otcOrder.makerAmount,
otcOrder.takerAmount
);
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken(
otcOrder.takerToken,
zrx,
_makeArray(
_makeOtcSubcall(otcOrder),
_makeUniswapV2BatchSubcall(_makeArray(address(dai), address(zrx)), otcOrder.takerAmount, false)
),
otcOrder.takerAmount,
0
);
}
// expired RFQ, fallback(UniswapV2)
function test_multiplexBatchSellTokenForToken_expiredRfqFallbackUniswapV2() public {
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder();
address uniV2Pool = _createUniswapV2Pool(uniV2Factory, dai, zrx, 10e18, 10e18);
_mintTo(address(rfqOrder.takerToken), rfqOrder.taker, rfqOrder.takerAmount);
rfqOrder.expiry = 0;
vm.expectEmit(true, true, true, true);
emit ExpiredRfqOrder(
zeroExDeployed.features.nativeOrdersFeature.getRfqOrderHash(rfqOrder),
rfqOrder.maker,
rfqOrder.expiry
);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), rfqOrder.taker, uniV2Pool, rfqOrder.takerAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), uniV2Pool, rfqOrder.taker, 906610893880149131);
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken(
rfqOrder.takerToken,
zrx,
_makeArray(
_makeRfqSubcall(rfqOrder),
_makeUniswapV2BatchSubcall(_makeArray(address(dai), address(zrx)), rfqOrder.takerAmount, false)
),
rfqOrder.takerAmount,
0
);
}
// expired OTC, fallback(UniswapV2)
function test_multiplexBatchSellTokenForToken_expiredOtcFallbackUniswapV2() public {
LibNativeOrder.OtcOrder memory otcOrder = _makeTestOtcOrder();
address uniV2Pool = _createUniswapV2Pool(uniV2Factory, dai, zrx, 10e18, 10e18);
_mintTo(address(otcOrder.takerToken), otcOrder.taker, otcOrder.takerAmount);
otcOrder.expiryAndNonce = 1;
vm.expectEmit(true, true, true, true);
emit ExpiredOtcOrder(
zeroExDeployed.features.otcOrdersFeature.getOtcOrderHash(otcOrder),
otcOrder.maker,
uint64(otcOrder.expiryAndNonce >> 192)
);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), otcOrder.taker, uniV2Pool, otcOrder.takerAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), uniV2Pool, otcOrder.taker, 906610893880149131);
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken(
otcOrder.takerToken,
zrx,
_makeArray(
_makeOtcSubcall(otcOrder),
_makeUniswapV2BatchSubcall(_makeArray(address(dai), address(zrx)), otcOrder.takerAmount, false)
),
otcOrder.takerAmount,
0
);
}
// expired RFQ, fallback(TransformERC20)
function test_multiplexBatchSellTokenForToken_expiredRfqFallbackTransformErc20() public {
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder();
_mintTo(address(rfqOrder.takerToken), rfqOrder.taker, rfqOrder.takerAmount);
rfqOrder.expiry = 0;
vm.expectEmit(true, true, true, true);
emit ExpiredRfqOrder(
zeroExDeployed.features.nativeOrdersFeature.getRfqOrderHash(rfqOrder),
rfqOrder.maker,
rfqOrder.expiry
);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), rfqOrder.taker, address(flashWallet), rfqOrder.takerAmount);
vm.expectEmit(true, true, true, true);
emit MintTransform(
address(flashWallet),
address(zeroExDeployed.zeroEx),
address(zeroExDeployed.zeroEx),
rfqOrder.taker,
abi.encode(dai, zrx, rfqOrder.takerAmount, 5e17, 0),
rfqOrder.takerAmount,
0
);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), address(flashWallet), address(0), 1e18);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), address(flashWallet), rfqOrder.taker, 5e17);
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken(
rfqOrder.takerToken,
zrx,
_makeArray(_makeRfqSubcall(rfqOrder), _makeMockTransformERC20Subcall(dai, zrx, rfqOrder.takerAmount, 5e17)),
rfqOrder.takerAmount,
0
);
}
// LiquidityProvider, UniV3, Sushiswap
function test_multiplexBatchSellTokenForToken_liquidityProviderUniV3Sushiswap() public {
address sushiswapPool = _createUniswapV2Pool(sushiFactory, dai, zrx, 10e18, 10e18);
address uniV3Pool = _createUniswapV3Pool(uniV3Factory, dai, zrx, 10e18, 10e18);
address[] memory tokens = _makeArray(address(dai), address(zrx));
IMultiplexFeature.BatchSellSubcall memory lpSubcall = _makeMockLiquidityProviderBatchSubcall(4e17);
IMultiplexFeature.BatchSellSubcall memory uniV3Subcall = _makeUniswapV3BatchSubcall(tokens, 5e17);
IMultiplexFeature.BatchSellSubcall memory sushiswapSubcall = _makeUniswapV2BatchSubcall(tokens, 6e17, true);
uint256 sellAmount = lpSubcall.sellAmount + uniV3Subcall.sellAmount + sushiswapSubcall.sellAmount - 1;
_mintTo(address(dai), address(this), sellAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), address(this), address(liquidityProvider), lpSubcall.sellAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), address(liquidityProvider), address(this), 0);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), uniV3Pool, address(this), 10e18);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), address(this), uniV3Pool, uniV3Subcall.sellAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), address(this), sushiswapPool, sushiswapSubcall.sellAmount - 1);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), sushiswapPool, address(this), 564435470174180520);
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken(
dai,
zrx,
_makeArray(lpSubcall, uniV3Subcall, sushiswapSubcall),
sellAmount,
0
);
}
// proportional fill amounts
function test_multiplexBatchSellTokenForToken_proportionalFillAmounts() public {
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder();
address uniV2Pool = _createUniswapV2Pool(uniV2Factory, dai, zrx, 10e18, 10e18);
uint256 sellAmount = 1e18;
_mintTo(address(dai), address(this), sellAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), rfqOrder.taker, rfqOrder.maker, 42e16);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), rfqOrder.maker, rfqOrder.taker, 42e16);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), rfqOrder.taker, uniV2Pool, 58e16);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), uniV2Pool, rfqOrder.taker, 546649448964196380);
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken(
dai,
zrx,
_makeArray(
_makeRfqSubcall(rfqOrder, _encodeFractionalFillAmount(42)),
_makeUniswapV2BatchSubcall(
_makeArray(address(dai), address(zrx)),
_encodeFractionalFillAmount(100),
false
)
),
sellAmount,
0
);
}
// RFQ, MultiHop(UniV3, UniV2)
function test_multiplexBatchSellTokenForToken_rfqMultiHopUniV3UniV2() public {
address uniV2Pool = _createUniswapV2Pool(uniV2Factory, shib, zrx, 10e18, 10e18);
address uniV3Pool = _createUniswapV3Pool(uniV3Factory, dai, shib, 10e18, 10e18);
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder();
IMultiplexFeature.BatchSellSubcall memory rfqSubcall = _makeRfqSubcall(rfqOrder);
IMultiplexFeature.BatchSellSubcall memory nestedMultiHopSubcall = _makeNestedMultiHopSellSubcall(
_makeArray(address(dai), address(shib), address(zrx)),
_makeArray(
_makeUniswapV3MultiHopSubcall(_makeArray(address(dai), address(shib))),
_makeUniswapV2MultiHopSubcall(_makeArray(address(shib), address(zrx)), false)
),
5e17
);
uint256 sellAmount = rfqSubcall.sellAmount + nestedMultiHopSubcall.sellAmount;
_mintTo(address(dai), address(this), sellAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), rfqOrder.taker, rfqOrder.maker, rfqOrder.takerAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), rfqOrder.maker, rfqOrder.taker, rfqOrder.makerAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(shib), uniV3Pool, uniV2Pool, 10e18);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), rfqOrder.taker, uniV3Pool, nestedMultiHopSubcall.sellAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), uniV2Pool, rfqOrder.taker, 4992488733099649474);
zeroExDeployed.zeroEx.multiplexBatchSellTokenForToken(
dai,
zrx,
_makeArray(rfqSubcall, nestedMultiHopSubcall),
sellAmount,
0
);
}
//// multihop sells
// reverts if given an invalid subcall type
function test_multiplexMultiHopSellTokenForToken_revertsIfGivenAnInvalidSubcallType() public {
vm.expectRevert("MultiplexFeature::_computeHopTarget/INVALID_SUBCALL");
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken(
_makeArray(address(dai), address(zrx)),
_makeArray(
IMultiplexFeature.MultiHopSellSubcall({id: IMultiplexFeature.MultiplexSubcall.Invalid, data: hex""})
),
1e18,
0
);
}
// reverts if minBuyAmount is not satisfied
function test_multiplexMultiHopSellTokenForToken_revertsIfMinBuyAmountIsNotSatisfied() public {
_createUniswapV2Pool(uniV2Factory, dai, zrx, 10e18, 10e18);
uint256 sellAmount = 5e17;
_mintTo(address(dai), address(this), sellAmount);
vm.expectRevert("MultiplexFeature::_multiplexMultiHopSell/UNDERBOUGHT");
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken(
_makeArray(address(dai), address(zrx)),
_makeArray(_makeUniswapV2MultiHopSubcall(_makeArray(address(dai), address(zrx)), false)),
sellAmount,
type(uint256).max
);
}
// reverts if array lengths are mismatched
function test_multiplexMultiHopSellTokenForToken_revertsIfArrayLengthsAreMismatched() public {
_createUniswapV2Pool(uniV2Factory, dai, zrx, 10e18, 10e18);
IMultiplexFeature.MultiHopSellSubcall memory uniswapV2Subcall = _makeUniswapV2MultiHopSubcall(
_makeArray(address(dai), address(zrx)),
false
);
uint256 sellAmount = 5e17;
_mintTo(address(dai), address(this), sellAmount);
vm.expectRevert("MultiplexFeature::_multiplexMultiHopSell/MISMATCHED_ARRAY_LENGTHS");
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken(
_makeArray(address(dai), address(zrx)),
_makeArray(uniswapV2Subcall, uniswapV2Subcall),
sellAmount,
0
);
}
// UniswapV2 -> LiquidityProvider
function test_multiplexMultiHopSellTokenForToken_uniswapV2ToLiquidityProvider() public {
address uniV2Pool = _createUniswapV2Pool(uniV2Factory, dai, shib, 10e18, 10e18);
IMultiplexFeature.MultiHopSellSubcall memory lpSubcall = _makeMockLiquidityProviderMultiHopSubcall();
IMultiplexFeature.MultiHopSellSubcall memory uniswapV2Subcall = _makeUniswapV2MultiHopSubcall(
_makeArray(address(dai), address(shib)),
false
);
uint256 sellAmount = 5e17;
uint256 buyAmount = 6e17;
_mintTo(address(dai), address(this), sellAmount);
_mintTo(address(zrx), address(liquidityProvider), buyAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), address(this), uniV2Pool, sellAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(shib), uniV2Pool, address(liquidityProvider), 474829737581559270);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), address(liquidityProvider), address(this), buyAmount);
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken(
_makeArray(address(dai), address(shib), address(zrx)),
_makeArray(uniswapV2Subcall, lpSubcall),
sellAmount,
buyAmount
);
}
// LiquidityProvider -> Sushiswap
function test_multiplexMultiHopSellTokenForToken_liquidityProviderToSushiswap() public {
address sushiPool = _createUniswapV2Pool(sushiFactory, shib, zrx, 10e18, 10e18);
IMultiplexFeature.MultiHopSellSubcall memory lpSubcall = _makeMockLiquidityProviderMultiHopSubcall();
IMultiplexFeature.MultiHopSellSubcall memory sushiswapSubcall = _makeUniswapV2MultiHopSubcall(
_makeArray(address(shib), address(zrx)),
true
);
uint256 sellAmount = 5e17;
uint256 shibAmount = 6e17;
_mintTo(address(dai), address(this), sellAmount);
_mintTo(address(shib), address(liquidityProvider), shibAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), address(this), address(liquidityProvider), sellAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(shib), address(liquidityProvider), sushiPool, shibAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), sushiPool, address(this), 564435470174180521);
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken(
_makeArray(address(dai), address(shib), address(zrx)),
_makeArray(lpSubcall, sushiswapSubcall),
sellAmount,
0
);
}
// UniswapV3 -> BatchSell(RFQ, UniswapV2)
function test_multiplexMultiHopSellTokenForToken_uniswapV3ToBatchSellRfqUniswapV2() public {
address uniV2Pool = _createUniswapV2Pool(uniV2Factory, shib, zrx, 10e18, 10e18);
address uniV3Pool = _createUniswapV3Pool(uniV3Factory, dai, shib, 10e18, 10e18);
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder();
rfqOrder.takerToken = shib;
rfqOrder.makerToken = zrx;
IMultiplexFeature.BatchSellSubcall memory rfqSubcall = _makeRfqSubcall(rfqOrder);
rfqSubcall.sellAmount = _encodeFractionalFillAmount(42);
uint256 sellAmount = 5e17;
_mintTo(address(dai), address(this), sellAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(shib), uniV3Pool, address(zeroExDeployed.zeroEx), 10e18);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), address(this), uniV3Pool, sellAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(shib), address(zeroExDeployed.zeroEx), rfqOrder.maker, 1e18);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), rfqOrder.maker, address(this), 1e18);
vm.expectEmit(true, true, true, true);
emit Transfer(address(shib), address(zeroExDeployed.zeroEx), uniV2Pool, 9e18);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), uniV2Pool, address(this), 4729352237389975227);
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken(
_makeArray(address(dai), address(shib), address(zrx)),
_makeArray(
_makeUniswapV3MultiHopSubcall(_makeArray(address(dai), address(shib))),
_makeNestedBatchSellSubcall(
_makeArray(
rfqSubcall,
_makeUniswapV2BatchSubcall(
_makeArray(address(shib), address(zrx)),
_encodeFractionalFillAmount(100),
false
)
)
)
),
sellAmount,
0
);
}
// BatchSell(RFQ, UniswapV2) -> UniswapV3
function test_multiplexMultiHopSellTokenForToken_batchSellRfqUniswapV2ToUniswapV3() public {
address uniV2Pool = _createUniswapV2Pool(uniV2Factory, dai, shib, 10e18, 10e18);
address uniV3Pool = _createUniswapV3Pool(uniV3Factory, shib, zrx, 10e18, 10e18);
LibNativeOrder.RfqOrder memory rfqOrder = _makeTestRfqOrder({makerToken: shib, takerToken: dai});
IMultiplexFeature.BatchSellSubcall memory rfqSubcall = _makeRfqSubcall(rfqOrder);
IMultiplexFeature.BatchSellSubcall memory uniswapV2Subcall = _makeUniswapV2BatchSubcall(
_makeArray(address(dai), address(shib)),
5e17,
false
);
uint256 sellAmount = rfqSubcall.sellAmount + uniswapV2Subcall.sellAmount;
_mintTo(address(dai), address(this), sellAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), address(this), rfqOrder.maker, rfqOrder.takerAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(shib), rfqOrder.maker, address(zeroExDeployed.zeroEx), rfqOrder.takerAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(dai), address(this), uniV2Pool, uniswapV2Subcall.sellAmount);
vm.expectEmit(true, true, true, true);
emit Transfer(address(shib), uniV2Pool, address(zeroExDeployed.zeroEx), 474829737581559270);
vm.expectEmit(true, true, true, true);
emit Transfer(address(zrx), uniV3Pool, address(this), 10e18);
vm.expectEmit(true, true, true, true);
emit Transfer(address(shib), address(zeroExDeployed.zeroEx), uniV3Pool, 1474829737581559270);
zeroExDeployed.zeroEx.multiplexMultiHopSellTokenForToken(
_makeArray(address(dai), address(shib), address(zrx)),
_makeArray(
_makeNestedBatchSellSubcall(_makeArray(rfqSubcall, uniswapV2Subcall)),
_makeUniswapV3MultiHopSubcall(_makeArray(address(shib), address(zrx)))
),
sellAmount,
0
);
}
}

View File

@ -15,7 +15,7 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "utils/ForkUtils.sol";
import {ForkUtils} from "utils/ForkUtils.sol";
import "utils/TestUtils.sol";
import "utils/DeployZeroEx.sol";
import "forge-std/Test.sol";
@ -33,7 +33,9 @@ contract WrapEth is Test, ForkUtils, TestUtils {
DeployZeroEx.ZeroExDeployed zeroExDeployed;
function setUp() public {
zeroExDeployed = new DeployZeroEx().deployZeroEx();
zeroExDeployed = new DeployZeroEx(
DeployZeroEx.ZeroExDeployConfiguration(address(0), address(0), address(0), 0, 0, 0, true)
).deployZeroEx();
vm.deal(address(this), 1e19);
}

View File

@ -103,7 +103,7 @@ contract RfqtV2Test is Test, ForkUtils, TestUtils {
order.makerAmount = 1e18;
order.takerAmount = 1e18;
uint privateKey;
(order.maker, privateKey) = getSigner();
(order.maker, privateKey) = _getSigner();
deal(address(order.makerToken), order.maker, 1e20);
vm.prank(order.maker);
IERC20Token(tokens.USDC).approve(address(addresses.exchangeProxy), 1e20);

View File

@ -15,7 +15,7 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "../utils/ForkUtils.sol";
import {ForkUtils} from "../utils/ForkUtils.sol";
import "../utils/TestUtils.sol";
import "../utils/DeployZeroEx.sol";
import "forge-std/Test.sol";
@ -33,7 +33,9 @@ contract WrapEthTest is Test, ForkUtils, TestUtils {
DeployZeroEx.ZeroExDeployed zeroExDeployed;
function setUp() public {
zeroExDeployed = new DeployZeroEx().deployZeroEx();
zeroExDeployed = new DeployZeroEx(
DeployZeroEx.ZeroExDeployConfiguration(address(0), address(0), address(0), 0, 0, 0, true)
).deployZeroEx();
vm.deal(address(this), 1e19);
}

View File

@ -31,9 +31,11 @@ import "src/features/MetaTransactionsFeature.sol";
import "src/features/nft_orders/ERC1155OrdersFeature.sol";
import "src/features/nft_orders/ERC721OrdersFeature.sol";
import "src/features/UniswapFeature.sol";
import "src/features/UniswapV3Feature.sol";
import "src/features/multiplex/MultiplexFeature.sol";
import "src/external/TransformerDeployer.sol";
import "src/external/FeeCollectorController.sol";
import "src/external/LiquidityProviderSandbox.sol";
import "src/transformers/WethTransformer.sol";
import "src/transformers/FillQuoteTransformer.sol";
import "src/transformers/PayTakerTransformer.sol";
@ -62,6 +64,7 @@ contract DeployZeroEx is Test {
BatchFillNativeOrdersFeature batchFillNativeOrdersFeature;
OtcOrdersFeature otcOrdersFeature;
UniswapFeature uniswapFeature;
UniswapV3Feature uniswapV3Feature;
FundRecoveryFeature fundRecoveryFeature;
TransformERC20Feature transformERC20Feature;
MetaTransactionsFeature metaTransactionsFeature;
@ -89,6 +92,22 @@ contract DeployZeroEx is Test {
ZeroExDeployed ZERO_EX_DEPLOYED;
struct ZeroExDeployConfiguration {
address uniswapFactory;
address sushiswapFactory;
address uniswapV3Factory;
bytes32 uniswapPairInitCodeHash;
bytes32 sushiswapPairInitCodeHash;
bytes32 uniswapV3PoolInitCodeHash;
bool logDeployed;
}
ZeroExDeployConfiguration ZERO_EX_DEPLOY_CONFIG;
constructor(ZeroExDeployConfiguration memory configuration) public {
ZERO_EX_DEPLOY_CONFIG = configuration;
}
function getDeployedZeroEx() public returns (ZeroExDeployed memory) {
if (!isDeployed) {
deployZeroEx();
@ -111,6 +130,7 @@ contract DeployZeroEx is Test {
);
emit log_named_address("OtcOrdersFeature", address(ZERO_EX_DEPLOYED.features.otcOrdersFeature));
emit log_named_address("UniswapFeature", address(ZERO_EX_DEPLOYED.features.uniswapFeature));
emit log_named_address("UniswapV3Feature", address(ZERO_EX_DEPLOYED.features.uniswapV3Feature));
emit log_named_address("FundRecoveryFeature", address(ZERO_EX_DEPLOYED.features.fundRecoveryFeature));
emit log_named_address("MetaTransactionsFeature", address(ZERO_EX_DEPLOYED.features.metaTransactionsFeature));
emit log_named_address("ERC1155OrdersFeature", address(ZERO_EX_DEPLOYED.features.erc1155OrdersFeature));
@ -174,6 +194,11 @@ contract DeployZeroEx is Test {
ZERO_EX_DEPLOYED.features.batchFillNativeOrdersFeature = new BatchFillNativeOrdersFeature(address(ZERO_EX));
ZERO_EX_DEPLOYED.features.otcOrdersFeature = new OtcOrdersFeature(address(ZERO_EX), ZERO_EX_DEPLOYED.weth);
ZERO_EX_DEPLOYED.features.uniswapFeature = new UniswapFeature(ZERO_EX_DEPLOYED.weth);
ZERO_EX_DEPLOYED.features.uniswapV3Feature = new UniswapV3Feature(
ZERO_EX_DEPLOYED.weth,
ZERO_EX_DEPLOY_CONFIG.uniswapV3Factory,
ZERO_EX_DEPLOY_CONFIG.uniswapV3PoolInitCodeHash
);
ZERO_EX_DEPLOYED.features.fundRecoveryFeature = new FundRecoveryFeature();
ZERO_EX_DEPLOYED.features.metaTransactionsFeature = new MetaTransactionsFeature(address(ZERO_EX));
ZERO_EX_DEPLOYED.features.erc1155OrdersFeature = new ERC1155OrdersFeature(
@ -187,11 +212,11 @@ contract DeployZeroEx is Test {
ZERO_EX_DEPLOYED.features.multiplexFeature = new MultiplexFeature(
address(ZERO_EX),
ZERO_EX_DEPLOYED.weth,
ILiquidityProviderSandbox(address(0)),
address(0), // uniswapFactory
address(0), // sushiswapFactory
bytes32(0), // uniswapPairInitCodeHash
bytes32(0) // sushiswapPairInitCodeHash
new LiquidityProviderSandbox(address(ZERO_EX)),
ZERO_EX_DEPLOY_CONFIG.uniswapFactory,
ZERO_EX_DEPLOY_CONFIG.sushiswapFactory,
ZERO_EX_DEPLOY_CONFIG.uniswapPairInitCodeHash,
ZERO_EX_DEPLOY_CONFIG.sushiswapPairInitCodeHash
);
initialMigration.initializeZeroEx(
@ -230,6 +255,11 @@ contract DeployZeroEx is Test {
abi.encodeWithSelector(UniswapFeature.migrate.selector),
address(this)
);
IZERO_EX.migrate(
address(ZERO_EX_DEPLOYED.features.uniswapV3Feature),
abi.encodeWithSelector(UniswapV3Feature.migrate.selector),
address(this)
);
IZERO_EX.migrate(
address(ZERO_EX_DEPLOYED.features.fundRecoveryFeature),
abi.encodeWithSelector(FundRecoveryFeature.migrate.selector),
@ -293,7 +323,10 @@ contract DeployZeroEx is Test {
ZERO_EX_DEPLOYED.zeroEx = IZERO_EX;
isDeployed = true;
logDeployedZeroEx();
if (ZERO_EX_DEPLOY_CONFIG.logDeployed) {
logDeployedZeroEx();
}
return ZERO_EX_DEPLOYED;
}
}

View File

@ -227,15 +227,6 @@ contract ForkUtils is Test {
}
}
//gets a dummy signer to be used for an OTC order
function getSigner() public returns (address, uint) {
string memory mnemonic = "test test test test test test test test test test test junk";
uint256 privateKey = vm.deriveKey(mnemonic, 0);
vm.label(vm.addr(privateKey), "zeroEx/MarketMaker");
return (vm.addr(privateKey), privateKey);
}
//read the uniswapV2 router addresses from file
function readLiquiditySourceAddresses() internal returns (string memory) {
string memory root = vm.projectRoot();

View File

@ -0,0 +1,140 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2023 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import {Test} from "forge-std/Test.sol";
import {IERC20Token} from "@0x/contracts-erc20/src/IERC20Token.sol";
import {IEtherToken} from "@0x/contracts-erc20/src/IEtherToken.sol";
import {WETH9V06} from "@0x/contracts-erc20/src/v06/WETH9V06.sol";
import {IFlashWallet} from "src/external/IFlashWallet.sol";
import {LibERC20Transformer} from "src/transformers/LibERC20Transformer.sol";
import {LibNativeOrder} from "src/features/libs/LibNativeOrder.sol";
import {LibSignature} from "src/features/libs/LibSignature.sol";
import {IMultiplexFeature} from "src/features/interfaces/IMultiplexFeature.sol";
import {ITransformERC20Feature} from "src/features/interfaces/ITransformERC20Feature.sol";
import {TestUtils} from "utils/TestUtils.sol";
import {DeployZeroEx} from "utils/DeployZeroEx.sol";
import {TestMintTokenERC20Transformer} from "../../contracts/test/TestMintTokenERC20Transformer.sol";
import {TestMintableERC20Token} from "../../contracts/test/tokens/TestMintableERC20Token.sol";
import {TestUniswapV2Factory} from "../../contracts/test/integration/TestUniswapV2Factory.sol";
import {TestUniswapV2Pool} from "../../contracts/test/integration/TestUniswapV2Pool.sol";
import {TestUniswapV3Factory} from "../../contracts/test/integration/TestUniswapV3Factory.sol";
import {TestUniswapV3Pool} from "../../contracts/test/integration/TestUniswapV3Pool.sol";
import {TestLiquidityProvider} from "../../contracts/test/integration/TestLiquidityProvider.sol";
contract LocalTest is Test, TestUtils {
uint24 internal constant UNIV3_POOL_FEE = 1234;
DeployZeroEx.ZeroExDeployed internal zeroExDeployed;
IFlashWallet internal flashWallet;
IERC20Token internal shib;
IERC20Token internal dai;
IERC20Token internal zrx;
IEtherToken internal weth;
TestUniswapV2Factory internal sushiFactory;
TestUniswapV2Factory internal uniV2Factory;
TestUniswapV3Factory internal uniV3Factory;
TestLiquidityProvider internal liquidityProvider;
uint256 internal transformerNonce;
address internal signerAddress;
uint256 internal signerKey;
function _infiniteApprovals() private {
shib.approve(address(zeroExDeployed.zeroEx), type(uint256).max);
dai.approve(address(zeroExDeployed.zeroEx), type(uint256).max);
zrx.approve(address(zeroExDeployed.zeroEx), type(uint256).max);
weth.approve(address(zeroExDeployed.zeroEx), type(uint256).max);
}
function setUp() public {
(signerAddress, signerKey) = _getSigner();
sushiFactory = new TestUniswapV2Factory();
uniV2Factory = new TestUniswapV2Factory();
uniV3Factory = new TestUniswapV3Factory();
liquidityProvider = new TestLiquidityProvider();
zeroExDeployed = new DeployZeroEx(
DeployZeroEx.ZeroExDeployConfiguration({
uniswapFactory: address(uniV2Factory),
sushiswapFactory: address(sushiFactory),
uniswapV3Factory: address(uniV3Factory),
uniswapPairInitCodeHash: uniV2Factory.POOL_INIT_CODE_HASH(),
sushiswapPairInitCodeHash: sushiFactory.POOL_INIT_CODE_HASH(),
uniswapV3PoolInitCodeHash: uniV3Factory.POOL_INIT_CODE_HASH(),
logDeployed: false
})
).deployZeroEx();
transformerNonce = zeroExDeployed.transformerDeployer.nonce();
vm.prank(zeroExDeployed.transformerDeployer.authorities(0));
zeroExDeployed.transformerDeployer.deploy(type(TestMintTokenERC20Transformer).creationCode);
flashWallet = zeroExDeployed.zeroEx.getTransformWallet();
shib = IERC20Token(address(new TestMintableERC20Token()));
dai = IERC20Token(address(new TestMintableERC20Token()));
zrx = IERC20Token(address(new TestMintableERC20Token()));
weth = zeroExDeployed.weth;
_infiniteApprovals();
vm.startPrank(signerAddress);
_infiniteApprovals();
vm.stopPrank();
vm.deal(address(this), 10e18);
}
function _mintTo(address token, address recipient, uint256 amount) internal {
if (token == address(weth)) {
IEtherToken(token).deposit{value: amount}();
WETH9V06(payable(token)).transfer(recipient, amount);
} else {
TestMintableERC20Token(token).mint(recipient, amount);
}
}
function _createUniswapV2Pool(
TestUniswapV2Factory factory,
IERC20Token tokenA,
IERC20Token tokenB,
uint112 balanceA,
uint112 balanceB
) internal returns (address poolAddress) {
TestUniswapV2Pool pool = factory.createPool(tokenA, tokenB);
_mintTo(address(tokenA), address(pool), balanceA);
_mintTo(address(tokenB), address(pool), balanceB);
(uint112 balance0, uint112 balance1) = tokenA < tokenB ? (balanceA, balanceB) : (balanceB, balanceA);
pool.setReserves(balance0, balance1, 0);
return address(pool);
}
function _createUniswapV3Pool(
TestUniswapV3Factory factory,
IERC20Token tokenA,
IERC20Token tokenB,
uint112 balanceA,
uint112 balanceB
) internal returns (address poolAddress) {
poolAddress = address(factory.createPool(tokenA, tokenB, UNIV3_POOL_FEE));
_mintTo(address(tokenA), poolAddress, balanceA);
_mintTo(address(tokenB), poolAddress, balanceB);
}
}

View File

@ -0,0 +1,304 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2023 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import {IERC20Token} from "@0x/contracts-erc20/src/IERC20Token.sol";
import {LibNativeOrder} from "src/features/libs/LibNativeOrder.sol";
import {LibSignature} from "src/features/libs/LibSignature.sol";
import {IMultiplexFeature} from "src/features/interfaces/IMultiplexFeature.sol";
import {ITransformERC20Feature} from "src/features/interfaces/ITransformERC20Feature.sol";
import {LocalTest} from "utils/LocalTest.sol";
contract MultiplexUtils is LocalTest {
function _makeTestRfqOrder(
IERC20Token makerToken,
IERC20Token takerToken
) internal returns (LibNativeOrder.RfqOrder memory order) {
order = LibNativeOrder.RfqOrder({
makerToken: makerToken,
takerToken: takerToken,
makerAmount: 1e18,
takerAmount: 1e18,
maker: signerAddress,
taker: address(this),
txOrigin: tx.origin,
pool: 0x0000000000000000000000000000000000000000000000000000000000000000,
expiry: uint64(block.timestamp + 60),
salt: 123
});
_mintTo(address(order.makerToken), order.maker, order.makerAmount);
}
function _makeTestRfqOrder() internal returns (LibNativeOrder.RfqOrder memory order) {
return _makeTestRfqOrder(zrx, dai);
}
function _makeTestOtcOrder() internal returns (LibNativeOrder.OtcOrder memory order) {
order = LibNativeOrder.OtcOrder({
makerToken: zrx,
takerToken: dai,
makerAmount: 1e18,
takerAmount: 1e18,
maker: signerAddress,
taker: address(this),
txOrigin: tx.origin,
expiryAndNonce: ((block.timestamp + 60) << 192) | 1
});
_mintTo(address(order.makerToken), order.maker, order.makerAmount);
}
function _makeRfqSubcall(
LibNativeOrder.RfqOrder memory order,
uint256 sellAmount
) internal view returns (IMultiplexFeature.BatchSellSubcall memory) {
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
signerKey,
zeroExDeployed.features.nativeOrdersFeature.getRfqOrderHash(order)
);
LibSignature.Signature memory sig = LibSignature.Signature(LibSignature.SignatureType.EIP712, v, r, s);
return
IMultiplexFeature.BatchSellSubcall({
id: IMultiplexFeature.MultiplexSubcall.RFQ,
sellAmount: sellAmount,
data: abi.encode(order, sig)
});
}
function _makeRfqSubcall(
LibNativeOrder.RfqOrder memory order
) internal view returns (IMultiplexFeature.BatchSellSubcall memory) {
return _makeRfqSubcall(order, order.takerAmount);
}
function _makeOtcSubcall(
LibNativeOrder.OtcOrder memory order
) internal view returns (IMultiplexFeature.BatchSellSubcall memory) {
(uint8 v, bytes32 r, bytes32 s) = vm.sign(
signerKey,
zeroExDeployed.features.otcOrdersFeature.getOtcOrderHash(order)
);
LibSignature.Signature memory sig = LibSignature.Signature(LibSignature.SignatureType.EIP712, v, r, s);
return
IMultiplexFeature.BatchSellSubcall({
id: IMultiplexFeature.MultiplexSubcall.OTC,
sellAmount: order.takerAmount,
data: abi.encode(order, sig)
});
}
function _makeUniswapV2MultiHopSubcall(
address[] memory tokens,
bool isSushi
) internal pure returns (IMultiplexFeature.MultiHopSellSubcall memory) {
return
IMultiplexFeature.MultiHopSellSubcall({
id: IMultiplexFeature.MultiplexSubcall.UniswapV2,
data: abi.encode(tokens, isSushi)
});
}
function _makeUniswapV2BatchSubcall(
address[] memory tokens,
uint256 sellAmount,
bool isSushi
) internal pure returns (IMultiplexFeature.BatchSellSubcall memory) {
return
IMultiplexFeature.BatchSellSubcall({
id: IMultiplexFeature.MultiplexSubcall.UniswapV2,
sellAmount: sellAmount,
data: abi.encode(tokens, isSushi)
});
}
function _encodePathUniswapV3(address[] memory tokens) private pure returns (bytes memory path) {
path = new bytes(tokens.length * 23 - 3);
for (uint256 i = 0; i < tokens.length; i++) {
assembly {
let p := add(add(path, 32), mul(i, 23))
if gt(i, 0) {
mstore(sub(p, 3), shl(232, UNIV3_POOL_FEE))
}
let a := add(add(tokens, 32), mul(i, 32))
mstore(p, shl(96, mload(a)))
}
}
}
function _makeUniswapV3MultiHopSubcall(
address[] memory tokens
) internal pure returns (IMultiplexFeature.MultiHopSellSubcall memory) {
return
IMultiplexFeature.MultiHopSellSubcall({
id: IMultiplexFeature.MultiplexSubcall.UniswapV3,
data: _encodePathUniswapV3(tokens)
});
}
function _makeUniswapV3BatchSubcall(
address[] memory tokens,
uint256 sellAmount
) internal pure returns (IMultiplexFeature.BatchSellSubcall memory) {
return
IMultiplexFeature.BatchSellSubcall({
id: IMultiplexFeature.MultiplexSubcall.UniswapV3,
sellAmount: sellAmount,
data: _encodePathUniswapV3(tokens)
});
}
function _makeMockLiquidityProviderMultiHopSubcall()
internal
view
returns (IMultiplexFeature.MultiHopSellSubcall memory)
{
return
IMultiplexFeature.MultiHopSellSubcall({
id: IMultiplexFeature.MultiplexSubcall.LiquidityProvider,
data: abi.encode(address(liquidityProvider), hex"")
});
}
function _makeMockLiquidityProviderBatchSubcall(
uint256 sellAmount
) internal view returns (IMultiplexFeature.BatchSellSubcall memory) {
return
IMultiplexFeature.BatchSellSubcall({
id: IMultiplexFeature.MultiplexSubcall.LiquidityProvider,
sellAmount: sellAmount,
data: abi.encode(address(liquidityProvider), hex"")
});
}
function _makeMockTransformERC20Subcall(
IERC20Token inputToken,
IERC20Token outputToken,
uint256 sellAmount,
uint256 mintAmount
) internal view returns (IMultiplexFeature.BatchSellSubcall memory) {
ITransformERC20Feature.Transformation[] memory transformations = new ITransformERC20Feature.Transformation[](1);
transformations[0] = ITransformERC20Feature.Transformation(
uint32(transformerNonce),
abi.encode(address(inputToken), address(outputToken), sellAmount, mintAmount, 0)
);
return
IMultiplexFeature.BatchSellSubcall({
id: IMultiplexFeature.MultiplexSubcall.TransformERC20,
sellAmount: sellAmount,
data: abi.encode(transformations)
});
}
function _makeNestedBatchSellSubcall(
IMultiplexFeature.BatchSellSubcall[] memory calls
) internal pure returns (IMultiplexFeature.MultiHopSellSubcall memory) {
return
IMultiplexFeature.MultiHopSellSubcall({
id: IMultiplexFeature.MultiplexSubcall.BatchSell,
data: abi.encode(calls)
});
}
function _makeNestedMultiHopSellSubcall(
address[] memory tokens,
IMultiplexFeature.MultiHopSellSubcall[] memory calls,
uint256 sellAmount
) internal pure returns (IMultiplexFeature.BatchSellSubcall memory) {
return
IMultiplexFeature.BatchSellSubcall({
id: IMultiplexFeature.MultiplexSubcall.MultiHopSell,
sellAmount: sellAmount,
data: abi.encode(tokens, calls)
});
}
function _makeArray(
IMultiplexFeature.MultiHopSellSubcall memory first
) internal pure returns (IMultiplexFeature.MultiHopSellSubcall[] memory subcalls) {
subcalls = new IMultiplexFeature.MultiHopSellSubcall[](1);
subcalls[0] = first;
}
function _makeArray(
IMultiplexFeature.MultiHopSellSubcall memory first,
IMultiplexFeature.MultiHopSellSubcall memory second
) internal pure returns (IMultiplexFeature.MultiHopSellSubcall[] memory subcalls) {
subcalls = new IMultiplexFeature.MultiHopSellSubcall[](2);
subcalls[0] = first;
subcalls[1] = second;
}
function _makeArray(
IMultiplexFeature.BatchSellSubcall memory first
) internal pure returns (IMultiplexFeature.BatchSellSubcall[] memory subcalls) {
subcalls = new IMultiplexFeature.BatchSellSubcall[](1);
subcalls[0] = first;
}
function _makeArray(
IMultiplexFeature.BatchSellSubcall memory first,
IMultiplexFeature.BatchSellSubcall memory second
) internal pure returns (IMultiplexFeature.BatchSellSubcall[] memory subcalls) {
subcalls = new IMultiplexFeature.BatchSellSubcall[](2);
subcalls[0] = first;
subcalls[1] = second;
}
function _makeArray(
IMultiplexFeature.BatchSellSubcall memory first,
IMultiplexFeature.BatchSellSubcall memory second,
IMultiplexFeature.BatchSellSubcall memory third
) internal pure returns (IMultiplexFeature.BatchSellSubcall[] memory subcalls) {
subcalls = new IMultiplexFeature.BatchSellSubcall[](3);
subcalls[0] = first;
subcalls[1] = second;
subcalls[2] = third;
}
function _makeArray(address first) internal pure returns (address[] memory addresses) {
addresses = new address[](1);
addresses[0] = first;
}
function _makeArray(address first, address second) internal pure returns (address[] memory addresses) {
addresses = new address[](2);
addresses[0] = first;
addresses[1] = second;
}
function _makeArray(
address first,
address second,
address third
) internal pure returns (address[] memory addresses) {
addresses = new address[](3);
addresses[0] = first;
addresses[1] = second;
addresses[2] = third;
}
function _encodeFractionalFillAmount(uint256 frac) internal pure returns (uint256) {
return (2 ** 255) + (frac * 1e16);
}
}

View File

@ -29,4 +29,13 @@ contract TestUtils is Test {
}
}
}
// gets a dummy signer
function _getSigner() internal returns (address, uint) {
string memory mnemonic = "test test test test test test test test test test test junk";
uint256 privateKey = vm.deriveKey(mnemonic, 0);
vm.label(vm.addr(privateKey), "zeroEx/MarketMaker");
return (vm.addr(privateKey), privateKey);
}
}