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
66 changed files with 1083 additions and 2098 deletions

View File

@@ -5,6 +5,18 @@
{
"note": "Rewrite the ZeroEx contract in Yul",
"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
);
}
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.
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(
address makerToken,
address takerToken,
address payable recipient,
address inputToken,
address outputToken,
address payable provider,
address recipient,
uint256 sellAmount,
uint256 minBuyAmount
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external
payable
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;
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/LibSafeMathV06.sol";
import "../errors/LibLiquidityProviderRichErrors.sol";
import "../external/ILiquidityProviderSandbox.sol";
import "../external/LiquidityProviderSandbox.sol";
import "../fixins/FixinCommon.sol";
import "../migrations/LibMigrate.sol";
import "../storage/LibLiquidityProviderStorage.sol";
import "../vendor/v3/IERC20Bridge.sol";
import "./IFeature.sol";
import "./ILiquidityProviderFeature.sol";
import "./libs/LibTokenSpender.sol";
@@ -39,7 +37,6 @@ contract LiquidityProviderFeature is
ILiquidityProviderFeature,
FixinCommon
{
using LibERC20TokenV06 for IERC20TokenV06;
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
@@ -50,16 +47,24 @@ contract LiquidityProviderFeature is
/// @dev ETH pseudo-token address.
address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev The WETH contract address.
IEtherTokenV06 public immutable weth;
/// @dev The sandbox contract address.
ILiquidityProviderSandbox public immutable sandbox;
/// @dev Store the WETH address in an immutable.
/// @param weth_ The weth token.
constructor(IEtherTokenV06 weth_)
/// @dev Event for data pipeline.
event LiquidityProviderSwap(
address inputToken,
address outputToken,
uint256 inputTokenAmount,
uint256 outputTokenAmount,
address provider,
address recipient
);
constructor(address zeroEx)
public
FixinCommon()
{
weth = weth_;
sandbox = new LiquidityProviderSandbox(zeroEx);
}
/// @dev Initialize and register this feature.
@@ -70,131 +75,102 @@ contract LiquidityProviderFeature is
returns (bytes4 success)
{
_registerFeatureFunction(this.sellToLiquidityProvider.selector);
_registerFeatureFunction(this.setLiquidityProviderForMarket.selector);
_registerFeatureFunction(this.getLiquidityProviderForMarket.selector);
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(
address makerToken,
address takerToken,
address payable recipient,
address inputToken,
address outputToken,
address payable provider,
address recipient,
uint256 sellAmount,
uint256 minBuyAmount
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external
override
payable
returns (uint256 boughtAmount)
{
address providerAddress = getLiquidityProviderForMarket(makerToken, takerToken);
if (recipient == address(0)) {
recipient = msg.sender;
}
if (takerToken == ETH_TOKEN_ADDRESS) {
// Wrap ETH.
weth.deposit{value: sellAmount}();
weth.transfer(providerAddress, sellAmount);
if (inputToken == ETH_TOKEN_ADDRESS) {
provider.transfer(sellAmount);
} else {
LibTokenSpender.spendERC20Tokens(
IERC20TokenV06(takerToken),
IERC20TokenV06(inputToken),
msg.sender,
providerAddress,
provider,
sellAmount
);
}
if (makerToken == ETH_TOKEN_ADDRESS) {
uint256 balanceBefore = weth.balanceOf(address(this));
IERC20Bridge(providerAddress).bridgeTransferFrom(
address(weth),
address(0),
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),
if (inputToken == ETH_TOKEN_ADDRESS) {
uint256 balanceBefore = IERC20TokenV06(outputToken).balanceOf(recipient);
sandbox.executeSellEthForToken(
provider,
outputToken,
recipient,
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) {
LibLiquidityProviderRichErrors.LiquidityProviderIncompleteSellError(
providerAddress,
makerToken,
takerToken,
provider,
outputToken,
inputToken,
sellAmount,
boughtAmount,
minBuyAmount
).rrevert();
}
}
/// @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
override
onlyOwner
{
LibLiquidityProviderStorage.getStorage()
.addressBook[xAsset][yAsset] = providerAddress;
LibLiquidityProviderStorage.getStorage()
.addressBook[yAsset][xAsset] = providerAddress;
emit LiquidityProviderForMarketUpdated(
xAsset,
yAsset,
providerAddress
emit LiquidityProviderSwap(
inputToken,
outputToken,
sellAmount,
boughtAmount,
provider,
recipient
);
}
/// @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.
string public constant override FEATURE_NAME = "TransformERC20";
/// @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.
/// Should be delegatecalled by `Migrate.migrate()`.

View File

@@ -37,7 +37,7 @@ contract UniswapFeature is
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "UniswapFeature";
/// @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.
IEtherTokenV06 private immutable WETH;
/// @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,
TransformERC20,
MetaTransactions,
ReentrancyGuard,
LiquidityProvider
ReentrancyGuard
}
/// @dev Get the storage slot given a storage ID. We assign unique, well-spaced

View File

@@ -1,19 +1,15 @@
/*
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;

View File

@@ -32,28 +32,26 @@ contract TestFillQuoteTransformerBridge {
uint256 amount;
}
bytes4 private constant ERC20_BRIDGE_PROXY_ID = 0xdc1600f3;
function bridgeTransferFrom(
address tokenAddress,
address from,
address to,
uint256 amount,
bytes calldata bridgeData
function sellTokenForToken(
address takerToken,
address makerToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external
returns (bytes4 success)
returns (uint256 boughtAmount)
{
FillBehavior memory behavior = abi.decode(bridgeData, (FillBehavior));
TestMintableERC20Token(tokenAddress).mint(
to,
LibMathV06.getPartialAmountFloor(
behavior.makerAssetMintRatio,
1e18,
behavior.amount
)
FillBehavior memory behavior = abi.decode(auxiliaryData, (FillBehavior));
boughtAmount = LibMathV06.getPartialAmountFloor(
behavior.makerAssetMintRatio,
1e18,
behavior.amount
);
TestMintableERC20Token(makerToken).mint(
recipient,
boughtAmount
);
return ERC20_BRIDGE_PROXY_ID;
}
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"
},
"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": "./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": {
"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 IERC20Transformer from '../generated-artifacts/IERC20Transformer.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 IOwnableFeature from '../generated-artifacts/IOwnableFeature.json';
import * as ISimpleFunctionRegistryFeature from '../generated-artifacts/ISimpleFunctionRegistryFeature.json';
@@ -54,4 +55,5 @@ export const artifacts = {
LogMetadataTransformer: LogMetadataTransformer as ContractArtifact,
BridgeAdapter: BridgeAdapter 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_erc20_transformer';
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_simple_function_registry_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 ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.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 InitialMigration from '../test/generated-artifacts/InitialMigration.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 LibERC20Transformer from '../test/generated-artifacts/LibERC20Transformer.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 LibMetaTransactionsStorage from '../test/generated-artifacts/LibMetaTransactionsStorage.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 LibWalletRichErrors from '../test/generated-artifacts/LibWalletRichErrors.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 MetaTransactionsFeature from '../test/generated-artifacts/MetaTransactionsFeature.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 TestLibSignature from '../test/generated-artifacts/TestLibSignature.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 TestMigrator from '../test/generated-artifacts/TestMigrator.json';
import * as TestMintableERC20Token from '../test/generated-artifacts/TestMintableERC20Token.json';
@@ -137,6 +139,8 @@ export const artifacts = {
FlashWallet: FlashWallet as ContractArtifact,
IAllowanceTarget: IAllowanceTarget as ContractArtifact,
IFlashWallet: IFlashWallet as ContractArtifact,
ILiquidityProviderSandbox: ILiquidityProviderSandbox as ContractArtifact,
LiquidityProviderSandbox: LiquidityProviderSandbox as ContractArtifact,
TransformerDeployer: TransformerDeployer as ContractArtifact,
BootstrapFeature: BootstrapFeature as ContractArtifact,
IBootstrapFeature: IBootstrapFeature as ContractArtifact,
@@ -168,7 +172,6 @@ export const artifacts = {
InitialMigration: InitialMigration as ContractArtifact,
LibBootstrap: LibBootstrap as ContractArtifact,
LibMigrate: LibMigrate as ContractArtifact,
LibLiquidityProviderStorage: LibLiquidityProviderStorage as ContractArtifact,
LibMetaTransactionsStorage: LibMetaTransactionsStorage as ContractArtifact,
LibOwnableStorage: LibOwnableStorage as ContractArtifact,
LibProxyStorage: LibProxyStorage as ContractArtifact,
@@ -217,6 +220,7 @@ export const artifacts = {
TestInitialMigration: TestInitialMigration as ContractArtifact,
TestLibSignature: TestLibSignature as ContractArtifact,
TestLibTokenSpender: TestLibTokenSpender as ContractArtifact,
TestLiquidityProvider: TestLiquidityProvider as ContractArtifact,
TestMetaTransactionsTransformERC20Feature: TestMetaTransactionsTransformERC20Feature as ContractArtifact,
TestMigrator: TestMigrator as ContractArtifact,
TestMintTokenERC20Transformer: TestMintTokenERC20Transformer as ContractArtifact,

View File

@@ -1,16 +1,25 @@
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 { IOwnableFeatureContract, IZeroExContract, LiquidityProviderFeatureContract } from '../../src/wrappers';
import { artifacts } from '../artifacts';
import { abis } from '../utils/abis';
import { fullMigrateAsync } from '../utils/migration';
import { IERC20BridgeEvents, TestBridgeContract, TestWethContract } from '../wrappers';
import {
LiquidityProviderSandboxContract,
TestBridgeContract,
TestBridgeEvents,
TestLiquidityProviderContract,
TestLiquidityProviderEvents,
TestWethContract,
} from '../wrappers';
blockchainTests('LiquidityProvider feature', env => {
let zeroEx: IZeroExContract;
let feature: LiquidityProviderFeatureContract;
let sandbox: LiquidityProviderSandboxContract;
let liquidityProvider: TestLiquidityProviderContract;
let token: DummyERC20TokenContract;
let weth: TestWethContract;
let owner: string;
@@ -47,102 +56,112 @@ blockchainTests('LiquidityProvider feature', env => {
env.provider,
env.txDefaults,
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)
.migrate(featureImpl.address, featureImpl.migrate().getABIEncodedTransactionData(), owner)
.awaitTransactionSuccessAsync();
liquidityProvider = await TestLiquidityProviderContract.deployFrom0xArtifactAsync(
artifacts.TestLiquidityProvider,
env.provider,
env.txDefaults,
artifacts,
token.address,
weth.address,
);
});
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())
blockchainTests.resets('Sandbox', () => {
it('Cannot call sandbox `executeSellTokenForToken` function directly', async () => {
const tx = sandbox
.executeSellTokenForToken(
liquidityProvider.address,
token.address,
weth.address,
taker,
constants.ZERO_AMOUNT,
constants.NULL_BYTES,
)
.awaitTransactionSuccessAsync({ from: taker });
return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(taker, owner));
return expect(tx).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(taker));
});
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', () => {
let liquidityProvider: TestBridgeContract;
const ETH_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
before(async () => {
liquidityProvider = await TestBridgeContract.deployFrom0xArtifactAsync(
it('Successfully executes an ERC20-ERC20 swap', async () => {
const tx = await feature
.sellToLiquidityProvider(
token.address,
weth.address,
liquidityProvider.address,
constants.NULL_ADDRESS,
constants.ONE_ETHER,
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,
token.address,
weth.address,
token.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 () => {
const [xAsset, yAsset] = [randomAddress(), randomAddress()];
const tx = feature
.sellToLiquidityProvider(
xAsset,
yAsset,
constants.NULL_ADDRESS,
constants.ONE_ETHER,
constants.ZERO_AMOUNT,
)
.awaitTransactionSuccessAsync({ from: taker });
return expect(tx).to.revertWith(
new ZeroExRevertErrors.LiquidityProvider.NoLiquidityProviderForMarketError(xAsset, yAsset),
);
});
it('Successfully executes an ERC20-ERC20 swap', async () => {
const tx = await feature
.sellToLiquidityProvider(
weth.address,
token.address,
weth.address,
bridge.address,
constants.NULL_ADDRESS,
constants.ONE_ETHER,
constants.ZERO_AMOUNT,
constants.NULL_BYTES,
)
.awaitTransactionSuccessAsync({ from: taker });
verifyEventsFromLogs(
@@ -153,22 +172,24 @@ blockchainTests('LiquidityProvider feature', env => {
outputToken: weth.address,
inputTokenAmount: constants.ONE_ETHER,
outputTokenAmount: constants.ZERO_AMOUNT,
from: constants.NULL_ADDRESS,
from: bridge.address,
to: taker,
},
],
IERC20BridgeEvents.ERC20BridgeTransfer,
TestBridgeEvents.ERC20BridgeTransfer,
);
});
it('Reverts if cannot fulfill the minimum buy amount', async () => {
const minBuyAmount = new BigNumber(1);
const tx = feature
.sellToLiquidityProvider(
weth.address,
token.address,
weth.address,
liquidityProvider.address,
constants.NULL_ADDRESS,
constants.ONE_ETHER,
minBuyAmount,
constants.NULL_BYTES,
)
.awaitTransactionSuccessAsync({ from: taker });
return expect(tx).to.revertWith(
@@ -185,36 +206,38 @@ blockchainTests('LiquidityProvider feature', env => {
it('Successfully executes an ETH-ERC20 swap', async () => {
const tx = await feature
.sellToLiquidityProvider(
token.address,
ETH_TOKEN_ADDRESS,
token.address,
liquidityProvider.address,
constants.NULL_ADDRESS,
constants.ONE_ETHER,
constants.ZERO_AMOUNT,
constants.NULL_BYTES,
)
.awaitTransactionSuccessAsync({ from: taker, value: constants.ONE_ETHER });
verifyEventsFromLogs(
tx.logs,
[
{
inputToken: weth.address,
outputToken: token.address,
inputTokenAmount: constants.ONE_ETHER,
outputTokenAmount: constants.ZERO_AMOUNT,
from: constants.NULL_ADDRESS,
to: taker,
recipient: taker,
minBuyAmount: constants.ZERO_AMOUNT,
ethBalance: constants.ONE_ETHER,
},
],
IERC20BridgeEvents.ERC20BridgeTransfer,
TestLiquidityProviderEvents.SellEthForToken,
);
});
it('Successfully executes an ERC20-ETH swap', async () => {
const tx = await feature
.sellToLiquidityProvider(
ETH_TOKEN_ADDRESS,
token.address,
ETH_TOKEN_ADDRESS,
liquidityProvider.address,
constants.NULL_ADDRESS,
constants.ONE_ETHER,
constants.ZERO_AMOUNT,
constants.NULL_BYTES,
)
.awaitTransactionSuccessAsync({ from: taker });
verifyEventsFromLogs(
@@ -222,14 +245,12 @@ blockchainTests('LiquidityProvider feature', env => {
[
{
inputToken: token.address,
outputToken: weth.address,
inputTokenAmount: constants.ONE_ETHER,
outputTokenAmount: constants.ZERO_AMOUNT,
from: constants.NULL_ADDRESS,
to: zeroEx.address,
recipient: taker,
minBuyAmount: constants.ZERO_AMOUNT,
inputTokenBalance: constants.ONE_ETHER,
},
],
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_liquidity_provider';
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_ownable_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_erc20_transformer';
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_storage';
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_wallet_rich_errors';
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/meta_transactions_feature';
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_lib_signature';
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_migrator';
export * from '../test/generated-wrappers/test_mint_token_erc20_transformer';

View File

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