Feature/liquidity provider sandbox (#16)

* Update liquidity provider feature to use sandbox

* add support for liquidity provider feature in the exchange proxy swap quote consumer

* Move to off-chain liquidity provider registry

* Update ILiquidityProvider interface

* Remove some unused artifacts and wrappers

* Consolidate ILiquidityProvider

* prettier

* lint

* Address PR feedback

* Add failover to sandbox

* Add test for failover behavior in LiquidityProviderSandbox

* Update changelogs

* Emit events for the new LiquidityProvider scenarios

* Fix swap quote consumer bug

* post-rebase fixes

* `@0x/contracts-zero-ex`: bump feature versions

* Add default field to TokenAdjacencyGraph

* update addresses

Co-authored-by: Lawrence Forman <me@merklejerk.com>
This commit is contained in:
mzhu25 2020-11-13 12:22:21 -08:00 committed by GitHub
parent 75dcd687e2
commit 7403c0255a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 1083 additions and 2098 deletions

View File

@ -5,6 +5,18 @@
{ {
"note": "Rewrite the ZeroEx contract in Yul", "note": "Rewrite the ZeroEx contract in Yul",
"pr": 23 "pr": 23
},
{
"note": "Update LiquidityProviderFeature to use off-chain registry and sandbox",
"pr": 16
},
{
"note": "Update ILiquidityProvider interface",
"pr": 16
},
{
"note": "Update ProtocolFeeUnfunded event to emit order hash",
"pr": 16
} }
] ]
}, },

View File

@ -45,19 +45,4 @@ library LibLiquidityProviderRichErrors {
minBuyAmount minBuyAmount
); );
} }
function NoLiquidityProviderForMarketError(
address xAsset,
address yAsset
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("NoLiquidityProviderForMarketError(address,address)")),
xAsset,
yAsset
);
}
} }

View File

