add tests for LiquidityProviderFeature
This commit is contained in:
parent
c6d738ed0c
commit
1c15ecacb0
@ -28,30 +28,12 @@ import "../errors/LibLiquidityProviderRichErrors.sol";
|
|||||||
import "../fixins/FixinCommon.sol";
|
import "../fixins/FixinCommon.sol";
|
||||||
import "../migrations/LibMigrate.sol";
|
import "../migrations/LibMigrate.sol";
|
||||||
import "../storage/LibLiquidityProviderStorage.sol";
|
import "../storage/LibLiquidityProviderStorage.sol";
|
||||||
|
import "../vendor/v3/IERC20Bridge.sol";
|
||||||
import "./IFeature.sol";
|
import "./IFeature.sol";
|
||||||
import "./ILiquidityProviderFeature.sol";
|
import "./ILiquidityProviderFeature.sol";
|
||||||
import "./ITokenSpenderFeature.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
|
contract LiquidityProviderFeature is
|
||||||
IFeature,
|
IFeature,
|
||||||
ILiquidityProviderFeature,
|
ILiquidityProviderFeature,
|
||||||
|
69
contracts/zero-ex/contracts/test/TestBridge.sol
Normal file
69
contracts/zero-ex/contracts/test/TestBridge.sol
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 "../src/vendor/v3/IERC20Bridge.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestBridge is
|
||||||
|
IERC20Bridge
|
||||||
|
{
|
||||||
|
IERC20TokenV06 public immutable xAsset;
|
||||||
|
IERC20TokenV06 public immutable yAsset;
|
||||||
|
|
||||||
|
constructor(IERC20TokenV06 xAsset_, IERC20TokenV06 yAsset_)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
xAsset = xAsset_;
|
||||||
|
yAsset = yAsset_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @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
|
||||||
|
override
|
||||||
|
returns (bytes4 success)
|
||||||
|
{
|
||||||
|
IERC20TokenV06 takerToken = tokenAddress == address(xAsset) ? yAsset : xAsset;
|
||||||
|
uint256 takerTokenBalance = takerToken.balanceOf(address(this));
|
||||||
|
emit ERC20BridgeTransfer(
|
||||||
|
address(takerToken),
|
||||||
|
tokenAddress,
|
||||||
|
takerTokenBalance,
|
||||||
|
amount,
|
||||||
|
from,
|
||||||
|
to
|
||||||
|
);
|
||||||
|
return 0xdecaf000;
|
||||||
|
}
|
||||||
|
}
|
@ -41,7 +41,7 @@
|
|||||||
"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",
|
||||||
"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|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"
|
"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|TestBridge|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": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -75,6 +75,7 @@ import * as OwnableFeature from '../test/generated-artifacts/OwnableFeature.json
|
|||||||
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json';
|
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json';
|
||||||
import * as SignatureValidatorFeature from '../test/generated-artifacts/SignatureValidatorFeature.json';
|
import * as SignatureValidatorFeature from '../test/generated-artifacts/SignatureValidatorFeature.json';
|
||||||
import * as SimpleFunctionRegistryFeature from '../test/generated-artifacts/SimpleFunctionRegistryFeature.json';
|
import * as SimpleFunctionRegistryFeature from '../test/generated-artifacts/SimpleFunctionRegistryFeature.json';
|
||||||
|
import * as TestBridge from '../test/generated-artifacts/TestBridge.json';
|
||||||
import * as TestCallTarget from '../test/generated-artifacts/TestCallTarget.json';
|
import * as TestCallTarget from '../test/generated-artifacts/TestCallTarget.json';
|
||||||
import * as TestDelegateCaller from '../test/generated-artifacts/TestDelegateCaller.json';
|
import * as TestDelegateCaller from '../test/generated-artifacts/TestDelegateCaller.json';
|
||||||
import * as TestFillQuoteTransformerBridge from '../test/generated-artifacts/TestFillQuoteTransformerBridge.json';
|
import * as TestFillQuoteTransformerBridge from '../test/generated-artifacts/TestFillQuoteTransformerBridge.json';
|
||||||
@ -182,6 +183,7 @@ export const artifacts = {
|
|||||||
IExchange: IExchange as ContractArtifact,
|
IExchange: IExchange as ContractArtifact,
|
||||||
IGasToken: IGasToken as ContractArtifact,
|
IGasToken: IGasToken as ContractArtifact,
|
||||||
ITestSimpleFunctionRegistryFeature: ITestSimpleFunctionRegistryFeature as ContractArtifact,
|
ITestSimpleFunctionRegistryFeature: ITestSimpleFunctionRegistryFeature as ContractArtifact,
|
||||||
|
TestBridge: TestBridge as ContractArtifact,
|
||||||
TestCallTarget: TestCallTarget as ContractArtifact,
|
TestCallTarget: TestCallTarget as ContractArtifact,
|
||||||
TestDelegateCaller: TestDelegateCaller as ContractArtifact,
|
TestDelegateCaller: TestDelegateCaller as ContractArtifact,
|
||||||
TestFillQuoteTransformerBridge: TestFillQuoteTransformerBridge as ContractArtifact,
|
TestFillQuoteTransformerBridge: TestFillQuoteTransformerBridge as ContractArtifact,
|
||||||
|
@ -1,26 +1,28 @@
|
|||||||
import {
|
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||||
blockchainTests,
|
import { blockchainTests, constants, expect, randomAddress, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
||||||
expect,
|
import { BigNumber, OwnableRevertErrors, ZeroExRevertErrors } from '@0x/utils';
|
||||||
getRandomInteger,
|
|
||||||
randomAddress,
|
|
||||||
verifyEventsFromLogs,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
|
|
||||||
|
|
||||||
import { IZeroExContract, TokenSpenderFeatureContract } from '../../src/wrappers';
|
import {
|
||||||
|
IOwnableFeatureContract,
|
||||||
|
IZeroExContract,
|
||||||
|
LiquidityProviderFeatureContract,
|
||||||
|
TokenSpenderFeatureContract,
|
||||||
|
} 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 { TestTokenSpenderERC20TokenContract, TestTokenSpenderERC20TokenEvents } from '../wrappers';
|
import { IERC20BridgeEvents, TestBridgeContract, TestWethContract } from '../wrappers';
|
||||||
|
|
||||||
blockchainTests.resets('LiquidityProvider feature', env => {
|
blockchainTests('LiquidityProvider feature', env => {
|
||||||
let zeroEx: IZeroExContract;
|
let zeroEx: IZeroExContract;
|
||||||
let feature: TokenSpenderFeatureContract;
|
let feature: LiquidityProviderFeatureContract;
|
||||||
let token: TestTokenSpenderERC20TokenContract;
|
let token: DummyERC20TokenContract;
|
||||||
let allowanceTarget: string;
|
let weth: TestWethContract;
|
||||||
|
let owner: string;
|
||||||
|
let taker: string;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const [owner] = await env.getAccountAddressesAsync();
|
[owner, taker] = await env.getAccountAddressesAsync();
|
||||||
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {
|
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {
|
||||||
tokenSpender: (await TokenSpenderFeatureContract.deployFrom0xArtifactAsync(
|
tokenSpender: (await TokenSpenderFeatureContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestTokenSpender,
|
artifacts.TestTokenSpender,
|
||||||
@ -29,20 +31,220 @@ blockchainTests.resets('LiquidityProvider feature', env => {
|
|||||||
artifacts,
|
artifacts,
|
||||||
)).address,
|
)).address,
|
||||||
});
|
});
|
||||||
feature = new TokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis);
|
const tokenSpender = new TokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis);
|
||||||
token = await TestTokenSpenderERC20TokenContract.deployFrom0xArtifactAsync(
|
const allowanceTarget = await tokenSpender.getAllowanceTarget().callAsync();
|
||||||
artifacts.TestTokenSpenderERC20Token,
|
|
||||||
|
token = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
|
erc20Artifacts.DummyERC20Token,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
erc20Artifacts,
|
||||||
|
constants.DUMMY_TOKEN_NAME,
|
||||||
|
constants.DUMMY_TOKEN_SYMBOL,
|
||||||
|
constants.DUMMY_TOKEN_DECIMALS,
|
||||||
|
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
||||||
|
);
|
||||||
|
await token.setBalance(taker, constants.INITIAL_ERC20_BALANCE).awaitTransactionSuccessAsync();
|
||||||
|
weth = await TestWethContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestWeth,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
);
|
);
|
||||||
allowanceTarget = await feature.getAllowanceTarget().callAsync();
|
await token
|
||||||
});
|
.approve(allowanceTarget, constants.INITIAL_ERC20_ALLOWANCE)
|
||||||
|
.awaitTransactionSuccessAsync({ from: taker });
|
||||||
|
|
||||||
|
feature = new LiquidityProviderFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis);
|
||||||
|
const featureImpl = await LiquidityProviderFeatureContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.LiquidityProviderFeature,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
weth.address,
|
||||||
|
);
|
||||||
|
await new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis)
|
||||||
|
.migrate(featureImpl.address, featureImpl.migrate().getABIEncodedTransactionData(), owner)
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
|
});
|
||||||
describe('Registry', () => {
|
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 TestBridgeContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestBridge,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
token.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 () => {
|
||||||
|
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,
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
|
constants.ONE_ETHER,
|
||||||
|
constants.ZERO_AMOUNT,
|
||||||
|
)
|
||||||
|
.awaitTransactionSuccessAsync({ from: taker });
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
tx.logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
inputToken: token.address,
|
||||||
|
outputToken: weth.address,
|
||||||
|
inputTokenAmount: constants.ONE_ETHER,
|
||||||
|
outputTokenAmount: constants.ZERO_AMOUNT,
|
||||||
|
from: constants.NULL_ADDRESS,
|
||||||
|
to: taker,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
IERC20BridgeEvents.ERC20BridgeTransfer,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('Reverts if cannot fulfill the minimum buy amount', async () => {
|
||||||
|
const minBuyAmount = new BigNumber(1);
|
||||||
|
const tx = feature
|
||||||
|
.sellToLiquidityProvider(
|
||||||
|
weth.address,
|
||||||
|
token.address,
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
|
constants.ONE_ETHER,
|
||||||
|
minBuyAmount,
|
||||||
|
)
|
||||||
|
.awaitTransactionSuccessAsync({ from: taker });
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.LiquidityProvider.LiquidityProviderIncompleteSellError(
|
||||||
|
liquidityProvider.address,
|
||||||
|
weth.address,
|
||||||
|
token.address,
|
||||||
|
constants.ONE_ETHER,
|
||||||
|
constants.ZERO_AMOUNT,
|
||||||
|
minBuyAmount,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('Successfully executes an ETH-ERC20 swap', async () => {
|
||||||
|
const tx = await feature
|
||||||
|
.sellToLiquidityProvider(
|
||||||
|
token.address,
|
||||||
|
ETH_TOKEN_ADDRESS,
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
|
constants.ONE_ETHER,
|
||||||
|
constants.ZERO_AMOUNT,
|
||||||
|
)
|
||||||
|
.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,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
IERC20BridgeEvents.ERC20BridgeTransfer,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('Successfully executes an ERC20-ETH swap', async () => {
|
||||||
|
const tx = await feature
|
||||||
|
.sellToLiquidityProvider(
|
||||||
|
ETH_TOKEN_ADDRESS,
|
||||||
|
token.address,
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
|
constants.ONE_ETHER,
|
||||||
|
constants.ZERO_AMOUNT,
|
||||||
|
)
|
||||||
|
.awaitTransactionSuccessAsync({ from: taker });
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
tx.logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
inputToken: token.address,
|
||||||
|
outputToken: weth.address,
|
||||||
|
inputTokenAmount: constants.ONE_ETHER,
|
||||||
|
outputTokenAmount: constants.ZERO_AMOUNT,
|
||||||
|
from: constants.NULL_ADDRESS,
|
||||||
|
to: zeroEx.address,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
IERC20BridgeEvents.ERC20BridgeTransfer,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
describe('Swap', () => {
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -73,6 +73,7 @@ export * from '../test/generated-wrappers/ownable_feature';
|
|||||||
export * from '../test/generated-wrappers/pay_taker_transformer';
|
export * from '../test/generated-wrappers/pay_taker_transformer';
|
||||||
export * from '../test/generated-wrappers/signature_validator_feature';
|
export * from '../test/generated-wrappers/signature_validator_feature';
|
||||||
export * from '../test/generated-wrappers/simple_function_registry_feature';
|
export * from '../test/generated-wrappers/simple_function_registry_feature';
|
||||||
|
export * from '../test/generated-wrappers/test_bridge';
|
||||||
export * from '../test/generated-wrappers/test_call_target';
|
export * from '../test/generated-wrappers/test_call_target';
|
||||||
export * from '../test/generated-wrappers/test_delegate_caller';
|
export * from '../test/generated-wrappers/test_delegate_caller';
|
||||||
export * from '../test/generated-wrappers/test_fill_quote_transformer_bridge';
|
export * from '../test/generated-wrappers/test_fill_quote_transformer_bridge';
|
||||||
|
@ -97,6 +97,7 @@
|
|||||||
"test/generated-artifacts/PayTakerTransformer.json",
|
"test/generated-artifacts/PayTakerTransformer.json",
|
||||||
"test/generated-artifacts/SignatureValidatorFeature.json",
|
"test/generated-artifacts/SignatureValidatorFeature.json",
|
||||||
"test/generated-artifacts/SimpleFunctionRegistryFeature.json",
|
"test/generated-artifacts/SimpleFunctionRegistryFeature.json",
|
||||||
|
"test/generated-artifacts/TestBridge.json",
|
||||||
"test/generated-artifacts/TestCallTarget.json",
|
"test/generated-artifacts/TestCallTarget.json",
|
||||||
"test/generated-artifacts/TestDelegateCaller.json",
|
"test/generated-artifacts/TestDelegateCaller.json",
|
||||||
"test/generated-artifacts/TestFillQuoteTransformerBridge.json",
|
"test/generated-artifacts/TestFillQuoteTransformerBridge.json",
|
||||||
|
@ -54,4 +54,5 @@ export const ZeroExRevertErrors = {
|
|||||||
Wallet: require('./revert_errors/zero-ex/wallet_revert_errors'),
|
Wallet: require('./revert_errors/zero-ex/wallet_revert_errors'),
|
||||||
MetaTransactions: require('./revert_errors/zero-ex/meta_transaction_revert_errors'),
|
MetaTransactions: require('./revert_errors/zero-ex/meta_transaction_revert_errors'),
|
||||||
SignatureValidator: require('./revert_errors/zero-ex/signature_validator_revert_errors'),
|
SignatureValidator: require('./revert_errors/zero-ex/signature_validator_revert_errors'),
|
||||||
|
LiquidityProvider: require('./revert_errors/zero-ex/liquidity_provider_revert_errors'),
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
import { RevertError } from '../../revert_error';
|
||||||
|
import { Numberish } from '../../types';
|
||||||
|
|
||||||
|
// tslint:disable:max-classes-per-file
|
||||||
|
export class LiquidityProviderIncompleteSellError extends RevertError {
|
||||||
|
constructor(
|
||||||
|
providerAddress?: string,
|
||||||
|
makerToken?: string,
|
||||||
|
takerToken?: string,
|
||||||
|
sellAmount?: Numberish,
|
||||||
|
boughtAmount?: Numberish,
|
||||||
|
minBuyAmount?: Numberish,
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
'LiquidityProviderIncompleteSellError',
|
||||||
|
'LiquidityProviderIncompleteSellError(address providerAddress, address makerToken, address takerToken, uint256 sellAmount, uint256 boughtAmount, uint256 minBuyAmount)',
|
||||||
|
{
|
||||||
|
providerAddress,
|
||||||
|
makerToken,
|
||||||
|
takerToken,
|
||||||
|
sellAmount,
|
||||||
|
boughtAmount,
|
||||||
|
minBuyAmount,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NoLiquidityProviderForMarketError extends RevertError {
|
||||||
|
constructor(xAsset?: string, yAsset?: string) {
|
||||||
|
super(
|
||||||
|
'NoLiquidityProviderForMarketError',
|
||||||
|
'NoLiquidityProviderForMarketError(address xAsset, address yAsset)',
|
||||||
|
{
|
||||||
|
xAsset,
|
||||||
|
yAsset,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const types = [LiquidityProviderIncompleteSellError, NoLiquidityProviderForMarketError];
|
||||||
|
|
||||||
|
// Register the types we've defined.
|
||||||
|
for (const type of types) {
|
||||||
|
RevertError.registerType(type);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user