Compare commits
9 Commits
feat/Multi
...
@0x/contra
Author | SHA1 | Date | |
---|---|---|---|
|
d97dab83cd | ||
|
f0a4f6aeca | ||
|
f8a183812e | ||
|
f6fd374590 | ||
|
09763bcf5e | ||
|
3ab12ed254 | ||
|
25731653ef | ||
|
ec82c42c1b | ||
|
38665ffc86 |
@@ -1,4 +1,21 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1677693479,
|
||||
"version": "4.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "4.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Migrated package to foundry"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1675210931,
|
||||
"version": "3.3.57",
|
||||
|
@@ -5,7 +5,11 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.0.0 - _February 16, 2023_
|
||||
## v4.0.1 - _March 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.0.0 - _Invalid date_
|
||||
|
||||
* Migrated package to foundry
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc20",
|
||||
"version": "4.0.0",
|
||||
"version": "4.0.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -24,11 +24,10 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol",
|
||||
"devDependencies": {
|
||||
"@0x/contracts-utils": "^4.8.38",
|
||||
"@0x/contracts-utils": "^4.8.39",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"typedoc": "~0.16.11"
|
||||
},
|
||||
"dependencies": {},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1677693479,
|
||||
"version": "5.4.48",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1675210931,
|
||||
"version": "5.4.47",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v5.4.48 - _March 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.47 - _February 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-test-utils",
|
||||
"version": "5.4.47",
|
||||
"version": "5.4.48",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -41,7 +41,7 @@
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.35",
|
||||
"@0x/base-contract": "^7.0.0",
|
||||
"@0x/contract-addresses": "^8.0.3",
|
||||
"@0x/contract-addresses": "^8.1.0",
|
||||
"@0x/dev-utils": "^5.0.0",
|
||||
"@0x/json-schemas": "^6.4.4",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1677693479,
|
||||
"version": "1.4.41",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1675210931,
|
||||
"version": "1.4.40",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.4.41 - _March 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.40 - _February 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-treasury",
|
||||
"version": "1.4.40",
|
||||
"version": "1.4.41",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -46,12 +46,12 @@
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.8.1",
|
||||
"@0x/contract-addresses": "^8.0.3",
|
||||
"@0x/contract-addresses": "^8.1.0",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-erc20": "3.3.57",
|
||||
"@0x/contracts-gen": "^2.0.48",
|
||||
"@0x/contracts-staking": "^2.0.45",
|
||||
"@0x/contracts-test-utils": "^5.4.47",
|
||||
"@0x/contracts-test-utils": "^5.4.48",
|
||||
"@0x/sol-compiler": "^4.8.2",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"@types/isomorphic-fetch": "^0.0.35",
|
||||
@@ -73,7 +73,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^7.0.0",
|
||||
"@0x/protocol-utils": "^11.17.6",
|
||||
"@0x/protocol-utils": "^11.18.0",
|
||||
"@0x/subproviders": "^7.0.0",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1677693479,
|
||||
"version": "4.8.39",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1675210931,
|
||||
"version": "4.8.38",
|
||||
|
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.8.39 - _March 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.8.38 - _February 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-utils",
|
||||
"version": "4.8.38",
|
||||
"version": "4.8.39",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -46,7 +46,7 @@
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.8.1",
|
||||
"@0x/contracts-gen": "^2.0.48",
|
||||
"@0x/contracts-test-utils": "^5.4.47",
|
||||
"@0x/contracts-test-utils": "^5.4.48",
|
||||
"@0x/dev-utils": "^5.0.0",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-compiler": "^4.8.2",
|
||||
|
@@ -4,8 +4,12 @@
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add KyberElastic mixin for Ethereum, Polygon, Arbitrum, Avalanche"
|
||||
},
|
||||
{
|
||||
"note": "Skip chain id validation in AbstractBridgeAdapter on testnets"
|
||||
}
|
||||
]
|
||||
],
|
||||
"timestamp": 1677693479
|
||||
},
|
||||
{
|
||||
"timestamp": 1675210931,
|
||||
|
@@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v0.39.0 - _March 1, 2023_
|
||||
|
||||
* Add KyberElastic mixin for Ethereum, Polygon, Arbitrum, Avalanche
|
||||
* Skip chain id validation in AbstractBridgeAdapter on testnets
|
||||
|
||||
## v0.38.6 - _February 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -391,7 +391,6 @@ contract MultiplexFeature is
|
||||
// Compute the expected address and transfer the input tokens
|
||||
// there if necessary.
|
||||
state.from = _computeHopTarget(params, 0);
|
||||
|
||||
// If the input tokens are currently held by `msg.sender` but
|
||||
// the first hop expects them elsewhere, perform a `transferFrom`.
|
||||
if (!params.useSelfBalance && state.from != msg.sender) {
|
||||
@@ -408,6 +407,7 @@ contract MultiplexFeature is
|
||||
// Compute the recipient of the tokens that will be
|
||||
// bought by the current hop.
|
||||
state.to = _computeHopTarget(params, state.hopIndex + 1);
|
||||
|
||||
if (subcall.id == MultiplexSubcall.UniswapV2) {
|
||||
_multiHopSellUniswapV2(state, params, subcall.data);
|
||||
} else if (subcall.id == MultiplexSubcall.UniswapV3) {
|
||||
@@ -416,8 +416,6 @@ contract MultiplexFeature is
|
||||
_multiHopSellLiquidityProvider(state, params, subcall.data);
|
||||
} else if (subcall.id == MultiplexSubcall.BatchSell) {
|
||||
_nestedBatchSell(state, params, subcall.data);
|
||||
} else if (subcall.id == MultiplexSubcall.OTC) {
|
||||
_multiHopSellOtcOrder(state, params, subcall.data);
|
||||
} else {
|
||||
revert("MultiplexFeature::_executeMultiHopSell/INVALID_SUBCALL");
|
||||
}
|
||||
@@ -533,13 +531,6 @@ contract MultiplexFeature is
|
||||
} else {
|
||||
target = address(this);
|
||||
}
|
||||
} else if (subcall.id == MultiplexSubcall.OTC) {
|
||||
//on the first call we want to pull tokens from the taker, subsequent calls should use the EP balance
|
||||
if (i == 0) {
|
||||
target = msg.sender;
|
||||
} else {
|
||||
target = address(this);
|
||||
}
|
||||
} else {
|
||||
revert("MultiplexFeature::_computeHopTarget/INVALID_SUBCALL");
|
||||
}
|
||||
|
@@ -65,33 +65,4 @@ abstract contract MultiplexOtc is FixinEIP712 {
|
||||
state.boughtAmount = state.boughtAmount.safeAdd(makerTokenFilledAmount);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function _multiHopSellOtcOrder(
|
||||
IMultiplexFeature.MultiHopSellState memory state,
|
||||
IMultiplexFeature.MultiHopSellParams memory params,
|
||||
bytes memory wrappedCallData
|
||||
) internal {
|
||||
// Decode the Otc order, and signature.
|
||||
(LibNativeOrder.OtcOrder memory order, LibSignature.Signature memory signature) = abi
|
||||
.decode(wrappedCallData, (LibNativeOrder.OtcOrder, LibSignature.Signature));
|
||||
//Make sure that the otc orders maker and taker tokens match the fill sequence in params.tokens[]
|
||||
require(
|
||||
address(order.takerToken) == params.tokens[state.hopIndex] &&
|
||||
address(order.makerToken) == params.tokens[state.hopIndex + 1],
|
||||
"MultiplexOtcOrder::_multiHopSellOtcOrder/INVALID_TOKENS"
|
||||
);
|
||||
uint256 sellAmount = state.outputTokenAmount;
|
||||
// Try filling the Otc order. Bubble up reverts.
|
||||
(uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount) = IOtcOrdersFeature(address(this))
|
||||
._fillOtcOrder(
|
||||
order,
|
||||
signature,
|
||||
sellAmount.safeDowncastToUint128(),
|
||||
state.hopIndex == 0 ? msg.sender : address(this),
|
||||
params.useSelfBalance,
|
||||
state.to
|
||||
);
|
||||
//store the bought amount for the next hop
|
||||
state.outputTokenAmount = makerTokenFilledAmount;
|
||||
}
|
||||
}
|
||||
|
@@ -23,8 +23,14 @@ abstract contract AbstractBridgeAdapter is IBridgeAdapter {
|
||||
assembly {
|
||||
chainId := chainid()
|
||||
}
|
||||
// Allow testing on Ganache
|
||||
if (chainId != expectedChainId && chainId != 1337) {
|
||||
// Skip chain id validation on Ganache (1337), Anvil (31337), Goerli (5), Mumbai (80001), Base Goerli (84531)
|
||||
bool skipValidation = (chainId == 1337 ||
|
||||
chainId == 31337 ||
|
||||
chainId == 5 ||
|
||||
chainId == 80001 ||
|
||||
chainId == 84531);
|
||||
|
||||
if (chainId != expectedChainId && !skipValidation) {
|
||||
revert(string(abi.encodePacked(expectedChainName, "BridgeAdapter.constructor: wrong chain ID")));
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-zero-ex",
|
||||
"version": "0.38.6",
|
||||
"version": "0.39.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -51,10 +51,10 @@
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.8.1",
|
||||
"@0x/contract-addresses": "^8.0.3",
|
||||
"@0x/contract-addresses": "^8.1.0",
|
||||
"@0x/contracts-erc20": "^3.3.57",
|
||||
"@0x/contracts-gen": "^2.0.48",
|
||||
"@0x/contracts-test-utils": "^5.4.47",
|
||||
"@0x/contracts-test-utils": "^5.4.48",
|
||||
"@0x/dev-utils": "^5.0.0",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-compiler": "^4.8.2",
|
||||
@@ -80,7 +80,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^7.0.0",
|
||||
"@0x/protocol-utils": "^11.17.6",
|
||||
"@0x/protocol-utils": "^11.18.0",
|
||||
"@0x/subproviders": "^7.0.0",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
|
615
contracts/zero-ex/tests/Multiplex.t.sol
Normal file
615
contracts/zero-ex/tests/Multiplex.t.sol
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -1,220 +0,0 @@
|
||||
// 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 "../utils/ForkUtils.sol";
|
||||
import "../utils/TestUtils.sol";
|
||||
import "src/IZeroEx.sol";
|
||||
import "@0x/contracts-erc20/src/IEtherToken.sol";
|
||||
import "src/features/TransformERC20Feature.sol";
|
||||
import "src/features/multiplex/MultiplexFeature.sol";
|
||||
import "src/external/TransformerDeployer.sol";
|
||||
import "src/transformers/WethTransformer.sol";
|
||||
import "src/transformers/FillQuoteTransformer.sol";
|
||||
import "src/transformers/bridges/BridgeProtocols.sol";
|
||||
import "src/features/OtcOrdersFeature.sol";
|
||||
|
||||
contract MultiplexRfqtTest is Test, ForkUtils, TestUtils {
|
||||
using LibERC20TokenV06 for IERC20Token;
|
||||
using LibERC20TokenV06 for IEtherToken;
|
||||
|
||||
function setUp() public {
|
||||
_setup();
|
||||
}
|
||||
|
||||
function test_swapEthForUSDTThroughFqtOtcs() public {
|
||||
log_string("SwapEthForUSDTThroughFqtOtc");
|
||||
/* */
|
||||
for (uint256 i = 0; i < 1; i++) {
|
||||
//skip fantom/avax failing test
|
||||
if (i == 3 || i == 4) {
|
||||
continue;
|
||||
}
|
||||
vm.selectFork(forkIds[chains[i]]);
|
||||
log_named_string(" Selecting Fork On", chains[i]);
|
||||
vm.deal(address(this), 1e18);
|
||||
labelAddresses(
|
||||
chains[i],
|
||||
indexChainsByChain[chains[i]],
|
||||
getTokens(i),
|
||||
getContractAddresses(i),
|
||||
getLiquiditySourceAddresses(i)
|
||||
);
|
||||
|
||||
//redeploy and migrate multiplexFeature and OtcOrders for logging
|
||||
|
||||
MultiplexFeature multiplexFeature = new MultiplexFeature(
|
||||
address(IZERO_EX),
|
||||
IEtherToken(tokens.WrappedNativeToken),
|
||||
ILiquidityProviderSandbox(addresses.exchangeProxyLiquidityProviderSandbox),
|
||||
address(0), // uniswapFactory
|
||||
address(0), // sushiswapFactory
|
||||
bytes32(0), // uniswapPairInitCodeHash
|
||||
bytes32(0) // sushiswapPairInitCodeHash
|
||||
);
|
||||
|
||||
OtcOrdersFeature otcOrdersFeature = new OtcOrdersFeature(
|
||||
address(addresses.exchangeProxy),
|
||||
tokens.WrappedNativeToken
|
||||
);
|
||||
|
||||
vm.label(address(multiplexFeature), "zeroEx/NewMultiplexFeature");
|
||||
vm.label(address(otcOrdersFeature), "zeroEx/NewOtcOrdersFeature");
|
||||
vm.prank(IZeroEx(addresses.exchangeProxy).owner());
|
||||
|
||||
IZeroEx(addresses.exchangeProxy).migrate(
|
||||
address(otcOrdersFeature),
|
||||
abi.encodeWithSelector(OtcOrdersFeature.migrate.selector),
|
||||
address(addresses.exchangeProxy)
|
||||
);
|
||||
vm.prank(IZeroEx(addresses.exchangeProxy).owner());
|
||||
IZeroEx(addresses.exchangeProxy).migrate(
|
||||
address(multiplexFeature),
|
||||
abi.encodeWithSelector(MultiplexFeature.migrate.selector),
|
||||
address(addresses.exchangeProxy)
|
||||
);
|
||||
swapMultihopOtc(getTokens(i), getContractAddresses(i), getLiquiditySourceAddresses(i));
|
||||
}
|
||||
}
|
||||
|
||||
/* solhint-disable function-max-lines */
|
||||
|
||||
function swapMultihopOtc(
|
||||
TokenAddresses memory tokens,
|
||||
ContractAddresses memory addresses,
|
||||
LiquiditySources memory sources
|
||||
) public onlyForked {
|
||||
IZERO_EX = IZeroEx(addresses.exchangeProxy);
|
||||
|
||||
address[] memory tradeTokens = new address[](3);
|
||||
tradeTokens[0] = address(tokens.WrappedNativeToken);
|
||||
tradeTokens[1] = address(tokens.USDC);
|
||||
tradeTokens[2] = address(tokens.DAI);
|
||||
|
||||
deal(tradeTokens[0], address(this), 1e18);
|
||||
|
||||
tokens.WrappedNativeToken.approveIfBelow(addresses.exchangeProxy, uint(-1));
|
||||
uint inputAmount = 1e18;
|
||||
uint outputAmount = 5e17;
|
||||
|
||||
IMultiplexFeature.MultiHopSellSubcall[] memory subcalls = new IMultiplexFeature.MultiHopSellSubcall[](2);
|
||||
|
||||
IMultiplexFeature.MultiHopSellSubcall memory subcall1;
|
||||
subcall1.id = IMultiplexFeature.MultiplexSubcall.OTC;
|
||||
|
||||
//subcall.data = abi.encode(address[], LibNativeOrder.OtcOrder, LibSignature.Signature);
|
||||
(LibNativeOrder.OtcOrder memory order1, LibSignature.Signature memory signature1) = createOtcOrder(
|
||||
tokens.WrappedNativeToken,
|
||||
tokens.USDC,
|
||||
1e18,
|
||||
5e17,
|
||||
0
|
||||
);
|
||||
subcall1.data = abi.encode(order1, signature1);
|
||||
|
||||
IMultiplexFeature.MultiHopSellSubcall memory subcall2;
|
||||
subcall2.id = IMultiplexFeature.MultiplexSubcall.OTC;
|
||||
(LibNativeOrder.OtcOrder memory order2, LibSignature.Signature memory signature2) = createOtcOrder(
|
||||
tokens.USDC,
|
||||
tokens.DAI,
|
||||
5e17,
|
||||
5e17,
|
||||
1
|
||||
);
|
||||
subcall2.data = abi.encode(order2, signature2);
|
||||
|
||||
subcalls[0] = subcall1;
|
||||
subcalls[1] = subcall2;
|
||||
|
||||
uint balanceBefore = tokens.DAI.balanceOf(address(this));
|
||||
emit log_named_uint("DAI Balance Before", balanceBefore);
|
||||
emit log_string("Multihop Rfqt: WETH->USDC->DAI");
|
||||
|
||||
/// @dev Sells `sellAmount` of the input token (`tokens[0]`)
|
||||
/// via the given sequence of tokens and calls.
|
||||
/// The last token in `tokens` is the output token that
|
||||
/// will ultimately be sent to `msg.sender`
|
||||
/// @param tokens The sequence of tokens to use for the sell,
|
||||
/// i.e. `tokens[i]` will be sold for `tokens[i+1]` via
|
||||
/// `calls[i]`.
|
||||
/// @param calls The sequence of calls to use for the sell.
|
||||
/// @param sellAmount The amount of `inputToken` to sell.
|
||||
/// @param minBuyAmount The minimum amount of output tokens that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of output tokens bought.
|
||||
IZERO_EX.multiplexMultiHopSellTokenForToken(
|
||||
// input token[] [input, intermediate, output]
|
||||
tradeTokens,
|
||||
//array of subcalls [{},{}]
|
||||
subcalls,
|
||||
// input token amount
|
||||
inputAmount,
|
||||
// min output token amount
|
||||
outputAmount
|
||||
);
|
||||
uint balanceAfter = tokens.DAI.balanceOf(address(this));
|
||||
emit log_named_uint("DAI Balance After", balanceAfter - balanceBefore);
|
||||
require(balanceAfter >= 5e17, "Failed: UNDERBOUGHT");
|
||||
}
|
||||
|
||||
function createOtcOrder(
|
||||
IERC20Token inputToken,
|
||||
IERC20Token ouputToken,
|
||||
uint128 takerAmount,
|
||||
uint128 makerAmount,
|
||||
uint bump
|
||||
) public returns (LibNativeOrder.OtcOrder memory order, LibSignature.Signature memory signature) {
|
||||
LibNativeOrder.OtcOrder memory order;
|
||||
FillQuoteTransformer.OtcOrderInfo memory orderInfo;
|
||||
LibSignature.Signature memory signature;
|
||||
order.makerToken = ouputToken;
|
||||
order.takerToken = inputToken;
|
||||
order.takerAmount = takerAmount;
|
||||
order.makerAmount = makerAmount;
|
||||
uint privateKey;
|
||||
|
||||
(order.maker, privateKey) = getSigner();
|
||||
|
||||
deal(address(order.makerToken), order.maker, 2e20);
|
||||
deal(address(order.takerToken), order.maker, 2e20);
|
||||
|
||||
vm.startPrank(order.maker);
|
||||
IERC20Token(order.makerToken).approveIfBelow(addresses.exchangeProxy, 2e20);
|
||||
|
||||
order.taker = address(0);
|
||||
order.txOrigin = address(tx.origin);
|
||||
order.expiryAndNonce = encodeExpiryAndNonce(order.maker, bump);
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, IZERO_EX.getOtcOrderHash(order));
|
||||
|
||||
vm.stopPrank();
|
||||
signature.signatureType = LibSignature.SignatureType.EIP712;
|
||||
signature.v = v;
|
||||
signature.r = r;
|
||||
signature.s = s;
|
||||
|
||||
return (order, signature);
|
||||
}
|
||||
|
||||
/* solhint-enable function-max-lines */
|
||||
function encodeExpiryAndNonce(address maker, uint bump) public returns (uint256) {
|
||||
uint256 expiry = (block.timestamp + 120) << 192;
|
||||
uint256 bucket = (0 + bump) << 128;
|
||||
uint256 nonce = vm.getNonce(maker);
|
||||
return expiry | bucket | nonce;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
|
140
contracts/zero-ex/tests/utils/LocalTest.sol
Normal file
140
contracts/zero-ex/tests/utils/LocalTest.sol
Normal 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);
|
||||
}
|
||||
}
|
304
contracts/zero-ex/tests/utils/MultiplexUtils.sol
Normal file
304
contracts/zero-ex/tests/utils/MultiplexUtils.sol
Normal 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);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,14 @@
|
||||
[
|
||||
{
|
||||
"version": "8.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Upgrade Mainnet and Polygon FillQuoteTransformers to support KyberElastic",
|
||||
"pr": 669
|
||||
}
|
||||
],
|
||||
"timestamp": 1677693479
|
||||
},
|
||||
{
|
||||
"version": "8.0.3",
|
||||
"changes": [
|
||||
|
@@ -6,6 +6,9 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v8.1.0 - _March 1, 2023_
|
||||
|
||||
* Upgrade Mainnet and Polygon FillQuoteTransformers to support KyberElastic (#669)
|
||||
## v8.0.3 - _February 1, 2023_
|
||||
|
||||
* Upgrade Arbitrum FillQuoteTransformer to support Woofi (#653)
|
||||
|
@@ -18,7 +18,7 @@
|
||||
"wethTransformer": "0xb2bc06a4efb20fc6553a69dbfa49b7be938034a7",
|
||||
"payTakerTransformer": "0xea500d073652336a58846ada15c25f2c6d2d241f",
|
||||
"affiliateFeeTransformer": "0x8146cbbe327364b13d0699f2ced39c637f92501a",
|
||||
"fillQuoteTransformer": "0xd8e10b0689d0b2f28136777635caa4abd2bb9f04",
|
||||
"fillQuoteTransformer": "0xd7a6ced9e0eaa8e5ad6e7335d25d46b6f456d4fd",
|
||||
"positiveSlippageFeeTransformer": "0x818a4a855bfeb16c305cb65e8d4fb239a308bc48"
|
||||
}
|
||||
},
|
||||
@@ -110,7 +110,7 @@
|
||||
"wethTransformer": "0xe309d011cc6f189a3e8dcba85922715a019fed38",
|
||||
"payTakerTransformer": "0xed8932ca083e1ef1960dea875a132926e6b242ab",
|
||||
"affiliateFeeTransformer": "0xf79071e2f860d48a08fd7e091d4b126a1d757148",
|
||||
"fillQuoteTransformer": "0xe35653ead385b6299099f6d43b7c17cc12df9110",
|
||||
"fillQuoteTransformer": "0x5fe5885aedc42cd51073c7e5af841e3a11556653",
|
||||
"positiveSlippageFeeTransformer": "0x8f5e7188f443a9a8dc180f4618fd23915043ea15"
|
||||
}
|
||||
},
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contract-addresses",
|
||||
"version": "8.0.3",
|
||||
"version": "8.1.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1677693479,
|
||||
"version": "13.22.17",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1675210931,
|
||||
"version": "13.22.16",
|
||||
|
@@ -6,6 +6,9 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v13.22.17 - _March 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
## v13.22.16 - _February 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contract-wrappers",
|
||||
"version": "13.22.16",
|
||||
"version": "13.22.17",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -59,7 +59,7 @@
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.35",
|
||||
"@0x/base-contract": "^7.0.0",
|
||||
"@0x/contract-addresses": "^8.0.3",
|
||||
"@0x/contract-addresses": "^8.1.0",
|
||||
"@0x/json-schemas": "^6.4.4",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/utils": "^7.0.0",
|
||||
|
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "11.18.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add KyberElastic support"
|
||||
}
|
||||
],
|
||||
"timestamp": 1677693479
|
||||
},
|
||||
{
|
||||
"timestamp": 1675210931,
|
||||
"version": "11.17.6",
|
||||
|
@@ -6,6 +6,9 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v11.18.0 - _March 1, 2023_
|
||||
|
||||
* Add KyberElastic support
|
||||
## v11.17.6 - _February 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/protocol-utils",
|
||||
"version": "11.17.6",
|
||||
"version": "11.18.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -62,8 +62,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.35",
|
||||
"@0x/contract-addresses": "^8.0.3",
|
||||
"@0x/contract-wrappers": "^13.22.16",
|
||||
"@0x/contract-addresses": "^8.1.0",
|
||||
"@0x/contract-wrappers": "^13.22.17",
|
||||
"@0x/json-schemas": "^6.4.4",
|
||||
"@0x/subproviders": "^7.0.0",
|
||||
"@0x/utils": "^7.0.0",
|
||||
|
Reference in New Issue
Block a user