@0x/contracts-zero-ex: Add LiquidityProviderFeature contracts

This commit is contained in:
Michael Zhu 2020-08-28 10:37:17 -07:00
parent a35d1b8a9d
commit 78e3cd39d1
14 changed files with 796 additions and 5 deletions

View File

@ -26,6 +26,7 @@ import "./features/ISignatureValidatorFeature.sol";
import "./features/ITransformERC20Feature.sol";
import "./features/IMetaTransactionsFeature.sol";
import "./features/IUniswapFeature.sol";
import "./features/ILiquidityProviderFeature.sol";
/// @dev Interface for a fully featured Exchange Proxy.
@ -36,7 +37,8 @@ interface IZeroEx is
ISignatureValidatorFeature,
ITransformERC20Feature,
IMetaTransactionsFeature,
IUniswapFeature
IUniswapFeature,
ILiquidityProviderFeature
{
// solhint-disable state-visibility

View File

@ -0,0 +1,63 @@
/*
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;
library LibLiquidityProviderRichErrors {
// solhint-disable func-name-mixedcase
function LiquidityProviderIncompleteSellError(
address providerAddress,
address makerToken,
address takerToken,
uint256 sellAmount,
uint256 boughtAmount,
uint256 minBuyAmount
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("LiquidityProviderIncompleteSellError(address,address,address,uint256,uint256,uint256)")),
providerAddress,
makerToken,
takerToken,
sellAmount,
boughtAmount,
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,66 @@
/*
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;
/// @dev Feature to swap directly with an on-chain liquidity provider.
interface ILiquidityProviderFeature {
event LiquidityProviderForMarketUpdated(
address indexed xAsset,
address indexed yAsset,
address providerAddress
);
function sellToLiquidityProvider(
address makerToken,
address takerToken,
address payable recipient,
uint256 sellAmount,
uint256 minBuyAmount
)
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

@ -0,0 +1,204 @@
/*
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";
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 "../fixins/FixinCommon.sol";
import "../storage/LibLiquidityProviderStorage.sol";
import "./IFeature.sol";
import "./ILiquidityProviderFeature.sol";
import "./ITokenSpenderFeature.sol";
interface IERC20Bridge {
/// @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);
}
contract LiquidityProviderFeature is
IFeature,
ILiquidityProviderFeature,
FixinCommon
{
using LibERC20TokenV06 for IERC20TokenV06;
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "LiquidityProviderFeature";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
/// @dev ETH pseudo-token address.
address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev The WETH contract address.
IEtherTokenV06 public immutable weth;
/// @dev Store the WETH address in an immutable.
/// @param weth_ The weth token.
constructor(IEtherTokenV06 weth_)
public
FixinCommon()
{
weth = weth_;
}
function sellToLiquidityProvider(
address makerToken,
address takerToken,
address payable recipient,
uint256 sellAmount,
uint256 minBuyAmount
)
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);
} else {
ITokenSpenderFeature(address(this))._spendERC20Tokens(
IERC20TokenV06(takerToken),
msg.sender,
providerAddress,
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),
recipient,
minBuyAmount,
""
);
boughtAmount = IERC20TokenV06(makerToken).balanceOf(recipient).safeSub(balanceBefore);
}
if (boughtAmount < minBuyAmount) {
LibLiquidityProviderRichErrors.LiquidityProviderIncompleteSellError(
providerAddress,
makerToken,
takerToken,
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
);
}
/// @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

@ -184,7 +184,7 @@ contract MetaTransactionsFeature is
/// @dev Execute a meta-transaction via `sender`. Privileged variant.
/// Only callable from within.
/// @param sender Who is executing the meta-transaction..
/// @param sender Who is executing the meta-transaction.
/// @param mtx The meta-transaction.
/// @param signature The signature by `mtx.signer`.
/// @return returnResult The ABI-encoded result of the underlying call.
@ -454,7 +454,7 @@ contract MetaTransactionsFeature is
}
/// @dev Make an arbitrary internal, meta-transaction call.
/// Warning: Do not let unadulerated `callData` into this function.
/// Warning: Do not let unadulterated `callData` into this function.
function _callSelf(bytes32 hash, bytes memory callData, uint256 value)
private
returns (bytes memory returnResult)

View File

@ -0,0 +1,45 @@
/*
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,7 +36,8 @@ library LibStorage {
TokenSpender,
TransformERC20,
MetaTransactions,
ReentrancyGuard
ReentrancyGuard,
LiquidityProvider
}
/// @dev Get the storage slot given a storage ID. We assign unique, well-spaced

View File

@ -41,7 +41,7 @@
"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",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BootstrapFeature|BridgeAdapter|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinReentrancyGuard|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|IMetaTransactionsFeature|IOwnableFeature|ISignatureValidatorFeature|ISimpleFunctionRegistryFeature|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignatureRichErrors|LibSignedCallData|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LogMetadataTransformer|MetaTransactionsFeature|MixinAdapterAddresses|MixinBalancer|MixinCurve|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|OwnableFeature|PayTakerTransformer|SignatureValidatorFeature|SimpleFunctionRegistryFeature|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|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|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinReentrancyGuard|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|ILiquidityProviderFeature|IMetaTransactionsFeature|IOwnableFeature|ISignatureValidatorFeature|ISimpleFunctionRegistryFeature|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibLiquidityProviderRichErrors|LibLiquidityProviderStorage|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignatureRichErrors|LibSignedCallData|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LogMetadataTransformer|MetaTransactionsFeature|MixinAdapterAddresses|MixinBalancer|MixinCurve|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|OwnableFeature|PayTakerTransformer|SignatureValidatorFeature|SimpleFunctionRegistryFeature|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx).json"
},
"repository": {
"type": "git",

View File

@ -24,6 +24,7 @@ import * as IExchange from '../test/generated-artifacts/IExchange.json';
import * as IFeature from '../test/generated-artifacts/IFeature.json';
import * as IFlashWallet from '../test/generated-artifacts/IFlashWallet.json';
import * as IGasToken from '../test/generated-artifacts/IGasToken.json';
import * as ILiquidityProviderFeature from '../test/generated-artifacts/ILiquidityProviderFeature.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';
@ -37,6 +38,8 @@ import * as IZeroEx from '../test/generated-artifacts/IZeroEx.json';
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';
@ -55,6 +58,7 @@ import * as LibTokenSpenderStorage from '../test/generated-artifacts/LibTokenSpe
import * as LibTransformERC20RichErrors from '../test/generated-artifacts/LibTransformERC20RichErrors.json';
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 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';
@ -104,6 +108,7 @@ export const artifacts = {
IZeroEx: IZeroEx as ContractArtifact,
ZeroEx: ZeroEx as ContractArtifact,
LibCommonRichErrors: LibCommonRichErrors as ContractArtifact,
LibLiquidityProviderRichErrors: LibLiquidityProviderRichErrors as ContractArtifact,
LibMetaTransactionsRichErrors: LibMetaTransactionsRichErrors as ContractArtifact,
LibOwnableRichErrors: LibOwnableRichErrors as ContractArtifact,
LibProxyRichErrors: LibProxyRichErrors as ContractArtifact,
@ -120,6 +125,7 @@ export const artifacts = {
BootstrapFeature: BootstrapFeature as ContractArtifact,
IBootstrapFeature: IBootstrapFeature as ContractArtifact,
IFeature: IFeature as ContractArtifact,
ILiquidityProviderFeature: ILiquidityProviderFeature as ContractArtifact,
IMetaTransactionsFeature: IMetaTransactionsFeature as ContractArtifact,
IOwnableFeature: IOwnableFeature as ContractArtifact,
ISignatureValidatorFeature: ISignatureValidatorFeature as ContractArtifact,
@ -127,6 +133,7 @@ export const artifacts = {
ITokenSpenderFeature: ITokenSpenderFeature as ContractArtifact,
ITransformERC20Feature: ITransformERC20Feature as ContractArtifact,
IUniswapFeature: IUniswapFeature as ContractArtifact,
LiquidityProviderFeature: LiquidityProviderFeature as ContractArtifact,
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
OwnableFeature: OwnableFeature as ContractArtifact,
SignatureValidatorFeature: SignatureValidatorFeature as ContractArtifact,
@ -142,6 +149,7 @@ 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,

View File

@ -0,0 +1,48 @@
import {
blockchainTests,
expect,
getRandomInteger,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
import { IZeroExContract, TokenSpenderFeatureContract } from '../../src/wrappers';
import { artifacts } from '../artifacts';
import { abis } from '../utils/abis';
import { fullMigrateAsync } from '../utils/migration';
import { TestTokenSpenderERC20TokenContract, TestTokenSpenderERC20TokenEvents } from '../wrappers';
blockchainTests.resets('LiquidityProvider feature', env => {
let zeroEx: IZeroExContract;
let feature: TokenSpenderFeatureContract;
let token: TestTokenSpenderERC20TokenContract;
let allowanceTarget: string;
before(async () => {
const [owner] = await env.getAccountAddressesAsync();
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {
tokenSpender: (await TokenSpenderFeatureContract.deployFrom0xArtifactAsync(
artifacts.TestTokenSpender,
env.provider,
env.txDefaults,
artifacts,
)).address,
});
feature = new TokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis);
token = await TestTokenSpenderERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.TestTokenSpenderERC20Token,
env.provider,
env.txDefaults,
artifacts,
);
allowanceTarget = await feature.getAllowanceTarget().callAsync();
});
describe('Registry', () => {
});
describe('Swap', () => {
});
});

View File

@ -22,6 +22,7 @@ export * from '../test/generated-wrappers/i_exchange';
export * from '../test/generated-wrappers/i_feature';
export * from '../test/generated-wrappers/i_flash_wallet';
export * from '../test/generated-wrappers/i_gas_token';
export * from '../test/generated-wrappers/i_liquidity_provider_feature';
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';
@ -35,6 +36,8 @@ export * from '../test/generated-wrappers/initial_migration';
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';
@ -53,6 +56,7 @@ export * from '../test/generated-wrappers/lib_token_spender_storage';
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/log_metadata_transformer';
export * from '../test/generated-wrappers/meta_transactions_feature';
export * from '../test/generated-wrappers/mixin_adapter_addresses';

View File

@ -45,6 +45,7 @@
"test/generated-artifacts/IFeature.json",
"test/generated-artifacts/IFlashWallet.json",
"test/generated-artifacts/IGasToken.json",
"test/generated-artifacts/ILiquidityProviderFeature.json",
"test/generated-artifacts/IMetaTransactionsFeature.json",
"test/generated-artifacts/IOwnableFeature.json",
"test/generated-artifacts/ISignatureValidatorFeature.json",
@ -58,6 +59,8 @@
"test/generated-artifacts/LibBootstrap.json",
"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",
@ -76,6 +79,7 @@
"test/generated-artifacts/LibTransformERC20RichErrors.json",
"test/generated-artifacts/LibTransformERC20Storage.json",
"test/generated-artifacts/LibWalletRichErrors.json",
"test/generated-artifacts/LiquidityProviderFeature.json",
"test/generated-artifacts/LogMetadataTransformer.json",
"test/generated-artifacts/MetaTransactionsFeature.json",
"test/generated-artifacts/MixinAdapterAddresses.json",

View File

@ -3,6 +3,16 @@
"contractName": "IZeroEx",
"compilerOutput": {
"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,
"inputs": [
@ -222,6 +232,16 @@
"stateMutability": "view",
"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": [
{
@ -366,6 +386,19 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "makerToken", "type": "address" },
{ "internalType": "address", "name": "takerToken", "type": "address" },
{ "internalType": "address payable", "name": "recipient", "type": "address" },
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{ "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" }
],
"name": "sellToLiquidityProvider",
"outputs": [{ "internalType": "uint256", "name": "boughtAmount", "type": "uint256" }],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{ "internalType": "contract IERC20TokenV06[]", "name": "tokens", "type": "address[]" },
@ -378,6 +411,17 @@
"stateMutability": "payable",
"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" }],
"name": "setQuoteSigner",
@ -492,6 +536,14 @@
"params": { "selector": "The function selector." },
"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))": {
"details": "Get the block at which a meta-transaction has been executed.",
"params": { "mtx": "The meta-transaction." },
@ -574,6 +626,14 @@
},
"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)": {
"details": "Replace the optional signer for `transformERC20()` calldata. Only callable by the owner.",
"params": { "quoteSigner": "The address of the new calldata signer." }

View File

@ -36,6 +36,7 @@ import * as ethers from 'ethers';
// tslint:enable:no-unused-variable
export type IZeroExEventArgs =
| IZeroExLiquidityProviderForMarketUpdatedEventArgs
| IZeroExMetaTransactionExecutedEventArgs
| IZeroExMigratedEventArgs
| IZeroExOwnershipTransferredEventArgs
@ -45,6 +46,7 @@ export type IZeroExEventArgs =
| IZeroExTransformerDeployerUpdatedEventArgs;
export enum IZeroExEvents {
LiquidityProviderForMarketUpdated = 'LiquidityProviderForMarketUpdated',
MetaTransactionExecuted = 'MetaTransactionExecuted',
Migrated = 'Migrated',
OwnershipTransferred = 'OwnershipTransferred',
@ -54,6 +56,12 @@ export enum IZeroExEvents {
TransformerDeployerUpdated = 'TransformerDeployerUpdated',
}
export interface IZeroExLiquidityProviderForMarketUpdatedEventArgs extends DecodedLogArgs {
xAsset: string;
yAsset: string;
providerAddress: string;
}
export interface IZeroExMetaTransactionExecutedEventArgs extends DecodedLogArgs {
hash: string;
selector: string;
@ -211,6 +219,29 @@ export class IZeroExContract extends BaseContract {
*/
public static ABI(): ContractAbi {
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,
inputs: [
@ -697,6 +728,27 @@ export class IZeroExContract extends BaseContract {
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
name: 'xAsset',
type: 'address',
},
{
name: 'yAsset',
type: 'address',
},
],
name: 'getLiquidityProviderForMarket',
outputs: [
{
name: 'providerAddress',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
@ -1000,6 +1052,39 @@ export class IZeroExContract extends BaseContract {
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
name: 'makerToken',
type: 'address',
},
{
name: 'takerToken',
type: 'address',
},
{
name: 'recipient',
type: 'address',
},
{
name: 'sellAmount',
type: 'uint256',
},
{
name: 'minBuyAmount',
type: 'uint256',
},
],
name: 'sellToLiquidityProvider',
outputs: [
{
name: 'boughtAmount',
type: 'uint256',
},
],
stateMutability: 'payable',
type: 'function',
},
{
inputs: [
{
@ -1029,6 +1114,26 @@ export class IZeroExContract extends BaseContract {
stateMutability: 'payable',
type: 'function',
},
{
inputs: [
{
name: 'xAsset',
type: 'address',
},
{
name: 'yAsset',
type: 'address',
},
{
name: 'providerAddress',
type: 'address',
},
],
name: 'setLiquidityProviderForMarket',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
@ -1743,6 +1848,60 @@ 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.
* @param mtx The meta-transaction.
@ -2447,6 +2606,69 @@ export class IZeroExContract extends BaseContract {
},
};
}
public sellToLiquidityProvider(
makerToken: string,
takerToken: string,
recipient: string,
sellAmount: BigNumber,
minBuyAmount: BigNumber,
): ContractTxFunctionObj<BigNumber> {
const self = (this as any) as IZeroExContract;
assert.isString('makerToken', makerToken);
assert.isString('takerToken', takerToken);
assert.isString('recipient', recipient);
assert.isBigNumber('sellAmount', sellAmount);
assert.isBigNumber('minBuyAmount', minBuyAmount);
const functionSignature = 'sellToLiquidityProvider(address,address,address,uint256,uint256)';
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<BigNumber> {
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<BigNumber>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, [
makerToken.toLowerCase(),
takerToken.toLowerCase(),
recipient.toLowerCase(),
sellAmount,
minBuyAmount,
]);
},
};
}
/**
* Efficiently sell directly to uniswap/sushiswap.
* @param tokens Sell path.
@ -2509,6 +2731,70 @@ 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.
* Only callable by the owner.