@ -0,0 +1,74 @@
/*
Copyright 2020 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.5;
pragma experimental ABIEncoderV2;
interface ILiquidityProviderSandbox {
/// @dev Calls `sellTokenForToken` on the given `provider` contract to
/// trigger a trade.
/// @param provider The address of the on-chain liquidity provider.
/// @param inputToken The token being sold.
/// @param outputToken The token being bought.
/// @param recipient The recipient of the bought tokens.
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
function executeSellTokenForToken(
address provider,
address inputToken,
address outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external;
/// @dev Calls `sellEthForToken` on the given `provider` contract to
/// trigger a trade.
/// @param provider The address of the on-chain liquidity provider.
/// @param outputToken The token being bought.
/// @param recipient The recipient of the bought tokens.
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
function executeSellEthForToken(
address provider,
address outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external;
/// @dev Calls `sellTokenForEth` on the given `provider` contract to
/// trigger a trade.
/// @param provider The address of the on-chain liquidity provider.
/// @param inputToken The token being sold.
/// @param recipient The recipient of the bought tokens.
/// @param minBuyAmount The minimum acceptable amount of ETH to buy.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
function executeSellTokenForEth(
address provider,
address inputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external;
}

View File

@ -0,0 +1,139 @@
/*
Copyright 2020 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.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibOwnableRichErrorsV06.sol";
import "../vendor/ILiquidityProvider.sol";
import "../vendor/v3/IERC20Bridge.sol";
import "./ILiquidityProviderSandbox.sol";
/// @dev A permissionless contract through which the ZeroEx contract can
/// safely trigger a trade on an external `ILiquidityProvider` contract.
contract LiquidityProviderSandbox is
ILiquidityProviderSandbox
{
using LibRichErrorsV06 for bytes;
/// @dev Store the owner as an immutable.
address public immutable owner;
constructor(address owner_)
public
{
owner = owner_;
}
/// @dev Allows only the (immutable) owner to call a function.
modifier onlyOwner() virtual {
if (msg.sender != owner) {
LibOwnableRichErrorsV06.OnlyOwnerError(
msg.sender,
owner
).rrevert();
}
_;
}
/// @dev Calls `sellTokenForToken` on the given `provider` contract to
/// trigger a trade.
/// @param provider The address of the on-chain liquidity provider.
/// @param inputToken The token being sold.
/// @param outputToken The token being bought.
/// @param recipient The recipient of the bought tokens.
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
function executeSellTokenForToken(
address provider,
address inputToken,
address outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external
onlyOwner
override
{
try ILiquidityProvider(provider).sellTokenForToken(
inputToken,
outputToken,
recipient,
minBuyAmount,
auxiliaryData
) {} catch {
IERC20Bridge(provider).bridgeTransferFrom(
outputToken,
provider,
recipient,
minBuyAmount,
auxiliaryData
);
}
}
/// @dev Calls `sellEthForToken` on the given `provider` contract to
/// trigger a trade.
/// @param provider The address of the on-chain liquidity provider.
/// @param outputToken The token being bought.
/// @param recipient The recipient of the bought tokens.
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
function executeSellEthForToken(
address provider,
address outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external
onlyOwner
override
{
ILiquidityProvider(provider).sellEthForToken(
outputToken,
recipient,
minBuyAmount,
auxiliaryData
);
}
/// @dev Calls `sellTokenForEth` on the given `provider` contract to
/// trigger a trade.
/// @param provider The address of the on-chain liquidity provider.
/// @param inputToken The token being sold.
/// @param recipient The recipient of the bought tokens.
/// @param minBuyAmount The minimum acceptable amount of ETH to buy.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
function executeSellTokenForEth(
address provider,
address inputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external
onlyOwner
override
{
ILiquidityProvider(provider).sellTokenForEth(
inputToken,
payable(recipient),
minBuyAmount,
auxiliaryData
);
}
}

View File

@ -22,45 +22,30 @@ pragma experimental ABIEncoderV2;
/// @dev Feature to swap directly with an on-chain liquidity provider. /// @dev Feature to swap directly with an on-chain liquidity provider.
interface ILiquidityProviderFeature { interface ILiquidityProviderFeature {
event LiquidityProviderForMarketUpdated(
address indexed xAsset,
address indexed yAsset,
address providerAddress
);
/// @dev Sells `sellAmount` of `inputToken` to the liquidity provider
/// at the given `provider` address.
/// @param inputToken The token being sold.
/// @param outputToken The token being bought.
/// @param provider The address of the on-chain liquidity provider
/// to trade with.
/// @param recipient The recipient of the bought tokens. If equal to
/// address(0), `msg.sender` is assumed to be the recipient.
/// @param sellAmount The amount of `inputToken` to sell.
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to
/// buy. Reverts if this amount is not satisfied.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
/// @return boughtAmount The amount of `outputToken` bought.
function sellToLiquidityProvider( function sellToLiquidityProvider(
address makerToken, address inputToken,
address takerToken, address outputToken,
address payable recipient, address payable provider,
address recipient,
uint256 sellAmount, uint256 sellAmount,
uint256 minBuyAmount uint256 minBuyAmount,
bytes calldata auxiliaryData
) )
external external
payable payable
returns (uint256 boughtAmount); returns (uint256 boughtAmount);
/// @dev Sets address of the liquidity provider for a market given
/// (xAsset, yAsset).
/// @param xAsset First asset managed by the liquidity provider.
/// @param yAsset Second asset managed by the liquidity provider.
/// @param providerAddress Address of the liquidity provider.
function setLiquidityProviderForMarket(
address xAsset,
address yAsset,
address providerAddress
)
external;
/// @dev Returns the address of the liquidity provider for a market given
/// (xAsset, yAsset), or reverts if pool does not exist.
/// @param xAsset First asset managed by the liquidity provider.
/// @param yAsset Second asset managed by the liquidity provider.
/// @return providerAddress Address of the liquidity provider.
function getLiquidityProviderForMarket(
address xAsset,
address yAsset
)
external
view
returns (address providerAddress);
} }

View File

@ -20,15 +20,13 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol"; import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "../errors/LibLiquidityProviderRichErrors.sol"; import "../errors/LibLiquidityProviderRichErrors.sol";
import "../external/ILiquidityProviderSandbox.sol";
import "../external/LiquidityProviderSandbox.sol";
import "../fixins/FixinCommon.sol"; import "../fixins/FixinCommon.sol";
import "../migrations/LibMigrate.sol"; import "../migrations/LibMigrate.sol";
import "../storage/LibLiquidityProviderStorage.sol";
import "../vendor/v3/IERC20Bridge.sol";
import "./IFeature.sol"; import "./IFeature.sol";
import "./ILiquidityProviderFeature.sol"; import "./ILiquidityProviderFeature.sol";
import "./libs/LibTokenSpender.sol"; import "./libs/LibTokenSpender.sol";
@ -39,7 +37,6 @@ contract LiquidityProviderFeature is
ILiquidityProviderFeature, ILiquidityProviderFeature,
FixinCommon FixinCommon
{ {
using LibERC20TokenV06 for IERC20TokenV06;
using LibSafeMathV06 for uint256; using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes; using LibRichErrorsV06 for bytes;
@ -50,16 +47,24 @@ contract LiquidityProviderFeature is
/// @dev ETH pseudo-token address. /// @dev ETH pseudo-token address.
address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev The WETH contract address. /// @dev The sandbox contract address.
IEtherTokenV06 public immutable weth; ILiquidityProviderSandbox public immutable sandbox;
/// @dev Store the WETH address in an immutable. /// @dev Event for data pipeline.
/// @param weth_ The weth token. event LiquidityProviderSwap(
constructor(IEtherTokenV06 weth_) address inputToken,
address outputToken,
uint256 inputTokenAmount,
uint256 outputTokenAmount,
address provider,
address recipient
);
constructor(address zeroEx)
public public
FixinCommon() FixinCommon()
{ {
weth = weth_; sandbox = new LiquidityProviderSandbox(zeroEx);
} }
/// @dev Initialize and register this feature. /// @dev Initialize and register this feature.
@ -70,131 +75,102 @@ contract LiquidityProviderFeature is
returns (bytes4 success) returns (bytes4 success)
{ {
_registerFeatureFunction(this.sellToLiquidityProvider.selector); _registerFeatureFunction(this.sellToLiquidityProvider.selector);
_registerFeatureFunction(this.setLiquidityProviderForMarket.selector);
_registerFeatureFunction(this.getLiquidityProviderForMarket.selector);
return LibMigrate.MIGRATE_SUCCESS; return LibMigrate.MIGRATE_SUCCESS;
} }
/// @dev Sells `sellAmount` of `inputToken` to the liquidity provider
/// at the given `provider` address.
/// @param inputToken The token being sold.
/// @param outputToken The token being bought.
/// @param provider The address of the on-chain liquidity provider
/// to trade with.
/// @param recipient The recipient of the bought tokens. If equal to
/// address(0), `msg.sender` is assumed to be the recipient.
/// @param sellAmount The amount of `inputToken` to sell.
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to
/// buy. Reverts if this amount is not satisfied.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
/// @return boughtAmount The amount of `outputToken` bought.
function sellToLiquidityProvider( function sellToLiquidityProvider(
address makerToken, address inputToken,
address takerToken, address outputToken,
address payable recipient, address payable provider,
address recipient,
uint256 sellAmount, uint256 sellAmount,
uint256 minBuyAmount uint256 minBuyAmount,
bytes calldata auxiliaryData
) )
external external
override override
payable payable
returns (uint256 boughtAmount) returns (uint256 boughtAmount)
{ {
address providerAddress = getLiquidityProviderForMarket(makerToken, takerToken);
if (recipient == address(0)) { if (recipient == address(0)) {
recipient = msg.sender; recipient = msg.sender;
} }
if (takerToken == ETH_TOKEN_ADDRESS) { if (inputToken == ETH_TOKEN_ADDRESS) {
// Wrap ETH. provider.transfer(sellAmount);
weth.deposit{value: sellAmount}();
weth.transfer(providerAddress, sellAmount);
} else { } else {
LibTokenSpender.spendERC20Tokens( LibTokenSpender.spendERC20Tokens(
IERC20TokenV06(takerToken), IERC20TokenV06(inputToken),
msg.sender, msg.sender,
providerAddress, provider,
sellAmount sellAmount
); );
} }
if (makerToken == ETH_TOKEN_ADDRESS) { if (inputToken == ETH_TOKEN_ADDRESS) {
uint256 balanceBefore = weth.balanceOf(address(this)); uint256 balanceBefore = IERC20TokenV06(outputToken).balanceOf(recipient);
IERC20Bridge(providerAddress).bridgeTransferFrom( sandbox.executeSellEthForToken(
address(weth), provider,
address(0), outputToken,
address(this),
minBuyAmount,
""
);
boughtAmount = weth.balanceOf(address(this)).safeSub(balanceBefore);
// Unwrap wETH and send ETH to recipient.
weth.withdraw(boughtAmount);
recipient.transfer(boughtAmount);
} else {
uint256 balanceBefore = IERC20TokenV06(makerToken).balanceOf(recipient);
IERC20Bridge(providerAddress).bridgeTransferFrom(
makerToken,
address(0),
recipient, recipient,
minBuyAmount, minBuyAmount,
"" auxiliaryData
); );
boughtAmount = IERC20TokenV06(makerToken).balanceOf(recipient).safeSub(balanceBefore); boughtAmount = IERC20TokenV06(outputToken).balanceOf(recipient).safeSub(balanceBefore);
} else if (outputToken == ETH_TOKEN_ADDRESS) {
uint256 balanceBefore = recipient.balance;
sandbox.executeSellTokenForEth(
provider,
inputToken,
recipient,
minBuyAmount,
auxiliaryData
);
boughtAmount = recipient.balance.safeSub(balanceBefore);
} else {
uint256 balanceBefore = IERC20TokenV06(outputToken).balanceOf(recipient);
sandbox.executeSellTokenForToken(
provider,
inputToken,
outputToken,
recipient,
minBuyAmount,
auxiliaryData
);
boughtAmount = IERC20TokenV06(outputToken).balanceOf(recipient).safeSub(balanceBefore);
} }
if (boughtAmount < minBuyAmount) { if (boughtAmount < minBuyAmount) {
LibLiquidityProviderRichErrors.LiquidityProviderIncompleteSellError( LibLiquidityProviderRichErrors.LiquidityProviderIncompleteSellError(
providerAddress, provider,
makerToken, outputToken,
takerToken, inputToken,
sellAmount, sellAmount,
boughtAmount, boughtAmount,
minBuyAmount minBuyAmount
).rrevert(); ).rrevert();
} }
}
/// @dev Sets address of the liquidity provider for a market given emit LiquidityProviderSwap(
/// (xAsset, yAsset). inputToken,
/// @param xAsset First asset managed by the liquidity provider. outputToken,
/// @param yAsset Second asset managed by the liquidity provider. sellAmount,
/// @param providerAddress Address of the liquidity provider. boughtAmount,
function setLiquidityProviderForMarket( provider,
address xAsset, recipient
address yAsset,
address providerAddress
)
external
override
onlyOwner
{
LibLiquidityProviderStorage.getStorage()
.addressBook[xAsset][yAsset] = providerAddress;
LibLiquidityProviderStorage.getStorage()
.addressBook[yAsset][xAsset] = providerAddress;
emit LiquidityProviderForMarketUpdated(
xAsset,
yAsset,
providerAddress
); );
} }
/// @dev Returns the address of the liquidity provider for a market given
/// (xAsset, yAsset), or reverts if pool does not exist.
/// @param xAsset First asset managed by the liquidity provider.
/// @param yAsset Second asset managed by the liquidity provider.
/// @return providerAddress Address of the liquidity provider.
function getLiquidityProviderForMarket(
address xAsset,
address yAsset
)
public
view
override
returns (address providerAddress)
{
if (xAsset == ETH_TOKEN_ADDRESS) {
providerAddress = LibLiquidityProviderStorage.getStorage()
.addressBook[address(weth)][yAsset];
} else if (yAsset == ETH_TOKEN_ADDRESS) {
providerAddress = LibLiquidityProviderStorage.getStorage()
.addressBook[xAsset][address(weth)];
} else {
providerAddress = LibLiquidityProviderStorage.getStorage()
.addressBook[xAsset][yAsset];
}
if (providerAddress == address(0)) {
LibLiquidityProviderRichErrors.NoLiquidityProviderForMarketError(
xAsset,
yAsset
).rrevert();
}
}
} }

View File

@ -58,7 +58,7 @@ contract TransformERC20Feature is
/// @dev Name of this feature. /// @dev Name of this feature.
string public constant override FEATURE_NAME = "TransformERC20"; string public constant override FEATURE_NAME = "TransformERC20";
/// @dev Version of this feature. /// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 2, 0); uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 3, 0);
/// @dev Initialize and register this feature. /// @dev Initialize and register this feature.
/// Should be delegatecalled by `Migrate.migrate()`. /// Should be delegatecalled by `Migrate.migrate()`.

View File

@ -37,7 +37,7 @@ contract UniswapFeature is
/// @dev Name of this feature. /// @dev Name of this feature.
string public constant override FEATURE_NAME = "UniswapFeature"; string public constant override FEATURE_NAME = "UniswapFeature";
/// @dev Version of this feature. /// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0); uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
/// @dev WETH contract. /// @dev WETH contract.
IEtherTokenV06 private immutable WETH; IEtherTokenV06 private immutable WETH;
/// @dev AllowanceTarget instance. /// @dev AllowanceTarget instance.

View File

@ -1,45 +0,0 @@
/*
Copyright 2020 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.5;
pragma experimental ABIEncoderV2;
import "./LibStorage.sol";
/// @dev Storage helpers for `LiquidityProviderFeature`.
library LibLiquidityProviderStorage {
/// @dev Storage bucket for this feature.
struct Storage {
// Mapping of taker token -> maker token -> liquidity provider address
// Note that addressBook[x][y] == addressBook[y][x] will always hold.
mapping (address => mapping (address => address)) addressBook;
}
/// @dev Get the storage bucket for this contract.
function getStorage() internal pure returns (Storage storage stor) {
uint256 storageSlot = LibStorage.getStorageSlot(
LibStorage.StorageId.LiquidityProvider
);
// Dip into assembly to change the slot pointed to by the local
// variable `stor`.
// See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
assembly { stor_slot := storageSlot }
}
}

View File

@ -36,8 +36,7 @@ library LibStorage {
TokenSpender, TokenSpender,
TransformERC20, TransformERC20,
MetaTransactions, MetaTransactions,
ReentrancyGuard, ReentrancyGuard
LiquidityProvider
} }
/// @dev Get the storage slot given a storage ID. We assign unique, well-spaced /// @dev Get the storage slot given a storage ID. We assign unique, well-spaced

View File

@ -1,19 +1,15 @@
/* /*
Copyright 2020 ZeroEx Intl. Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
pragma solidity ^0.6.5; pragma solidity ^0.6.5;

View File

@ -32,28 +32,26 @@ contract TestFillQuoteTransformerBridge {
uint256 amount; uint256 amount;
} }
bytes4 private constant ERC20_BRIDGE_PROXY_ID = 0xdc1600f3; function sellTokenForToken(
address takerToken,
function bridgeTransferFrom( address makerToken,
address tokenAddress, address recipient,
address from, uint256 minBuyAmount,
address to, bytes calldata auxiliaryData
uint256 amount,
bytes calldata bridgeData
) )
external external
returns (bytes4 success) returns (uint256 boughtAmount)
{ {
FillBehavior memory behavior = abi.decode(bridgeData, (FillBehavior)); FillBehavior memory behavior = abi.decode(auxiliaryData, (FillBehavior));
TestMintableERC20Token(tokenAddress).mint( boughtAmount = LibMathV06.getPartialAmountFloor(
to,
LibMathV06.getPartialAmountFloor(
behavior.makerAssetMintRatio, behavior.makerAssetMintRatio,
1e18, 1e18,
behavior.amount behavior.amount
)
); );
return ERC20_BRIDGE_PROXY_ID; TestMintableERC20Token(makerToken).mint(
recipient,
boughtAmount
);
} }
function encodeBehaviorData(FillBehavior calldata behavior) function encodeBehaviorData(FillBehavior calldata behavior)

View File

@ -0,0 +1,135 @@
/*
Copyright 2020 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.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
contract TestLiquidityProvider {
event SellTokenForToken(
address inputToken,
address outputToken,
address recipient,
uint256 minBuyAmount,
uint256 inputTokenBalance
);
event SellEthForToken(
address outputToken,
address recipient,
uint256 minBuyAmount,
uint256 ethBalance
);
event SellTokenForEth(
address inputToken,
address recipient,
uint256 minBuyAmount,
uint256 inputTokenBalance
);
IERC20TokenV06 public immutable xAsset;
IERC20TokenV06 public immutable yAsset;
constructor(IERC20TokenV06 xAsset_, IERC20TokenV06 yAsset_)
public
{
xAsset = xAsset_;
yAsset = yAsset_;
}
receive() external payable {}
/// @dev Trades `inputToken` for `outputToken`. The amount of `inputToken`
/// to sell must be transferred to the contract prior to calling this
/// function to trigger the trade.
/// @param inputToken The token being sold.
/// @param outputToken The token being bought.
/// @param recipient The recipient of the bought tokens.
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
/// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
/// @return boughtAmount The amount of `outputToken` bought.
function sellTokenForToken(
address inputToken,
address outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external
returns (uint256 boughtAmount)
{
emit SellTokenForToken(
inputToken,
outputToken,
recipient,
minBuyAmount,
IERC20TokenV06(inputToken).balanceOf(address(this))
);
}
/// @dev Trades ETH for token. ETH must be sent to the contract prior to
/// calling this function to trigger the trade.
/// @param outputToken The token being bought.
/// @param recipient The recipient of the bought tokens.
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
/// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
/// @return boughtAmount The amount of `outputToken` bought.
function sellEthForToken(
address outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external
returns (uint256 boughtAmount)
{
emit SellEthForToken(
outputToken,
recipient,
minBuyAmount,
address(this).balance
);
}
/// @dev Trades token for ETH. The token must be sent to the contract prior
/// to calling this function to trigger the trade.
/// @param inputToken The token being sold.
/// @param recipient The recipient of the bought tokens.
/// @param minBuyAmount The minimum acceptable amount of ETH to buy.
/// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
/// @return boughtAmount The amount of ETH bought.
function sellTokenForEth(
address inputToken,
address payable recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external
returns (uint256 boughtAmount)
{
emit SellTokenForEth(
inputToken,
recipient,
minBuyAmount,
IERC20TokenV06(inputToken).balanceOf(address(this))
);
}
}

View File

@ -40,9 +40,9 @@
"publish:private": "yarn build && gitpkg publish" "publish:private": "yarn build && gitpkg publish"
}, },
"config": { "config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,SignatureValidatorFeature,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature", "publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,SignatureValidatorFeature,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProvider",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BootstrapFeature|BridgeAdapter|FeeCollector|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|ILiquidityProvider|ILiquidityProviderFeature|IMetaTransactionsFeature|IOwnableFeature|ISignatureValidatorFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibLiquidityProviderRichErrors|LibLiquidityProviderStorage|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibOrderHash|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSignedCallData|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpender|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LogMetadataTransformer|MetaTransactionsFeature|MixinAdapterAddresses|MixinBalancer|MixinCurve|MixinDodo|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinShell|MixinSushiswap|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|OwnableFeature|PayTakerTransformer|SignatureValidatorFeature|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestLibSignature|TestLibTokenSpender|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestProtocolFees|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx).json" "abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BootstrapFeature|BridgeAdapter|FeeCollector|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IOwnableFeature|ISignatureValidatorFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibOrderHash|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSignedCallData|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpender|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAdapterAddresses|MixinBalancer|MixinCurve|MixinDodo|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinShell|MixinSushiswap|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|OwnableFeature|PayTakerTransformer|SignatureValidatorFeature|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestLibSignature|TestLibTokenSpender|TestLiquidityProvider|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestProtocolFees|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx).json"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -12,6 +12,7 @@ import * as FullMigration from '../generated-artifacts/FullMigration.json';
import * as IAllowanceTarget from '../generated-artifacts/IAllowanceTarget.json'; import * as IAllowanceTarget from '../generated-artifacts/IAllowanceTarget.json';
import * as IERC20Transformer from '../generated-artifacts/IERC20Transformer.json'; import * as IERC20Transformer from '../generated-artifacts/IERC20Transformer.json';
import * as IFlashWallet from '../generated-artifacts/IFlashWallet.json'; import * as IFlashWallet from '../generated-artifacts/IFlashWallet.json';
import * as ILiquidityProvider from '../generated-artifacts/ILiquidityProvider.json';
import * as InitialMigration from '../generated-artifacts/InitialMigration.json'; import * as InitialMigration from '../generated-artifacts/InitialMigration.json';
import * as IOwnableFeature from '../generated-artifacts/IOwnableFeature.json'; import * as IOwnableFeature from '../generated-artifacts/IOwnableFeature.json';
import * as ISimpleFunctionRegistryFeature from '../generated-artifacts/ISimpleFunctionRegistryFeature.json'; import * as ISimpleFunctionRegistryFeature from '../generated-artifacts/ISimpleFunctionRegistryFeature.json';
@ -54,4 +55,5 @@ export const artifacts = {
LogMetadataTransformer: LogMetadataTransformer as ContractArtifact, LogMetadataTransformer: LogMetadataTransformer as ContractArtifact,
BridgeAdapter: BridgeAdapter as ContractArtifact, BridgeAdapter: BridgeAdapter as ContractArtifact,
LiquidityProviderFeature: LiquidityProviderFeature as ContractArtifact, LiquidityProviderFeature: LiquidityProviderFeature as ContractArtifact,
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
}; };

View File

@ -10,6 +10,7 @@ export * from '../generated-wrappers/full_migration';
export * from '../generated-wrappers/i_allowance_target'; export * from '../generated-wrappers/i_allowance_target';
export * from '../generated-wrappers/i_erc20_transformer'; export * from '../generated-wrappers/i_erc20_transformer';
export * from '../generated-wrappers/i_flash_wallet'; export * from '../generated-wrappers/i_flash_wallet';
export * from '../generated-wrappers/i_liquidity_provider';
export * from '../generated-wrappers/i_ownable_feature'; export * from '../generated-wrappers/i_ownable_feature';
export * from '../generated-wrappers/i_simple_function_registry_feature'; export * from '../generated-wrappers/i_simple_function_registry_feature';
export * from '../generated-wrappers/i_token_spender_feature'; export * from '../generated-wrappers/i_token_spender_feature';

View File

@ -28,6 +28,7 @@ import * as IFlashWallet from '../test/generated-artifacts/IFlashWallet.json';
import * as IGasToken from '../test/generated-artifacts/IGasToken.json'; import * as IGasToken from '../test/generated-artifacts/IGasToken.json';
import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.json'; import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.json';
import * as ILiquidityProviderFeature from '../test/generated-artifacts/ILiquidityProviderFeature.json'; import * as ILiquidityProviderFeature from '../test/generated-artifacts/ILiquidityProviderFeature.json';
import * as ILiquidityProviderSandbox from '../test/generated-artifacts/ILiquidityProviderSandbox.json';
import * as IMetaTransactionsFeature from '../test/generated-artifacts/IMetaTransactionsFeature.json'; import * as IMetaTransactionsFeature from '../test/generated-artifacts/IMetaTransactionsFeature.json';
import * as InitialMigration from '../test/generated-artifacts/InitialMigration.json'; import * as InitialMigration from '../test/generated-artifacts/InitialMigration.json';
import * as IOwnableFeature from '../test/generated-artifacts/IOwnableFeature.json'; import * as IOwnableFeature from '../test/generated-artifacts/IOwnableFeature.json';
@ -43,7 +44,6 @@ import * as LibBootstrap from '../test/generated-artifacts/LibBootstrap.json';
import * as LibCommonRichErrors from '../test/generated-artifacts/LibCommonRichErrors.json'; import * as LibCommonRichErrors from '../test/generated-artifacts/LibCommonRichErrors.json';
import * as LibERC20Transformer from '../test/generated-artifacts/LibERC20Transformer.json'; import * as LibERC20Transformer from '../test/generated-artifacts/LibERC20Transformer.json';
import * as LibLiquidityProviderRichErrors from '../test/generated-artifacts/LibLiquidityProviderRichErrors.json'; import * as LibLiquidityProviderRichErrors from '../test/generated-artifacts/LibLiquidityProviderRichErrors.json';
import * as LibLiquidityProviderStorage from '../test/generated-artifacts/LibLiquidityProviderStorage.json';
import * as LibMetaTransactionsRichErrors from '../test/generated-artifacts/LibMetaTransactionsRichErrors.json'; import * as LibMetaTransactionsRichErrors from '../test/generated-artifacts/LibMetaTransactionsRichErrors.json';
import * as LibMetaTransactionsStorage from '../test/generated-artifacts/LibMetaTransactionsStorage.json'; import * as LibMetaTransactionsStorage from '../test/generated-artifacts/LibMetaTransactionsStorage.json';
import * as LibMigrate from '../test/generated-artifacts/LibMigrate.json'; import * as LibMigrate from '../test/generated-artifacts/LibMigrate.json';
@ -66,6 +66,7 @@ import * as LibTransformERC20RichErrors from '../test/generated-artifacts/LibTra
import * as LibTransformERC20Storage from '../test/generated-artifacts/LibTransformERC20Storage.json'; import * as LibTransformERC20Storage from '../test/generated-artifacts/LibTransformERC20Storage.json';
import * as LibWalletRichErrors from '../test/generated-artifacts/LibWalletRichErrors.json'; import * as LibWalletRichErrors from '../test/generated-artifacts/LibWalletRichErrors.json';
import * as LiquidityProviderFeature from '../test/generated-artifacts/LiquidityProviderFeature.json'; import * as LiquidityProviderFeature from '../test/generated-artifacts/LiquidityProviderFeature.json';
import * as LiquidityProviderSandbox from '../test/generated-artifacts/LiquidityProviderSandbox.json';
import * as LogMetadataTransformer from '../test/generated-artifacts/LogMetadataTransformer.json'; import * as LogMetadataTransformer from '../test/generated-artifacts/LogMetadataTransformer.json';
import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransactionsFeature.json'; import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransactionsFeature.json';
import * as MixinAdapterAddresses from '../test/generated-artifacts/MixinAdapterAddresses.json'; import * as MixinAdapterAddresses from '../test/generated-artifacts/MixinAdapterAddresses.json';
@ -95,6 +96,7 @@ import * as TestFullMigration from '../test/generated-artifacts/TestFullMigratio
import * as TestInitialMigration from '../test/generated-artifacts/TestInitialMigration.json'; import * as TestInitialMigration from '../test/generated-artifacts/TestInitialMigration.json';
import * as TestLibSignature from '../test/generated-artifacts/TestLibSignature.json'; import * as TestLibSignature from '../test/generated-artifacts/TestLibSignature.json';
import * as TestLibTokenSpender from '../test/generated-artifacts/TestLibTokenSpender.json'; import * as TestLibTokenSpender from '../test/generated-artifacts/TestLibTokenSpender.json';
import * as TestLiquidityProvider from '../test/generated-artifacts/TestLiquidityProvider.json';
import * as TestMetaTransactionsTransformERC20Feature from '../test/generated-artifacts/TestMetaTransactionsTransformERC20Feature.json'; import * as TestMetaTransactionsTransformERC20Feature from '../test/generated-artifacts/TestMetaTransactionsTransformERC20Feature.json';
import * as TestMigrator from '../test/generated-artifacts/TestMigrator.json'; import * as TestMigrator from '../test/generated-artifacts/TestMigrator.json';
import * as TestMintableERC20Token from '../test/generated-artifacts/TestMintableERC20Token.json'; import * as TestMintableERC20Token from '../test/generated-artifacts/TestMintableERC20Token.json';
@ -137,6 +139,8 @@ export const artifacts = {
FlashWallet: FlashWallet as ContractArtifact, FlashWallet: FlashWallet as ContractArtifact,
IAllowanceTarget: IAllowanceTarget as ContractArtifact, IAllowanceTarget: IAllowanceTarget as ContractArtifact,
IFlashWallet: IFlashWallet as ContractArtifact, IFlashWallet: IFlashWallet as ContractArtifact,
ILiquidityProviderSandbox: ILiquidityProviderSandbox as ContractArtifact,
LiquidityProviderSandbox: LiquidityProviderSandbox as ContractArtifact,
TransformerDeployer: TransformerDeployer as ContractArtifact, TransformerDeployer: TransformerDeployer as ContractArtifact,
BootstrapFeature: BootstrapFeature as ContractArtifact, BootstrapFeature: BootstrapFeature as ContractArtifact,
IBootstrapFeature: IBootstrapFeature as ContractArtifact, IBootstrapFeature: IBootstrapFeature as ContractArtifact,
@ -168,7 +172,6 @@ export const artifacts = {
InitialMigration: InitialMigration as ContractArtifact, InitialMigration: InitialMigration as ContractArtifact,
LibBootstrap: LibBootstrap as ContractArtifact, LibBootstrap: LibBootstrap as ContractArtifact,
LibMigrate: LibMigrate as ContractArtifact, LibMigrate: LibMigrate as ContractArtifact,
LibLiquidityProviderStorage: LibLiquidityProviderStorage as ContractArtifact,
LibMetaTransactionsStorage: LibMetaTransactionsStorage as ContractArtifact, LibMetaTransactionsStorage: LibMetaTransactionsStorage as ContractArtifact,
LibOwnableStorage: LibOwnableStorage as ContractArtifact, LibOwnableStorage: LibOwnableStorage as ContractArtifact,
LibProxyStorage: LibProxyStorage as ContractArtifact, LibProxyStorage: LibProxyStorage as ContractArtifact,
@ -217,6 +220,7 @@ export const artifacts = {
TestInitialMigration: TestInitialMigration as ContractArtifact, TestInitialMigration: TestInitialMigration as ContractArtifact,
TestLibSignature: TestLibSignature as ContractArtifact, TestLibSignature: TestLibSignature as ContractArtifact,
TestLibTokenSpender: TestLibTokenSpender as ContractArtifact, TestLibTokenSpender: TestLibTokenSpender as ContractArtifact,
TestLiquidityProvider: TestLiquidityProvider as ContractArtifact,
TestMetaTransactionsTransformERC20Feature: TestMetaTransactionsTransformERC20Feature as ContractArtifact, TestMetaTransactionsTransformERC20Feature: TestMetaTransactionsTransformERC20Feature as ContractArtifact,
TestMigrator: TestMigrator as ContractArtifact, TestMigrator: TestMigrator as ContractArtifact,
TestMintTokenERC20Transformer: TestMintTokenERC20Transformer as ContractArtifact, TestMintTokenERC20Transformer: TestMintTokenERC20Transformer as ContractArtifact,

View File

@ -1,16 +1,25 @@
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20'; import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
import { blockchainTests, constants, expect, randomAddress, verifyEventsFromLogs } from '@0x/contracts-test-utils'; import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import { BigNumber, OwnableRevertErrors, ZeroExRevertErrors } from '@0x/utils'; import { BigNumber, OwnableRevertErrors, ZeroExRevertErrors } from '@0x/utils';
import { IOwnableFeatureContract, IZeroExContract, LiquidityProviderFeatureContract } from '../../src/wrappers'; import { IOwnableFeatureContract, IZeroExContract, LiquidityProviderFeatureContract } from '../../src/wrappers';
import { artifacts } from '../artifacts'; import { artifacts } from '../artifacts';
import { abis } from '../utils/abis'; import { abis } from '../utils/abis';
import { fullMigrateAsync } from '../utils/migration'; import { fullMigrateAsync } from '../utils/migration';
import { IERC20BridgeEvents, TestBridgeContract, TestWethContract } from '../wrappers'; import {
LiquidityProviderSandboxContract,
TestBridgeContract,
TestBridgeEvents,
TestLiquidityProviderContract,
TestLiquidityProviderEvents,
TestWethContract,
} from '../wrappers';
blockchainTests('LiquidityProvider feature', env => { blockchainTests('LiquidityProvider feature', env => {
let zeroEx: IZeroExContract; let zeroEx: IZeroExContract;
let feature: LiquidityProviderFeatureContract; let feature: LiquidityProviderFeatureContract;
let sandbox: LiquidityProviderSandboxContract;
let liquidityProvider: TestLiquidityProviderContract;
let token: DummyERC20TokenContract; let token: DummyERC20TokenContract;
let weth: TestWethContract; let weth: TestWethContract;
let owner: string; let owner: string;
@ -47,102 +56,112 @@ blockchainTests('LiquidityProvider feature', env => {
env.provider, env.provider,
env.txDefaults, env.txDefaults,
artifacts, artifacts,
weth.address, zeroEx.address,
);
sandbox = new LiquidityProviderSandboxContract(
await featureImpl.sandbox().callAsync(),
env.provider,
env.txDefaults,
); );
await new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis) await new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis)
.migrate(featureImpl.address, featureImpl.migrate().getABIEncodedTransactionData(), owner) .migrate(featureImpl.address, featureImpl.migrate().getABIEncodedTransactionData(), owner)
.awaitTransactionSuccessAsync(); .awaitTransactionSuccessAsync();
});
describe('Registry', () => {
it('`getLiquidityProviderForMarket` reverts if address is not set', async () => {
const [xAsset, yAsset] = [randomAddress(), randomAddress()];
let tx = feature.getLiquidityProviderForMarket(xAsset, yAsset).awaitTransactionSuccessAsync();
expect(tx).to.revertWith(
new ZeroExRevertErrors.LiquidityProvider.NoLiquidityProviderForMarketError(xAsset, yAsset),
);
tx = feature.getLiquidityProviderForMarket(yAsset, xAsset).awaitTransactionSuccessAsync();
return expect(tx).to.revertWith(
new ZeroExRevertErrors.LiquidityProvider.NoLiquidityProviderForMarketError(yAsset, xAsset),
);
});
it('can set/get a liquidity provider address for a given market', async () => {
const expectedAddress = randomAddress();
await feature
.setLiquidityProviderForMarket(token.address, weth.address, expectedAddress)
.awaitTransactionSuccessAsync();
let actualAddress = await feature.getLiquidityProviderForMarket(token.address, weth.address).callAsync();
expect(actualAddress).to.equal(expectedAddress);
actualAddress = await feature.getLiquidityProviderForMarket(weth.address, token.address).callAsync();
expect(actualAddress).to.equal(expectedAddress);
});
it('can update a liquidity provider address for a given market', async () => {
const expectedAddress = randomAddress();
await feature
.setLiquidityProviderForMarket(token.address, weth.address, expectedAddress)
.awaitTransactionSuccessAsync();
let actualAddress = await feature.getLiquidityProviderForMarket(token.address, weth.address).callAsync();
expect(actualAddress).to.equal(expectedAddress);
actualAddress = await feature.getLiquidityProviderForMarket(weth.address, token.address).callAsync();
expect(actualAddress).to.equal(expectedAddress);
});
it('can effectively remove a liquidity provider for a market by setting the address to 0', async () => {
await feature
.setLiquidityProviderForMarket(token.address, weth.address, constants.NULL_ADDRESS)
.awaitTransactionSuccessAsync();
const tx = feature
.getLiquidityProviderForMarket(token.address, weth.address)
.awaitTransactionSuccessAsync();
return expect(tx).to.revertWith(
new ZeroExRevertErrors.LiquidityProvider.NoLiquidityProviderForMarketError(token.address, weth.address),
);
});
it('reverts if non-owner attempts to set an address', async () => {
const tx = feature
.setLiquidityProviderForMarket(randomAddress(), randomAddress(), randomAddress())
.awaitTransactionSuccessAsync({ from: taker });
return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(taker, owner));
});
});
blockchainTests.resets('Swap', () => {
let liquidityProvider: TestBridgeContract;
const ETH_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
before(async () => { liquidityProvider = await TestLiquidityProviderContract.deployFrom0xArtifactAsync(
liquidityProvider = await TestBridgeContract.deployFrom0xArtifactAsync( artifacts.TestLiquidityProvider,
artifacts.TestBridge,
env.provider, env.provider,
env.txDefaults, env.txDefaults,
artifacts, artifacts,
token.address, token.address,
weth.address, weth.address,
); );
await feature
.setLiquidityProviderForMarket(token.address, weth.address, liquidityProvider.address)
.awaitTransactionSuccessAsync();
}); });
it('Cannot execute a swap for a market without a liquidity provider set', async () => { blockchainTests.resets('Sandbox', () => {
const [xAsset, yAsset] = [randomAddress(), randomAddress()]; it('Cannot call sandbox `executeSellTokenForToken` function directly', async () => {
const tx = feature const tx = sandbox
.sellToLiquidityProvider( .executeSellTokenForToken(
xAsset, liquidityProvider.address,
yAsset, token.address,
constants.NULL_ADDRESS, weth.address,
constants.ONE_ETHER, taker,
constants.ZERO_AMOUNT, constants.ZERO_AMOUNT,
constants.NULL_BYTES,
) )
.awaitTransactionSuccessAsync({ from: taker }); .awaitTransactionSuccessAsync({ from: taker });
return expect(tx).to.revertWith( return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(taker));
new ZeroExRevertErrors.LiquidityProvider.NoLiquidityProviderForMarketError(xAsset, yAsset),
);
}); });
it('Cannot call sandbox `executeSellEthForToken` function directly', async () => {
const tx = sandbox
.executeSellEthForToken(
liquidityProvider.address,
token.address,
taker,
constants.ZERO_AMOUNT,
constants.NULL_BYTES,
)
.awaitTransactionSuccessAsync({ from: taker });
return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(taker));
});
it('Cannot call sandbox `executeSellTokenForEth` function directly', async () => {
const tx = sandbox
.executeSellTokenForEth(
liquidityProvider.address,
token.address,
taker,
constants.ZERO_AMOUNT,
constants.NULL_BYTES,
)
.awaitTransactionSuccessAsync({ from: taker });
return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(taker));
});
});
blockchainTests.resets('Swap', () => {
const ETH_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
it('Successfully executes an ERC20-ERC20 swap', async () => { it('Successfully executes an ERC20-ERC20 swap', async () => {
const tx = await feature const tx = await feature
.sellToLiquidityProvider( .sellToLiquidityProvider(
weth.address,
token.address, token.address,
weth.address,
liquidityProvider.address,
constants.NULL_ADDRESS, constants.NULL_ADDRESS,
constants.ONE_ETHER, constants.ONE_ETHER,
constants.ZERO_AMOUNT, constants.ZERO_AMOUNT,
constants.NULL_BYTES,
)
.awaitTransactionSuccessAsync({ from: taker });
verifyEventsFromLogs(
tx.logs,
[
{
inputToken: token.address,
outputToken: weth.address,
recipient: taker,
minBuyAmount: constants.ZERO_AMOUNT,
inputTokenBalance: constants.ONE_ETHER,
},
],
TestLiquidityProviderEvents.SellTokenForToken,
);
});
it('Successfully executes an ERC20-ERC20 swap (backwards-compatibility)', async () => {
const bridge = await TestBridgeContract.deployFrom0xArtifactAsync(
artifacts.TestBridge,
env.provider,
env.txDefaults,
artifacts,
weth.address,
token.address,
);
const tx = await feature
.sellToLiquidityProvider(
token.address,
weth.address,
bridge.address,
constants.NULL_ADDRESS,
constants.ONE_ETHER,
constants.ZERO_AMOUNT,
constants.NULL_BYTES,
) )
.awaitTransactionSuccessAsync({ from: taker }); .awaitTransactionSuccessAsync({ from: taker });
verifyEventsFromLogs( verifyEventsFromLogs(
@ -153,22 +172,24 @@ blockchainTests('LiquidityProvider feature', env => {
outputToken: weth.address, outputToken: weth.address,
inputTokenAmount: constants.ONE_ETHER, inputTokenAmount: constants.ONE_ETHER,
outputTokenAmount: constants.ZERO_AMOUNT, outputTokenAmount: constants.ZERO_AMOUNT,
from: constants.NULL_ADDRESS, from: bridge.address,
to: taker, to: taker,
}, },
], ],
IERC20BridgeEvents.ERC20BridgeTransfer, TestBridgeEvents.ERC20BridgeTransfer,
); );
}); });
it('Reverts if cannot fulfill the minimum buy amount', async () => { it('Reverts if cannot fulfill the minimum buy amount', async () => {
const minBuyAmount = new BigNumber(1); const minBuyAmount = new BigNumber(1);
const tx = feature const tx = feature
.sellToLiquidityProvider( .sellToLiquidityProvider(
weth.address,
token.address, token.address,
weth.address,
liquidityProvider.address,
constants.NULL_ADDRESS, constants.NULL_ADDRESS,
constants.ONE_ETHER, constants.ONE_ETHER,
minBuyAmount, minBuyAmount,
constants.NULL_BYTES,
) )
.awaitTransactionSuccessAsync({ from: taker }); .awaitTransactionSuccessAsync({ from: taker });
return expect(tx).to.revertWith( return expect(tx).to.revertWith(
@ -185,36 +206,38 @@ blockchainTests('LiquidityProvider feature', env => {
it('Successfully executes an ETH-ERC20 swap', async () => { it('Successfully executes an ETH-ERC20 swap', async () => {
const tx = await feature const tx = await feature
.sellToLiquidityProvider( .sellToLiquidityProvider(
token.address,
ETH_TOKEN_ADDRESS, ETH_TOKEN_ADDRESS,
token.address,
liquidityProvider.address,
constants.NULL_ADDRESS, constants.NULL_ADDRESS,
constants.ONE_ETHER, constants.ONE_ETHER,
constants.ZERO_AMOUNT, constants.ZERO_AMOUNT,
constants.NULL_BYTES,
) )
.awaitTransactionSuccessAsync({ from: taker, value: constants.ONE_ETHER }); .awaitTransactionSuccessAsync({ from: taker, value: constants.ONE_ETHER });
verifyEventsFromLogs( verifyEventsFromLogs(
tx.logs, tx.logs,
[ [
{ {
inputToken: weth.address,
outputToken: token.address, outputToken: token.address,
inputTokenAmount: constants.ONE_ETHER, recipient: taker,
outputTokenAmount: constants.ZERO_AMOUNT, minBuyAmount: constants.ZERO_AMOUNT,
from: constants.NULL_ADDRESS, ethBalance: constants.ONE_ETHER,
to: taker,
}, },
], ],
IERC20BridgeEvents.ERC20BridgeTransfer, TestLiquidityProviderEvents.SellEthForToken,
); );
}); });
it('Successfully executes an ERC20-ETH swap', async () => { it('Successfully executes an ERC20-ETH swap', async () => {
const tx = await feature const tx = await feature
.sellToLiquidityProvider( .sellToLiquidityProvider(
ETH_TOKEN_ADDRESS,
token.address, token.address,
ETH_TOKEN_ADDRESS,
liquidityProvider.address,
constants.NULL_ADDRESS, constants.NULL_ADDRESS,
constants.ONE_ETHER, constants.ONE_ETHER,
constants.ZERO_AMOUNT, constants.ZERO_AMOUNT,
constants.NULL_BYTES,
) )
.awaitTransactionSuccessAsync({ from: taker }); .awaitTransactionSuccessAsync({ from: taker });
verifyEventsFromLogs( verifyEventsFromLogs(
@ -222,14 +245,12 @@ blockchainTests('LiquidityProvider feature', env => {
[ [
{ {
inputToken: token.address, inputToken: token.address,
outputToken: weth.address, recipient: taker,
inputTokenAmount: constants.ONE_ETHER, minBuyAmount: constants.ZERO_AMOUNT,
outputTokenAmount: constants.ZERO_AMOUNT, inputTokenBalance: constants.ONE_ETHER,
from: constants.NULL_ADDRESS,
to: zeroEx.address,
}, },
], ],
IERC20BridgeEvents.ERC20BridgeTransfer, TestLiquidityProviderEvents.SellTokenForEth,
); );
}); });
}); });

View File

@ -26,6 +26,7 @@ export * from '../test/generated-wrappers/i_flash_wallet';
export * from '../test/generated-wrappers/i_gas_token'; export * from '../test/generated-wrappers/i_gas_token';
export * from '../test/generated-wrappers/i_liquidity_provider'; export * from '../test/generated-wrappers/i_liquidity_provider';
export * from '../test/generated-wrappers/i_liquidity_provider_feature'; export * from '../test/generated-wrappers/i_liquidity_provider_feature';
export * from '../test/generated-wrappers/i_liquidity_provider_sandbox';
export * from '../test/generated-wrappers/i_meta_transactions_feature'; export * from '../test/generated-wrappers/i_meta_transactions_feature';
export * from '../test/generated-wrappers/i_ownable_feature'; export * from '../test/generated-wrappers/i_ownable_feature';
export * from '../test/generated-wrappers/i_signature_validator_feature'; export * from '../test/generated-wrappers/i_signature_validator_feature';
@ -41,7 +42,6 @@ export * from '../test/generated-wrappers/lib_bootstrap';
export * from '../test/generated-wrappers/lib_common_rich_errors'; export * from '../test/generated-wrappers/lib_common_rich_errors';
export * from '../test/generated-wrappers/lib_erc20_transformer'; export * from '../test/generated-wrappers/lib_erc20_transformer';
export * from '../test/generated-wrappers/lib_liquidity_provider_rich_errors'; export * from '../test/generated-wrappers/lib_liquidity_provider_rich_errors';
export * from '../test/generated-wrappers/lib_liquidity_provider_storage';
export * from '../test/generated-wrappers/lib_meta_transactions_rich_errors'; export * from '../test/generated-wrappers/lib_meta_transactions_rich_errors';
export * from '../test/generated-wrappers/lib_meta_transactions_storage'; export * from '../test/generated-wrappers/lib_meta_transactions_storage';
export * from '../test/generated-wrappers/lib_migrate'; export * from '../test/generated-wrappers/lib_migrate';
@ -64,6 +64,7 @@ export * from '../test/generated-wrappers/lib_transform_erc20_rich_errors';
export * from '../test/generated-wrappers/lib_transform_erc20_storage'; export * from '../test/generated-wrappers/lib_transform_erc20_storage';
export * from '../test/generated-wrappers/lib_wallet_rich_errors'; export * from '../test/generated-wrappers/lib_wallet_rich_errors';
export * from '../test/generated-wrappers/liquidity_provider_feature'; export * from '../test/generated-wrappers/liquidity_provider_feature';
export * from '../test/generated-wrappers/liquidity_provider_sandbox';
export * from '../test/generated-wrappers/log_metadata_transformer'; export * from '../test/generated-wrappers/log_metadata_transformer';
export * from '../test/generated-wrappers/meta_transactions_feature'; export * from '../test/generated-wrappers/meta_transactions_feature';
export * from '../test/generated-wrappers/mixin_adapter_addresses'; export * from '../test/generated-wrappers/mixin_adapter_addresses';
@ -93,6 +94,7 @@ export * from '../test/generated-wrappers/test_full_migration';
export * from '../test/generated-wrappers/test_initial_migration'; export * from '../test/generated-wrappers/test_initial_migration';
export * from '../test/generated-wrappers/test_lib_signature'; export * from '../test/generated-wrappers/test_lib_signature';
export * from '../test/generated-wrappers/test_lib_token_spender'; export * from '../test/generated-wrappers/test_lib_token_spender';
export * from '../test/generated-wrappers/test_liquidity_provider';
export * from '../test/generated-wrappers/test_meta_transactions_transform_erc20_feature'; export * from '../test/generated-wrappers/test_meta_transactions_transform_erc20_feature';
export * from '../test/generated-wrappers/test_migrator'; export * from '../test/generated-wrappers/test_migrator';
export * from '../test/generated-wrappers/test_mint_token_erc20_transformer'; export * from '../test/generated-wrappers/test_mint_token_erc20_transformer';

View File

@ -10,6 +10,7 @@
"generated-artifacts/IAllowanceTarget.json", "generated-artifacts/IAllowanceTarget.json",
"generated-artifacts/IERC20Transformer.json", "generated-artifacts/IERC20Transformer.json",
"generated-artifacts/IFlashWallet.json", "generated-artifacts/IFlashWallet.json",
"generated-artifacts/ILiquidityProvider.json",
"generated-artifacts/IOwnableFeature.json", "generated-artifacts/IOwnableFeature.json",
"generated-artifacts/ISimpleFunctionRegistryFeature.json", "generated-artifacts/ISimpleFunctionRegistryFeature.json",
"generated-artifacts/ITokenSpenderFeature.json", "generated-artifacts/ITokenSpenderFeature.json",
@ -50,6 +51,7 @@
"test/generated-artifacts/IGasToken.json", "test/generated-artifacts/IGasToken.json",
"test/generated-artifacts/ILiquidityProvider.json", "test/generated-artifacts/ILiquidityProvider.json",
"test/generated-artifacts/ILiquidityProviderFeature.json", "test/generated-artifacts/ILiquidityProviderFeature.json",
"test/generated-artifacts/ILiquidityProviderSandbox.json",
"test/generated-artifacts/IMetaTransactionsFeature.json", "test/generated-artifacts/IMetaTransactionsFeature.json",
"test/generated-artifacts/IOwnableFeature.json", "test/generated-artifacts/IOwnableFeature.json",
"test/generated-artifacts/ISignatureValidatorFeature.json", "test/generated-artifacts/ISignatureValidatorFeature.json",
@ -65,7 +67,6 @@
"test/generated-artifacts/LibCommonRichErrors.json", "test/generated-artifacts/LibCommonRichErrors.json",
"test/generated-artifacts/LibERC20Transformer.json", "test/generated-artifacts/LibERC20Transformer.json",
"test/generated-artifacts/LibLiquidityProviderRichErrors.json", "test/generated-artifacts/LibLiquidityProviderRichErrors.json",
"test/generated-artifacts/LibLiquidityProviderStorage.json",
"test/generated-artifacts/LibMetaTransactionsRichErrors.json", "test/generated-artifacts/LibMetaTransactionsRichErrors.json",
"test/generated-artifacts/LibMetaTransactionsStorage.json", "test/generated-artifacts/LibMetaTransactionsStorage.json",
"test/generated-artifacts/LibMigrate.json", "test/generated-artifacts/LibMigrate.json",
@ -88,6 +89,7 @@
"test/generated-artifacts/LibTransformERC20Storage.json", "test/generated-artifacts/LibTransformERC20Storage.json",
"test/generated-artifacts/LibWalletRichErrors.json", "test/generated-artifacts/LibWalletRichErrors.json",
"test/generated-artifacts/LiquidityProviderFeature.json", "test/generated-artifacts/LiquidityProviderFeature.json",
"test/generated-artifacts/LiquidityProviderSandbox.json",
"test/generated-artifacts/LogMetadataTransformer.json", "test/generated-artifacts/LogMetadataTransformer.json",
"test/generated-artifacts/MetaTransactionsFeature.json", "test/generated-artifacts/MetaTransactionsFeature.json",
"test/generated-artifacts/MixinAdapterAddresses.json", "test/generated-artifacts/MixinAdapterAddresses.json",
@ -117,6 +119,7 @@
"test/generated-artifacts/TestInitialMigration.json", "test/generated-artifacts/TestInitialMigration.json",
"test/generated-artifacts/TestLibSignature.json", "test/generated-artifacts/TestLibSignature.json",
"test/generated-artifacts/TestLibTokenSpender.json", "test/generated-artifacts/TestLibTokenSpender.json",
"test/generated-artifacts/TestLiquidityProvider.json",
"test/generated-artifacts/TestMetaTransactionsTransformERC20Feature.json", "test/generated-artifacts/TestMetaTransactionsTransformERC20Feature.json",
"test/generated-artifacts/TestMigrator.json", "test/generated-artifacts/TestMigrator.json",
"test/generated-artifacts/TestMintTokenERC20Transformer.json", "test/generated-artifacts/TestMintTokenERC20Transformer.json",

View File

@ -1,4 +1,17 @@
[ [
{
"version": "5.1.0",
"changes": [
{
"note": "Add support for LiquidityProvider feature in the swap quote consumer",
"pr": 16
},
{
"note": "Remove support for MultiBridge 😞",
"pr": 16
}
]
},
{ {
"timestamp": 1604620645, "timestamp": 1604620645,
"version": "5.0.3", "version": "5.0.3",

View File

@ -20,8 +20,7 @@ pragma solidity ^0.6;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol"; import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
import "./interfaces/ILiquidityProvider.sol"; import "@0x/contracts-zero-ex/contracts/src/vendor/ILiquidityProvider.sol";
import "./interfaces/ILiquidityProviderRegistry.sol";
import "./ApproximateBuys.sol"; import "./ApproximateBuys.sol";
import "./SamplerUtils.sol"; import "./SamplerUtils.sol";
@ -34,37 +33,26 @@ contract LiquidityProviderSampler is
uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k
/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider. /// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
/// @param registryAddress Address of the liquidity provider registry contract. /// @param providerAddress Address of the liquidity provider.
/// @param takerToken Address of the taker token (what to sell). /// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy). /// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample. /// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token /// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount. /// amount.
function sampleSellsFromLiquidityProviderRegistry( function sampleSellsFromLiquidityProvider(
address registryAddress, address providerAddress,
address takerToken, address takerToken,
address makerToken, address makerToken,
uint256[] memory takerTokenAmounts uint256[] memory takerTokenAmounts
) )
public public
view view
returns (uint256[] memory makerTokenAmounts, address providerAddress) returns (uint256[] memory makerTokenAmounts)
{ {
// Initialize array of maker token amounts. // Initialize array of maker token amounts.
uint256 numSamples = takerTokenAmounts.length; uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples); makerTokenAmounts = new uint256[](numSamples);
// Query registry for provider address.
providerAddress = _getLiquidityProviderFromRegistry(
registryAddress,
takerToken,
makerToken
);
// If provider doesn't exist, return all zeros.
if (providerAddress == address(0)) {
return (makerTokenAmounts, providerAddress);
}
for (uint256 i = 0; i < numSamples; i++) { for (uint256 i = 0; i < numSamples; i++) {
try try
ILiquidityProvider(providerAddress).getSellQuote ILiquidityProvider(providerAddress).getSellQuote
@ -81,68 +69,33 @@ contract LiquidityProviderSampler is
} }
/// @dev Sample buy quotes from an arbitrary on-chain liquidity provider. /// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
/// @param registryAddress Address of the liquidity provider registry contract. /// @param providerAddress Address of the liquidity provider.
/// @param takerToken Address of the taker token (what to sell). /// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy). /// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample. /// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token /// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount. /// amount.
function sampleBuysFromLiquidityProviderRegistry( function sampleBuysFromLiquidityProvider(
address registryAddress, address providerAddress,
address takerToken, address takerToken,
address makerToken, address makerToken,
uint256[] memory makerTokenAmounts uint256[] memory makerTokenAmounts
) )
public public
view view
returns (uint256[] memory takerTokenAmounts, address providerAddress) returns (uint256[] memory takerTokenAmounts)
{ {
providerAddress = _getLiquidityProviderFromRegistry(
registryAddress,
takerToken,
makerToken
);
takerTokenAmounts = _sampleApproximateBuys( takerTokenAmounts = _sampleApproximateBuys(
ApproximateBuyQuoteOpts({ ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken, registryAddress), makerTokenData: abi.encode(makerToken, providerAddress),
takerTokenData: abi.encode(takerToken, registryAddress), takerTokenData: abi.encode(takerToken, providerAddress),
getSellQuoteCallback: _sampleSellForApproximateBuyFromLiquidityProviderRegistry getSellQuoteCallback: _sampleSellForApproximateBuyFromLiquidityProvider
}), }),
makerTokenAmounts makerTokenAmounts
); );
} }
/// @dev Returns the address of a liquidity provider for the given market function _sampleSellForApproximateBuyFromLiquidityProvider(
/// (takerToken, makerToken), from a registry of liquidity providers.
/// Returns address(0) if no such provider exists in the registry.
/// @param takerToken Taker asset managed by liquidity provider.
/// @param makerToken Maker asset managed by liquidity provider.
/// @return providerAddress Address of the liquidity provider.
function _getLiquidityProviderFromRegistry(
address registryAddress,
address takerToken,
address makerToken
)
private
view
returns (address providerAddress)
{
if (registryAddress == address(0)) {
return address(0);
}
bytes memory callData = abi.encodeWithSelector(
ILiquidityProviderRegistry.getLiquidityProviderForMarket.selector,
takerToken,
makerToken
);
(bool didSucceed, bytes memory returnData) = registryAddress.staticcall(callData);
if (didSucceed && returnData.length == 32) {
return LibBytesV06.readAddress(returnData, 12);
}
}
function _sampleSellForApproximateBuyFromLiquidityProviderRegistry(
bytes memory takerTokenData, bytes memory takerTokenData,
bytes memory makerTokenData, bytes memory makerTokenData,
uint256 sellAmount uint256 sellAmount
@ -151,15 +104,15 @@ contract LiquidityProviderSampler is
view view
returns (uint256 buyAmount) returns (uint256 buyAmount)
{ {
(address takerToken, address plpRegistryAddress) = (address takerToken, address providerAddress) =
abi.decode(takerTokenData, (address, address)); abi.decode(takerTokenData, (address, address));
(address makerToken) = (address makerToken) =
abi.decode(makerTokenData, (address)); abi.decode(makerTokenData, (address));
try try
this.sampleSellsFromLiquidityProviderRegistry this.sampleSellsFromLiquidityProvider
{gas: DEFAULT_CALL_GAS} {gas: DEFAULT_CALL_GAS}
(plpRegistryAddress, takerToken, makerToken, _toSingleValueArray(sellAmount)) (providerAddress, takerToken, makerToken, _toSingleValueArray(sellAmount))
returns (uint256[] memory amounts, address) returns (uint256[] memory amounts)
{ {
return amounts[0]; return amounts[0];
} catch (bytes memory) { } catch (bytes memory) {

View File

@ -1,70 +0,0 @@
/*
Copyright 2020 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;
interface ILiquidityProvider {
/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
/// @param tokenAddress The address of the ERC20 token to transfer.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
/// @return success The magic bytes `0xdc1600f3` if successful.
function bridgeTransferFrom(
address tokenAddress,
address from,
address to,
uint256 amount,
bytes calldata bridgeData
)
external
returns (bytes4 success);
/// @dev Quotes the amount of `makerToken` that would be obtained by
/// selling `sellAmount` of `takerToken`.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param sellAmount Amount of `takerToken` to sell.
/// @return makerTokenAmount Amount of `makerToken` that would be obtained.
function getSellQuote(
address takerToken,
address makerToken,
uint256 sellAmount
)
external
view
returns (uint256 makerTokenAmount);
/// @dev Quotes the amount of `takerToken` that would need to be sold in
/// order to obtain `buyAmount` of `makerToken`.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param buyAmount Amount of `makerToken` to buy.
/// @return takerTokenAmount Amount of `takerToken` that would need to be sold.
function getBuyQuote(
address takerToken,
address makerToken,
uint256 buyAmount
)
external
view
returns (uint256 takerTokenAmount);
}

View File

@ -1,36 +0,0 @@
/*
Copyright 2020 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;
interface ILiquidityProviderRegistry {
/// @dev Returns the address of a liquidity provider for the given market
/// (takerToken, makerToken), reverting if the pool does not exist.
/// @param takerToken Taker asset managed by liquidity provider.
/// @param makerToken Maker asset managed by liquidity provider.
/// @return providerAddress Address of the liquidity provider.
function getLiquidityProviderForMarket(
address takerToken,
address makerToken
)
external
view
returns (address providerAddress);
}

View File

@ -1,44 +0,0 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
contract DummyLiquidityProviderRegistry
{
address private constant NULL_ADDRESS = address(0x0);
mapping (address => mapping (address => address)) internal _gAddressBook;
/// @dev Sets address of pool for a market given market (xAsset, yAsset).
/// @param xToken First asset managed by pool.
/// @param yToken Second asset managed by pool.
/// @param poolAddress Address of pool.
function setLiquidityProviderForMarket(
address xToken,
address yToken,
address poolAddress
)
external
{
_gAddressBook[xToken][yToken] = poolAddress;
_gAddressBook[yToken][xToken] = poolAddress;
}
/// @dev Returns the address of pool for a market given market (xAsset, yAsset), or reverts if pool does not exist.
/// @param xToken First asset managed by pool.
/// @param yToken Second asset managed by pool.
/// @return poolAddress Address of pool.
function getLiquidityProviderForMarket(
address xToken,
address yToken
)
external
view
returns (address poolAddress)
{
poolAddress = _gAddressBook[xToken][yToken];
require(
poolAddress != NULL_ADDRESS,
"Registry/MARKET_PAIR_NOT_SET"
);
}
}

View File

@ -36,9 +36,9 @@
"publish:private": "yarn build && gitpkg publish" "publish:private": "yarn build && gitpkg publish"
}, },
"config": { "config": {
"publicInterfaceContracts": "ERC20BridgeSampler,ILiquidityProvider,ILiquidityProviderRegistry,DummyLiquidityProviderRegistry,DummyLiquidityProvider", "publicInterfaceContracts": "ERC20BridgeSampler",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalancerSampler|CurveSampler|DODOSampler|DeploymentConstants|DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|Eth2DaiSampler|IBalancer|ICurve|IEth2Dai|IKyberNetwork|ILiquidityProvider|ILiquidityProviderRegistry|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SushiSwapSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler).json", "abis": "./test/generated-artifacts/@(ApproximateBuys|BalancerSampler|CurveSampler|DODOSampler|DeploymentConstants|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|IBalancer|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SushiSwapSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler).json",
"postpublish": { "postpublish": {
"assets": [] "assets": []
} }
@ -94,6 +94,7 @@
"@0x/contracts-gen": "^2.0.18", "@0x/contracts-gen": "^2.0.18",
"@0x/contracts-test-utils": "^5.3.10", "@0x/contracts-test-utils": "^5.3.10",
"@0x/contracts-utils": "^4.5.7", "@0x/contracts-utils": "^4.5.7",
"@0x/contracts-zero-ex": "^0.8.0",
"@0x/mesh-rpc-client": "^9.4.2", "@0x/mesh-rpc-client": "^9.4.2",
"@0x/migrations": "^6.4.7", "@0x/migrations": "^6.4.7",
"@0x/sol-compiler": "^4.2.7", "@0x/sol-compiler": "^4.2.7",

View File

@ -5,15 +5,5 @@
*/ */
import { ContractArtifact } from 'ethereum-types'; import { ContractArtifact } from 'ethereum-types';
import * as DummyLiquidityProvider from '../generated-artifacts/DummyLiquidityProvider.json';
import * as DummyLiquidityProviderRegistry from '../generated-artifacts/DummyLiquidityProviderRegistry.json';
import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json'; import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json';
import * as ILiquidityProvider from '../generated-artifacts/ILiquidityProvider.json'; export const artifacts = { ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact };
import * as ILiquidityProviderRegistry from '../generated-artifacts/ILiquidityProviderRegistry.json';
export const artifacts = {
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
ILiquidityProviderRegistry: ILiquidityProviderRegistry as ContractArtifact,
DummyLiquidityProviderRegistry: DummyLiquidityProviderRegistry as ContractArtifact,
DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,
};

View File

@ -147,10 +147,10 @@ export {
GetMarketOrdersRfqtOpts, GetMarketOrdersRfqtOpts,
KyberFillData, KyberFillData,
LiquidityProviderFillData, LiquidityProviderFillData,
LiquidityProviderRegistry,
MarketDepth, MarketDepth,
MarketDepthSide, MarketDepthSide,
MooniswapFillData, MooniswapFillData,
MultiBridgeFillData,
MultiHopFillData, MultiHopFillData,
NativeCollapsedFill, NativeCollapsedFill,
NativeFillData, NativeFillData,

View File

@ -34,7 +34,7 @@ import { getSwapMinBuyAmount } from './utils';
// tslint:disable-next-line:custom-no-magic-numbers // tslint:disable-next-line:custom-no-magic-numbers
const MAX_UINT256 = new BigNumber(2).pow(256).minus(1); const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
const { NULL_ADDRESS, ZERO_AMOUNT } = constants; const { NULL_ADDRESS, NULL_BYTES, ZERO_AMOUNT } = constants;
export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase { export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
public readonly provider: ZeroExProvider; public readonly provider: ZeroExProvider;
@ -96,9 +96,16 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
const buyToken = getTokenFromAssetData(quote.makerAssetData); const buyToken = getTokenFromAssetData(quote.makerAssetData);
const sellAmount = quote.worstCaseQuoteInfo.totalTakerAssetAmount; const sellAmount = quote.worstCaseQuoteInfo.totalTakerAssetAmount;
let minBuyAmount = getSwapMinBuyAmount(quote); let minBuyAmount = getSwapMinBuyAmount(quote);
let ethAmount = quote.worstCaseQuoteInfo.protocolFeeInWeiAmount;
if (isFromETH) {
ethAmount = ethAmount.plus(sellAmount);
}
const { buyTokenFeeAmount, sellTokenFeeAmount, recipient: feeRecipient } = affiliateFee;
// VIP routes. // VIP routes.
if (isDirectUniswapCompatible(quote, optsWithDefaults)) { if (
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap])
) {
const source = quote.orders[0].fills[0].source; const source = quote.orders[0].fills[0].source;
const fillData = quote.orders[0].fills[0].fillData as UniswapV2FillData; const fillData = quote.orders[0].fills[0].fillData as UniswapV2FillData;
return { return {
@ -124,6 +131,26 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
}; };
} }
if (isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.LiquidityProvider])) {
const target = quote.orders[0].makerAddress;
return {
calldataHexString: this._exchangeProxy
.sellToLiquidityProvider(
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
target,
NULL_ADDRESS,
sellAmount,
minBuyAmount,
NULL_BYTES,
)
.getABIEncodedTransactionData(),
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this.contractAddresses.exchangeProxyAllowanceTarget,
};
}
// Build up the transforms. // Build up the transforms.
const transforms = []; const transforms = [];
if (isFromETH) { if (isFromETH) {
@ -198,8 +225,6 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
} }
// This transformer pays affiliate fees. // This transformer pays affiliate fees.
const { buyTokenFeeAmount, sellTokenFeeAmount, recipient: feeRecipient } = affiliateFee;
if (buyTokenFeeAmount.isGreaterThan(0) && feeRecipient !== NULL_ADDRESS) { if (buyTokenFeeAmount.isGreaterThan(0) && feeRecipient !== NULL_ADDRESS) {
transforms.push({ transforms.push({
deploymentNonce: this.transformerNonces.affiliateFeeTransformer, deploymentNonce: this.transformerNonces.affiliateFeeTransformer,
@ -216,7 +241,6 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
// Adjust the minimum buy amount by the fee. // Adjust the minimum buy amount by the fee.
minBuyAmount = BigNumber.max(0, minBuyAmount.minus(buyTokenFeeAmount)); minBuyAmount = BigNumber.max(0, minBuyAmount.minus(buyTokenFeeAmount));
} }
if (sellTokenFeeAmount.isGreaterThan(0) && feeRecipient !== NULL_ADDRESS) { if (sellTokenFeeAmount.isGreaterThan(0) && feeRecipient !== NULL_ADDRESS) {
throw new Error('Affiliate fees denominated in sell token are not yet supported'); throw new Error('Affiliate fees denominated in sell token are not yet supported');
} }
@ -240,11 +264,6 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
) )
.getABIEncodedTransactionData(); .getABIEncodedTransactionData();
let ethAmount = quote.worstCaseQuoteInfo.protocolFeeInWeiAmount;
if (isFromETH) {
ethAmount = ethAmount.plus(sellAmount);
}
return { return {
calldataHexString, calldataHexString,
ethAmount, ethAmount,
@ -266,7 +285,11 @@ function isBuyQuote(quote: SwapQuote): quote is MarketBuySwapQuote {
return quote.type === MarketOperation.Buy; return quote.type === MarketOperation.Buy;
} }
function isDirectUniswapCompatible(quote: SwapQuote, opts: ExchangeProxyContractOpts): boolean { function isDirectSwapCompatible(
quote: SwapQuote,
opts: ExchangeProxyContractOpts,
directSources: ERC20BridgeSource[],
): boolean {
// Must not be a mtx. // Must not be a mtx.
if (opts.isMetaTransaction) { if (opts.isMetaTransaction) {
return false; return false;
@ -285,8 +308,7 @@ function isDirectUniswapCompatible(quote: SwapQuote, opts: ExchangeProxyContract
return false; return false;
} }
const fill = order.fills[0]; const fill = order.fills[0];
// And that fill must be uniswap v2 or sushiswap. if (!directSources.includes(fill.source)) {
if (![ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap].includes(fill.source)) {
return false; return false;
} }
return true; return true;

View File

@ -5,7 +5,7 @@ import { MarketOperation, SwapQuote } from '../types';
import { ERC20BridgeSource } from '../utils/market_operation_utils/types'; import { ERC20BridgeSource } from '../utils/market_operation_utils/types';
/** /**
* Compute the mminimum buy token amount for market operations by inferring * Compute the minimum buy token amount for market operations by inferring
* the slippage from the orders in a quote. We cannot rely on * the slippage from the orders in a quote. We cannot rely on
* `worstCaseQuoteInfo.makerAssetAmount` because that does not stop at * `worstCaseQuoteInfo.makerAssetAmount` because that does not stop at
* maximum slippage. * maximum slippage.

View File

@ -165,8 +165,9 @@ export class SwapQuoter {
expiryBufferMs, expiryBufferMs,
permittedOrderFeeTypes, permittedOrderFeeTypes,
samplerGasLimit, samplerGasLimit,
liquidityProviderRegistryAddress,
rfqt, rfqt,
tokenAdjacencyGraph,
liquidityProviderRegistry,
} = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options); } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options);
const provider = providerUtils.standardizeOrThrow(supportedProvider); const provider = providerUtils.standardizeOrThrow(supportedProvider);
assert.isValidOrderbook('orderbook', orderbook); assert.isValidOrderbook('orderbook', orderbook);
@ -208,13 +209,21 @@ export class SwapQuoter {
}, },
); );
this._marketOperationUtils = new MarketOperationUtils( this._marketOperationUtils = new MarketOperationUtils(
new DexOrderSampler(samplerContract, samplerOverrides, provider), new DexOrderSampler(
samplerContract,
samplerOverrides,
provider,
undefined,
undefined,
undefined,
tokenAdjacencyGraph,
liquidityProviderRegistry,
),
this._contractAddresses, this._contractAddresses,
{ {
chainId, chainId,
exchangeAddress: this._contractAddresses.exchange, exchangeAddress: this._contractAddresses.exchange,
}, },
liquidityProviderRegistryAddress,
); );
this._swapQuoteCalculator = new SwapQuoteCalculator(this._marketOperationUtils); this._swapQuoteCalculator = new SwapQuoteCalculator(this._marketOperationUtils);
} }

View File

@ -4,7 +4,13 @@ import { TakerRequestQueryParams } from '@0x/quote-server';
import { SignedOrder } from '@0x/types'; import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import { ERC20BridgeSource, GetMarketOrdersOpts, OptimizedMarketOrder } from './utils/market_operation_utils/types'; import {
ERC20BridgeSource,
GetMarketOrdersOpts,
LiquidityProviderRegistry,
OptimizedMarketOrder,
TokenAdjacencyGraph,
} from './utils/market_operation_utils/types';
import { QuoteReport } from './utils/quote_report_generator'; import { QuoteReport } from './utils/quote_report_generator';
/** /**
@ -310,11 +316,12 @@ export interface SwapQuoterOpts extends OrderPrunerOpts {
ethereumRpcUrl?: string; ethereumRpcUrl?: string;
contractAddresses?: AssetSwapperContractAddresses; contractAddresses?: AssetSwapperContractAddresses;
samplerGasLimit?: number; samplerGasLimit?: number;
liquidityProviderRegistryAddress?: string;
multiBridgeAddress?: string; multiBridgeAddress?: string;
ethGasStationUrl?: string; ethGasStationUrl?: string;
rfqt?: SwapQuoterRfqtOpts; rfqt?: SwapQuoterRfqtOpts;
samplerOverrides?: SamplerOverrides; samplerOverrides?: SamplerOverrides;
tokenAdjacencyGraph?: TokenAdjacencyGraph;
liquidityProviderRegistry?: LiquidityProviderRegistry;
} }
/** /**

View File

@ -13,6 +13,7 @@ import {
FeeSchedule, FeeSchedule,
FillData, FillData,
GetMarketOrdersOpts, GetMarketOrdersOpts,
LiquidityProviderRegistry,
MultiHopFillData, MultiHopFillData,
SnowSwapFillData, SnowSwapFillData,
SushiSwapFillData, SushiSwapFillData,
@ -24,8 +25,7 @@ import {
/** /**
* Valid sources for market sell. * Valid sources for market sell.
*/ */
export const SELL_SOURCE_FILTER = new SourceFilters( export const SELL_SOURCE_FILTER = new SourceFilters([
[
ERC20BridgeSource.Native, ERC20BridgeSource.Native,
ERC20BridgeSource.Uniswap, ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2, ERC20BridgeSource.UniswapV2,
@ -44,15 +44,13 @@ export const SELL_SOURCE_FILTER = new SourceFilters(
ERC20BridgeSource.MultiHop, ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Dodo, ERC20BridgeSource.Dodo,
ERC20BridgeSource.Cream, ERC20BridgeSource.Cream,
], ERC20BridgeSource.LiquidityProvider,
[ERC20BridgeSource.MultiBridge], ]);
);
/** /**
* Valid sources for market buy. * Valid sources for market buy.
*/ */
export const BUY_SOURCE_FILTER = new SourceFilters( export const BUY_SOURCE_FILTER = new SourceFilters([
[
ERC20BridgeSource.Native, ERC20BridgeSource.Native,
ERC20BridgeSource.Uniswap, ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2, ERC20BridgeSource.UniswapV2,
@ -70,9 +68,8 @@ export const BUY_SOURCE_FILTER = new SourceFilters(
ERC20BridgeSource.MultiHop, ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Dodo, ERC20BridgeSource.Dodo,
ERC20BridgeSource.Cream, ERC20BridgeSource.Cream,
], ERC20BridgeSource.LiquidityProvider,
[ERC20BridgeSource.MultiBridge], ]);
);
/** /**
* 0x Protocol Fee Multiplier * 0x Protocol Fee Multiplier
@ -352,6 +349,8 @@ export const MAINNET_KYBER_TOKEN_RESERVE_IDS: { [token: string]: string } = {
'0xaa42414e44000000000000000000000000000000000000000000000000000000', '0xaa42414e44000000000000000000000000000000000000000000000000000000',
}; };
export const LIQUIDITY_PROVIDER_REGISTRY: LiquidityProviderRegistry = {};
export const MAINNET_SUSHI_SWAP_ROUTER = '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'; export const MAINNET_SUSHI_SWAP_ROUTER = '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F';
export const MAINNET_SHELL_POOLS = { export const MAINNET_SHELL_POOLS = {
@ -544,5 +543,5 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
exchangeProxyOverhead: () => ZERO_AMOUNT, exchangeProxyOverhead: () => ZERO_AMOUNT,
allowFallback: true, allowFallback: true,
shouldGenerateQuoteReport: false, shouldGenerateQuoteReport: false,
tokenAdjacencyGraph: {}, tokenAdjacencyGraph: { default: [] },
}; };

View File

@ -78,7 +78,6 @@ export async function getRfqtIndicativeQuotesAsync(
export class MarketOperationUtils { export class MarketOperationUtils {
private readonly _wethAddress: string; private readonly _wethAddress: string;
private readonly _multiBridge: string;
private readonly _sellSources: SourceFilters; private readonly _sellSources: SourceFilters;
private readonly _buySources: SourceFilters; private readonly _buySources: SourceFilters;
private readonly _feeSources = new SourceFilters(FEE_QUOTE_SOURCES); private readonly _feeSources = new SourceFilters(FEE_QUOTE_SOURCES);
@ -108,19 +107,10 @@ export class MarketOperationUtils {
private readonly _sampler: DexOrderSampler, private readonly _sampler: DexOrderSampler,
private readonly contractAddresses: AssetSwapperContractAddresses, private readonly contractAddresses: AssetSwapperContractAddresses,
private readonly _orderDomain: OrderDomain, private readonly _orderDomain: OrderDomain,
private readonly _liquidityProviderRegistry: string = NULL_ADDRESS,
) { ) {
this._wethAddress = contractAddresses.etherToken.toLowerCase(); this._wethAddress = contractAddresses.etherToken.toLowerCase();
this._multiBridge = contractAddresses.multiBridge.toLowerCase(); this._buySources = BUY_SOURCE_FILTER;
const optionalQuoteSources = []; this._sellSources = SELL_SOURCE_FILTER;
if (this._liquidityProviderRegistry !== NULL_ADDRESS) {
optionalQuoteSources.push(ERC20BridgeSource.LiquidityProvider);
}
if (this._multiBridge !== NULL_ADDRESS) {
optionalQuoteSources.push(ERC20BridgeSource.MultiBridge);
}
this._buySources = BUY_SOURCE_FILTER.validate(optionalQuoteSources);
this._sellSources = SELL_SOURCE_FILTER.validate(optionalQuoteSources);
} }
/** /**
@ -176,42 +166,21 @@ export class MarketOperationUtils {
// Get native order fillable amounts. // Get native order fillable amounts.
this._sampler.getOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchange), this._sampler.getOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchange),
// Get ETH -> maker token price. // Get ETH -> maker token price.
this._sampler.getMedianSellRate( this._sampler.getMedianSellRate(feeSourceFilters.sources, makerToken, this._wethAddress, ONE_ETHER),
feeSourceFilters.sources,
makerToken,
this._wethAddress,
ONE_ETHER,
this._liquidityProviderRegistry,
this._multiBridge,
),
// Get ETH -> taker token price. // Get ETH -> taker token price.
this._sampler.getMedianSellRate( this._sampler.getMedianSellRate(feeSourceFilters.sources, takerToken, this._wethAddress, ONE_ETHER),
feeSourceFilters.sources,
takerToken,
this._wethAddress,
ONE_ETHER,
this._liquidityProviderRegistry,
this._multiBridge,
),
// Get sell quotes for taker -> maker. // Get sell quotes for taker -> maker.
this._sampler.getSellQuotes( this._sampler.getSellQuotes(
quoteSourceFilters.exclude(offChainSources).sources, quoteSourceFilters.exclude(offChainSources).sources,
makerToken, makerToken,
takerToken, takerToken,
sampleAmounts, sampleAmounts,
this._wethAddress,
_opts.tokenAdjacencyGraph,
this._liquidityProviderRegistry,
this._multiBridge,
), ),
this._sampler.getTwoHopSellQuotes( this._sampler.getTwoHopSellQuotes(
quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [], quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [],
makerToken, makerToken,
takerToken, takerToken,
takerAmount, takerAmount,
this._wethAddress,
_opts.tokenAdjacencyGraph,
this._liquidityProviderRegistry,
), ),
); );
@ -327,41 +296,21 @@ export class MarketOperationUtils {
// Get native order fillable amounts. // Get native order fillable amounts.
this._sampler.getOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchange), this._sampler.getOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchange),
// Get ETH -> makerToken token price. // Get ETH -> makerToken token price.
this._sampler.getMedianSellRate( this._sampler.getMedianSellRate(feeSourceFilters.sources, makerToken, this._wethAddress, ONE_ETHER),
feeSourceFilters.sources,
makerToken,
this._wethAddress,
ONE_ETHER,
this._liquidityProviderRegistry,
this._multiBridge,
),
// Get ETH -> taker token price. // Get ETH -> taker token price.
this._sampler.getMedianSellRate( this._sampler.getMedianSellRate(feeSourceFilters.sources, takerToken, this._wethAddress, ONE_ETHER),
feeSourceFilters.sources,
takerToken,
this._wethAddress,
ONE_ETHER,
this._liquidityProviderRegistry,
this._multiBridge,
),
// Get buy quotes for taker -> maker. // Get buy quotes for taker -> maker.
this._sampler.getBuyQuotes( this._sampler.getBuyQuotes(
quoteSourceFilters.exclude(offChainSources).sources, quoteSourceFilters.exclude(offChainSources).sources,
makerToken, makerToken,
takerToken, takerToken,
sampleAmounts, sampleAmounts,
this._wethAddress,
_opts.tokenAdjacencyGraph,
this._liquidityProviderRegistry,
), ),
this._sampler.getTwoHopBuyQuotes( this._sampler.getTwoHopBuyQuotes(
quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [], quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [],
makerToken, makerToken,
takerToken, takerToken,
makerAmount, makerAmount,
this._wethAddress,
_opts.tokenAdjacencyGraph,
this._liquidityProviderRegistry,
), ),
); );
const isPriceAwareRfqEnabled = const isPriceAwareRfqEnabled =
@ -391,10 +340,6 @@ export class MarketOperationUtils {
offChainBalancerQuotes, offChainBalancerQuotes,
offChainCreamQuotes, offChainCreamQuotes,
] = await Promise.all([samplerPromise, rfqtPromise, offChainBalancerPromise, offChainCreamPromise]); ] = await Promise.all([samplerPromise, rfqtPromise, offChainBalancerPromise, offChainCreamPromise]);
// Attach the MultiBridge address to the sample fillData
(dexQuotes.find(quotes => quotes[0] && quotes[0].source === ERC20BridgeSource.MultiBridge) || []).forEach(
q => (q.fillData = { poolAddress: this._multiBridge }),
);
const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals; const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals;
return { return {
side: MarketOperation.Buy, side: MarketOperation.Buy,
@ -482,7 +427,6 @@ export class MarketOperationUtils {
getNativeOrderTokens(orders[0])[1], getNativeOrderTokens(orders[0])[1],
this._wethAddress, this._wethAddress,
ONE_ETHER, ONE_ETHER,
this._wethAddress,
), ),
), ),
...batchNativeOrders.map((orders, i) => ...batchNativeOrders.map((orders, i) =>
@ -491,8 +435,6 @@ export class MarketOperationUtils {
getNativeOrderTokens(orders[0])[0], getNativeOrderTokens(orders[0])[0],
getNativeOrderTokens(orders[0])[1], getNativeOrderTokens(orders[0])[1],
[makerAmounts[i]], [makerAmounts[i]],
this._wethAddress,
_opts.tokenAdjacencyGraph,
), ),
), ),
]; ];

View File

@ -0,0 +1,12 @@
import { LiquidityProviderRegistry } from './types';
// tslint:disable completed-docs
export function getLiquidityProvidersForPair(
registry: LiquidityProviderRegistry,
takerToken: string,
makerToken: string,
): string[] {
return Object.entries(registry)
.filter(([, tokens]) => [makerToken, takerToken].every(t => tokens.includes(t)))
.map(([providerAddress]) => providerAddress);
}

View File

@ -1,13 +0,0 @@
import { NULL_ADDRESS, TOKENS } from './constants';
// tslint:disable completed-docs
export function getMultiBridgeIntermediateToken(takerToken: string, makerToken: string): string {
let intermediateToken = NULL_ADDRESS;
if (takerToken !== TOKENS.WETH && makerToken !== TOKENS.WETH) {
intermediateToken = TOKENS.WETH;
} else if (takerToken === TOKENS.USDC || makerToken === TOKENS.USDC) {
intermediateToken = TOKENS.DAI;
}
return intermediateToken;
}

View File

@ -21,19 +21,11 @@ export function getIntermediateTokens(
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
tokenAdjacencyGraph: TokenAdjacencyGraph, tokenAdjacencyGraph: TokenAdjacencyGraph,
wethAddress: string,
): string[] { ): string[] {
let intermediateTokens = []; const intermediateTokens = _.intersection(
if (makerToken === wethAddress) { _.get(tokenAdjacencyGraph, takerToken, tokenAdjacencyGraph.default),
intermediateTokens = _.get(tokenAdjacencyGraph, takerToken, [] as string[]); _.get(tokenAdjacencyGraph, makerToken, tokenAdjacencyGraph.default),
} else if (takerToken === wethAddress) {
intermediateTokens = _.get(tokenAdjacencyGraph, makerToken, [] as string[]);
} else {
intermediateTokens = _.union(
_.intersection(_.get(tokenAdjacencyGraph, takerToken, []), _.get(tokenAdjacencyGraph, makerToken, [])),
[wethAddress],
); );
}
return _.uniqBy(intermediateTokens, a => a.toLowerCase()).filter( return _.uniqBy(intermediateTokens, a => a.toLowerCase()).filter(
token => token.toLowerCase() !== makerToken.toLowerCase() && token.toLowerCase() !== takerToken.toLowerCase(), token => token.toLowerCase() !== makerToken.toLowerCase() && token.toLowerCase() !== takerToken.toLowerCase(),
); );

View File

@ -15,7 +15,6 @@ import {
WALLET_SIGNATURE, WALLET_SIGNATURE,
ZERO_AMOUNT, ZERO_AMOUNT,
} from './constants'; } from './constants';
import { getMultiBridgeIntermediateToken } from './multibridge_utils';
import { import {
AggregationError, AggregationError,
BalancerFillData, BalancerFillData,
@ -28,7 +27,6 @@ import {
KyberFillData, KyberFillData,
LiquidityProviderFillData, LiquidityProviderFillData,
MooniswapFillData, MooniswapFillData,
MultiBridgeFillData,
MultiHopFillData, MultiHopFillData,
NativeCollapsedFill, NativeCollapsedFill,
OptimizedMarketOrder, OptimizedMarketOrder,
@ -193,8 +191,6 @@ function getBridgeAddressFromFill(fill: CollapsedFill, opts: CreateOrderFromPath
return opts.contractAddresses.creamBridge; return opts.contractAddresses.creamBridge;
case ERC20BridgeSource.LiquidityProvider: case ERC20BridgeSource.LiquidityProvider:
return (fill.fillData as LiquidityProviderFillData).poolAddress; return (fill.fillData as LiquidityProviderFillData).poolAddress;
case ERC20BridgeSource.MultiBridge:
return (fill.fillData as MultiBridgeFillData).poolAddress;
case ERC20BridgeSource.MStable: case ERC20BridgeSource.MStable:
return opts.contractAddresses.mStableBridge; return opts.contractAddresses.mStableBridge;
case ERC20BridgeSource.Mooniswap: case ERC20BridgeSource.Mooniswap:
@ -301,13 +297,6 @@ export function createBridgeOrder(
createSushiSwapBridgeData(sushiSwapFillData.tokenAddressPath, sushiSwapFillData.router), createSushiSwapBridgeData(sushiSwapFillData.tokenAddressPath, sushiSwapFillData.router),
); );
break; break;
case ERC20BridgeSource.MultiBridge:
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
makerToken,
bridgeAddress,
createMultiBridgeData(takerToken, makerToken),
);
break;
case ERC20BridgeSource.Kyber: case ERC20BridgeSource.Kyber:
const kyberFillData = (fill as CollapsedFill<KyberFillData>).fillData!; // tslint:disable-line:no-non-null-assertion const kyberFillData = (fill as CollapsedFill<KyberFillData>).fillData!; // tslint:disable-line:no-non-null-assertion
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
@ -372,15 +361,6 @@ function createBridgeData(tokenAddress: string): string {
return encoder.encode({ tokenAddress }); return encoder.encode({ tokenAddress });
} }
function createMultiBridgeData(takerToken: string, makerToken: string): string {
const intermediateToken = getMultiBridgeIntermediateToken(takerToken, makerToken);
const encoder = AbiEncoder.create([
{ name: 'takerToken', type: 'address' },
{ name: 'intermediateToken', type: 'address' },
]);
return encoder.encode({ takerToken, intermediateToken });
}
function createBalancerBridgeData(takerToken: string, poolAddress: string): string { function createBalancerBridgeData(takerToken: string, poolAddress: string): string {
const encoder = AbiEncoder.create([ const encoder = AbiEncoder.create([
{ name: 'takerToken', type: 'address' }, { name: 'takerToken', type: 'address' },

View File

@ -2,7 +2,7 @@ import { BigNumber } from '@0x/utils';
import { MarketOperation } from '../../types'; import { MarketOperation } from '../../types';
import { POSITIVE_INF, SOURCE_FLAGS, ZERO_AMOUNT } from './constants'; import { POSITIVE_INF, ZERO_AMOUNT } from './constants';
import { createBridgeOrder, createNativeOrder, CreateOrderFromPathOpts, getMakerTakerTokens } from './orders'; import { createBridgeOrder, createNativeOrder, CreateOrderFromPathOpts, getMakerTakerTokens } from './orders';
import { getCompleteRate, getRate } from './rate_utils'; import { getCompleteRate, getRate } from './rate_utils';
import { import {
@ -202,7 +202,7 @@ export class Path {
} }
} }
} }
return doSourcesConflict(this.sourceFlags); return true;
} }
public isValidNextFill(fill: Fill): boolean { public isValidNextFill(fill: Fill): boolean {
@ -215,7 +215,7 @@ export class Path {
if (fill.parent) { if (fill.parent) {
return false; return false;
} }
return doSourcesConflict(this.sourceFlags | fill.flags); return true;
} }
private _collapseFills(): ReadonlyArray<CollapsedFill> { private _collapseFills(): ReadonlyArray<CollapsedFill> {
@ -268,9 +268,3 @@ export interface CollapsedPath extends Path {
readonly collapsedFills: ReadonlyArray<CollapsedFill>; readonly collapsedFills: ReadonlyArray<CollapsedFill>;
readonly orders: OptimizedMarketOrder[]; readonly orders: OptimizedMarketOrder[];
} }
const MULTIBRIDGE_SOURCES = SOURCE_FLAGS.LiquidityProvider | SOURCE_FLAGS.Uniswap;
export function doSourcesConflict(flags: number): boolean {
const multiBridgeConflict = flags & SOURCE_FLAGS.MultiBridge && flags & MULTIBRIDGE_SOURCES;
return !multiBridgeConflict;
}

View File

@ -8,7 +8,7 @@ import { BalancerPoolsCache } from './balancer_utils';
import { BancorService } from './bancor_service'; import { BancorService } from './bancor_service';
import { CreamPoolsCache } from './cream_utils'; import { CreamPoolsCache } from './cream_utils';
import { SamplerOperations } from './sampler_operations'; import { SamplerOperations } from './sampler_operations';
import { BatchedOperation } from './types'; import { BatchedOperation, LiquidityProviderRegistry, TokenAdjacencyGraph } from './types';
/** /**
* Generate sample amounts up to `maxFillAmount`. * Generate sample amounts up to `maxFillAmount`.
@ -40,8 +40,18 @@ export class DexOrderSampler extends SamplerOperations {
balancerPoolsCache?: BalancerPoolsCache, balancerPoolsCache?: BalancerPoolsCache,
creamPoolsCache?: CreamPoolsCache, creamPoolsCache?: CreamPoolsCache,
getBancorServiceFn?: () => BancorService, getBancorServiceFn?: () => BancorService,
tokenAdjacencyGraph?: TokenAdjacencyGraph,
liquidityProviderRegistry?: LiquidityProviderRegistry,
) { ) {
super(_samplerContract, provider, balancerPoolsCache, creamPoolsCache, getBancorServiceFn); super(
_samplerContract,
provider,
balancerPoolsCache,
creamPoolsCache,
getBancorServiceFn,
tokenAdjacencyGraph,
liquidityProviderRegistry,
);
} }
/* Type overloads for `executeAsync()`. Could skip this if we would upgrade TS. */ /* Type overloads for `executeAsync()`. Could skip this if we would upgrade TS. */

View File

@ -1,17 +1,17 @@
import { SupportedProvider } from '@0x/dev-utils'; import { SupportedProvider } from '@0x/dev-utils';
import { SignedOrder } from '@0x/types'; import { SignedOrder } from '@0x/types';
import { BigNumber, NULL_ADDRESS } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { ERC20BridgeSamplerContract } from '../../wrappers'; import { ERC20BridgeSamplerContract } from '../../wrappers';
import { BalancerPoolsCache, computeBalancerBuyQuote, computeBalancerSellQuote } from './balancer_utils'; import { BalancerPoolsCache, computeBalancerBuyQuote, computeBalancerSellQuote } from './balancer_utils';
import { BancorService } from './bancor_service'; import { BancorService } from './bancor_service';
import { MAINNET_SUSHI_SWAP_ROUTER, MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants'; import { LIQUIDITY_PROVIDER_REGISTRY, MAINNET_SUSHI_SWAP_ROUTER, MAX_UINT256, ZERO_AMOUNT } from './constants';
import { CreamPoolsCache } from './cream_utils'; import { CreamPoolsCache } from './cream_utils';
import { getCurveInfosForPair, getSnowSwapInfosForPair, getSwerveInfosForPair } from './curve_utils'; import { getCurveInfosForPair, getSnowSwapInfosForPair, getSwerveInfosForPair } from './curve_utils';
import { getKyberReserveIdsForPair } from './kyber_utils'; import { getKyberReserveIdsForPair } from './kyber_utils';
import { getMultiBridgeIntermediateToken } from './multibridge_utils'; import { getLiquidityProvidersForPair } from './liquidity_provider_utils';
import { getIntermediateTokens } from './multihop_utils'; import { getIntermediateTokens } from './multihop_utils';
import { SamplerContractOperation } from './sampler_contract_operation'; import { SamplerContractOperation } from './sampler_contract_operation';
import { getShellsForPair } from './shell_utils'; import { getShellsForPair } from './shell_utils';
@ -28,8 +28,8 @@ import {
HopInfo, HopInfo,
KyberFillData, KyberFillData,
LiquidityProviderFillData, LiquidityProviderFillData,
LiquidityProviderRegistry,
MooniswapFillData, MooniswapFillData,
MultiBridgeFillData,
MultiHopFillData, MultiHopFillData,
ShellFillData, ShellFillData,
SnowSwapFillData, SnowSwapFillData,
@ -47,7 +47,6 @@ import {
*/ */
export const TWO_HOP_SOURCE_FILTERS = SourceFilters.all().exclude([ export const TWO_HOP_SOURCE_FILTERS = SourceFilters.all().exclude([
ERC20BridgeSource.MultiHop, ERC20BridgeSource.MultiHop,
ERC20BridgeSource.MultiBridge,
ERC20BridgeSource.Native, ERC20BridgeSource.Native,
]); ]);
/** /**
@ -80,6 +79,8 @@ export class SamplerOperations {
public readonly balancerPoolsCache: BalancerPoolsCache = new BalancerPoolsCache(), public readonly balancerPoolsCache: BalancerPoolsCache = new BalancerPoolsCache(),
public readonly creamPoolsCache: CreamPoolsCache = new CreamPoolsCache(), public readonly creamPoolsCache: CreamPoolsCache = new CreamPoolsCache(),
protected readonly getBancorServiceFn?: () => BancorService, // for dependency injection in tests protected readonly getBancorServiceFn?: () => BancorService, // for dependency injection in tests
protected readonly tokenAdjacencyGraph: TokenAdjacencyGraph = { default: [] },
public readonly liquidityProviderRegistry: LiquidityProviderRegistry = LIQUIDITY_PROVIDER_REGISTRY,
) {} ) {}
public async getBancorServiceAsync(): Promise<BancorService> { public async getBancorServiceAsync(): Promise<BancorService> {
@ -221,64 +222,32 @@ export class SamplerOperations {
} }
public getLiquidityProviderSellQuotes( public getLiquidityProviderSellQuotes(
registryAddress: string, providerAddress: string,
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
takerFillAmounts: BigNumber[], takerFillAmounts: BigNumber[],
): SourceQuoteOperation<LiquidityProviderFillData> { ): SourceQuoteOperation<LiquidityProviderFillData> {
return new SamplerContractOperation({ return new SamplerContractOperation({
source: ERC20BridgeSource.LiquidityProvider, source: ERC20BridgeSource.LiquidityProvider,
fillData: {} as LiquidityProviderFillData, // tslint:disable-line:no-object-literal-type-assertion fillData: { poolAddress: providerAddress },
contract: this._samplerContract, contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromLiquidityProviderRegistry, function: this._samplerContract.sampleSellsFromLiquidityProvider,
params: [registryAddress, takerToken, makerToken, takerFillAmounts], params: [providerAddress, takerToken, makerToken, takerFillAmounts],
callback: (callResults: string, fillData: LiquidityProviderFillData): BigNumber[] => {
const [samples, poolAddress] = this._samplerContract.getABIDecodedReturnData<[BigNumber[], string]>(
'sampleSellsFromLiquidityProviderRegistry',
callResults,
);
fillData.poolAddress = poolAddress;
return samples;
},
}); });
} }
public getLiquidityProviderBuyQuotes( public getLiquidityProviderBuyQuotes(
registryAddress: string, providerAddress: string,
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
makerFillAmounts: BigNumber[], makerFillAmounts: BigNumber[],
): SourceQuoteOperation<LiquidityProviderFillData> { ): SourceQuoteOperation<LiquidityProviderFillData> {
return new SamplerContractOperation({ return new SamplerContractOperation({
source: ERC20BridgeSource.LiquidityProvider, source: ERC20BridgeSource.LiquidityProvider,
fillData: {} as LiquidityProviderFillData, // tslint:disable-line:no-object-literal-type-assertion fillData: { poolAddress: providerAddress },
contract: this._samplerContract, contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromLiquidityProviderRegistry, function: this._samplerContract.sampleBuysFromLiquidityProvider,
params: [registryAddress, takerToken, makerToken, makerFillAmounts], params: [providerAddress, takerToken, makerToken, makerFillAmounts],
callback: (callResults: string, fillData: LiquidityProviderFillData): BigNumber[] => {
const [samples, poolAddress] = this._samplerContract.getABIDecodedReturnData<[BigNumber[], string]>(
'sampleBuysFromLiquidityProviderRegistry',
callResults,
);
fillData.poolAddress = poolAddress;
return samples;
},
});
}
public getMultiBridgeSellQuotes(
multiBridgeAddress: string,
makerToken: string,
intermediateToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
): SourceQuoteOperation<MultiBridgeFillData> {
return new SamplerContractOperation({
source: ERC20BridgeSource.MultiBridge,
fillData: { poolAddress: multiBridgeAddress },
contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromMultiBridge,
params: [multiBridgeAddress, takerToken, intermediateToken, makerToken, takerFillAmounts],
}); });
} }
@ -669,34 +638,15 @@ export class SamplerOperations {
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
sellAmount: BigNumber, sellAmount: BigNumber,
wethAddress: string,
tokenAdjacencyGraph: TokenAdjacencyGraph,
liquidityProviderRegistryAddress?: string,
): BatchedOperation<Array<DexSample<MultiHopFillData>>> { ): BatchedOperation<Array<DexSample<MultiHopFillData>>> {
const _sources = TWO_HOP_SOURCE_FILTERS.getAllowed(sources); const _sources = TWO_HOP_SOURCE_FILTERS.getAllowed(sources);
if (_sources.length === 0) { if (_sources.length === 0) {
return SamplerOperations.constant([]); return SamplerOperations.constant([]);
} }
const intermediateTokens = getIntermediateTokens(makerToken, takerToken, tokenAdjacencyGraph, wethAddress); const intermediateTokens = getIntermediateTokens(makerToken, takerToken, this.tokenAdjacencyGraph);
const subOps = intermediateTokens.map(intermediateToken => { const subOps = intermediateTokens.map(intermediateToken => {
const firstHopOps = this._getSellQuoteOperations( const firstHopOps = this._getSellQuoteOperations(_sources, intermediateToken, takerToken, [ZERO_AMOUNT]);
_sources, const secondHopOps = this._getSellQuoteOperations(_sources, makerToken, intermediateToken, [ZERO_AMOUNT]);
intermediateToken,
takerToken,
[ZERO_AMOUNT],
wethAddress,
tokenAdjacencyGraph,
liquidityProviderRegistryAddress,
);
const secondHopOps = this._getSellQuoteOperations(
_sources,
makerToken,
intermediateToken,
[ZERO_AMOUNT],
wethAddress,
tokenAdjacencyGraph,
liquidityProviderRegistryAddress,
);
return new SamplerContractOperation({ return new SamplerContractOperation({
contract: this._samplerContract, contract: this._samplerContract,
source: ERC20BridgeSource.MultiHop, source: ERC20BridgeSource.MultiHop,
@ -747,34 +697,19 @@ export class SamplerOperations {
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
buyAmount: BigNumber, buyAmount: BigNumber,
wethAddress: string,
tokenAdjacencyGraph: TokenAdjacencyGraph,
liquidityProviderRegistryAddress?: string,
): BatchedOperation<Array<DexSample<MultiHopFillData>>> { ): BatchedOperation<Array<DexSample<MultiHopFillData>>> {
const _sources = TWO_HOP_SOURCE_FILTERS.getAllowed(sources); const _sources = TWO_HOP_SOURCE_FILTERS.getAllowed(sources);
if (_sources.length === 0) { if (_sources.length === 0) {
return SamplerOperations.constant([]); return SamplerOperations.constant([]);
} }
const intermediateTokens = getIntermediateTokens(makerToken, takerToken, tokenAdjacencyGraph, wethAddress); const intermediateTokens = getIntermediateTokens(makerToken, takerToken, this.tokenAdjacencyGraph);
const subOps = intermediateTokens.map(intermediateToken => { const subOps = intermediateTokens.map(intermediateToken => {
const firstHopOps = this._getBuyQuoteOperations( const firstHopOps = this._getBuyQuoteOperations(_sources, intermediateToken, takerToken, [
_sources, new BigNumber(0),
intermediateToken, ]);
takerToken, const secondHopOps = this._getBuyQuoteOperations(_sources, makerToken, intermediateToken, [
[new BigNumber(0)], new BigNumber(0),
wethAddress, ]);
tokenAdjacencyGraph, // TODO is this a bad idea?
liquidityProviderRegistryAddress,
);
const secondHopOps = this._getBuyQuoteOperations(
_sources,
makerToken,
intermediateToken,
[new BigNumber(0)],
wethAddress,
tokenAdjacencyGraph,
liquidityProviderRegistryAddress,
);
return new SamplerContractOperation({ return new SamplerContractOperation({
contract: this._samplerContract, contract: this._samplerContract,
source: ERC20BridgeSource.MultiHop, source: ERC20BridgeSource.MultiHop,
@ -853,17 +788,10 @@ export class SamplerOperations {
): SourceQuoteOperation<ShellFillData> { ): SourceQuoteOperation<ShellFillData> {
return new SamplerContractOperation({ return new SamplerContractOperation({
source: ERC20BridgeSource.Shell, source: ERC20BridgeSource.Shell,
fillData: { poolAddress },
contract: this._samplerContract, contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromShell, function: this._samplerContract.sampleSellsFromShell,
params: [poolAddress, takerToken, makerToken, takerFillAmounts], params: [poolAddress, takerToken, makerToken, takerFillAmounts],
callback: (callResults: string, fillData: ShellFillData): BigNumber[] => {
const samples = this._samplerContract.getABIDecodedReturnData<BigNumber[]>(
'sampleSellsFromShell',
callResults,
);
fillData.poolAddress = poolAddress;
return samples;
},
}); });
} }
@ -875,17 +803,10 @@ export class SamplerOperations {
): SourceQuoteOperation { ): SourceQuoteOperation {
return new SamplerContractOperation({ return new SamplerContractOperation({
source: ERC20BridgeSource.Shell, source: ERC20BridgeSource.Shell,
fillData: { poolAddress },
contract: this._samplerContract, contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromShell, function: this._samplerContract.sampleBuysFromShell,
params: [poolAddress, takerToken, makerToken, makerFillAmounts], params: [poolAddress, takerToken, makerToken, makerFillAmounts],
callback: (callResults: string, fillData: ShellFillData): BigNumber[] => {
const samples = this._samplerContract.getABIDecodedReturnData<BigNumber[]>(
'sampleBuysFromShell',
callResults,
);
fillData.poolAddress = poolAddress;
return samples;
},
}); });
} }
@ -936,52 +857,36 @@ export class SamplerOperations {
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
takerFillAmount: BigNumber, takerFillAmount: BigNumber,
liquidityProviderRegistryAddress?: string,
multiBridgeAddress?: string,
): BatchedOperation<BigNumber> { ): BatchedOperation<BigNumber> {
if (makerToken.toLowerCase() === takerToken.toLowerCase()) { if (makerToken.toLowerCase() === takerToken.toLowerCase()) {
return SamplerOperations.constant(new BigNumber(1)); return SamplerOperations.constant(new BigNumber(1));
} }
const getSellQuotes = this.getSellQuotes( const subOps = this._getSellQuoteOperations(sources, makerToken, takerToken, [takerFillAmount], {
sources, default: [],
makerToken, });
takerToken,
[takerFillAmount],
NULL_ADDRESS, // weth address
{}, // token adjacency
liquidityProviderRegistryAddress,
multiBridgeAddress,
);
return { return {
encodeCall: () => { encodeCall: () => {
const encodedCall = getSellQuotes.encodeCall(); const subCalls = subOps.map(op => op.encodeCall());
// All soures were excluded return this._samplerContract.batchCall(subCalls).getABIEncodedTransactionData();
if (encodedCall === NULL_BYTES) {
return NULL_BYTES;
}
return this._samplerContract.batchCall([encodedCall]).getABIEncodedTransactionData();
}, },
handleCallResults: callResults => { handleCallResults: callResults => {
if (callResults === NULL_BYTES) {
return ZERO_AMOUNT;
}
const rawSubCallResults = this._samplerContract.getABIDecodedReturnData<string[]>( const rawSubCallResults = this._samplerContract.getABIDecodedReturnData<string[]>(
'batchCall', 'batchCall',
callResults, callResults,
); );
const samples = getSellQuotes.handleCallResults(rawSubCallResults[0]); const samples = subOps.map((op, i) => op.handleCallResults(rawSubCallResults[i]));
if (samples.length === 0) { if (samples.length === 0) {
return ZERO_AMOUNT; return ZERO_AMOUNT;
} }
const flatSortedSamples = samples const flatSortedSamples = samples
.reduce((acc, v) => acc.concat(...v)) .reduce((acc, v) => acc.concat(...v))
.filter(v => !v.output.isZero()) .filter(v => !v.isZero())
.sort((a, b) => a.output.comparedTo(b.output)); .sort((a, b) => a.comparedTo(b));
if (flatSortedSamples.length === 0) { if (flatSortedSamples.length === 0) {
return ZERO_AMOUNT; return ZERO_AMOUNT;
} }
const medianSample = flatSortedSamples[Math.floor(flatSortedSamples.length / 2)]; const medianSample = flatSortedSamples[Math.floor(flatSortedSamples.length / 2)];
return medianSample.output.div(medianSample.input); return medianSample.div(takerFillAmount);
}, },
}; };
} }
@ -991,21 +896,8 @@ export class SamplerOperations {
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
takerFillAmounts: BigNumber[], takerFillAmounts: BigNumber[],
wethAddress: string,
tokenAdjacencyGraph: TokenAdjacencyGraph,
liquidityProviderRegistryAddress?: string,
multiBridgeAddress?: string,
): BatchedOperation<DexSample[][]> { ): BatchedOperation<DexSample[][]> {
const subOps = this._getSellQuoteOperations( const subOps = this._getSellQuoteOperations(sources, makerToken, takerToken, takerFillAmounts);
sources,
makerToken,
takerToken,
takerFillAmounts,
wethAddress,
tokenAdjacencyGraph,
liquidityProviderRegistryAddress,
multiBridgeAddress,
);
return { return {
encodeCall: () => { encodeCall: () => {
const subCalls = subOps.map(op => op.encodeCall()); const subCalls = subOps.map(op => op.encodeCall());
@ -1034,19 +926,8 @@ export class SamplerOperations {
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
makerFillAmounts: BigNumber[], makerFillAmounts: BigNumber[],
wethAddress: string,
tokenAdjacencyGraph: TokenAdjacencyGraph,
liquidityProviderRegistryAddress?: string,
): BatchedOperation<DexSample[][]> { ): BatchedOperation<DexSample[][]> {
const subOps = this._getBuyQuoteOperations( const subOps = this._getBuyQuoteOperations(sources, makerToken, takerToken, makerFillAmounts);
sources,
makerToken,
takerToken,
makerFillAmounts,
wethAddress,
tokenAdjacencyGraph,
liquidityProviderRegistryAddress,
);
return { return {
encodeCall: () => { encodeCall: () => {
const subCalls = subOps.map(op => op.encodeCall()); const subCalls = subOps.map(op => op.encodeCall());
@ -1075,21 +956,12 @@ export class SamplerOperations {
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
takerFillAmounts: BigNumber[], takerFillAmounts: BigNumber[],
wethAddress: string, tokenAdjacencyGraph: TokenAdjacencyGraph = this.tokenAdjacencyGraph,
tokenAdjacencyGraph: TokenAdjacencyGraph,
liquidityProviderRegistryAddress?: string,
multiBridgeAddress?: string,
): SourceQuoteOperation[] { ): SourceQuoteOperation[] {
const _sources = BATCH_SOURCE_FILTERS.exclude(
liquidityProviderRegistryAddress ? [] : [ERC20BridgeSource.LiquidityProvider],
)
.exclude(multiBridgeAddress || multiBridgeAddress === NULL_ADDRESS ? [] : [ERC20BridgeSource.MultiBridge])
.getAllowed(sources);
// Find the adjacent tokens in the provided tooken adjacency graph, // Find the adjacent tokens in the provided tooken adjacency graph,
// e.g if this is DAI->USDC we may check for DAI->WETH->USDC // e.g if this is DAI->USDC we may check for DAI->WETH->USDC
const intermediateTokens = getIntermediateTokens(makerToken, takerToken, tokenAdjacencyGraph, wethAddress); const intermediateTokens = getIntermediateTokens(makerToken, takerToken, tokenAdjacencyGraph);
const _sources = BATCH_SOURCE_FILTERS.getAllowed(sources);
return _.flatten( return _.flatten(
_sources.map( _sources.map(
(source): SourceQuoteOperation | SourceQuoteOperation[] => { (source): SourceQuoteOperation | SourceQuoteOperation[] => {
@ -1144,30 +1016,12 @@ export class SamplerOperations {
), ),
); );
case ERC20BridgeSource.LiquidityProvider: case ERC20BridgeSource.LiquidityProvider:
if (liquidityProviderRegistryAddress === undefined) { return getLiquidityProvidersForPair(
throw new Error( this.liquidityProviderRegistry,
'Cannot sample liquidity from a LiquidityProvider liquidity pool, if a registry is not provided.',
);
}
return this.getLiquidityProviderSellQuotes(
liquidityProviderRegistryAddress,
makerToken,
takerToken, takerToken,
takerFillAmounts,
);
case ERC20BridgeSource.MultiBridge:
if (multiBridgeAddress === undefined) {
throw new Error(
'Cannot sample liquidity from MultiBridge if an address is not provided.',
);
}
const intermediateToken = getMultiBridgeIntermediateToken(takerToken, makerToken);
return this.getMultiBridgeSellQuotes(
multiBridgeAddress,
makerToken, makerToken,
intermediateToken, ).map(pool =>
takerToken, this.getLiquidityProviderSellQuotes(pool, makerToken, takerToken, takerFillAmounts),
takerFillAmounts,
); );
case ERC20BridgeSource.MStable: case ERC20BridgeSource.MStable:
return this.getMStableSellQuotes(makerToken, takerToken, takerFillAmounts); return this.getMStableSellQuotes(makerToken, takerToken, takerFillAmounts);
@ -1216,18 +1070,11 @@ export class SamplerOperations {
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
makerFillAmounts: BigNumber[], makerFillAmounts: BigNumber[],
wethAddress: string,
tokenAdjacencyGraph: TokenAdjacencyGraph,
liquidityProviderRegistryAddress?: string,
): SourceQuoteOperation[] { ): SourceQuoteOperation[] {
const _sources = BATCH_SOURCE_FILTERS.exclude(
liquidityProviderRegistryAddress ? [] : [ERC20BridgeSource.LiquidityProvider],
).getAllowed(sources);
// Find the adjacent tokens in the provided tooken adjacency graph, // Find the adjacent tokens in the provided tooken adjacency graph,
// e.g if this is DAI->USDC we may check for DAI->WETH->USDC // e.g if this is DAI->USDC we may check for DAI->WETH->USDC
const intermediateTokens = getIntermediateTokens(makerToken, takerToken, tokenAdjacencyGraph, wethAddress); const intermediateTokens = getIntermediateTokens(makerToken, takerToken, this.tokenAdjacencyGraph);
const _sources = BATCH_SOURCE_FILTERS.getAllowed(sources);
return _.flatten( return _.flatten(
_sources.map( _sources.map(
(source): SourceQuoteOperation | SourceQuoteOperation[] => { (source): SourceQuoteOperation | SourceQuoteOperation[] => {
@ -1282,16 +1129,12 @@ export class SamplerOperations {
), ),
); );
case ERC20BridgeSource.LiquidityProvider: case ERC20BridgeSource.LiquidityProvider:
if (liquidityProviderRegistryAddress === undefined) { return getLiquidityProvidersForPair(
throw new Error( this.liquidityProviderRegistry,
'Cannot sample liquidity from a LiquidityProvider liquidity pool, if a registry is not provided.',
);
}
return this.getLiquidityProviderBuyQuotes(
liquidityProviderRegistryAddress,
makerToken,
takerToken, takerToken,
makerFillAmounts, makerToken,
).map(pool =>
this.getLiquidityProviderBuyQuotes(pool, makerToken, takerToken, makerFillAmounts),
); );
case ERC20BridgeSource.MStable: case ERC20BridgeSource.MStable:
return this.getMStableBuyQuotes(makerToken, takerToken, makerFillAmounts); return this.getMStableBuyQuotes(makerToken, takerToken, makerFillAmounts);

View File

@ -132,10 +132,6 @@ export interface LiquidityProviderFillData extends FillData {
poolAddress: string; poolAddress: string;
} }
export interface MultiBridgeFillData extends FillData {
poolAddress: string;
}
export interface BancorFillData extends FillData { export interface BancorFillData extends FillData {
path: string[]; path: string[];
networkAddress: string; networkAddress: string;
@ -375,6 +371,11 @@ export interface MarketSideLiquidity {
export interface TokenAdjacencyGraph { export interface TokenAdjacencyGraph {
[token: string]: string[]; [token: string]: string[];
default: string[];
}
export interface LiquidityProviderRegistry {
[address: string]: [string, string];
} }
export interface GenerateOptimizedOrdersOpts { export interface GenerateOptimizedOrdersOpts {

View File

@ -3,8 +3,4 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually. * Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* ----------------------------------------------------------------------------- * -----------------------------------------------------------------------------
*/ */
export * from '../generated-wrappers/dummy_liquidity_provider';
export * from '../generated-wrappers/dummy_liquidity_provider_registry';
export * from '../generated-wrappers/erc20_bridge_sampler'; export * from '../generated-wrappers/erc20_bridge_sampler';
export * from '../generated-wrappers/i_liquidity_provider';
export * from '../generated-wrappers/i_liquidity_provider_registry';

View File

@ -11,15 +11,12 @@ import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json';
import * as DeploymentConstants from '../test/generated-artifacts/DeploymentConstants.json'; import * as DeploymentConstants from '../test/generated-artifacts/DeploymentConstants.json';
import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json'; import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json';
import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquidityProvider.json'; import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquidityProvider.json';
import * as DummyLiquidityProviderRegistry from '../test/generated-artifacts/DummyLiquidityProviderRegistry.json';
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json'; import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
import * as Eth2DaiSampler from '../test/generated-artifacts/Eth2DaiSampler.json'; import * as Eth2DaiSampler from '../test/generated-artifacts/Eth2DaiSampler.json';
import * as IBalancer from '../test/generated-artifacts/IBalancer.json'; import * as IBalancer from '../test/generated-artifacts/IBalancer.json';
import * as ICurve from '../test/generated-artifacts/ICurve.json'; import * as ICurve from '../test/generated-artifacts/ICurve.json';
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json'; import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json'; import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.json';
import * as ILiquidityProviderRegistry from '../test/generated-artifacts/ILiquidityProviderRegistry.json';
import * as IMooniswap from '../test/generated-artifacts/IMooniswap.json'; import * as IMooniswap from '../test/generated-artifacts/IMooniswap.json';
import * as IMStable from '../test/generated-artifacts/IMStable.json'; import * as IMStable from '../test/generated-artifacts/IMStable.json';
import * as IMultiBridge from '../test/generated-artifacts/IMultiBridge.json'; import * as IMultiBridge from '../test/generated-artifacts/IMultiBridge.json';
@ -64,8 +61,6 @@ export const artifacts = {
ICurve: ICurve as ContractArtifact, ICurve: ICurve as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact, IEth2Dai: IEth2Dai as ContractArtifact,
IKyberNetwork: IKyberNetwork as ContractArtifact, IKyberNetwork: IKyberNetwork as ContractArtifact,
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
ILiquidityProviderRegistry: ILiquidityProviderRegistry as ContractArtifact,
IMStable: IMStable as ContractArtifact, IMStable: IMStable as ContractArtifact,
IMooniswap: IMooniswap as ContractArtifact, IMooniswap: IMooniswap as ContractArtifact,
IMultiBridge: IMultiBridge as ContractArtifact, IMultiBridge: IMultiBridge as ContractArtifact,
@ -73,7 +68,6 @@ export const artifacts = {
IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact, IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact, IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact, DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,
DummyLiquidityProviderRegistry: DummyLiquidityProviderRegistry as ContractArtifact,
TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact, TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact,
TestNativeOrderSampler: TestNativeOrderSampler as ContractArtifact, TestNativeOrderSampler: TestNativeOrderSampler as ContractArtifact,
}; };

View File

@ -11,11 +11,7 @@ import { BigNumber, hexUtils } from '@0x/utils';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { artifacts } from '../artifacts'; import { artifacts } from '../artifacts';
import { import { DummyLiquidityProviderContract, TestERC20BridgeSamplerContract } from '../wrappers';
DummyLiquidityProviderContract,
DummyLiquidityProviderRegistryContract,
TestERC20BridgeSamplerContract,
} from '../wrappers';
// tslint:disable: custom-no-magic-numbers // tslint:disable: custom-no-magic-numbers
@ -817,7 +813,6 @@ blockchainTests('erc20-bridge-sampler', env => {
const yAsset = randomAddress(); const yAsset = randomAddress();
const sampleAmounts = getSampleAmounts(yAsset); const sampleAmounts = getSampleAmounts(yAsset);
let liquidityProvider: DummyLiquidityProviderContract; let liquidityProvider: DummyLiquidityProviderContract;
let registryContract: DummyLiquidityProviderRegistryContract;
before(async () => { before(async () => {
liquidityProvider = await DummyLiquidityProviderContract.deployFrom0xArtifactAsync( liquidityProvider = await DummyLiquidityProviderContract.deployFrom0xArtifactAsync(
@ -826,61 +821,33 @@ blockchainTests('erc20-bridge-sampler', env => {
env.txDefaults, env.txDefaults,
{}, {},
); );
registryContract = await DummyLiquidityProviderRegistryContract.deployFrom0xArtifactAsync(
artifacts.DummyLiquidityProviderRegistry,
env.provider,
env.txDefaults,
{},
);
await registryContract
.setLiquidityProviderForMarket(xAsset, yAsset, liquidityProvider.address)
.awaitTransactionSuccessAsync();
}); });
it('should be able to query sells from the liquidity provider', async () => { it('should be able to query sells from the liquidity provider', async () => {
const [quotes, providerAddress] = await testContract const quotes = await testContract
.sampleSellsFromLiquidityProviderRegistry(registryContract.address, yAsset, xAsset, sampleAmounts) .sampleSellsFromLiquidityProvider(liquidityProvider.address, yAsset, xAsset, sampleAmounts)
.callAsync(); .callAsync();
quotes.forEach((value, idx) => { quotes.forEach((value, idx) => {
expect(value).is.bignumber.eql(sampleAmounts[idx].minus(1)); expect(value).is.bignumber.eql(sampleAmounts[idx].minus(1));
}); });
expect(providerAddress).to.equal(liquidityProvider.address);
}); });
it('should be able to query buys from the liquidity provider', async () => { it('should be able to query buys from the liquidity provider', async () => {
const [quotes, providerAddress] = await testContract const quotes = await testContract
.sampleBuysFromLiquidityProviderRegistry(registryContract.address, yAsset, xAsset, sampleAmounts) .sampleBuysFromLiquidityProvider(liquidityProvider.address, yAsset, xAsset, sampleAmounts)
.callAsync(); .callAsync();
quotes.forEach((value, idx) => { quotes.forEach((value, idx) => {
expect(value).is.bignumber.eql(sampleAmounts[idx].plus(1)); expect(value).is.bignumber.eql(sampleAmounts[idx].plus(1));
}); });
expect(providerAddress).to.equal(liquidityProvider.address);
}); });
it('should just return zeros if the liquidity provider cannot be found', async () => { it('should just return zeros if the liquidity provider does not exist', async () => {
const [quotes, providerAddress] = await testContract const quotes = await testContract
.sampleBuysFromLiquidityProviderRegistry( .sampleBuysFromLiquidityProvider(randomAddress(), yAsset, xAsset, sampleAmounts)
registryContract.address,
yAsset,
randomAddress(),
sampleAmounts,
)
.callAsync(); .callAsync();
quotes.forEach(value => { quotes.forEach(value => {
expect(value).is.bignumber.eql(constants.ZERO_AMOUNT); expect(value).is.bignumber.eql(constants.ZERO_AMOUNT);
}); });
expect(providerAddress).to.equal(constants.NULL_ADDRESS);
});
it('should just return zeros if the registry does not exist', async () => {
const [quotes, providerAddress] = await testContract
.sampleBuysFromLiquidityProviderRegistry(randomAddress(), yAsset, xAsset, sampleAmounts)
.callAsync();
quotes.forEach(value => {
expect(value).is.bignumber.eql(constants.ZERO_AMOUNT);
});
expect(providerAddress).to.equal(constants.NULL_ADDRESS);
}); });
}); });

View File

@ -36,7 +36,7 @@ describe('DexSampler tests', () => {
const wethAddress = getContractAddressesForChainOrThrow(CHAIN_ID).etherToken; const wethAddress = getContractAddressesForChainOrThrow(CHAIN_ID).etherToken;
const exchangeAddress = getContractAddressesForChainOrThrow(CHAIN_ID).exchange; const exchangeAddress = getContractAddressesForChainOrThrow(CHAIN_ID).exchange;
const tokenAdjacencyGraph: TokenAdjacencyGraph = {}; const tokenAdjacencyGraph: TokenAdjacencyGraph = { default: [wethAddress] };
describe('getSampleAmounts()', () => { describe('getSampleAmounts()', () => {
const FILL_AMOUNT = getRandomInteger(1, 1e18); const FILL_AMOUNT = getRandomInteger(1, 1e18);
@ -159,26 +159,31 @@ describe('DexSampler tests', () => {
it('getLiquidityProviderSellQuotes()', async () => { it('getLiquidityProviderSellQuotes()', async () => {
const expectedMakerToken = randomAddress(); const expectedMakerToken = randomAddress();
const expectedTakerToken = randomAddress(); const expectedTakerToken = randomAddress();
const registry = randomAddress();
const poolAddress = randomAddress(); const poolAddress = randomAddress();
const sampler = new MockSamplerContract({ const sampler = new MockSamplerContract({
sampleSellsFromLiquidityProviderRegistry: (registryAddress, takerToken, makerToken, _fillAmounts) => { sampleSellsFromLiquidityProvider: (providerAddress, takerToken, makerToken, _fillAmounts) => {
expect(registryAddress).to.eq(registry); expect(providerAddress).to.eq(poolAddress);
expect(takerToken).to.eq(expectedTakerToken); expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken); expect(makerToken).to.eq(expectedMakerToken);
return [[toBaseUnitAmount(1001)], poolAddress]; return [toBaseUnitAmount(1001)];
}, },
}); });
const dexOrderSampler = new DexOrderSampler(sampler); const dexOrderSampler = new DexOrderSampler(
sampler,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
{ [poolAddress]: [expectedMakerToken, expectedTakerToken] },
);
const [result] = await dexOrderSampler.executeAsync( const [result] = await dexOrderSampler.executeAsync(
dexOrderSampler.getSellQuotes( dexOrderSampler.getSellQuotes(
[ERC20BridgeSource.LiquidityProvider], [ERC20BridgeSource.LiquidityProvider],
expectedMakerToken, expectedMakerToken,
expectedTakerToken, expectedTakerToken,
[toBaseUnitAmount(1000)], [toBaseUnitAmount(1000)],
wethAddress,
tokenAdjacencyGraph,
registry,
), ),
); );
expect(result).to.deep.equal([ expect(result).to.deep.equal([
@ -196,26 +201,31 @@ describe('DexSampler tests', () => {
it('getLiquidityProviderBuyQuotes()', async () => { it('getLiquidityProviderBuyQuotes()', async () => {
const expectedMakerToken = randomAddress(); const expectedMakerToken = randomAddress();
const expectedTakerToken = randomAddress(); const expectedTakerToken = randomAddress();
const registry = randomAddress();
const poolAddress = randomAddress(); const poolAddress = randomAddress();
const sampler = new MockSamplerContract({ const sampler = new MockSamplerContract({
sampleBuysFromLiquidityProviderRegistry: (registryAddress, takerToken, makerToken, _fillAmounts) => { sampleBuysFromLiquidityProvider: (providerAddress, takerToken, makerToken, _fillAmounts) => {
expect(registryAddress).to.eq(registry); expect(providerAddress).to.eq(poolAddress);
expect(takerToken).to.eq(expectedTakerToken); expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken); expect(makerToken).to.eq(expectedMakerToken);
return [[toBaseUnitAmount(999)], poolAddress]; return [toBaseUnitAmount(999)];
}, },
}); });
const dexOrderSampler = new DexOrderSampler(sampler); const dexOrderSampler = new DexOrderSampler(
sampler,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
{ [poolAddress]: [expectedMakerToken, expectedTakerToken] },
);
const [result] = await dexOrderSampler.executeAsync( const [result] = await dexOrderSampler.executeAsync(
dexOrderSampler.getBuyQuotes( dexOrderSampler.getBuyQuotes(
[ERC20BridgeSource.LiquidityProvider], [ERC20BridgeSource.LiquidityProvider],
expectedMakerToken, expectedMakerToken,
expectedTakerToken, expectedTakerToken,
[toBaseUnitAmount(1000)], [toBaseUnitAmount(1000)],
wethAddress,
tokenAdjacencyGraph,
registry,
), ),
); );
expect(result).to.deep.equal([ expect(result).to.deep.equal([
@ -230,50 +240,6 @@ describe('DexSampler tests', () => {
]); ]);
}); });
it('getMultiBridgeSellQuotes()', async () => {
const expectedTakerToken = randomAddress();
const expectedMakerToken = randomAddress();
const multiBridge = randomAddress();
const sampler = new MockSamplerContract({
sampleSellsFromMultiBridge: (
multiBridgeAddress,
takerToken,
_intermediateToken,
makerToken,
_fillAmounts,
) => {
expect(multiBridgeAddress).to.eq(multiBridge);
expect(takerToken).to.eq(expectedTakerToken);
expect(makerToken).to.eq(expectedMakerToken);
return [toBaseUnitAmount(1001)];
},
});
const dexOrderSampler = new DexOrderSampler(sampler);
const [result] = await dexOrderSampler.executeAsync(
dexOrderSampler.getSellQuotes(
[ERC20BridgeSource.MultiBridge],
expectedMakerToken,
expectedTakerToken,
[toBaseUnitAmount(1000)],
wethAddress,
tokenAdjacencyGraph,
randomAddress(),
multiBridge,
),
);
expect(result).to.deep.equal([
[
{
source: 'MultiBridge',
output: toBaseUnitAmount(1001),
input: toBaseUnitAmount(1000),
fillData: { poolAddress: multiBridge },
},
],
]);
});
it('getEth2DaiSellQuotes()', async () => { it('getEth2DaiSellQuotes()', async () => {
const expectedTakerToken = randomAddress(); const expectedTakerToken = randomAddress();
const expectedMakerToken = randomAddress(); const expectedMakerToken = randomAddress();
@ -416,15 +382,21 @@ describe('DexSampler tests', () => {
return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue()); return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue());
}, },
}); });
const dexOrderSampler = new DexOrderSampler(sampler); const dexOrderSampler = new DexOrderSampler(
sampler,
undefined,
undefined,
undefined,
undefined,
undefined,
tokenAdjacencyGraph,
);
const [quotes] = await dexOrderSampler.executeAsync( const [quotes] = await dexOrderSampler.executeAsync(
dexOrderSampler.getSellQuotes( dexOrderSampler.getSellQuotes(
sources, sources,
expectedMakerToken, expectedMakerToken,
expectedTakerToken, expectedTakerToken,
expectedTakerFillAmounts, expectedTakerFillAmounts,
wethAddress,
tokenAdjacencyGraph,
), ),
); );
const expectedQuotes = sources.map(s => const expectedQuotes = sources.map(s =>
@ -561,16 +533,17 @@ describe('DexSampler tests', () => {
return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue()); return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue());
}, },
}); });
const dexOrderSampler = new DexOrderSampler(sampler); const dexOrderSampler = new DexOrderSampler(
const [quotes] = await dexOrderSampler.executeAsync( sampler,
dexOrderSampler.getBuyQuotes( undefined,
sources, undefined,
expectedMakerToken, undefined,
expectedTakerToken, undefined,
expectedMakerFillAmounts, undefined,
wethAddress,
tokenAdjacencyGraph, tokenAdjacencyGraph,
), );
const [quotes] = await dexOrderSampler.executeAsync(
dexOrderSampler.getBuyQuotes(sources, expectedMakerToken, expectedTakerToken, expectedMakerFillAmounts),
); );
const expectedQuotes = sources.map(s => const expectedQuotes = sources.map(s =>
expectedMakerFillAmounts.map(a => ({ expectedMakerFillAmounts.map(a => ({

View File

@ -20,7 +20,7 @@ import { constants } from '../src/constants';
import { ExchangeProxySwapQuoteConsumer } from '../src/quote_consumers/exchange_proxy_swap_quote_consumer'; import { ExchangeProxySwapQuoteConsumer } from '../src/quote_consumers/exchange_proxy_swap_quote_consumer';
import { getSwapMinBuyAmount } from '../src/quote_consumers/utils'; import { getSwapMinBuyAmount } from '../src/quote_consumers/utils';
import { MarketBuySwapQuote, MarketOperation, MarketSellSwapQuote } from '../src/types'; import { MarketBuySwapQuote, MarketOperation, MarketSellSwapQuote } from '../src/types';
import { OptimizedMarketOrder } from '../src/utils/market_operation_utils/types'; import { ERC20BridgeSource, OptimizedMarketOrder } from '../src/utils/market_operation_utils/types';
import { chaiSetup } from './utils/chai_setup'; import { chaiSetup } from './utils/chai_setup';
@ -161,7 +161,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
); );
} }
const callDataEncoder = AbiEncoder.createMethod('transformERC20', [ const transformERC20Encoder = AbiEncoder.createMethod('transformERC20', [
{ type: 'address', name: 'inputToken' }, { type: 'address', name: 'inputToken' },
{ type: 'address', name: 'outputToken' }, { type: 'address', name: 'outputToken' },
{ type: 'uint256', name: 'inputTokenAmount' }, { type: 'uint256', name: 'inputTokenAmount' },
@ -173,7 +173,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
}, },
]); ]);
interface CallArgs { interface TransformERC20Args {
inputToken: string; inputToken: string;
outputToken: string; outputToken: string;
inputTokenAmount: BigNumber; inputTokenAmount: BigNumber;
@ -184,11 +184,31 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
}>; }>;
} }
const liquidityProviderEncoder = AbiEncoder.createMethod('sellToLiquidityProvider', [
{ type: 'address', name: 'inputToken' },
{ type: 'address', name: 'outputToken' },
{ type: 'address', name: 'target' },
{ type: 'address', name: 'recipient' },
{ type: 'uint256', name: 'sellAmount' },
{ type: 'uint256', name: 'minBuyAmount' },
{ type: 'bytes', name: 'auxiliaryData' },
]);
interface LiquidityProviderArgs {
inputToken: string;
outputToken: string;
target: string;
recipient: string;
sellAmount: BigNumber;
minBuyAmount: BigNumber;
auxiliaryData: string;
}
describe('getCalldataOrThrow()', () => { describe('getCalldataOrThrow()', () => {
it('can produce a sell quote', async () => { it('can produce a sell quote', async () => {
const quote = getRandomSellQuote(); const quote = getRandomSellQuote();
const callInfo = await consumer.getCalldataOrThrowAsync(quote); const callInfo = await consumer.getCalldataOrThrowAsync(quote);
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs; const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args;
expect(callArgs.inputToken).to.eq(TAKER_TOKEN); expect(callArgs.inputToken).to.eq(TAKER_TOKEN);
expect(callArgs.outputToken).to.eq(MAKER_TOKEN); expect(callArgs.outputToken).to.eq(MAKER_TOKEN);
expect(callArgs.inputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAssetAmount); expect(callArgs.inputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAssetAmount);
@ -217,7 +237,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
it('can produce a buy quote', async () => { it('can produce a buy quote', async () => {
const quote = getRandomBuyQuote(); const quote = getRandomBuyQuote();
const callInfo = await consumer.getCalldataOrThrowAsync(quote); const callInfo = await consumer.getCalldataOrThrowAsync(quote);
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs; const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args;
expect(callArgs.inputToken).to.eq(TAKER_TOKEN); expect(callArgs.inputToken).to.eq(TAKER_TOKEN);
expect(callArgs.outputToken).to.eq(MAKER_TOKEN); expect(callArgs.outputToken).to.eq(MAKER_TOKEN);
expect(callArgs.inputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAssetAmount); expect(callArgs.inputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAssetAmount);
@ -246,7 +266,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
it('ERC20 -> ERC20 does not have a WETH transformer', async () => { it('ERC20 -> ERC20 does not have a WETH transformer', async () => {
const quote = getRandomSellQuote(); const quote = getRandomSellQuote();
const callInfo = await consumer.getCalldataOrThrowAsync(quote); const callInfo = await consumer.getCalldataOrThrowAsync(quote);
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs; const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args;
const nonces = callArgs.transformations.map(t => t.deploymentNonce); const nonces = callArgs.transformations.map(t => t.deploymentNonce);
expect(nonces).to.not.include(consumer.transformerNonces.wethTransformer); expect(nonces).to.not.include(consumer.transformerNonces.wethTransformer);
}); });
@ -256,7 +276,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
const callInfo = await consumer.getCalldataOrThrowAsync(quote, { const callInfo = await consumer.getCalldataOrThrowAsync(quote, {
extensionContractOpts: { isFromETH: true }, extensionContractOpts: { isFromETH: true },
}); });
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs; const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args;
expect(callArgs.transformations[0].deploymentNonce.toNumber()).to.eq( expect(callArgs.transformations[0].deploymentNonce.toNumber()).to.eq(
consumer.transformerNonces.wethTransformer, consumer.transformerNonces.wethTransformer,
); );
@ -270,7 +290,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
const callInfo = await consumer.getCalldataOrThrowAsync(quote, { const callInfo = await consumer.getCalldataOrThrowAsync(quote, {
extensionContractOpts: { isToETH: true }, extensionContractOpts: { isToETH: true },
}); });
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs; const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args;
expect(callArgs.transformations[1].deploymentNonce.toNumber()).to.eq( expect(callArgs.transformations[1].deploymentNonce.toNumber()).to.eq(
consumer.transformerNonces.wethTransformer, consumer.transformerNonces.wethTransformer,
); );
@ -278,7 +298,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
expect(wethTransformerData.amount).to.bignumber.eq(MAX_UINT256); expect(wethTransformerData.amount).to.bignumber.eq(MAX_UINT256);
expect(wethTransformerData.token).to.eq(contractAddresses.etherToken); expect(wethTransformerData.token).to.eq(contractAddresses.etherToken);
}); });
it('Appends an affiliate fee transformer after the fill if a buy token affiliate fee is provided', async () => { it('Appends an affiliate fee transformer after the fill if a buy token affiliate fee is provided', async () => {
const quote = getRandomSellQuote(); const quote = getRandomSellQuote();
const affiliateFee = { const affiliateFee = {
recipient: randomAddress(), recipient: randomAddress(),
@ -288,7 +308,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
const callInfo = await consumer.getCalldataOrThrowAsync(quote, { const callInfo = await consumer.getCalldataOrThrowAsync(quote, {
extensionContractOpts: { affiliateFee }, extensionContractOpts: { affiliateFee },
}); });
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs; const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args;
expect(callArgs.transformations[1].deploymentNonce.toNumber()).to.eq( expect(callArgs.transformations[1].deploymentNonce.toNumber()).to.eq(
consumer.transformerNonces.affiliateFeeTransformer, consumer.transformerNonces.affiliateFeeTransformer,
); );
@ -315,7 +335,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
const callInfo = await consumer.getCalldataOrThrowAsync(quote, { const callInfo = await consumer.getCalldataOrThrowAsync(quote, {
extensionContractOpts: { isTwoHop: true }, extensionContractOpts: { isTwoHop: true },
}); });
const callArgs = callDataEncoder.decode(callInfo.calldataHexString) as CallArgs; const callArgs = transformERC20Encoder.decode(callInfo.calldataHexString) as TransformERC20Args;
expect(callArgs.inputToken).to.eq(TAKER_TOKEN); expect(callArgs.inputToken).to.eq(TAKER_TOKEN);
expect(callArgs.outputToken).to.eq(MAKER_TOKEN); expect(callArgs.outputToken).to.eq(MAKER_TOKEN);
expect(callArgs.inputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAssetAmount); expect(callArgs.inputTokenAmount).to.bignumber.eq(quote.worstCaseQuoteInfo.totalTakerAssetAmount);
@ -357,5 +377,35 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
INTERMEDIATE_TOKEN, INTERMEDIATE_TOKEN,
]); ]);
}); });
it('Uses the `LiquidityProviderFeature` if given a single LiquidityProvider order', async () => {
const quote = {
...getRandomSellQuote(),
orders: [
{
...getRandomOrder(),
fills: [
{
source: ERC20BridgeSource.LiquidityProvider,
sourcePathId: '',
input: constants.ZERO_AMOUNT,
output: constants.ZERO_AMOUNT,
subFills: [],
},
],
},
],
};
const callInfo = await consumer.getCalldataOrThrowAsync(quote);
const callArgs = liquidityProviderEncoder.decode(callInfo.calldataHexString) as LiquidityProviderArgs;
expect(callArgs).to.deep.equal({
inputToken: TAKER_TOKEN,
outputToken: MAKER_TOKEN,
target: quote.orders[0].makerAddress,
recipient: constants.NULL_ADDRESS,
sellAmount: quote.worstCaseQuoteInfo.totalTakerAssetAmount,
minBuyAmount: getSwapMinBuyAmount(quote),
auxiliaryData: constants.NULL_BYTES,
});
});
}); });
}); });

View File

@ -11,7 +11,7 @@ import {
} from '@0x/contracts-test-utils'; } from '@0x/contracts-test-utils';
import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils'; import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
import { AssetProxyId, ERC20BridgeAssetData, SignedOrder } from '@0x/types'; import { AssetProxyId, ERC20BridgeAssetData, SignedOrder } from '@0x/types';
import { BigNumber, fromTokenUnitAmount, hexUtils, NULL_ADDRESS } from '@0x/utils'; import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper'; import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash'; import * as _ from 'lodash';
import * as TypeMoq from 'typemoq'; import * as TypeMoq from 'typemoq';
@ -63,10 +63,11 @@ const DEFAULT_EXCLUDED = [
ERC20BridgeSource.Shell, ERC20BridgeSource.Shell,
ERC20BridgeSource.Cream, ERC20BridgeSource.Cream,
ERC20BridgeSource.Dodo, ERC20BridgeSource.Dodo,
ERC20BridgeSource.LiquidityProvider,
]; ];
const BUY_SOURCES = BUY_SOURCE_FILTER.sources; const BUY_SOURCES = BUY_SOURCE_FILTER.sources;
const SELL_SOURCES = SELL_SOURCE_FILTER.sources; const SELL_SOURCES = SELL_SOURCE_FILTER.sources;
const TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = {}; const TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = { default: [] };
const PRICE_AWARE_RFQ_ENABLED: PriceAwareRFQFlags = { const PRICE_AWARE_RFQ_ENABLED: PriceAwareRFQFlags = {
isFirmPriceAwareEnabled: true, isFirmPriceAwareEnabled: true,
isIndicativePriceAwareEnabled: true, isIndicativePriceAwareEnabled: true,
@ -77,7 +78,6 @@ describe('MarketOperationUtils tests', () => {
const CHAIN_ID = ChainId.Mainnet; const CHAIN_ID = ChainId.Mainnet;
const contractAddresses = { const contractAddresses = {
...getContractAddressesForChainOrThrow(CHAIN_ID), ...getContractAddressesForChainOrThrow(CHAIN_ID),
multiBridge: NULL_ADDRESS,
...BRIDGE_ADDRESSES_BY_CHAIN[CHAIN_ID], ...BRIDGE_ADDRESSES_BY_CHAIN[CHAIN_ID],
}; };
@ -242,38 +242,6 @@ describe('MarketOperationUtils tests', () => {
}; };
} }
function callTradeOperationAndRetainLiquidityProviderParams(
tradeOperation: (rates: RatesBySource) => GetMultipleQuotesOperation,
rates: RatesBySource,
): [{ sources: ERC20BridgeSource[]; liquidityProviderAddress?: string }, GetMultipleQuotesOperation] {
const liquidityPoolParams: { sources: ERC20BridgeSource[]; liquidityProviderAddress?: string } = {
sources: [],
liquidityProviderAddress: undefined,
};
const fn = (
sources: ERC20BridgeSource[],
makerToken: string,
takerToken: string,
fillAmounts: BigNumber[],
wethAddress: string,
tokenAdjacencyGraph: TokenAdjacencyGraph = TOKEN_ADJACENCY_GRAPH,
liquidityProviderAddress?: string,
) => {
liquidityPoolParams.liquidityProviderAddress = liquidityProviderAddress;
liquidityPoolParams.sources = liquidityPoolParams.sources.concat(sources);
return tradeOperation(rates)(
sources,
makerToken,
takerToken,
fillAmounts,
wethAddress,
TOKEN_ADJACENCY_GRAPH,
liquidityProviderAddress,
);
};
return [liquidityPoolParams, fn];
}
function createGetMultipleBuyQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation { function createGetMultipleBuyQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation {
return ( return (
sources: ERC20BridgeSource[], sources: ERC20BridgeSource[],
@ -335,7 +303,6 @@ describe('MarketOperationUtils tests', () => {
[ERC20BridgeSource.Bancor]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Bancor]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Curve]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Curve]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.LiquidityProvider]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.LiquidityProvider]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.MultiBridge]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.MStable]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.MStable]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Mooniswap]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Mooniswap]: _.times(NUM_SAMPLES, () => 0),
[ERC20BridgeSource.Swerve]: _.times(NUM_SAMPLES, () => 0), [ERC20BridgeSource.Swerve]: _.times(NUM_SAMPLES, () => 0),
@ -480,6 +447,7 @@ describe('MarketOperationUtils tests', () => {
}, },
balancerPoolsCache: new BalancerPoolsCache(), balancerPoolsCache: new BalancerPoolsCache(),
creamPoolsCache: new CreamPoolsCache(), creamPoolsCache: new CreamPoolsCache(),
liquidityProviderRegistry: {},
} as any) as DexOrderSampler; } as any) as DexOrderSampler;
function replaceSamplerOps(ops: Partial<typeof DEFAULT_OPS> = {}): void { function replaceSamplerOps(ops: Partial<typeof DEFAULT_OPS> = {}): void {
@ -617,54 +585,6 @@ describe('MarketOperationUtils tests', () => {
expect(_.uniq(sourcesPolled).sort()).to.deep.equals(SELL_SOURCES.slice().sort()); expect(_.uniq(sourcesPolled).sort()).to.deep.equals(SELL_SOURCES.slice().sort());
}); });
it('polls the liquidity provider when the registry is provided in the arguments', async () => {
const [args, fn] = callTradeOperationAndRetainLiquidityProviderParams(
createGetMultipleSellQuotesOperationFromRates,
DEFAULT_RATES,
);
replaceSamplerOps({
getSellQuotes: fn,
getTwoHopSellQuotes: (sources: ERC20BridgeSource[], ..._args: any[]) => {
if (sources.length !== 0) {
args.sources.push(ERC20BridgeSource.MultiHop);
args.sources.push(...sources);
}
return DEFAULT_OPS.getTwoHopSellQuotes(..._args);
},
getBalancerSellQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
) => {
args.sources = args.sources.concat(ERC20BridgeSource.Balancer);
return DEFAULT_OPS.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts);
},
getCreamSellQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
) => {
args.sources = args.sources.concat(ERC20BridgeSource.Cream);
return DEFAULT_OPS.getCreamSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts);
},
});
const registryAddress = randomAddress();
const newMarketOperationUtils = new MarketOperationUtils(
MOCK_SAMPLER,
contractAddresses,
ORDER_DOMAIN,
registryAddress,
);
await newMarketOperationUtils.getMarketSellOrdersAsync(ORDERS, FILL_AMOUNT, {
...DEFAULT_OPTS,
excludedSources: [],
});
expect(_.uniq(args.sources).sort()).to.deep.equals(
SELL_SOURCES.concat([ERC20BridgeSource.LiquidityProvider]).sort(),
);
expect(args.liquidityProviderAddress).to.eql(registryAddress);
});
it('does not poll DEXes in `excludedSources`', async () => { it('does not poll DEXes in `excludedSources`', async () => {
const excludedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai]; const excludedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai];
let sourcesPolled: ERC20BridgeSource[] = []; let sourcesPolled: ERC20BridgeSource[] = [];
@ -1341,41 +1261,28 @@ describe('MarketOperationUtils tests', () => {
}); });
it('is able to create a order from LiquidityProvider', async () => { it('is able to create a order from LiquidityProvider', async () => {
const registryAddress = randomAddress();
const liquidityProviderAddress = (DEFAULT_FILL_DATA[ERC20BridgeSource.LiquidityProvider] as any) const liquidityProviderAddress = (DEFAULT_FILL_DATA[ERC20BridgeSource.LiquidityProvider] as any)
.poolAddress; .poolAddress;
const xAsset = randomAddress(); const rates: RatesBySource = {};
const yAsset = randomAddress(); rates[ERC20BridgeSource.LiquidityProvider] = [1, 1, 1, 1];
const toSell = fromTokenUnitAmount(10); MOCK_SAMPLER.liquidityProviderRegistry[liquidityProviderAddress] = [MAKER_TOKEN, TAKER_TOKEN];
const [getSellQuotesParams, getSellQuotesFn] = callTradeOperationAndRetainLiquidityProviderParams(
createGetMultipleSellQuotesOperationFromRates,
{
[ERC20BridgeSource.LiquidityProvider]: createDecreasingRates(5),
},
);
replaceSamplerOps({ replaceSamplerOps({
getOrderFillableTakerAmounts: () => [constants.ZERO_AMOUNT], getOrderFillableTakerAmounts: () => [constants.ZERO_AMOUNT],
getSellQuotes: getSellQuotesFn, getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
}); });
const sampler = new MarketOperationUtils( const sampler = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN);
MOCK_SAMPLER,
contractAddresses,
ORDER_DOMAIN,
registryAddress,
);
const ordersAndReport = await sampler.getMarketSellOrdersAsync( const ordersAndReport = await sampler.getMarketSellOrdersAsync(
[ [
createOrder({ createOrder({
makerAssetData: assetDataUtils.encodeERC20AssetData(xAsset), makerAssetData: assetDataUtils.encodeERC20AssetData(MAKER_TOKEN),
takerAssetData: assetDataUtils.encodeERC20AssetData(yAsset), takerAssetData: assetDataUtils.encodeERC20AssetData(TAKER_TOKEN),
}), }),
], ],
Web3Wrapper.toBaseUnitAmount(10, 18), FILL_AMOUNT,
{ {
excludedSources: SELL_SOURCES.concat(ERC20BridgeSource.Bancor), includedSources: [ERC20BridgeSource.LiquidityProvider],
excludedSources: [],
numSamples: 4, numSamples: 4,
bridgeSlippage: 0, bridgeSlippage: 0,
}, },
@ -1390,9 +1297,7 @@ describe('MarketOperationUtils tests', () => {
) as ERC20BridgeAssetData; ) as ERC20BridgeAssetData;
expect(decodedAssetData.assetProxyId).to.eql(AssetProxyId.ERC20Bridge); expect(decodedAssetData.assetProxyId).to.eql(AssetProxyId.ERC20Bridge);
expect(decodedAssetData.bridgeAddress).to.eql(liquidityProviderAddress); expect(decodedAssetData.bridgeAddress).to.eql(liquidityProviderAddress);
expect(result[0].takerAssetAmount).to.bignumber.eql(toSell); expect(result[0].takerAssetAmount).to.bignumber.eql(FILL_AMOUNT);
expect(getSellQuotesParams.sources).contains(ERC20BridgeSource.LiquidityProvider);
expect(getSellQuotesParams.liquidityProviderAddress).is.eql(registryAddress);
}); });
it('factors in exchange proxy gas overhead', async () => { it('factors in exchange proxy gas overhead', async () => {
@ -1403,20 +1308,16 @@ describe('MarketOperationUtils tests', () => {
[ERC20BridgeSource.Uniswap]: [1, 1, 1, 1], [ERC20BridgeSource.Uniswap]: [1, 1, 1, 1],
[ERC20BridgeSource.LiquidityProvider]: [0.9999, 0.9999, 0.9999, 0.9999], [ERC20BridgeSource.LiquidityProvider]: [0.9999, 0.9999, 0.9999, 0.9999],
}; };
MOCK_SAMPLER.liquidityProviderRegistry[randomAddress()] = [MAKER_TOKEN, TAKER_TOKEN];
replaceSamplerOps({ replaceSamplerOps({
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates), getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
getMedianSellRate: createGetMedianSellRate(ETH_TO_MAKER_RATE), getMedianSellRate: createGetMedianSellRate(ETH_TO_MAKER_RATE),
}); });
const optimizer = new MarketOperationUtils( const optimizer = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN);
MOCK_SAMPLER,
contractAddresses,
ORDER_DOMAIN,
randomAddress(), // liquidity provider registry
);
const gasPrice = 100e9; // 100 gwei const gasPrice = 100e9; // 100 gwei
const exchangeProxyOverhead = (sourceFlags: number) => const exchangeProxyOverhead = (sourceFlags: number) =>
sourceFlags === SOURCE_FLAGS.LiquidityProvider sourceFlags === SOURCE_FLAGS.LiquidityProvider
? new BigNumber(3e4).times(gasPrice) ? constants.ZERO_AMOUNT
: new BigNumber(1.3e5).times(gasPrice); : new BigNumber(1.3e5).times(gasPrice);
const improvedOrdersResponse = await optimizer.getMarketSellOrdersAsync( const improvedOrdersResponse = await optimizer.getMarketSellOrdersAsync(
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
@ -1424,12 +1325,12 @@ describe('MarketOperationUtils tests', () => {
{ {
...DEFAULT_OPTS, ...DEFAULT_OPTS,
numSamples: 4, numSamples: 4,
excludedSources: [ includedSources: [
...(DEFAULT_OPTS.excludedSources as ERC20BridgeSource[]), ERC20BridgeSource.Native,
ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Uniswap,
ERC20BridgeSource.Kyber, ERC20BridgeSource.LiquidityProvider,
ERC20BridgeSource.Bancor,
], ],
excludedSources: [],
exchangeProxyOverhead, exchangeProxyOverhead,
}, },
); );
@ -1529,54 +1430,6 @@ describe('MarketOperationUtils tests', () => {
expect(_.uniq(sourcesPolled).sort()).to.deep.equals(BUY_SOURCES.sort()); expect(_.uniq(sourcesPolled).sort()).to.deep.equals(BUY_SOURCES.sort());
}); });
it('polls the liquidity provider when the registry is provided in the arguments', async () => {
const [args, fn] = callTradeOperationAndRetainLiquidityProviderParams(
createGetMultipleBuyQuotesOperationFromRates,
DEFAULT_RATES,
);
replaceSamplerOps({
getBuyQuotes: fn,
getTwoHopBuyQuotes: (sources: ERC20BridgeSource[], ..._args: any[]) => {
if (sources.length !== 0) {
args.sources.push(ERC20BridgeSource.MultiHop);
args.sources.push(...sources);
}
return DEFAULT_OPS.getTwoHopBuyQuotes(..._args);
},
getBalancerBuyQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
) => {
args.sources = args.sources.concat(ERC20BridgeSource.Balancer);
return DEFAULT_OPS.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts);
},
getCreamBuyQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
) => {
args.sources = args.sources.concat(ERC20BridgeSource.Cream);
return DEFAULT_OPS.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts);
},
});
const registryAddress = randomAddress();
const newMarketOperationUtils = new MarketOperationUtils(
MOCK_SAMPLER,
contractAddresses,
ORDER_DOMAIN,
registryAddress,
);
await newMarketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, {
...DEFAULT_OPTS,
excludedSources: [],
});
expect(_.uniq(args.sources).sort()).to.deep.eq(
BUY_SOURCES.concat([ERC20BridgeSource.LiquidityProvider]).sort(),
);
expect(args.liquidityProviderAddress).to.eql(registryAddress);
});
it('does not poll DEXes in `excludedSources`', async () => { it('does not poll DEXes in `excludedSources`', async () => {
const excludedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai]; const excludedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai];
let sourcesPolled: ERC20BridgeSource[] = []; let sourcesPolled: ERC20BridgeSource[] = [];
@ -1874,20 +1727,16 @@ describe('MarketOperationUtils tests', () => {
[ERC20BridgeSource.Uniswap]: [1, 1, 1, 1], [ERC20BridgeSource.Uniswap]: [1, 1, 1, 1],
[ERC20BridgeSource.LiquidityProvider]: [0.9999, 0.9999, 0.9999, 0.9999], [ERC20BridgeSource.LiquidityProvider]: [0.9999, 0.9999, 0.9999, 0.9999],
}; };
MOCK_SAMPLER.liquidityProviderRegistry[randomAddress()] = [MAKER_TOKEN, TAKER_TOKEN];
replaceSamplerOps({ replaceSamplerOps({
getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates),
getMedianSellRate: createGetMedianSellRate(ETH_TO_TAKER_RATE), getMedianSellRate: createGetMedianSellRate(ETH_TO_TAKER_RATE),
}); });
const optimizer = new MarketOperationUtils( const optimizer = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN);
MOCK_SAMPLER,
contractAddresses,
ORDER_DOMAIN,
randomAddress(), // liquidity provider registry
);
const gasPrice = 100e9; // 100 gwei const gasPrice = 100e9; // 100 gwei
const exchangeProxyOverhead = (sourceFlags: number) => const exchangeProxyOverhead = (sourceFlags: number) =>
sourceFlags === SOURCE_FLAGS.LiquidityProvider sourceFlags === SOURCE_FLAGS.LiquidityProvider
? new BigNumber(3e4).times(gasPrice) ? constants.ZERO_AMOUNT
: new BigNumber(1.3e5).times(gasPrice); : new BigNumber(1.3e5).times(gasPrice);
const improvedOrdersResponse = await optimizer.getMarketBuyOrdersAsync( const improvedOrdersResponse = await optimizer.getMarketBuyOrdersAsync(
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]), createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
@ -1895,11 +1744,12 @@ describe('MarketOperationUtils tests', () => {
{ {
...DEFAULT_OPTS, ...DEFAULT_OPTS,
numSamples: 4, numSamples: 4,
excludedSources: [ includedSources: [
...(DEFAULT_OPTS.excludedSources as ERC20BridgeSource[]), ERC20BridgeSource.Native,
ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Uniswap,
ERC20BridgeSource.Kyber, ERC20BridgeSource.LiquidityProvider,
], ],
excludedSources: [],
exchangeProxyOverhead, exchangeProxyOverhead,
}, },
); );

View File

@ -37,19 +37,12 @@ export type SampleBuysKyberHandler = (
) => [string, SampleResults]; ) => [string, SampleResults];
export type SampleBuysMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults; export type SampleBuysMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults;
export type SampleSellsLPHandler = ( export type SampleSellsLPHandler = (
registryAddress: string, providerAddress: string,
takerToken: string, takerToken: string,
makerToken: string, makerToken: string,
takerTokenAmounts: BigNumber[], takerTokenAmounts: BigNumber[],
) => [SampleResults, string];
export type SampleSellsMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults;
export type SampleSellsMBHandler = (
multiBridgeAddress: string,
takerToken: string,
intermediateToken: string,
makerToken: string,
takerTokenAmounts: BigNumber[],
) => SampleResults; ) => SampleResults;
export type SampleSellsMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults;
const DUMMY_PROVIDER = { const DUMMY_PROVIDER = {
sendAsync: (..._args: any[]): any => { sendAsync: (..._args: any[]): any => {
@ -61,15 +54,14 @@ interface Handlers {
getOrderFillableMakerAssetAmounts: GetOrderFillableAssetAmountHandler; getOrderFillableMakerAssetAmounts: GetOrderFillableAssetAmountHandler;
getOrderFillableTakerAssetAmounts: GetOrderFillableAssetAmountHandler; getOrderFillableTakerAssetAmounts: GetOrderFillableAssetAmountHandler;
sampleSellsFromKyberNetwork: SampleSellsKyberHandler; sampleSellsFromKyberNetwork: SampleSellsKyberHandler;
sampleSellsFromLiquidityProviderRegistry: SampleSellsLPHandler; sampleSellsFromLiquidityProvider: SampleSellsLPHandler;
sampleSellsFromMultiBridge: SampleSellsMBHandler;
sampleSellsFromEth2Dai: SampleSellsHandler; sampleSellsFromEth2Dai: SampleSellsHandler;
sampleSellsFromUniswap: SampleSellsHandler; sampleSellsFromUniswap: SampleSellsHandler;
sampleSellsFromUniswapV2: SampleSellsMultihopHandler; sampleSellsFromUniswapV2: SampleSellsMultihopHandler;
sampleBuysFromEth2Dai: SampleBuysHandler; sampleBuysFromEth2Dai: SampleBuysHandler;
sampleBuysFromUniswap: SampleBuysHandler; sampleBuysFromUniswap: SampleBuysHandler;
sampleBuysFromUniswapV2: SampleBuysMultihopHandler; sampleBuysFromUniswapV2: SampleBuysMultihopHandler;
sampleBuysFromLiquidityProviderRegistry: SampleSellsLPHandler; sampleBuysFromLiquidityProvider: SampleSellsLPHandler;
} }
// tslint:disable: no-unbound-method // tslint:disable: no-unbound-method
@ -171,35 +163,17 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
); );
} }
public sampleSellsFromLiquidityProviderRegistry( public sampleSellsFromLiquidityProvider(
registryAddress: string, providerAddress: string,
takerToken: string, takerToken: string,
makerToken: string, makerToken: string,
takerAssetAmounts: BigNumber[], takerAssetAmounts: BigNumber[],
): ContractTxFunctionObj<[BigNumber[], string]> {
return this._wrapCall(
super.sampleSellsFromLiquidityProviderRegistry,
this._handlers.sampleSellsFromLiquidityProviderRegistry,
registryAddress,
takerToken,
makerToken,
takerAssetAmounts,
);
}
public sampleSellsFromMultiBridge(
multiBridgeAddress: string,
takerToken: string,
intermediateToken: string,
makerToken: string,
takerAssetAmounts: BigNumber[],
): ContractTxFunctionObj<BigNumber[]> { ): ContractTxFunctionObj<BigNumber[]> {
return this._wrapCall( return this._wrapCall(
super.sampleSellsFromMultiBridge, super.sampleSellsFromLiquidityProvider,
this._handlers.sampleSellsFromMultiBridge, this._handlers.sampleSellsFromLiquidityProvider,
multiBridgeAddress, providerAddress,
takerToken, takerToken,
intermediateToken,
makerToken, makerToken,
takerAssetAmounts, takerAssetAmounts,
); );

View File

@ -9,15 +9,12 @@ export * from '../test/generated-wrappers/curve_sampler';
export * from '../test/generated-wrappers/d_o_d_o_sampler'; export * from '../test/generated-wrappers/d_o_d_o_sampler';
export * from '../test/generated-wrappers/deployment_constants'; export * from '../test/generated-wrappers/deployment_constants';
export * from '../test/generated-wrappers/dummy_liquidity_provider'; export * from '../test/generated-wrappers/dummy_liquidity_provider';
export * from '../test/generated-wrappers/dummy_liquidity_provider_registry';
export * from '../test/generated-wrappers/erc20_bridge_sampler'; export * from '../test/generated-wrappers/erc20_bridge_sampler';
export * from '../test/generated-wrappers/eth2_dai_sampler'; export * from '../test/generated-wrappers/eth2_dai_sampler';
export * from '../test/generated-wrappers/i_balancer'; export * from '../test/generated-wrappers/i_balancer';
export * from '../test/generated-wrappers/i_curve'; export * from '../test/generated-wrappers/i_curve';
export * from '../test/generated-wrappers/i_eth2_dai'; export * from '../test/generated-wrappers/i_eth2_dai';
export * from '../test/generated-wrappers/i_kyber_network'; export * from '../test/generated-wrappers/i_kyber_network';
export * from '../test/generated-wrappers/i_liquidity_provider';
export * from '../test/generated-wrappers/i_liquidity_provider_registry';
export * from '../test/generated-wrappers/i_m_stable'; export * from '../test/generated-wrappers/i_m_stable';
export * from '../test/generated-wrappers/i_mooniswap'; export * from '../test/generated-wrappers/i_mooniswap';
export * from '../test/generated-wrappers/i_multi_bridge'; export * from '../test/generated-wrappers/i_multi_bridge';

View File

@ -3,26 +3,19 @@
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true }, "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [ "files": [
"generated-artifacts/DummyLiquidityProvider.json",
"generated-artifacts/DummyLiquidityProviderRegistry.json",
"generated-artifacts/ERC20BridgeSampler.json", "generated-artifacts/ERC20BridgeSampler.json",
"generated-artifacts/ILiquidityProvider.json",
"generated-artifacts/ILiquidityProviderRegistry.json",
"test/generated-artifacts/ApproximateBuys.json", "test/generated-artifacts/ApproximateBuys.json",
"test/generated-artifacts/BalancerSampler.json", "test/generated-artifacts/BalancerSampler.json",
"test/generated-artifacts/CurveSampler.json", "test/generated-artifacts/CurveSampler.json",
"test/generated-artifacts/DODOSampler.json", "test/generated-artifacts/DODOSampler.json",
"test/generated-artifacts/DeploymentConstants.json", "test/generated-artifacts/DeploymentConstants.json",
"test/generated-artifacts/DummyLiquidityProvider.json", "test/generated-artifacts/DummyLiquidityProvider.json",
"test/generated-artifacts/DummyLiquidityProviderRegistry.json",
"test/generated-artifacts/ERC20BridgeSampler.json", "test/generated-artifacts/ERC20BridgeSampler.json",
"test/generated-artifacts/Eth2DaiSampler.json", "test/generated-artifacts/Eth2DaiSampler.json",
"test/generated-artifacts/IBalancer.json", "test/generated-artifacts/IBalancer.json",
"test/generated-artifacts/ICurve.json", "test/generated-artifacts/ICurve.json",
"test/generated-artifacts/IEth2Dai.json", "test/generated-artifacts/IEth2Dai.json",
"test/generated-artifacts/IKyberNetwork.json", "test/generated-artifacts/IKyberNetwork.json",
"test/generated-artifacts/ILiquidityProvider.json",
"test/generated-artifacts/ILiquidityProviderRegistry.json",
"test/generated-artifacts/IMStable.json", "test/generated-artifacts/IMStable.json",
"test/generated-artifacts/IMooniswap.json", "test/generated-artifacts/IMooniswap.json",
"test/generated-artifacts/IMultiBridge.json", "test/generated-artifacts/IMultiBridge.json",

View File

@ -1,4 +1,13 @@
[ [
{
"version": "5.3.0",
"changes": [
{
"note": "Add `exchangeProxyLiquidityProviderSandbox` addresses",
"pr": 16
}
]
},
{ {
"version": "5.2.0", "version": "5.2.0",
"changes": [ "changes": [

View File

@ -32,6 +32,7 @@
"exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb", "exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb",
"exchangeProxyTransformerDeployer": "0x39dce47a67ad34344eab877eae3ef1fa2a1d50bb", "exchangeProxyTransformerDeployer": "0x39dce47a67ad34344eab877eae3ef1fa2a1d50bb",
"exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18",
"exchangeProxyLiquidityProviderSandbox": "0xdb971b18ea5075734cec1241732cc1b41031dfc9",
"transformers": { "transformers": {
"wethTransformer": "0x68c0bb685099dc7cb5c5ce2b26185945b357383e", "wethTransformer": "0x68c0bb685099dc7cb5c5ce2b26185945b357383e",
"payTakerTransformer": "0x49b9df2c58491764cf40cb052dd4243df63622c7", "payTakerTransformer": "0x49b9df2c58491764cf40cb052dd4243df63622c7",
@ -72,6 +73,7 @@
"exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb", "exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb",
"exchangeProxyTransformerDeployer": "0x1c9a27658dd303a31205a3b245e8993b92d4d502", "exchangeProxyTransformerDeployer": "0x1c9a27658dd303a31205a3b245e8993b92d4d502",
"exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18",
"exchangeProxyLiquidityProviderSandbox": "0xb8afda68a9834969a69ebd4aab201feff814d170",
"transformers": { "transformers": {
"wethTransformer": "0x8d822fe2b42f60531203e288f5f357fa79474437", "wethTransformer": "0x8d822fe2b42f60531203e288f5f357fa79474437",
"payTakerTransformer": "0x150652244723102faeaefa4c79597d097ffa26c6", "payTakerTransformer": "0x150652244723102faeaefa4c79597d097ffa26c6",
@ -112,6 +114,7 @@
"exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb", "exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb",
"exchangeProxyTransformerDeployer": "0x1c9a27658dd303a31205a3b245e8993b92d4d502", "exchangeProxyTransformerDeployer": "0x1c9a27658dd303a31205a3b245e8993b92d4d502",
"exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18",
"exchangeProxyLiquidityProviderSandbox": "0xb8afda68a9834969a69ebd4aab201feff814d170",
"transformers": { "transformers": {
"wethTransformer": "0x8d822fe2b42f60531203e288f5f357fa79474437", "wethTransformer": "0x8d822fe2b42f60531203e288f5f357fa79474437",
"payTakerTransformer": "0x150652244723102faeaefa4c79597d097ffa26c6", "payTakerTransformer": "0x150652244723102faeaefa4c79597d097ffa26c6",
@ -152,6 +155,7 @@
"exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb", "exchangeProxyAllowanceTarget": "0xf740b67da229f2f10bcbd38a7979992fcc71b8eb",
"exchangeProxyTransformerDeployer": "0x1b62de2dbb5e7aa519e9c442721ecef75702807f", "exchangeProxyTransformerDeployer": "0x1b62de2dbb5e7aa519e9c442721ecef75702807f",
"exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18", "exchangeProxyFlashWallet": "0x22f9dcf4647084d6c31b2765f6910cd85c178c18",
"exchangeProxyLiquidityProviderSandbox": "0x598d7a659d1f163d94abe3628674f8a2569ff344",
"transformers": { "transformers": {
"wethTransformer": "0x9ce35b5ee9e710535e3988e3f8731d9ca9dba17d", "wethTransformer": "0x9ce35b5ee9e710535e3988e3f8731d9ca9dba17d",
"payTakerTransformer": "0x5a53e7b02a83aa9f60ccf4e424f0442c255bc977", "payTakerTransformer": "0x5a53e7b02a83aa9f60ccf4e424f0442c255bc977",
@ -192,6 +196,7 @@
"exchangeProxyAllowanceTarget": "0x8362c3ebd90041b30ec45908332e592721642637", "exchangeProxyAllowanceTarget": "0x8362c3ebd90041b30ec45908332e592721642637",
"exchangeProxyTransformerDeployer": "0x5409ed021d9299bf6814279a6a1411a7e866a631", "exchangeProxyTransformerDeployer": "0x5409ed021d9299bf6814279a6a1411a7e866a631",
"exchangeProxyFlashWallet": "0xb9682a8e7920b431f1d412b8510f0077410c8faa", "exchangeProxyFlashWallet": "0xb9682a8e7920b431f1d412b8510f0077410c8faa",
"exchangeProxyLiquidityProviderSandbox": "0x0000000000000000000000000000000000000000",
"transformers": { "transformers": {
"wethTransformer": "0xc6b0d3c45a6b5092808196cb00df5c357d55e1d5", "wethTransformer": "0xc6b0d3c45a6b5092808196cb00df5c357d55e1d5",
"payTakerTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3", "payTakerTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3",

View File

@ -33,6 +33,7 @@ export interface ContractAddresses {
exchangeProxyAllowanceTarget: string; exchangeProxyAllowanceTarget: string;
exchangeProxyTransformerDeployer: string; exchangeProxyTransformerDeployer: string;
exchangeProxyFlashWallet: string; exchangeProxyFlashWallet: string;
exchangeProxyLiquidityProviderSandbox: string;
transformers: { transformers: {
wethTransformer: string; wethTransformer: string;
payTakerTransformer: string; payTakerTransformer: string;

View File

@ -1,4 +1,13 @@
[ [
{
"version": "3.9.0",
"changes": [
{
"note": "Update IZeroEx artifact and remove some unused artifacts",
"pr": 16
}
]
},
{ {
"timestamp": 1604376968, "timestamp": 1604376968,
"version": "3.8.2", "version": "3.8.2",

View File

@ -1,81 +0,0 @@
{
"schemaVersion": "2.0.0",
"contractName": "DummyLiquidityProvider",
"compilerOutput": {
"abi": [
{
"constant": true,
"inputs": [
{ "internalType": "address", "name": "", "type": "address" },
{ "internalType": "address", "name": "", "type": "address" },
{ "internalType": "uint256", "name": "buyAmount", "type": "uint256" }
],
"name": "getBuyQuote",
"outputs": [{ "internalType": "uint256", "name": "takerTokenAmount", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "internalType": "address", "name": "", "type": "address" },
{ "internalType": "address", "name": "", "type": "address" },
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" }
],
"name": "getSellQuote",
"outputs": [{ "internalType": "uint256", "name": "makerTokenAmount", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
}
],
"devdoc": {
"methods": {
"getBuyQuote(address,address,uint256)": {
"details": "Quotes the amount of `takerToken` that would need to be sold in order to obtain `buyAmount` of `makerToken`.",
"params": { "buyAmount": "Amount of `makerToken` to buy." },
"return": "takerTokenAmount Amount of `takerToken` that would need to be sold."
},
"getSellQuote(address,address,uint256)": {
"details": "Quotes the amount of `makerToken` that would be obtained by selling `sellAmount` of `takerToken`.",
"params": { "sellAmount": "Amount of `takerToken` to sell." },
"return": "makerTokenAmount Amount of `makerToken` that would be obtained."
}
}
},
"evm": {
"bytecode": {
"object": "0x608060405234801561001057600080fd5b50610159806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063343fbcdd1461003b57806345060eb014610064575b600080fd5b61004e6100493660046100a8565b610077565b60405161005b91906100e8565b60405180910390f35b61004e6100723660046100a8565b61009f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60010192915050565b6000806000606084860312156100bc578283fd5b83356100c7816100f1565b925060208401356100d7816100f1565b929592945050506040919091013590565b90815260200190565b73ffffffffffffffffffffffffffffffffffffffff8116811461011357600080fd5b5056fea365627a7a72315820981135e6e25d9062a0a9bcf7e08e326cde449b18310db7488d1db4e79ef0f6f36c6578706572696d656e74616cf564736f6c63430005100040"
},
"deployedBytecode": {
"object": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063343fbcdd1461003b57806345060eb014610064575b600080fd5b61004e6100493660046100a8565b610077565b60405161005b91906100e8565b60405180910390f35b61004e6100723660046100a8565b61009f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0192915050565b60010192915050565b6000806000606084860312156100bc578283fd5b83356100c7816100f1565b925060208401356100d7816100f1565b929592945050506040919091013590565b90815260200190565b73ffffffffffffffffffffffffffffffffffffffff8116811461011357600080fd5b5056fea365627a7a72315820981135e6e25d9062a0a9bcf7e08e326cde449b18310db7488d1db4e79ef0f6f36c6578706572696d656e74616cf564736f6c63430005100040"
}
}
},
"compiler": {
"name": "solc",
"version": "soljson-v0.5.16+commit.9c3226ce.js",
"settings": {
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
"abi",
"devdoc",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
},
"evmVersion": "istanbul"
}
},
"chains": {}
}

View File

@ -1,83 +0,0 @@
{
"schemaVersion": "2.0.0",
"contractName": "DummyLiquidityProviderRegistry",
"compilerOutput": {
"abi": [
{
"constant": true,
"inputs": [
{ "internalType": "address", "name": "xToken", "type": "address" },
{ "internalType": "address", "name": "yToken", "type": "address" }
],
"name": "getLiquidityProviderForMarket",
"outputs": [{ "internalType": "address", "name": "poolAddress", "type": "address" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "internalType": "address", "name": "xToken", "type": "address" },
{ "internalType": "address", "name": "yToken", "type": "address" },
{ "internalType": "address", "name": "poolAddress", "type": "address" }
],
"name": "setLiquidityProviderForMarket",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
],
"devdoc": {
"methods": {
"getLiquidityProviderForMarket(address,address)": {
"details": "Returns the address of pool for a market given market (xAsset, yAsset), or reverts if pool does not exist.",
"params": { "xToken": "First asset managed by pool.", "yToken": "Second asset managed by pool." },
"return": "Address of pool."
},
"setLiquidityProviderForMarket(address,address,address)": {
"details": "Sets address of pool for a market given market (xAsset, yAsset).",
"params": {
"poolAddress": "Address of pool.",
"xToken": "First asset managed by pool.",
"yToken": "Second asset managed by pool."
}
}
}
},
"evm": {
"bytecode": {
"object": "0x608060405234801561001057600080fd5b506102a6806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063153f59971461003b57806384da8d1e14610064575b600080fd5b61004e610049366004610192565b610079565b60405161005b919061020b565b60405180910390f35b6100776100723660046101c6565b6100f2565b005b73ffffffffffffffffffffffffffffffffffffffff808316600090815260208181526040808320858516845290915290205416806100ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100e39061022c565b60405180910390fd5b92915050565b73ffffffffffffffffffffffffffffffffffffffff92831660008181526020818152604080832095871683529481528482208054969094167fffffffffffffffffffffffff0000000000000000000000000000000000000000968716811790945581815284822092825291909152919091208054909216179055565b803573ffffffffffffffffffffffffffffffffffffffff811681146100ec57600080fd5b600080604083850312156101a4578182fd5b6101ae848461016e565b91506101bd846020850161016e565b90509250929050565b6000806000606084860312156101da578081fd5b6101e4858561016e565b92506101f3856020860161016e565b9150610202856040860161016e565b90509250925092565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6020808252601c908201527f52656769737472792f4d41524b45545f504149525f4e4f545f5345540000000060408201526060019056fea365627a7a723158200b589233a17eab806bfb7e334f40bc1ba4502479e55b2aa562c069bc440ceb476c6578706572696d656e74616cf564736f6c63430005100040"
},
"deployedBytecode": {
"object": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063153f59971461003b57806384da8d1e14610064575b600080fd5b61004e610049366004610192565b610079565b60405161005b919061020b565b60405180910390f35b6100776100723660046101c6565b6100f2565b005b73ffffffffffffffffffffffffffffffffffffffff808316600090815260208181526040808320858516845290915290205416806100ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100e39061022c565b60405180910390fd5b92915050565b73ffffffffffffffffffffffffffffffffffffffff92831660008181526020818152604080832095871683529481528482208054969094167fffffffffffffffffffffffff0000000000000000000000000000000000000000968716811790945581815284822092825291909152919091208054909216179055565b803573ffffffffffffffffffffffffffffffffffffffff811681146100ec57600080fd5b600080604083850312156101a4578182fd5b6101ae848461016e565b91506101bd846020850161016e565b90509250929050565b6000806000606084860312156101da578081fd5b6101e4858561016e565b92506101f3856020860161016e565b9150610202856040860161016e565b90509250925092565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b6020808252601c908201527f52656769737472792f4d41524b45545f504149525f4e4f545f5345540000000060408201526060019056fea365627a7a723158200b589233a17eab806bfb7e334f40bc1ba4502479e55b2aa562c069bc440ceb476c6578706572696d656e74616cf564736f6c63430005100040"
}
}
},
"compiler": {
"name": "solc",
"version": "soljson-v0.5.16+commit.9c3226ce.js",
"settings": {
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
"abi",
"devdoc",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
},
"evmVersion": "istanbul"
}
},
"chains": {}
}

View File

@ -1,58 +0,0 @@
{
"schemaVersion": "2.0.0",
"contractName": "ILiquidityProviderRegistry",
"compilerOutput": {
"abi": [
{
"constant": true,
"inputs": [
{ "internalType": "address", "name": "takerToken", "type": "address" },
{ "internalType": "address", "name": "makerToken", "type": "address" }
],
"name": "getLiquidityProviderForMarket",
"outputs": [{ "internalType": "address", "name": "providerAddress", "type": "address" }],
"payable": false,
"stateMutability": "view",
"type": "function"
}
],
"devdoc": {
"methods": {
"getLiquidityProviderForMarket(address,address)": {
"details": "Returns the address of a liquidity provider for the given market (takerToken, makerToken), reverting if the pool does not exist.",
"params": {
"makerToken": "Maker asset managed by liquidity provider.",
"takerToken": "Taker asset managed by liquidity provider."
},
"return": "Address of the liquidity provider."
}
}
},
"evm": { "bytecode": { "object": "0x" }, "deployedBytecode": { "object": "0x" } }
},
"compiler": {
"name": "solc",
"version": "soljson-v0.5.16+commit.9c3226ce.js",
"settings": {
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
"abi",
"devdoc",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
},
"evmVersion": "istanbul"
}
},
"chains": {}
}

View File

@ -3,16 +3,6 @@
"contractName": "IZeroEx", "contractName": "IZeroEx",
"compilerOutput": { "compilerOutput": {
"abi": [ "abi": [
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "address", "name": "xAsset", "type": "address" },
{ "indexed": true, "internalType": "address", "name": "yAsset", "type": "address" },
{ "indexed": false, "internalType": "address", "name": "providerAddress", "type": "address" }
],
"name": "LiquidityProviderForMarketUpdated",
"type": "event"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
@ -232,16 +222,6 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{ "internalType": "address", "name": "xAsset", "type": "address" },
{ "internalType": "address", "name": "yAsset", "type": "address" }
],
"name": "getLiquidityProviderForMarket",
"outputs": [{ "internalType": "address", "name": "providerAddress", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
@ -390,9 +370,11 @@
"inputs": [ "inputs": [
{ "internalType": "address", "name": "makerToken", "type": "address" }, { "internalType": "address", "name": "makerToken", "type": "address" },
{ "internalType": "address", "name": "takerToken", "type": "address" }, { "internalType": "address", "name": "takerToken", "type": "address" },
{ "internalType": "address payable", "name": "recipient", "type": "address" }, { "internalType": "address payable", "name": "target", "type": "address" },
{ "internalType": "address", "name": "recipient", "type": "address" },
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" }, { "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{ "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" } { "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" },
{ "internalType": "bytes", "name": "auxiliaryData", "type": "bytes" }
], ],
"name": "sellToLiquidityProvider", "name": "sellToLiquidityProvider",
"outputs": [{ "internalType": "uint256", "name": "boughtAmount", "type": "uint256" }], "outputs": [{ "internalType": "uint256", "name": "boughtAmount", "type": "uint256" }],
@ -411,17 +393,6 @@
"stateMutability": "payable", "stateMutability": "payable",
"type": "function" "type": "function"
}, },
{
"inputs": [
{ "internalType": "address", "name": "xAsset", "type": "address" },
{ "internalType": "address", "name": "yAsset", "type": "address" },
{ "internalType": "address", "name": "providerAddress", "type": "address" }
],
"name": "setLiquidityProviderForMarket",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [{ "internalType": "address", "name": "quoteSigner", "type": "address" }], "inputs": [{ "internalType": "address", "name": "quoteSigner", "type": "address" }],
"name": "setQuoteSigner", "name": "setQuoteSigner",
@ -536,14 +507,6 @@
"params": { "selector": "The function selector." }, "params": { "selector": "The function selector." },
"returns": { "impl": "The implementation contract address." } "returns": { "impl": "The implementation contract address." }
}, },
"getLiquidityProviderForMarket(address,address)": {
"details": "Returns the address of the liquidity provider for a market given (xAsset, yAsset), or reverts if pool does not exist.",
"params": {
"xAsset": "First asset managed by the liquidity provider.",
"yAsset": "Second asset managed by the liquidity provider."
},
"returns": { "providerAddress": "Address of the liquidity provider." }
},
"getMetaTransactionExecutedBlock((address,address,uint256,uint256,uint256,uint256,bytes,uint256,address,uint256))": { "getMetaTransactionExecutedBlock((address,address,uint256,uint256,uint256,uint256,bytes,uint256,address,uint256))": {
"details": "Get the block at which a meta-transaction has been executed.", "details": "Get the block at which a meta-transaction has been executed.",
"params": { "mtx": "The meta-transaction." }, "params": { "mtx": "The meta-transaction." },
@ -616,6 +579,19 @@
"targetImpl": "The address of an older implementation of the function." "targetImpl": "The address of an older implementation of the function."
} }
}, },
"sellToLiquidityProvider(address,address,address,address,uint256,uint256,bytes)": {
"details": "Sells `sellAmount` of `takerToken` to the liquidity provider at the given `target`.",
"params": {
"auxiliaryData": "Auxiliary data supplied to the `target` contract.",
"makerToken": "The token being bought.",
"minBuyAmount": "The minimum acceptable amount of `makerToken` to buy. Reverts if this amount is not satisfied.",
"recipient": "The recipient of the bought tokens. If equal to address(0), `msg.sender` is assumed to be the recipient.",
"sellAmount": "The amount of `takerToken` to sell.",
"takerToken": "The token being sold.",
"target": "The address of the on-chain liquidity provider to trade with."
},
"returns": { "boughtAmount": "The amount of `makerToken` bought." }
},
"sellToUniswap(address[],uint256,uint256,bool)": { "sellToUniswap(address[],uint256,uint256,bool)": {
"details": "Efficiently sell directly to uniswap/sushiswap.", "details": "Efficiently sell directly to uniswap/sushiswap.",
"params": { "params": {
@ -626,14 +602,6 @@
}, },
"returns": { "buyAmount": "Amount of `tokens[-1]` bought." } "returns": { "buyAmount": "Amount of `tokens[-1]` bought." }
}, },
"setLiquidityProviderForMarket(address,address,address)": {
"details": "Sets address of the liquidity provider for a market given (xAsset, yAsset).",
"params": {
"providerAddress": "Address of the liquidity provider.",
"xAsset": "First asset managed by the liquidity provider.",
"yAsset": "Second asset managed by the liquidity provider."
}
},
"setQuoteSigner(address)": { "setQuoteSigner(address)": {
"details": "Replace the optional signer for `transformERC20()` calldata. Only callable by the owner.", "details": "Replace the optional signer for `transformERC20()` calldata. Only callable by the owner.",
"params": { "quoteSigner": "The address of the new calldata signer." } "params": { "quoteSigner": "The address of the new calldata signer." }

View File

@ -11,7 +11,7 @@
}, },
"scripts": { "scripts": {
"artifacts_copy": "node lib/src/copy.js", "artifacts_copy": "node lib/src/copy.js",
"artifacts_transform": "node lib/src/transform.js ./artifacts && prettier --write ./artifacts/*.json && cp -r ./artifacts/ ../../python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/", "artifacts_transform": "node lib/src/transform.js ./artifacts && prettier --write ./artifacts/*.json",
"artifacts_update": "yarn artifacts_copy && yarn artifacts_transform && yarn build", "artifacts_update": "yarn artifacts_copy && yarn artifacts_transform && yarn build",
"build": "yarn tsc -b", "build": "yarn tsc -b",
"build:ci": "yarn build", "build:ci": "yarn build",

View File

@ -1,4 +1,13 @@
[ [
{
"version": "13.10.0",
"changes": [
{
"note": "Update IZeroEx wrapper and remove ILiquidityProviderRegistry wrapper",
"pr": 16
}
]
},
{ {
"timestamp": 1604385937, "timestamp": 1604385937,
"version": "13.9.5", "version": "13.9.5",

View File

@ -1,327 +0,0 @@
// tslint:disable:no-consecutive-blank-lines ordered-imports align trailing-comma enum-naming
// tslint:disable:whitespace no-unbound-method no-trailing-whitespace
// tslint:disable:no-unused-variable
import {
AwaitTransactionSuccessOpts,
ContractFunctionObj,
ContractTxFunctionObj,
SendTransactionOpts,
BaseContract,
PromiseWithTransactionHash,
methodAbiToFunctionSignature,
linkLibrariesInBytecode,
} from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
BlockParam,
BlockParamLiteral,
BlockRange,
CallData,
ContractAbi,
ContractArtifact,
DecodedLogArgs,
MethodAbi,
TransactionReceiptWithDecodedLogs,
TxData,
TxDataPayable,
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, hexUtils, logUtils, providerUtils } from '@0x/utils';
import { EventCallback, IndexedFilterValues, SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
import * as ethers from 'ethers';
// tslint:enable:no-unused-variable
/* istanbul ignore next */
// tslint:disable:array-type
// tslint:disable:no-parameter-reassignment
// tslint:disable-next-line:class-name
export class ILiquidityProviderRegistryContract extends BaseContract {
/**
* @ignore
*/
public static deployedBytecode: string | undefined;
public static contractName = 'ILiquidityProviderRegistry';
private readonly _methodABIIndex: { [name: string]: number } = {};
public static async deployFrom0xArtifactAsync(
artifact: ContractArtifact | SimpleContractArtifact,
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
): Promise<ILiquidityProviderRegistryContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return ILiquidityProviderRegistryContract.deployAsync(
bytecode,
abi,
provider,
txDefaults,
logDecodeDependenciesAbiOnly,
);
}
public static async deployWithLibrariesFrom0xArtifactAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
): Promise<ILiquidityProviderRegistryContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
const libraryAddresses = await ILiquidityProviderRegistryContract._deployLibrariesAsync(
artifact,
libraryArtifacts,
new Web3Wrapper(provider),
txDefaults,
);
const bytecode = linkLibrariesInBytecode(artifact, libraryAddresses);
return ILiquidityProviderRegistryContract.deployAsync(
bytecode,
abi,
provider,
txDefaults,
logDecodeDependenciesAbiOnly,
);
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractAbi },
): Promise<ILiquidityProviderRegistryContract> {
assert.isHexString('bytecode', bytecode);
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const constructorAbi = BaseContract._lookupConstructorAbi(abi);
[] = BaseContract._formatABIDataItemList(constructorAbi.inputs, [], BaseContract._bigNumberToString);
const iface = new ethers.utils.Interface(abi);
const deployInfo = iface.deployFunction;
const txData = deployInfo.encode(bytecode, []);
const web3Wrapper = new Web3Wrapper(provider);
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
{
data: txData,
...txDefaults,
},
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`ILiquidityProviderRegistry successfully deployed at ${txReceipt.contractAddress}`);
const contractInstance = new ILiquidityProviderRegistryContract(
txReceipt.contractAddress as string,
provider,
txDefaults,
logDecodeDependencies,
);
contractInstance.constructorArgs = [];
return contractInstance;
}
/**
* @returns The contract ABI
*/
public static ABI(): ContractAbi {
const abi = [
{
constant: true,
inputs: [
{
name: 'takerToken',
type: 'address',
},
{
name: 'makerToken',
type: 'address',
},
],
name: 'getLiquidityProviderForMarket',
outputs: [
{
name: 'providerAddress',
type: 'address',
},
],
payable: false,
stateMutability: 'view',
type: 'function',
},
] as ContractAbi;
return abi;
}
protected static async _deployLibrariesAsync(
artifact: ContractArtifact,
libraryArtifacts: { [libraryName: string]: ContractArtifact },
web3Wrapper: Web3Wrapper,
txDefaults: Partial<TxData>,
libraryAddresses: { [libraryName: string]: string } = {},
): Promise<{ [libraryName: string]: string }> {
const links = artifact.compilerOutput.evm.bytecode.linkReferences;
// Go through all linked libraries, recursively deploying them if necessary.
for (const link of Object.values(links)) {
for (const libraryName of Object.keys(link)) {
if (!libraryAddresses[libraryName]) {
// Library not yet deployed.
const libraryArtifact = libraryArtifacts[libraryName];
if (!libraryArtifact) {
throw new Error(`Missing artifact for linked library "${libraryName}"`);
}
// Deploy any dependent libraries used by this library.
await ILiquidityProviderRegistryContract._deployLibrariesAsync(
libraryArtifact,
libraryArtifacts,
web3Wrapper,
txDefaults,
libraryAddresses,
);
// Deploy this library.
const linkedLibraryBytecode = linkLibrariesInBytecode(libraryArtifact, libraryAddresses);
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
{
data: linkedLibraryBytecode,
...txDefaults,
},
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const { contractAddress } = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`${libraryArtifact.contractName} successfully deployed at ${contractAddress}`);
libraryAddresses[libraryArtifact.contractName] = contractAddress as string;
}
}
}
return libraryAddresses;
}
public getFunctionSignature(methodName: string): string {
const index = this._methodABIIndex[methodName];
const methodAbi = ILiquidityProviderRegistryContract.ABI()[index] as MethodAbi; // tslint:disable-line:no-unnecessary-type-assertion
const functionSignature = methodAbiToFunctionSignature(methodAbi);
return functionSignature;
}
public getABIDecodedTransactionData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as ILiquidityProviderRegistryContract;
const abiEncoder = self._lookupAbiEncoder(functionSignature);
const abiDecodedCallData = abiEncoder.strictDecode<T>(callData);
return abiDecodedCallData;
}
public getABIDecodedReturnData<T>(methodName: string, callData: string): T {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as ILiquidityProviderRegistryContract;
const abiEncoder = self._lookupAbiEncoder(functionSignature);
const abiDecodedCallData = abiEncoder.strictDecodeReturnValue<T>(callData);
return abiDecodedCallData;
}
public getSelector(methodName: string): string {
const functionSignature = this.getFunctionSignature(methodName);
const self = (this as any) as ILiquidityProviderRegistryContract;
const abiEncoder = self._lookupAbiEncoder(functionSignature);
return abiEncoder.getSelector();
}
/**
* Returns the address of a liquidity provider for the given market
* (takerToken, makerToken), reverting if the pool does not exist.
* @param takerToken Taker asset managed by liquidity provider.
* @param makerToken Maker asset managed by liquidity provider.
* @returns Address of the liquidity provider.
*/
public getLiquidityProviderForMarket(takerToken: string, makerToken: string): ContractFunctionObj<string> {
const self = (this as any) as ILiquidityProviderRegistryContract;
assert.isString('takerToken', takerToken);
assert.isString('makerToken', makerToken);
const functionSignature = 'getLiquidityProviderForMarket(address,address)';
return {
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<string> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ data: this.getABIEncodedTransactionData(), ...callData },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<string>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, [
takerToken.toLowerCase(),
makerToken.toLowerCase(),
]);
},
};
}
constructor(
address: string,
supportedProvider: SupportedProvider,
txDefaults?: Partial<TxData>,
logDecodeDependencies?: { [contractName: string]: ContractAbi },
deployedBytecode: string | undefined = ILiquidityProviderRegistryContract.deployedBytecode,
) {
super(
'ILiquidityProviderRegistry',
ILiquidityProviderRegistryContract.ABI(),
address,
supportedProvider,
txDefaults,
logDecodeDependencies,
deployedBytecode,
);
classUtils.bindAll(this, ['_abiEncoderByFunctionSignature', 'address', '_web3Wrapper']);
ILiquidityProviderRegistryContract.ABI().forEach((item, index) => {
if (item.type === 'function') {
const methodAbi = item as MethodAbi;
this._methodABIIndex[methodAbi.name] = index;
}
});
}
}
// tslint:disable:max-file-line-count
// tslint:enable:no-unbound-method no-parameter-reassignment no-consecutive-blank-lines ordered-imports align
// tslint:enable:trailing-comma whitespace no-trailing-whitespace

View File

@ -36,7 +36,6 @@ import * as ethers from 'ethers';
// tslint:enable:no-unused-variable // tslint:enable:no-unused-variable
export type IZeroExEventArgs = export type IZeroExEventArgs =
| IZeroExLiquidityProviderForMarketUpdatedEventArgs
| IZeroExMetaTransactionExecutedEventArgs | IZeroExMetaTransactionExecutedEventArgs
| IZeroExMigratedEventArgs | IZeroExMigratedEventArgs
| IZeroExOwnershipTransferredEventArgs | IZeroExOwnershipTransferredEventArgs
@ -46,7 +45,6 @@ export type IZeroExEventArgs =
| IZeroExTransformerDeployerUpdatedEventArgs; | IZeroExTransformerDeployerUpdatedEventArgs;
export enum IZeroExEvents { export enum IZeroExEvents {
LiquidityProviderForMarketUpdated = 'LiquidityProviderForMarketUpdated',
MetaTransactionExecuted = 'MetaTransactionExecuted', MetaTransactionExecuted = 'MetaTransactionExecuted',
Migrated = 'Migrated', Migrated = 'Migrated',
OwnershipTransferred = 'OwnershipTransferred', OwnershipTransferred = 'OwnershipTransferred',
@ -56,12 +54,6 @@ export enum IZeroExEvents {
TransformerDeployerUpdated = 'TransformerDeployerUpdated', TransformerDeployerUpdated = 'TransformerDeployerUpdated',
} }
export interface IZeroExLiquidityProviderForMarketUpdatedEventArgs extends DecodedLogArgs {
xAsset: string;
yAsset: string;
providerAddress: string;
}
export interface IZeroExMetaTransactionExecutedEventArgs extends DecodedLogArgs { export interface IZeroExMetaTransactionExecutedEventArgs extends DecodedLogArgs {
hash: string; hash: string;
selector: string; selector: string;
@ -219,29 +211,6 @@ export class IZeroExContract extends BaseContract {
*/ */
public static ABI(): ContractAbi { public static ABI(): ContractAbi {
const abi = [ const abi = [
{
anonymous: false,
inputs: [
{
name: 'xAsset',
type: 'address',
indexed: true,
},
{
name: 'yAsset',
type: 'address',
indexed: true,
},
{
name: 'providerAddress',
type: 'address',
indexed: false,
},
],
name: 'LiquidityProviderForMarketUpdated',
outputs: [],
type: 'event',
},
{ {
anonymous: false, anonymous: false,
inputs: [ inputs: [
@ -728,27 +697,6 @@ export class IZeroExContract extends BaseContract {
stateMutability: 'view', stateMutability: 'view',
type: 'function', type: 'function',
}, },
{
inputs: [
{
name: 'xAsset',
type: 'address',
},
{
name: 'yAsset',
type: 'address',
},
],
name: 'getLiquidityProviderForMarket',
outputs: [
{
name: 'providerAddress',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{ {
inputs: [ inputs: [
{ {
@ -1062,6 +1010,10 @@ export class IZeroExContract extends BaseContract {
name: 'takerToken', name: 'takerToken',
type: 'address', type: 'address',
}, },
{
name: 'target',
type: 'address',
},
{ {
name: 'recipient', name: 'recipient',
type: 'address', type: 'address',
@ -1074,6 +1026,10 @@ export class IZeroExContract extends BaseContract {
name: 'minBuyAmount', name: 'minBuyAmount',
type: 'uint256', type: 'uint256',
}, },
{
name: 'auxiliaryData',
type: 'bytes',
},
], ],
name: 'sellToLiquidityProvider', name: 'sellToLiquidityProvider',
outputs: [ outputs: [
@ -1114,26 +1070,6 @@ export class IZeroExContract extends BaseContract {
stateMutability: 'payable', stateMutability: 'payable',
type: 'function', type: 'function',
}, },
{
inputs: [
{
name: 'xAsset',
type: 'address',
},
{
name: 'yAsset',
type: 'address',
},
{
name: 'providerAddress',
type: 'address',
},
],
name: 'setLiquidityProviderForMarket',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{ {
inputs: [ inputs: [
{ {
@ -1848,60 +1784,6 @@ export class IZeroExContract extends BaseContract {
}, },
}; };
} }
/**
* Returns the address of the liquidity provider for a market given
* (xAsset, yAsset), or reverts if pool does not exist.
* @param xAsset First asset managed by the liquidity provider.
* @param yAsset Second asset managed by the liquidity provider.
*/
public getLiquidityProviderForMarket(xAsset: string, yAsset: string): ContractTxFunctionObj<string> {
const self = (this as any) as IZeroExContract;
assert.isString('xAsset', xAsset);
assert.isString('yAsset', yAsset);
const functionSignature = 'getLiquidityProviderForMarket(address,address)';
return {
async sendTransactionAsync(
txData?: Partial<TxData> | undefined,
opts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
{ data: this.getABIEncodedTransactionData(), ...txData },
this.estimateGasAsync.bind(this),
);
if (opts.shouldValidate !== false) {
await this.callAsync(txDataWithDefaults);
}
return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
},
awaitTransactionSuccessAsync(
txData?: Partial<TxData>,
opts: AwaitTransactionSuccessOpts = { shouldValidate: true },
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts);
},
async estimateGasAsync(txData?: Partial<TxData> | undefined): Promise<number> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({
data: this.getABIEncodedTransactionData(),
...txData,
});
return self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
},
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<string> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ data: this.getABIEncodedTransactionData(), ...callData },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<string>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, [xAsset.toLowerCase(), yAsset.toLowerCase()]);
},
};
}
/** /**
* Get the block at which a meta-transaction has been executed. * Get the block at which a meta-transaction has been executed.
* @param mtx The meta-transaction. * @param mtx The meta-transaction.
@ -2606,20 +2488,38 @@ export class IZeroExContract extends BaseContract {
}, },
}; };
} }
/**
* Sells `sellAmount` of `takerToken` to the liquidity provider
* at the given `target`.
* @param makerToken The token being bought.
* @param takerToken The token being sold.
* @param target The address of the on-chain liquidity provider to trade
* with.
* @param recipient The recipient of the bought tokens. If equal to
* address(0), `msg.sender` is assumed to be the recipient.
* @param sellAmount The amount of `takerToken` to sell.
* @param minBuyAmount The minimum acceptable amount of `makerToken` to
* buy. Reverts if this amount is not satisfied.
* @param auxiliaryData Auxiliary data supplied to the `target` contract.
*/
public sellToLiquidityProvider( public sellToLiquidityProvider(
makerToken: string, makerToken: string,
takerToken: string, takerToken: string,
target: string,
recipient: string, recipient: string,
sellAmount: BigNumber, sellAmount: BigNumber,
minBuyAmount: BigNumber, minBuyAmount: BigNumber,
auxiliaryData: string,
): ContractTxFunctionObj<BigNumber> { ): ContractTxFunctionObj<BigNumber> {
const self = (this as any) as IZeroExContract; const self = (this as any) as IZeroExContract;
assert.isString('makerToken', makerToken); assert.isString('makerToken', makerToken);
assert.isString('takerToken', takerToken); assert.isString('takerToken', takerToken);
assert.isString('target', target);
assert.isString('recipient', recipient); assert.isString('recipient', recipient);
assert.isBigNumber('sellAmount', sellAmount); assert.isBigNumber('sellAmount', sellAmount);
assert.isBigNumber('minBuyAmount', minBuyAmount); assert.isBigNumber('minBuyAmount', minBuyAmount);
const functionSignature = 'sellToLiquidityProvider(address,address,address,uint256,uint256)'; assert.isString('auxiliaryData', auxiliaryData);
const functionSignature = 'sellToLiquidityProvider(address,address,address,address,uint256,uint256,bytes)';
return { return {
async sendTransactionAsync( async sendTransactionAsync(
@ -2662,9 +2562,11 @@ export class IZeroExContract extends BaseContract {
return self._strictEncodeArguments(functionSignature, [ return self._strictEncodeArguments(functionSignature, [
makerToken.toLowerCase(), makerToken.toLowerCase(),
takerToken.toLowerCase(), takerToken.toLowerCase(),
target.toLowerCase(),
recipient.toLowerCase(), recipient.toLowerCase(),
sellAmount, sellAmount,
minBuyAmount, minBuyAmount,
auxiliaryData,
]); ]);
}, },
}; };
@ -2731,70 +2633,6 @@ export class IZeroExContract extends BaseContract {
}, },
}; };
} }
/**
* Sets address of the liquidity provider for a market given
* (xAsset, yAsset).
* @param xAsset First asset managed by the liquidity provider.
* @param yAsset Second asset managed by the liquidity provider.
* @param providerAddress Address of the liquidity provider.
*/
public setLiquidityProviderForMarket(
xAsset: string,
yAsset: string,
providerAddress: string,
): ContractTxFunctionObj<void> {
const self = (this as any) as IZeroExContract;
assert.isString('xAsset', xAsset);
assert.isString('yAsset', yAsset);
assert.isString('providerAddress', providerAddress);
const functionSignature = 'setLiquidityProviderForMarket(address,address,address)';
return {
async sendTransactionAsync(
txData?: Partial<TxData> | undefined,
opts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
{ data: this.getABIEncodedTransactionData(), ...txData },
this.estimateGasAsync.bind(this),
);
if (opts.shouldValidate !== false) {
await this.callAsync(txDataWithDefaults);
}
return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
},
awaitTransactionSuccessAsync(
txData?: Partial<TxData>,
opts: AwaitTransactionSuccessOpts = { shouldValidate: true },
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts);
},
async estimateGasAsync(txData?: Partial<TxData> | undefined): Promise<number> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({
data: this.getABIEncodedTransactionData(),
...txData,
});
return self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
},
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ data: this.getABIEncodedTransactionData(), ...callData },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, [
xAsset.toLowerCase(),
yAsset.toLowerCase(),
providerAddress.toLowerCase(),
]);
},
};
}
/** /**
* Replace the optional signer for `transformERC20()` calldata. * Replace the optional signer for `transformERC20()` calldata.
* Only callable by the owner. * Only callable by the owner.

View File

@ -125,7 +125,6 @@ export {
IZeroExContract, IZeroExContract,
IZeroExEventArgs, IZeroExEventArgs,
IZeroExEvents, IZeroExEvents,
IZeroExLiquidityProviderForMarketUpdatedEventArgs,
IZeroExMetaTransactionExecutedEventArgs, IZeroExMetaTransactionExecutedEventArgs,
IZeroExMigratedEventArgs, IZeroExMigratedEventArgs,
IZeroExOwnershipTransferredEventArgs, IZeroExOwnershipTransferredEventArgs,

View File

@ -1,4 +1,13 @@
[ [
{
"version": "6.5.0",
"changes": [
{
"note": "Add `exchangeProxyLiquidityProviderSandbox` address",
"pr": 16
}
]
},
{ {
"timestamp": 1604385937, "timestamp": 1604385937,
"version": "6.4.7", "version": "6.4.7",

View File

@ -402,6 +402,7 @@ export async function runMigrationsAsync(
exchangeProxyAllowanceTarget: exchangeProxyAllowanceTargetAddress, exchangeProxyAllowanceTarget: exchangeProxyAllowanceTargetAddress,
exchangeProxyTransformerDeployer: txDefaults.from, exchangeProxyTransformerDeployer: txDefaults.from,
exchangeProxyFlashWallet: exchangeProxyFlashWalletAddress, exchangeProxyFlashWallet: exchangeProxyFlashWalletAddress,
exchangeProxyLiquidityProviderSandbox: NULL_ADDRESS,
transformers: { transformers: {
wethTransformer: wethTransformer.address, wethTransformer: wethTransformer.address,
payTakerTransformer: payTakerTransformer.address, payTakerTransformer: payTakerTransformer.address,