Move LibAssetDataTransfer and MixinWeth(Utils) to contracts-extensions
This commit is contained in:
parent
a2fcab47d4
commit
329719472a
@ -52,6 +52,8 @@
|
|||||||
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.1.0",
|
"@0x/abi-gen": "^5.1.0",
|
||||||
|
"@0x/contracts-asset-proxy": "^3.1.1",
|
||||||
|
"@0x/contracts-erc20": "^3.0.4",
|
||||||
"@0x/contracts-erc721": "^3.0.4",
|
"@0x/contracts-erc721": "^3.0.4",
|
||||||
"@0x/contracts-exchange": "^3.1.0",
|
"@0x/contracts-exchange": "^3.1.0",
|
||||||
"@0x/contracts-exchange-libs": "^4.1.0",
|
"@0x/contracts-exchange-libs": "^4.1.0",
|
||||||
|
@ -13,20 +13,22 @@ export const godsUnchainedUtils = {
|
|||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Encodes the given proto and quality into ERC1155 asset data to be used as the takerAssetData
|
* Encodes the given proto and quality into ERC1155 asset data to be used as the takerAssetData
|
||||||
* of a property-based GodsUnchained order. Must also provide the address of the Broker and
|
* of a property-based GodsUnchained order. Must also provide the addresses of the Broker,
|
||||||
* the GodsUnchainedValidator contract. The optional bundleSize parameter specifies how many
|
* GodsUnchained, and GodsUnchainedValidator contracts. The optional bundleSize parameter specifies
|
||||||
* cards are expected for each "unit" of the takerAssetAmount. For example, If the
|
* how many cards are expected for each "unit" of the takerAssetAmount. For example, If the
|
||||||
* takerAssetAmount is 3 and the bundleSize is 2, the taker must provide 2, 4, or 6 cards
|
* takerAssetAmount is 3 and the bundleSize is 2, the taker must provide 2, 4, or 6 cards
|
||||||
* with the given proto and quality to fill the order. If an odd number is provided, the fill fails.
|
* with the given proto and quality to fill the order. If an odd number is provided, the fill fails.
|
||||||
*/
|
*/
|
||||||
encodeBrokerAssetData(
|
encodeBrokerAssetData(
|
||||||
brokerAddress: string,
|
brokerAddress: string,
|
||||||
|
godsUnchainedAddress: string,
|
||||||
validatorAddress: string,
|
validatorAddress: string,
|
||||||
proto: BigNumber,
|
proto: BigNumber,
|
||||||
quality: BigNumber,
|
quality: BigNumber,
|
||||||
bundleSize: number = 1,
|
bundleSize: number = 1,
|
||||||
): string {
|
): string {
|
||||||
const dataEncoder = AbiEncoder.create([
|
const dataEncoder = AbiEncoder.create([
|
||||||
|
{ name: 'godsUnchainedAddress', type: 'address' },
|
||||||
{ name: 'validatorAddress', type: 'address' },
|
{ name: 'validatorAddress', type: 'address' },
|
||||||
{ name: 'propertyData', type: 'bytes' },
|
{ name: 'propertyData', type: 'bytes' },
|
||||||
]);
|
]);
|
||||||
@ -34,7 +36,7 @@ export const godsUnchainedUtils = {
|
|||||||
{ name: 'proto', type: 'uint16' },
|
{ name: 'proto', type: 'uint16' },
|
||||||
{ name: 'quality', type: 'uint8' },
|
{ name: 'quality', type: 'uint8' },
|
||||||
]).encode({ proto, quality });
|
]).encode({ proto, quality });
|
||||||
const data = dataEncoder.encode({ validatorAddress, propertyData });
|
const data = dataEncoder.encode({ godsUnchainedAddress, validatorAddress, propertyData });
|
||||||
return assetDataUtils.encodeERC1155AssetData(brokerAddress, [], [new BigNumber(bundleSize)], data);
|
return assetDataUtils.encodeERC1155AssetData(brokerAddress, [], [new BigNumber(bundleSize)], data);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { blockchainTests, constants, expect, getRandomInteger, randomAddress } from '@0x/contracts-test-utils';
|
import { blockchainTests, constants, expect, getRandomInteger } from '@0x/contracts-test-utils';
|
||||||
import { assetDataUtils } from '@0x/order-utils';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
@ -39,28 +38,18 @@ blockchainTests.resets('GodsUnchainedValidator unit tests', env => {
|
|||||||
it('succeeds if assetData proto and quality match propertyData', async () => {
|
it('succeeds if assetData proto and quality match propertyData', async () => {
|
||||||
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
||||||
await godsUnchained.setTokenProperties(tokenId, proto, quality).awaitTransactionSuccessAsync();
|
await godsUnchained.setTokenProperties(tokenId, proto, quality).awaitTransactionSuccessAsync();
|
||||||
const assetData = assetDataUtils.encodeERC721AssetData(godsUnchained.address, tokenId);
|
await validator.checkBrokerAsset(tokenId, propertyData).callAsync();
|
||||||
await validator.checkBrokerAsset(assetData, propertyData).callAsync();
|
|
||||||
});
|
|
||||||
it('reverts if assetData tokenAddress is not the GU contract address', async () => {
|
|
||||||
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
|
||||||
await godsUnchained.setTokenProperties(tokenId, proto, quality).awaitTransactionSuccessAsync();
|
|
||||||
const assetData = assetDataUtils.encodeERC721AssetData(randomAddress(), tokenId);
|
|
||||||
const tx = validator.checkBrokerAsset(assetData, propertyData).callAsync();
|
|
||||||
expect(tx).to.revertWith('TOKEN_ADDRESS_MISMATCH');
|
|
||||||
});
|
});
|
||||||
it("reverts if assetData proto doesn't match propertyData", async () => {
|
it("reverts if assetData proto doesn't match propertyData", async () => {
|
||||||
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
||||||
await godsUnchained.setTokenProperties(tokenId, proto.plus(1), quality).awaitTransactionSuccessAsync();
|
await godsUnchained.setTokenProperties(tokenId, proto.plus(1), quality).awaitTransactionSuccessAsync();
|
||||||
const assetData = assetDataUtils.encodeERC721AssetData(godsUnchained.address, tokenId);
|
const tx = validator.checkBrokerAsset(tokenId, propertyData).callAsync();
|
||||||
const tx = validator.checkBrokerAsset(assetData, propertyData).callAsync();
|
|
||||||
expect(tx).to.revertWith('PROTO_MISMATCH');
|
expect(tx).to.revertWith('PROTO_MISMATCH');
|
||||||
});
|
});
|
||||||
it("reverts if assetData quality doesn't match proeprtyData", async () => {
|
it("reverts if assetData quality doesn't match proeprtyData", async () => {
|
||||||
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
const tokenId = getRandomInteger(0, constants.MAX_UINT256);
|
||||||
await godsUnchained.setTokenProperties(tokenId, proto, quality.plus(1)).awaitTransactionSuccessAsync();
|
await godsUnchained.setTokenProperties(tokenId, proto, quality.plus(1)).awaitTransactionSuccessAsync();
|
||||||
const assetData = assetDataUtils.encodeERC721AssetData(godsUnchained.address, tokenId);
|
const tx = validator.checkBrokerAsset(tokenId, propertyData).callAsync();
|
||||||
const tx = validator.checkBrokerAsset(assetData, propertyData).callAsync();
|
|
||||||
expect(tx).to.revertWith('QUALITY_MISMATCH');
|
expect(tx).to.revertWith('QUALITY_MISMATCH');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -41,13 +41,13 @@ yarn install
|
|||||||
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
PKG=@0x/contracts-extensions yarn build
|
PKG=@0x/contracts-dev-utils yarn build
|
||||||
```
|
```
|
||||||
|
|
||||||
Or continuously rebuild on change:
|
Or continuously rebuild on change:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
PKG=@0x/contracts-extensions yarn watch
|
PKG=@0x/contracts-dev-utils yarn watch
|
||||||
```
|
```
|
||||||
|
|
||||||
### Clean
|
### Clean
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"note": "Moved LibAssetDataTransfer to exchange-libs",
|
"note": "Refactor, moved LibAssetDataTransfer and MixinWeth(Utils) to extensions",
|
||||||
"pr": 2455
|
"pr": 2455
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -19,19 +19,32 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "./MixinForwarderCore.sol";
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
||||||
import "./libs/LibConstants.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||||
|
import "@0x/contracts-extensions/contracts/src/LibAssetDataTransfer.sol";
|
||||||
|
import "@0x/contracts-extensions/contracts/src/MixinWethUtils.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||||
|
import "./libs/LibForwarderRichErrors.sol";
|
||||||
|
import "./MixinExchangeWrapper.sol";
|
||||||
import "./MixinReceiver.sol";
|
import "./MixinReceiver.sol";
|
||||||
|
import "./interfaces/IForwarder.sol";
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
|
||||||
// MixinAssets, MixinExchangeWrapper, and MixinWeth are all inherited via
|
|
||||||
// MixinForwarderCore.
|
|
||||||
contract Forwarder is
|
contract Forwarder is
|
||||||
LibConstants,
|
IForwarder,
|
||||||
MixinForwarderCore,
|
Ownable,
|
||||||
|
MixinWethUtils,
|
||||||
|
MixinExchangeWrapper,
|
||||||
MixinReceiver
|
MixinReceiver
|
||||||
{
|
{
|
||||||
|
using LibBytes for bytes;
|
||||||
|
using LibAssetDataTransfer for bytes;
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
address _exchange,
|
address _exchange,
|
||||||
address _exchangeV2,
|
address _exchangeV2,
|
||||||
@ -39,11 +52,158 @@ contract Forwarder is
|
|||||||
)
|
)
|
||||||
public
|
public
|
||||||
Ownable()
|
Ownable()
|
||||||
LibConstants(
|
MixinWethUtils(
|
||||||
_exchange,
|
_exchange,
|
||||||
_exchangeV2,
|
|
||||||
_weth
|
_weth
|
||||||
)
|
)
|
||||||
MixinForwarderCore()
|
MixinExchangeWrapper(
|
||||||
{}
|
_exchange,
|
||||||
|
_exchangeV2
|
||||||
|
)
|
||||||
|
{} // solhint-disable-line no-empty-blocks
|
||||||
|
|
||||||
|
/// @dev Withdraws assets from this contract. It may be used by the owner to withdraw assets
|
||||||
|
/// that were accidentally sent to this contract.
|
||||||
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param amount Amount of the asset to withdraw.
|
||||||
|
function withdrawAsset(
|
||||||
|
bytes calldata assetData,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
onlyOwner
|
||||||
|
{
|
||||||
|
assetData.transferOut(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Approves the respective proxy for a given asset to transfer tokens on the Forwarder contract's behalf.
|
||||||
|
/// This is necessary because an order fee denominated in the maker asset (i.e. a percentage fee) is sent by the
|
||||||
|
/// Forwarder contract to the fee recipient.
|
||||||
|
/// This method needs to be called before forwarding orders of a maker asset that hasn't
|
||||||
|
/// previously been approved.
|
||||||
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||||
|
function approveMakerAssetProxy(bytes calldata assetData)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
bytes4 proxyId = assetData.readBytes4(0);
|
||||||
|
bytes4 erc20ProxyId = IAssetData(address(0)).ERC20Token.selector;
|
||||||
|
|
||||||
|
// For now we only care about ERC20, since percentage fees on ERC721 tokens are invalid.
|
||||||
|
if (proxyId == erc20ProxyId) {
|
||||||
|
address proxyAddress = EXCHANGE.getAssetProxy(erc20ProxyId);
|
||||||
|
if (proxyAddress == address(0)) {
|
||||||
|
LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
|
||||||
|
}
|
||||||
|
address token = assetData.readAddress(16);
|
||||||
|
LibERC20Token.approve(token, proxyAddress, MAX_UINT256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Purchases as much of orders' makerAssets as possible by selling as much of the ETH value sent
|
||||||
|
/// as possible, accounting for order and forwarder fees.
|
||||||
|
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
|
||||||
|
/// @param signatures Proofs that orders have been created by makers.
|
||||||
|
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
||||||
|
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
||||||
|
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
|
||||||
|
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
|
||||||
|
function marketSellOrdersWithEth(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
bytes[] memory signatures,
|
||||||
|
uint256[] memory ethFeeAmounts,
|
||||||
|
address payable[] memory feeRecipients
|
||||||
|
)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
returns (
|
||||||
|
uint256 wethSpentAmount,
|
||||||
|
uint256 makerAssetAcquiredAmount
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Pay ETH affiliate fees to all feeRecipient addresses
|
||||||
|
uint256 wethRemaining = _transferEthFeesAndWrapRemaining(
|
||||||
|
ethFeeAmounts,
|
||||||
|
feeRecipients
|
||||||
|
);
|
||||||
|
// Spends up to wethRemaining to fill orders, transfers purchased assets to msg.sender,
|
||||||
|
// and pays WETH order fees.
|
||||||
|
(
|
||||||
|
wethSpentAmount,
|
||||||
|
makerAssetAcquiredAmount
|
||||||
|
) = _marketSellNoThrow(
|
||||||
|
orders,
|
||||||
|
wethRemaining,
|
||||||
|
signatures
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure that no extra WETH owned by this contract has been spent.
|
||||||
|
if (wethSpentAmount > wethRemaining) {
|
||||||
|
LibRichErrors.rrevert(LibForwarderRichErrors.OverspentWethError(
|
||||||
|
wethSpentAmount,
|
||||||
|
msg.value
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate amount of WETH that hasn't been spent.
|
||||||
|
wethRemaining = wethRemaining.safeSub(wethSpentAmount);
|
||||||
|
|
||||||
|
// Refund remaining ETH to msg.sender.
|
||||||
|
_transferEthRefund(wethRemaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction.
|
||||||
|
/// The Forwarder may *fill* more than makerAssetBuyAmount of the makerAsset so that it can
|
||||||
|
/// pay takerFees where takerFeeAssetData == makerAssetData (i.e. percentage fees).
|
||||||
|
/// Any ETH not spent will be refunded to sender.
|
||||||
|
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
|
||||||
|
/// @param makerAssetBuyAmount Desired amount of makerAsset to purchase.
|
||||||
|
/// @param signatures Proofs that orders have been created by makers.
|
||||||
|
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
||||||
|
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
||||||
|
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
|
||||||
|
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
|
||||||
|
function marketBuyOrdersWithEth(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
uint256 makerAssetBuyAmount,
|
||||||
|
bytes[] memory signatures,
|
||||||
|
uint256[] memory ethFeeAmounts,
|
||||||
|
address payable[] memory feeRecipients
|
||||||
|
)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
returns (
|
||||||
|
uint256 wethSpentAmount,
|
||||||
|
uint256 makerAssetAcquiredAmount
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Pay ETH affiliate fees to all feeRecipient addresses
|
||||||
|
uint256 wethRemaining = _transferEthFeesAndWrapRemaining(
|
||||||
|
ethFeeAmounts,
|
||||||
|
feeRecipients
|
||||||
|
);
|
||||||
|
|
||||||
|
// Attempts to fill the desired amount of makerAsset and trasnfer purchased assets to msg.sender.
|
||||||
|
(
|
||||||
|
wethSpentAmount,
|
||||||
|
makerAssetAcquiredAmount
|
||||||
|
) = _marketBuyFillOrKill(
|
||||||
|
orders,
|
||||||
|
makerAssetBuyAmount,
|
||||||
|
signatures
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure that no extra WETH owned by this contract has been spent.
|
||||||
|
if (wethSpentAmount > wethRemaining) {
|
||||||
|
LibRichErrors.rrevert(LibForwarderRichErrors.OverspentWethError(
|
||||||
|
wethSpentAmount,
|
||||||
|
msg.value
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate amount of WETH that hasn't been spent.
|
||||||
|
wethRemaining = wethRemaining.safeSub(wethSpentAmount);
|
||||||
|
|
||||||
|
// Refund remaining ETH to msg.sender.
|
||||||
|
_transferEthRefund(wethRemaining);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibAssetDataTransfer.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
|
||||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
|
||||||
import "./libs/LibConstants.sol";
|
|
||||||
import "./libs/LibForwarderRichErrors.sol";
|
|
||||||
import "./interfaces/IAssets.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinAssets is
|
|
||||||
Ownable,
|
|
||||||
LibConstants,
|
|
||||||
IAssets
|
|
||||||
{
|
|
||||||
using LibBytes for bytes;
|
|
||||||
using LibAssetDataTransfer for bytes;
|
|
||||||
|
|
||||||
/// @dev Withdraws assets from this contract. It may be used by the owner to withdraw assets
|
|
||||||
/// that were accidentally sent to this contract.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of the asset to withdraw.
|
|
||||||
function withdrawAsset(
|
|
||||||
bytes calldata assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
onlyOwner
|
|
||||||
{
|
|
||||||
assetData.transferOut(amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Approves the respective proxy for a given asset to transfer tokens on the Forwarder contract's behalf.
|
|
||||||
/// This is necessary because an order fee denominated in the maker asset (i.e. a percentage fee) is sent by the
|
|
||||||
/// Forwarder contract to the fee recipient.
|
|
||||||
/// This method needs to be called before forwarding orders of a maker asset that hasn't
|
|
||||||
/// previously been approved.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
function approveMakerAssetProxy(bytes calldata assetData)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
bytes4 proxyId = assetData.readBytes4(0);
|
|
||||||
bytes4 erc20ProxyId = IAssetData(address(0)).ERC20Token.selector;
|
|
||||||
|
|
||||||
// For now we only care about ERC20, since percentage fees on ERC721 tokens are invalid.
|
|
||||||
if (proxyId == erc20ProxyId) {
|
|
||||||
address proxyAddress = EXCHANGE.getAssetProxy(erc20ProxyId);
|
|
||||||
if (proxyAddress == address(0)) {
|
|
||||||
LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
|
|
||||||
}
|
|
||||||
address token = assetData.readAddress(16);
|
|
||||||
LibERC20Token.approve(token, proxyAddress, MAX_UINT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,28 +19,60 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
import "@0x/contracts-extensions/contracts/src/LibAssetDataTransfer.sol";
|
||||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
import "./libs/LibConstants.sol";
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
import "./libs/LibForwarderRichErrors.sol";
|
import "./libs/LibForwarderRichErrors.sol";
|
||||||
import "./interfaces/IExchangeV2.sol";
|
import "./interfaces/IExchangeV2.sol";
|
||||||
import "./MixinAssets.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinExchangeWrapper is
|
contract MixinExchangeWrapper {
|
||||||
LibConstants,
|
|
||||||
MixinAssets
|
// The v2 order id is the first 4 bytes of the ExchangeV2 order schema hash.
|
||||||
{
|
// bytes4(keccak256(abi.encodePacked(
|
||||||
|
// "Order(",
|
||||||
|
// "address makerAddress,",
|
||||||
|
// "address takerAddress,",
|
||||||
|
// "address feeRecipientAddress,",
|
||||||
|
// "address senderAddress,",
|
||||||
|
// "uint256 makerAssetAmount,",
|
||||||
|
// "uint256 takerAssetAmount,",
|
||||||
|
// "uint256 makerFee,",
|
||||||
|
// "uint256 takerFee,",
|
||||||
|
// "uint256 expirationTimeSeconds,",
|
||||||
|
// "uint256 salt,",
|
||||||
|
// "bytes makerAssetData,",
|
||||||
|
// "bytes takerAssetData",
|
||||||
|
// ")"
|
||||||
|
// )));
|
||||||
|
bytes4 constant public EXCHANGE_V2_ORDER_ID = 0x770501f8;
|
||||||
|
|
||||||
|
// solhint-disable var-name-mixedcase
|
||||||
|
IExchange internal EXCHANGE;
|
||||||
|
IExchangeV2 internal EXCHANGE_V2;
|
||||||
|
// solhint-enable var-name-mixedcase
|
||||||
|
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
|
using LibAssetDataTransfer for bytes;
|
||||||
using LibSafeMath for uint256;
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
address _exchange,
|
||||||
|
address _exchangeV2
|
||||||
|
)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
EXCHANGE = IExchange(_exchange);
|
||||||
|
EXCHANGE_V2 = IExchangeV2(_exchangeV2);
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Fills the input order.
|
/// @dev Fills the input order.
|
||||||
/// Returns false if the transaction would otherwise revert.
|
/// Returns false if the transaction would otherwise revert.
|
||||||
/// @param order Order struct containing order specifications.
|
/// @param order Order struct containing order specifications.
|
||||||
|
@ -1,147 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
|
||||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
|
||||||
import "./libs/LibConstants.sol";
|
|
||||||
import "./libs/LibForwarderRichErrors.sol";
|
|
||||||
import "./interfaces/IAssets.sol";
|
|
||||||
import "./interfaces/IForwarderCore.sol";
|
|
||||||
import "./MixinExchangeWrapper.sol";
|
|
||||||
import "./MixinWeth.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinForwarderCore is
|
|
||||||
LibConstants,
|
|
||||||
IAssets,
|
|
||||||
IForwarderCore,
|
|
||||||
MixinWeth,
|
|
||||||
MixinExchangeWrapper
|
|
||||||
{
|
|
||||||
using LibBytes for bytes;
|
|
||||||
using LibSafeMath for uint256;
|
|
||||||
|
|
||||||
/// @dev Constructor approves ERC20 proxy to transfer WETH on this contract's behalf.
|
|
||||||
constructor ()
|
|
||||||
public
|
|
||||||
{
|
|
||||||
address proxyAddress = EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC20Token.selector);
|
|
||||||
if (proxyAddress == address(0)) {
|
|
||||||
LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError());
|
|
||||||
}
|
|
||||||
ETHER_TOKEN.approve(proxyAddress, MAX_UINT);
|
|
||||||
|
|
||||||
address protocolFeeCollector = EXCHANGE.protocolFeeCollector();
|
|
||||||
if (protocolFeeCollector != address(0)) {
|
|
||||||
ETHER_TOKEN.approve(protocolFeeCollector, MAX_UINT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Purchases as much of orders' makerAssets as possible by selling as much of the ETH value sent
|
|
||||||
/// as possible, accounting for order and forwarder fees.
|
|
||||||
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
|
|
||||||
/// @param signatures Proofs that orders have been created by makers.
|
|
||||||
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
|
||||||
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
|
||||||
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
|
|
||||||
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
|
|
||||||
function marketSellOrdersWithEth(
|
|
||||||
LibOrder.Order[] memory orders,
|
|
||||||
bytes[] memory signatures,
|
|
||||||
uint256[] memory ethFeeAmounts,
|
|
||||||
address payable[] memory feeRecipients
|
|
||||||
)
|
|
||||||
public
|
|
||||||
payable
|
|
||||||
returns (
|
|
||||||
uint256 wethSpentAmount,
|
|
||||||
uint256 makerAssetAcquiredAmount
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Pay ETH affiliate fees to all feeRecipient addresses
|
|
||||||
uint256 wethRemaining = _transferEthFeesAndWrapRemaining(
|
|
||||||
ethFeeAmounts,
|
|
||||||
feeRecipients
|
|
||||||
);
|
|
||||||
// Spends up to wethRemaining to fill orders, transfers purchased assets to msg.sender,
|
|
||||||
// and pays WETH order fees.
|
|
||||||
(
|
|
||||||
wethSpentAmount,
|
|
||||||
makerAssetAcquiredAmount
|
|
||||||
) = _marketSellNoThrow(
|
|
||||||
orders,
|
|
||||||
wethRemaining,
|
|
||||||
signatures
|
|
||||||
);
|
|
||||||
|
|
||||||
// Refund remaining ETH to msg.sender.
|
|
||||||
_transferEthRefund(wethRemaining, wethSpentAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction.
|
|
||||||
/// The Forwarder may *fill* more than makerAssetBuyAmount of the makerAsset so that it can
|
|
||||||
/// pay takerFees where takerFeeAssetData == makerAssetData (i.e. percentage fees).
|
|
||||||
/// Any ETH not spent will be refunded to sender.
|
|
||||||
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
|
|
||||||
/// @param makerAssetBuyAmount Desired amount of makerAsset to purchase.
|
|
||||||
/// @param signatures Proofs that orders have been created by makers.
|
|
||||||
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
|
||||||
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
|
||||||
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
|
|
||||||
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
|
|
||||||
function marketBuyOrdersWithEth(
|
|
||||||
LibOrder.Order[] memory orders,
|
|
||||||
uint256 makerAssetBuyAmount,
|
|
||||||
bytes[] memory signatures,
|
|
||||||
uint256[] memory ethFeeAmounts,
|
|
||||||
address payable[] memory feeRecipients
|
|
||||||
)
|
|
||||||
public
|
|
||||||
payable
|
|
||||||
returns (
|
|
||||||
uint256 wethSpentAmount,
|
|
||||||
uint256 makerAssetAcquiredAmount
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Pay ETH affiliate fees to all feeRecipient addresses
|
|
||||||
uint256 wethRemaining = _transferEthFeesAndWrapRemaining(
|
|
||||||
ethFeeAmounts,
|
|
||||||
feeRecipients
|
|
||||||
);
|
|
||||||
|
|
||||||
// Attempts to fill the desired amount of makerAsset and trasnfer purchased assets to msg.sender.
|
|
||||||
(
|
|
||||||
wethSpentAmount,
|
|
||||||
makerAssetAcquiredAmount
|
|
||||||
) = _marketBuyFillOrKill(
|
|
||||||
orders,
|
|
||||||
makerAssetBuyAmount,
|
|
||||||
signatures
|
|
||||||
);
|
|
||||||
|
|
||||||
// Refund remaining ETH to msg.sender.
|
|
||||||
_transferEthRefund(wethRemaining, wethSpentAmount);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
|
|
||||||
|
|
||||||
contract IAssets {
|
|
||||||
|
|
||||||
/// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to
|
|
||||||
/// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be
|
|
||||||
/// used to withdraw assets that were accidentally sent to this contract.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of ERC20 token to withdraw.
|
|
||||||
function withdrawAsset(
|
|
||||||
bytes calldata assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Approves the respective proxy for a given asset to transfer tokens on the Forwarder contract's behalf.
|
|
||||||
/// This is necessary because an order fee denominated in the maker asset (i.e. a percentage fee) is sent by the
|
|
||||||
/// Forwarder contract to the fee recipient.
|
|
||||||
/// This method needs to be called before forwarding orders of a maker asset that hasn't
|
|
||||||
/// previously been approved.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
function approveMakerAssetProxy(
|
|
||||||
bytes calldata assetData
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
}
|
|
@ -19,12 +19,77 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "./IForwarderCore.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
import "./IAssets.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
contract IForwarder {
|
||||||
contract IForwarder is
|
|
||||||
IForwarderCore,
|
/// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to
|
||||||
IAssets
|
/// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be
|
||||||
{}
|
/// used to withdraw assets that were accidentally sent to this contract.
|
||||||
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param amount Amount of ERC20 token to withdraw.
|
||||||
|
function withdrawAsset(
|
||||||
|
bytes calldata assetData,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
|
||||||
|
/// @dev Approves the respective proxy for a given asset to transfer tokens on the Forwarder contract's behalf.
|
||||||
|
/// This is necessary because an order fee denominated in the maker asset (i.e. a percentage fee) is sent by the
|
||||||
|
/// Forwarder contract to the fee recipient.
|
||||||
|
/// This method needs to be called before forwarding orders of a maker asset that hasn't
|
||||||
|
/// previously been approved.
|
||||||
|
/// @param assetData Byte array encoded for the respective asset proxy.
|
||||||
|
function approveMakerAssetProxy(
|
||||||
|
bytes calldata assetData
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
|
||||||
|
/// @dev Purchases as much of orders' makerAssets as possible by selling as much of the ETH value sent
|
||||||
|
/// as possible, accounting for order and forwarder fees.
|
||||||
|
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
|
||||||
|
/// @param signatures Proofs that orders have been created by makers.
|
||||||
|
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
||||||
|
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
||||||
|
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
|
||||||
|
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
|
||||||
|
function marketSellOrdersWithEth(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
bytes[] memory signatures,
|
||||||
|
uint256[] memory ethFeeAmounts,
|
||||||
|
address payable[] memory feeRecipients
|
||||||
|
)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
returns (
|
||||||
|
uint256 wethSpentAmount,
|
||||||
|
uint256 makerAssetAcquiredAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction.
|
||||||
|
/// The Forwarder may *fill* more than makerAssetBuyAmount of the makerAsset so that it can
|
||||||
|
/// pay takerFees where takerFeeAssetData == makerAssetData (i.e. percentage fees).
|
||||||
|
/// Any ETH not spent will be refunded to sender.
|
||||||
|
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
|
||||||
|
/// @param makerAssetBuyAmount Desired amount of makerAsset to purchase.
|
||||||
|
/// @param signatures Proofs that orders have been created by makers.
|
||||||
|
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
||||||
|
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
||||||
|
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
|
||||||
|
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
|
||||||
|
function marketBuyOrdersWithEth(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
uint256 makerAssetBuyAmount,
|
||||||
|
bytes[] memory signatures,
|
||||||
|
uint256[] memory ethFeeAmounts,
|
||||||
|
address payable[] memory feeRecipients
|
||||||
|
)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
returns (
|
||||||
|
uint256 wethSpentAmount,
|
||||||
|
uint256 makerAssetAcquiredAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract IForwarderCore {
|
|
||||||
|
|
||||||
/// @dev Purchases as much of orders' makerAssets as possible by selling as much of the ETH value sent
|
|
||||||
/// as possible, accounting for order and forwarder fees.
|
|
||||||
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
|
|
||||||
/// @param signatures Proofs that orders have been created by makers.
|
|
||||||
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
|
||||||
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
|
||||||
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
|
|
||||||
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
|
|
||||||
function marketSellOrdersWithEth(
|
|
||||||
LibOrder.Order[] memory orders,
|
|
||||||
bytes[] memory signatures,
|
|
||||||
uint256[] memory ethFeeAmounts,
|
|
||||||
address payable[] memory feeRecipients
|
|
||||||
)
|
|
||||||
public
|
|
||||||
payable
|
|
||||||
returns (
|
|
||||||
uint256 wethSpentAmount,
|
|
||||||
uint256 makerAssetAcquiredAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
/// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction.
|
|
||||||
/// The Forwarder may *fill* more than makerAssetBuyAmount of the makerAsset so that it can
|
|
||||||
/// pay takerFees where takerFeeAssetData == makerAssetData (i.e. percentage fees).
|
|
||||||
/// Any ETH not spent will be refunded to sender.
|
|
||||||
/// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
|
|
||||||
/// @param makerAssetBuyAmount Desired amount of makerAsset to purchase.
|
|
||||||
/// @param signatures Proofs that orders have been created by makers.
|
|
||||||
/// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
|
|
||||||
/// @param feeRecipients Addresses that will receive ETH when orders are filled.
|
|
||||||
/// @return wethSpentAmount Amount of WETH spent on the given set of orders.
|
|
||||||
/// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders.
|
|
||||||
function marketBuyOrdersWithEth(
|
|
||||||
LibOrder.Order[] memory orders,
|
|
||||||
uint256 makerAssetBuyAmount,
|
|
||||||
bytes[] memory signatures,
|
|
||||||
uint256[] memory ethFeeAmounts,
|
|
||||||
address payable[] memory feeRecipients
|
|
||||||
)
|
|
||||||
public
|
|
||||||
payable
|
|
||||||
returns (
|
|
||||||
uint256 wethSpentAmount,
|
|
||||||
uint256 makerAssetAcquiredAmount
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
|
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
|
||||||
import "../interfaces/IExchangeV2.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract LibConstants {
|
|
||||||
|
|
||||||
uint256 constant internal MAX_UINT = uint256(-1);
|
|
||||||
|
|
||||||
// The v2 order id is the first 4 bytes of the ExchangeV2 order schema hash.
|
|
||||||
// bytes4(keccak256(abi.encodePacked(
|
|
||||||
// "Order(",
|
|
||||||
// "address makerAddress,",
|
|
||||||
// "address takerAddress,",
|
|
||||||
// "address feeRecipientAddress,",
|
|
||||||
// "address senderAddress,",
|
|
||||||
// "uint256 makerAssetAmount,",
|
|
||||||
// "uint256 takerAssetAmount,",
|
|
||||||
// "uint256 makerFee,",
|
|
||||||
// "uint256 takerFee,",
|
|
||||||
// "uint256 expirationTimeSeconds,",
|
|
||||||
// "uint256 salt,",
|
|
||||||
// "bytes makerAssetData,",
|
|
||||||
// "bytes takerAssetData",
|
|
||||||
// ")"
|
|
||||||
// )));
|
|
||||||
bytes4 constant public EXCHANGE_V2_ORDER_ID = 0x770501f8;
|
|
||||||
|
|
||||||
// solhint-disable var-name-mixedcase
|
|
||||||
IExchange internal EXCHANGE;
|
|
||||||
IExchangeV2 internal EXCHANGE_V2;
|
|
||||||
IEtherToken internal ETHER_TOKEN;
|
|
||||||
// solhint-enable var-name-mixedcase
|
|
||||||
|
|
||||||
constructor (
|
|
||||||
address _exchange,
|
|
||||||
address _exchangeV2,
|
|
||||||
address _weth
|
|
||||||
)
|
|
||||||
public
|
|
||||||
{
|
|
||||||
EXCHANGE = IExchange(_exchange);
|
|
||||||
EXCHANGE_V2 = IExchangeV2(_exchangeV2);
|
|
||||||
ETHER_TOKEN = IEtherToken(_weth);
|
|
||||||
}
|
|
||||||
}
|
|
@ -33,22 +33,10 @@ library LibForwarderRichErrors {
|
|||||||
bytes4 internal constant UNSUPPORTED_FEE_ERROR_SELECTOR =
|
bytes4 internal constant UNSUPPORTED_FEE_ERROR_SELECTOR =
|
||||||
0x31360af1;
|
0x31360af1;
|
||||||
|
|
||||||
// bytes4(keccak256("InsufficientEthForFeeError(uint256,uint256)"))
|
|
||||||
bytes4 internal constant INSUFFICIENT_ETH_FOR_FEE_ERROR_SELECTOR =
|
|
||||||
0xecf40fd9;
|
|
||||||
|
|
||||||
// bytes4(keccak256("OverspentWethError(uint256,uint256)"))
|
// bytes4(keccak256("OverspentWethError(uint256,uint256)"))
|
||||||
bytes4 internal constant OVERSPENT_WETH_ERROR_SELECTOR =
|
bytes4 internal constant OVERSPENT_WETH_ERROR_SELECTOR =
|
||||||
0xcdcbed5d;
|
0xcdcbed5d;
|
||||||
|
|
||||||
// bytes4(keccak256("DefaultFunctionWethContractOnlyError(address)"))
|
|
||||||
bytes4 internal constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR =
|
|
||||||
0x08b18698;
|
|
||||||
|
|
||||||
// bytes4(keccak256("EthFeeLengthMismatchError(uint256,uint256)"))
|
|
||||||
bytes4 internal constant ETH_FEE_LENGTH_MISMATCH_ERROR_SELECTOR =
|
|
||||||
0x3ecb6ceb;
|
|
||||||
|
|
||||||
// solhint-disable func-name-mixedcase
|
// solhint-disable func-name-mixedcase
|
||||||
function UnregisteredAssetProxyError()
|
function UnregisteredAssetProxyError()
|
||||||
internal
|
internal
|
||||||
@ -86,21 +74,6 @@ library LibForwarderRichErrors {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function InsufficientEthForFeeError(
|
|
||||||
uint256 ethFeeRequired,
|
|
||||||
uint256 ethAvailable
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (bytes memory)
|
|
||||||
{
|
|
||||||
return abi.encodeWithSelector(
|
|
||||||
INSUFFICIENT_ETH_FOR_FEE_ERROR_SELECTOR,
|
|
||||||
ethFeeRequired,
|
|
||||||
ethAvailable
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function OverspentWethError(
|
function OverspentWethError(
|
||||||
uint256 wethSpent,
|
uint256 wethSpent,
|
||||||
uint256 msgValue
|
uint256 msgValue
|
||||||
@ -115,32 +88,4 @@ library LibForwarderRichErrors {
|
|||||||
msgValue
|
msgValue
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DefaultFunctionWethContractOnlyError(
|
|
||||||
address senderAddress
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (bytes memory)
|
|
||||||
{
|
|
||||||
return abi.encodeWithSelector(
|
|
||||||
DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR,
|
|
||||||
senderAddress
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function EthFeeLengthMismatchError(
|
|
||||||
uint256 ethFeesLength,
|
|
||||||
uint256 feeRecipientsLength
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (bytes memory)
|
|
||||||
{
|
|
||||||
return abi.encodeWithSelector(
|
|
||||||
ETH_FEE_LENGTH_MISMATCH_ERROR_SELECTOR,
|
|
||||||
ethFeesLength,
|
|
||||||
feeRecipientsLength
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,14 +19,12 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibAssetDataTransfer.sol";
|
import "@0x/contracts-extensions/contracts/src/LibAssetDataTransfer.sol";
|
||||||
import "../src/MixinExchangeWrapper.sol";
|
import "../src/MixinExchangeWrapper.sol";
|
||||||
import "../src/libs/LibConstants.sol";
|
|
||||||
import "../src/MixinReceiver.sol";
|
import "../src/MixinReceiver.sol";
|
||||||
|
|
||||||
|
|
||||||
contract TestForwarder is
|
contract TestForwarder is
|
||||||
LibConstants,
|
|
||||||
MixinExchangeWrapper,
|
MixinExchangeWrapper,
|
||||||
MixinReceiver
|
MixinReceiver
|
||||||
{
|
{
|
||||||
@ -35,8 +33,7 @@ contract TestForwarder is
|
|||||||
// solhint-disable no-empty-blocks
|
// solhint-disable no-empty-blocks
|
||||||
constructor ()
|
constructor ()
|
||||||
public
|
public
|
||||||
LibConstants(
|
MixinExchangeWrapper(
|
||||||
address(0),
|
|
||||||
address(0),
|
address(0),
|
||||||
address(0)
|
address(0)
|
||||||
)
|
)
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "Forwarder,IExchangeV2",
|
"publicInterfaceContracts": "Forwarder,IExchangeV2",
|
||||||
"abis": "./test/generated-artifacts/@(Forwarder|IAssets|IExchangeV2|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinReceiver|MixinWeth|TestForwarder).json",
|
"abis": "./test/generated-artifacts/@(Forwarder|IExchangeV2|IForwarder|LibForwarderRichErrors|MixinExchangeWrapper|MixinReceiver|TestForwarder).json",
|
||||||
"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."
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -6,30 +6,18 @@
|
|||||||
import { ContractArtifact } from 'ethereum-types';
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
import * as Forwarder from '../test/generated-artifacts/Forwarder.json';
|
import * as Forwarder from '../test/generated-artifacts/Forwarder.json';
|
||||||
import * as IAssets from '../test/generated-artifacts/IAssets.json';
|
|
||||||
import * as IExchangeV2 from '../test/generated-artifacts/IExchangeV2.json';
|
import * as IExchangeV2 from '../test/generated-artifacts/IExchangeV2.json';
|
||||||
import * as IForwarder from '../test/generated-artifacts/IForwarder.json';
|
import * as IForwarder from '../test/generated-artifacts/IForwarder.json';
|
||||||
import * as IForwarderCore from '../test/generated-artifacts/IForwarderCore.json';
|
|
||||||
import * as LibConstants from '../test/generated-artifacts/LibConstants.json';
|
|
||||||
import * as LibForwarderRichErrors from '../test/generated-artifacts/LibForwarderRichErrors.json';
|
import * as LibForwarderRichErrors from '../test/generated-artifacts/LibForwarderRichErrors.json';
|
||||||
import * as MixinAssets from '../test/generated-artifacts/MixinAssets.json';
|
|
||||||
import * as MixinExchangeWrapper from '../test/generated-artifacts/MixinExchangeWrapper.json';
|
import * as MixinExchangeWrapper from '../test/generated-artifacts/MixinExchangeWrapper.json';
|
||||||
import * as MixinForwarderCore from '../test/generated-artifacts/MixinForwarderCore.json';
|
|
||||||
import * as MixinReceiver from '../test/generated-artifacts/MixinReceiver.json';
|
import * as MixinReceiver from '../test/generated-artifacts/MixinReceiver.json';
|
||||||
import * as MixinWeth from '../test/generated-artifacts/MixinWeth.json';
|
|
||||||
import * as TestForwarder from '../test/generated-artifacts/TestForwarder.json';
|
import * as TestForwarder from '../test/generated-artifacts/TestForwarder.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
Forwarder: Forwarder as ContractArtifact,
|
Forwarder: Forwarder as ContractArtifact,
|
||||||
MixinAssets: MixinAssets as ContractArtifact,
|
|
||||||
MixinExchangeWrapper: MixinExchangeWrapper as ContractArtifact,
|
MixinExchangeWrapper: MixinExchangeWrapper as ContractArtifact,
|
||||||
MixinForwarderCore: MixinForwarderCore as ContractArtifact,
|
|
||||||
MixinReceiver: MixinReceiver as ContractArtifact,
|
MixinReceiver: MixinReceiver as ContractArtifact,
|
||||||
MixinWeth: MixinWeth as ContractArtifact,
|
|
||||||
IAssets: IAssets as ContractArtifact,
|
|
||||||
IExchangeV2: IExchangeV2 as ContractArtifact,
|
IExchangeV2: IExchangeV2 as ContractArtifact,
|
||||||
IForwarder: IForwarder as ContractArtifact,
|
IForwarder: IForwarder as ContractArtifact,
|
||||||
IForwarderCore: IForwarderCore as ContractArtifact,
|
|
||||||
LibConstants: LibConstants as ContractArtifact,
|
|
||||||
LibForwarderRichErrors: LibForwarderRichErrors as ContractArtifact,
|
LibForwarderRichErrors: LibForwarderRichErrors as ContractArtifact,
|
||||||
TestForwarder: TestForwarder as ContractArtifact,
|
TestForwarder: TestForwarder as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
@ -4,15 +4,9 @@
|
|||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
export * from '../test/generated-wrappers/forwarder';
|
export * from '../test/generated-wrappers/forwarder';
|
||||||
export * from '../test/generated-wrappers/i_assets';
|
|
||||||
export * from '../test/generated-wrappers/i_exchange_v2';
|
export * from '../test/generated-wrappers/i_exchange_v2';
|
||||||
export * from '../test/generated-wrappers/i_forwarder';
|
export * from '../test/generated-wrappers/i_forwarder';
|
||||||
export * from '../test/generated-wrappers/i_forwarder_core';
|
|
||||||
export * from '../test/generated-wrappers/lib_constants';
|
|
||||||
export * from '../test/generated-wrappers/lib_forwarder_rich_errors';
|
export * from '../test/generated-wrappers/lib_forwarder_rich_errors';
|
||||||
export * from '../test/generated-wrappers/mixin_assets';
|
|
||||||
export * from '../test/generated-wrappers/mixin_exchange_wrapper';
|
export * from '../test/generated-wrappers/mixin_exchange_wrapper';
|
||||||
export * from '../test/generated-wrappers/mixin_forwarder_core';
|
|
||||||
export * from '../test/generated-wrappers/mixin_receiver';
|
export * from '../test/generated-wrappers/mixin_receiver';
|
||||||
export * from '../test/generated-wrappers/mixin_weth';
|
|
||||||
export * from '../test/generated-wrappers/test_forwarder';
|
export * from '../test/generated-wrappers/test_forwarder';
|
||||||
|
@ -6,17 +6,11 @@
|
|||||||
"generated-artifacts/Forwarder.json",
|
"generated-artifacts/Forwarder.json",
|
||||||
"generated-artifacts/IExchangeV2.json",
|
"generated-artifacts/IExchangeV2.json",
|
||||||
"test/generated-artifacts/Forwarder.json",
|
"test/generated-artifacts/Forwarder.json",
|
||||||
"test/generated-artifacts/IAssets.json",
|
|
||||||
"test/generated-artifacts/IExchangeV2.json",
|
"test/generated-artifacts/IExchangeV2.json",
|
||||||
"test/generated-artifacts/IForwarder.json",
|
"test/generated-artifacts/IForwarder.json",
|
||||||
"test/generated-artifacts/IForwarderCore.json",
|
|
||||||
"test/generated-artifacts/LibConstants.json",
|
|
||||||
"test/generated-artifacts/LibForwarderRichErrors.json",
|
"test/generated-artifacts/LibForwarderRichErrors.json",
|
||||||
"test/generated-artifacts/MixinAssets.json",
|
|
||||||
"test/generated-artifacts/MixinExchangeWrapper.json",
|
"test/generated-artifacts/MixinExchangeWrapper.json",
|
||||||
"test/generated-artifacts/MixinForwarderCore.json",
|
|
||||||
"test/generated-artifacts/MixinReceiver.json",
|
"test/generated-artifacts/MixinReceiver.json",
|
||||||
"test/generated-artifacts/MixinWeth.json",
|
|
||||||
"test/generated-artifacts/TestForwarder.json"
|
"test/generated-artifacts/TestForwarder.json"
|
||||||
],
|
],
|
||||||
"exclude": ["./deploy/solc/solc_bin"]
|
"exclude": ["./deploy/solc/solc_bin"]
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "IWallet,LibEIP712ExchangeDomain,LibExchangeRichErrors,LibMath,LibMathRichErrors,LibOrder,LibZeroExTransaction",
|
"publicInterfaceContracts": "IWallet,LibEIP712ExchangeDomain,LibExchangeRichErrors,LibMath,LibMathRichErrors,LibOrder,LibZeroExTransaction",
|
||||||
"abis": "./test/generated-artifacts/@(IWallet|LibAssetDataTransfer|LibAssetDataTransferRichErrors|LibEIP712ExchangeDomain|LibExchangeRichErrors|LibFillResults|LibMath|LibMathRichErrors|LibOrder|LibZeroExTransaction|TestLibEIP712ExchangeDomain|TestLibFillResults|TestLibMath|TestLibOrder|TestLibZeroExTransaction).json",
|
"abis": "./test/generated-artifacts/@(IWallet|LibEIP712ExchangeDomain|LibExchangeRichErrors|LibFillResults|LibMath|LibMathRichErrors|LibOrder|LibZeroExTransaction|TestLibEIP712ExchangeDomain|TestLibFillResults|TestLibMath|TestLibOrder|TestLibZeroExTransaction).json",
|
||||||
"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."
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -8,7 +8,7 @@ export {
|
|||||||
LibOrderContract,
|
LibOrderContract,
|
||||||
LibZeroExTransactionContract,
|
LibZeroExTransactionContract,
|
||||||
} from './wrappers';
|
} from './wrappers';
|
||||||
export { LibAssetDataTransferRevertErrors, LibMathRevertErrors } from '@0x/utils';
|
export { LibMathRevertErrors } from '@0x/utils';
|
||||||
|
|
||||||
import * as ReferenceFunctionsToExport from './reference_functions';
|
import * as ReferenceFunctionsToExport from './reference_functions';
|
||||||
export import ReferenceFunctions = ReferenceFunctionsToExport;
|
export import ReferenceFunctions = ReferenceFunctionsToExport;
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
import { ContractArtifact } from 'ethereum-types';
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
import * as IWallet from '../test/generated-artifacts/IWallet.json';
|
import * as IWallet from '../test/generated-artifacts/IWallet.json';
|
||||||
import * as LibAssetDataTransfer from '../test/generated-artifacts/LibAssetDataTransfer.json';
|
|
||||||
import * as LibAssetDataTransferRichErrors from '../test/generated-artifacts/LibAssetDataTransferRichErrors.json';
|
|
||||||
import * as LibEIP712ExchangeDomain from '../test/generated-artifacts/LibEIP712ExchangeDomain.json';
|
import * as LibEIP712ExchangeDomain from '../test/generated-artifacts/LibEIP712ExchangeDomain.json';
|
||||||
import * as LibExchangeRichErrors from '../test/generated-artifacts/LibExchangeRichErrors.json';
|
import * as LibExchangeRichErrors from '../test/generated-artifacts/LibExchangeRichErrors.json';
|
||||||
import * as LibFillResults from '../test/generated-artifacts/LibFillResults.json';
|
import * as LibFillResults from '../test/generated-artifacts/LibFillResults.json';
|
||||||
@ -22,8 +20,6 @@ import * as TestLibOrder from '../test/generated-artifacts/TestLibOrder.json';
|
|||||||
import * as TestLibZeroExTransaction from '../test/generated-artifacts/TestLibZeroExTransaction.json';
|
import * as TestLibZeroExTransaction from '../test/generated-artifacts/TestLibZeroExTransaction.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
IWallet: IWallet as ContractArtifact,
|
IWallet: IWallet as ContractArtifact,
|
||||||
LibAssetDataTransfer: LibAssetDataTransfer as ContractArtifact,
|
|
||||||
LibAssetDataTransferRichErrors: LibAssetDataTransferRichErrors as ContractArtifact,
|
|
||||||
LibEIP712ExchangeDomain: LibEIP712ExchangeDomain as ContractArtifact,
|
LibEIP712ExchangeDomain: LibEIP712ExchangeDomain as ContractArtifact,
|
||||||
LibExchangeRichErrors: LibExchangeRichErrors as ContractArtifact,
|
LibExchangeRichErrors: LibExchangeRichErrors as ContractArtifact,
|
||||||
LibFillResults: LibFillResults as ContractArtifact,
|
LibFillResults: LibFillResults as ContractArtifact,
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
export * from '../test/generated-wrappers/i_wallet';
|
export * from '../test/generated-wrappers/i_wallet';
|
||||||
export * from '../test/generated-wrappers/lib_asset_data_transfer';
|
|
||||||
export * from '../test/generated-wrappers/lib_asset_data_transfer_rich_errors';
|
|
||||||
export * from '../test/generated-wrappers/lib_e_i_p712_exchange_domain';
|
export * from '../test/generated-wrappers/lib_e_i_p712_exchange_domain';
|
||||||
export * from '../test/generated-wrappers/lib_exchange_rich_errors';
|
export * from '../test/generated-wrappers/lib_exchange_rich_errors';
|
||||||
export * from '../test/generated-wrappers/lib_fill_results';
|
export * from '../test/generated-wrappers/lib_fill_results';
|
||||||
|
@ -11,8 +11,6 @@
|
|||||||
"generated-artifacts/LibOrder.json",
|
"generated-artifacts/LibOrder.json",
|
||||||
"generated-artifacts/LibZeroExTransaction.json",
|
"generated-artifacts/LibZeroExTransaction.json",
|
||||||
"test/generated-artifacts/IWallet.json",
|
"test/generated-artifacts/IWallet.json",
|
||||||
"test/generated-artifacts/LibAssetDataTransfer.json",
|
|
||||||
"test/generated-artifacts/LibAssetDataTransferRichErrors.json",
|
|
||||||
"test/generated-artifacts/LibEIP712ExchangeDomain.json",
|
"test/generated-artifacts/LibEIP712ExchangeDomain.json",
|
||||||
"test/generated-artifacts/LibExchangeRichErrors.json",
|
"test/generated-artifacts/LibExchangeRichErrors.json",
|
||||||
"test/generated-artifacts/LibFillResults.json",
|
"test/generated-artifacts/LibFillResults.json",
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "6.0.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "New year, new me: remove everything, add MixinWethUtils and LibAssetDataTransfer",
|
||||||
|
"pr": 2455
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1580811564,
|
"timestamp": 1580811564,
|
||||||
"version": "5.1.4",
|
"version": "5.1.4",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
## Extensions
|
## Extensions
|
||||||
|
|
||||||
This package implements various extensions to the 0x protocol. Extension contracts can add various rules around how orders are settled while still getting the interoperability and security benefits of using the underlying 0x protocol contracts. Addresses of the deployed contracts can be found in this 0x [guide](https://0x.org/docs/guides/0x-cheat-sheet) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
This package includes utility smart contracts that may be useful for developing on top of the core 0x Exchange. For example, `MixinWethUtils` includes functions to pay affiliate fees in ETH, wrap ETH to be used for 0x trades and protocol fees, and unwrap WETH to refund the sender.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ npm install @0x/contracts-extensions --save
|
|||||||
|
|
||||||
## Bug bounty
|
## Bug bounty
|
||||||
|
|
||||||
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program).
|
A bug bounty for the 3.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
|
||||||
import "./interfaces/IThresholdAsset.sol";
|
|
||||||
import "./MixinBalanceThresholdFilterCore.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract BalanceThresholdFilter is
|
|
||||||
MixinBalanceThresholdFilterCore
|
|
||||||
{
|
|
||||||
|
|
||||||
/// @dev Constructs BalanceThresholdFilter.
|
|
||||||
/// @param exchange Address of 0x exchange.
|
|
||||||
/// @param thresholdAsset The asset that must be held by makers/takers.
|
|
||||||
/// @param balanceThreshold The minimum balance of `thresholdAsset` that must be held by makers/takers.
|
|
||||||
constructor (
|
|
||||||
address exchange,
|
|
||||||
address thresholdAsset,
|
|
||||||
uint256 balanceThreshold
|
|
||||||
)
|
|
||||||
public
|
|
||||||
{
|
|
||||||
EXCHANGE = IExchange(exchange);
|
|
||||||
THRESHOLD_ASSET = IThresholdAsset(thresholdAsset);
|
|
||||||
BALANCE_THRESHOLD = balanceThreshold;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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/LICENSE2.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.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
|
||||||
import "./MixinExchangeCalldata.sol";
|
|
||||||
import "./interfaces/IBalanceThresholdFilterCore.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinBalanceThresholdFilterCore is
|
|
||||||
IBalanceThresholdFilterCore,
|
|
||||||
MixinExchangeCalldata
|
|
||||||
{
|
|
||||||
|
|
||||||
/// @dev Executes an Exchange transaction iff the maker and taker meet
|
|
||||||
/// the hold at least `BALANCE_THRESHOLD` of the asset `THRESHOLD_ASSET` OR
|
|
||||||
/// the exchange function is a cancellation.
|
|
||||||
/// Supported Exchange functions:
|
|
||||||
/// batchFillOrders
|
|
||||||
/// batchFillOrdersNoThrow
|
|
||||||
/// batchFillOrKillOrders
|
|
||||||
/// fillOrder
|
|
||||||
/// fillOrderNoThrow
|
|
||||||
/// fillOrKillOrder
|
|
||||||
/// marketBuyOrders
|
|
||||||
/// marketBuyOrdersNoThrow
|
|
||||||
/// marketSellOrders
|
|
||||||
/// marketSellOrdersNoThrow
|
|
||||||
/// matchOrders
|
|
||||||
/// cancelOrder
|
|
||||||
/// batchCancelOrders
|
|
||||||
/// cancelOrdersUpTo
|
|
||||||
/// Trying to call any other exchange function will throw.
|
|
||||||
/// @param salt Arbitrary number to ensure uniqueness of transaction hash.
|
|
||||||
/// @param signerAddress Address of transaction signer.
|
|
||||||
/// @param signedExchangeTransaction AbiV2 encoded calldata.
|
|
||||||
/// @param signature Proof of signer transaction by signer.
|
|
||||||
function executeTransaction(
|
|
||||||
uint256 salt,
|
|
||||||
address signerAddress,
|
|
||||||
bytes calldata signedExchangeTransaction,
|
|
||||||
bytes calldata signature
|
|
||||||
)
|
|
||||||
external
|
|
||||||
{
|
|
||||||
// Get accounts whose balances must be validated
|
|
||||||
address[] memory addressesToValidate = _getAddressesToValidate(signerAddress);
|
|
||||||
|
|
||||||
// Validate account balances
|
|
||||||
uint256 balanceThreshold = BALANCE_THRESHOLD;
|
|
||||||
IThresholdAsset thresholdAsset = THRESHOLD_ASSET;
|
|
||||||
for (uint256 i = 0; i < addressesToValidate.length; ++i) {
|
|
||||||
uint256 addressBalance = thresholdAsset.balanceOf(addressesToValidate[i]);
|
|
||||||
require(
|
|
||||||
addressBalance >= balanceThreshold,
|
|
||||||
"AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
emit ValidatedAddresses(addressesToValidate);
|
|
||||||
|
|
||||||
LibZeroExTransaction.ZeroExTransaction memory transaction = LibZeroExTransaction.ZeroExTransaction({
|
|
||||||
salt: salt,
|
|
||||||
data: signedExchangeTransaction,
|
|
||||||
signerAddress: signerAddress
|
|
||||||
});
|
|
||||||
|
|
||||||
// All addresses are valid. Execute exchange function.
|
|
||||||
EXCHANGE.executeTransaction(transaction, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Constructs an array of addresses to be validated.
|
|
||||||
/// Addresses depend on which Exchange function is to be called
|
|
||||||
/// (defined by `signedExchangeTransaction` above).
|
|
||||||
/// @param signerAddress Address of transaction signer.
|
|
||||||
/// @return addressesToValidate Array of addresses to validate.
|
|
||||||
function _getAddressesToValidate(address signerAddress)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (address[] memory addressesToValidate)
|
|
||||||
{
|
|
||||||
bytes4 exchangeFunctionSelector = bytes4(_exchangeCalldataload(0));
|
|
||||||
// solhint-disable expression-indent
|
|
||||||
if (
|
|
||||||
exchangeFunctionSelector == IExchange(address(0)).batchFillOrders.selector ||
|
|
||||||
exchangeFunctionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector ||
|
|
||||||
exchangeFunctionSelector == IExchange(address(0)).batchFillOrKillOrders.selector ||
|
|
||||||
exchangeFunctionSelector == IExchange(address(0)).marketBuyOrders.selector ||
|
|
||||||
exchangeFunctionSelector == IExchange(address(0)).marketBuyOrdersNoThrow.selector ||
|
|
||||||
exchangeFunctionSelector == IExchange(address(0)).marketSellOrders.selector ||
|
|
||||||
exchangeFunctionSelector == IExchange(address(0)).marketSellOrdersNoThrow.selector
|
|
||||||
) {
|
|
||||||
addressesToValidate = _loadMakerAddressesFromOrderArray(0);
|
|
||||||
addressesToValidate = addressesToValidate.append(signerAddress);
|
|
||||||
} else if (
|
|
||||||
exchangeFunctionSelector == IExchange(address(0)).fillOrder.selector ||
|
|
||||||
exchangeFunctionSelector == IExchange(address(0)).fillOrderNoThrow.selector ||
|
|
||||||
exchangeFunctionSelector == IExchange(address(0)).fillOrKillOrder.selector
|
|
||||||
) {
|
|
||||||
address makerAddress = _loadMakerAddressFromOrder(0);
|
|
||||||
addressesToValidate = addressesToValidate.append(makerAddress);
|
|
||||||
addressesToValidate = addressesToValidate.append(signerAddress);
|
|
||||||
} else if (exchangeFunctionSelector == IExchange(address(0)).matchOrders.selector) {
|
|
||||||
address leftMakerAddress = _loadMakerAddressFromOrder(0);
|
|
||||||
addressesToValidate = addressesToValidate.append(leftMakerAddress);
|
|
||||||
address rightMakerAddress = _loadMakerAddressFromOrder(1);
|
|
||||||
addressesToValidate = addressesToValidate.append(rightMakerAddress);
|
|
||||||
addressesToValidate = addressesToValidate.append(signerAddress);
|
|
||||||
} else if (
|
|
||||||
exchangeFunctionSelector != IExchange(address(0)).cancelOrder.selector &&
|
|
||||||
exchangeFunctionSelector != IExchange(address(0)).batchCancelOrders.selector &&
|
|
||||||
exchangeFunctionSelector != IExchange(address(0)).cancelOrdersUpTo.selector
|
|
||||||
) {
|
|
||||||
revert("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR");
|
|
||||||
}
|
|
||||||
// solhint-enable expression-indent
|
|
||||||
return addressesToValidate;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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/LICENSE2.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.5.9;
|
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinExchangeCalldata {
|
|
||||||
|
|
||||||
using LibAddressArray for address[];
|
|
||||||
|
|
||||||
/// @dev Emulates the `calldataload` opcode on the embedded Exchange calldata,
|
|
||||||
/// which is accessed through `signedExchangeTransaction`.
|
|
||||||
/// @param offset Offset into the Exchange calldata.
|
|
||||||
/// @return value Corresponding 32 byte value stored at `offset`.
|
|
||||||
function _exchangeCalldataload(uint256 offset)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (bytes32 value)
|
|
||||||
{
|
|
||||||
assembly {
|
|
||||||
// Pointer to exchange transaction
|
|
||||||
// 0x04 for calldata selector
|
|
||||||
// 0x40 to access `signedExchangeTransaction`, which is the third parameter
|
|
||||||
let exchangeTxPtr := calldataload(0x44)
|
|
||||||
|
|
||||||
// Offset into Exchange calldata
|
|
||||||
// We compute this by adding 0x24 to the `exchangeTxPtr` computed above.
|
|
||||||
// 0x04 for calldata selector
|
|
||||||
// 0x20 for length field of `signedExchangeTransaction`
|
|
||||||
let exchangeCalldataOffset := add(exchangeTxPtr, add(0x24, offset))
|
|
||||||
value := calldataload(exchangeCalldataOffset)
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Convenience function that skips the 4 byte selector when loading
|
|
||||||
/// from the embedded Exchange calldata.
|
|
||||||
/// @param offset Offset into the Exchange calldata (minus the 4 byte selector)
|
|
||||||
/// @return value Corresponding 32 byte value stored at `offset` + 4.
|
|
||||||
function _loadExchangeData(uint256 offset)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (bytes32 value)
|
|
||||||
{
|
|
||||||
value = _exchangeCalldataload(offset + 4);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Extracts the maker address from an order stored in the Exchange calldata
|
|
||||||
/// (which is embedded in `signedExchangeTransaction`).
|
|
||||||
/// @param orderParamIndex Index of the order in the Exchange function's signature.
|
|
||||||
/// @return makerAddress The extracted maker address.
|
|
||||||
function _loadMakerAddressFromOrder(uint256 orderParamIndex)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (address makerAddress)
|
|
||||||
{
|
|
||||||
uint256 orderOffsetInBytes = orderParamIndex * 32;
|
|
||||||
uint256 orderPtr = uint256(_loadExchangeData(orderOffsetInBytes));
|
|
||||||
makerAddress = address(uint256(_loadExchangeData(orderPtr)));
|
|
||||||
return makerAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Extracts the maker addresses from an array of orders stored in the Exchange calldata
|
|
||||||
/// (which is embedded in `signedExchangeTransaction`).
|
|
||||||
/// @param orderArrayParamIndex Index of the order array in the Exchange function's signature
|
|
||||||
/// @return makerAddresses The extracted maker addresses.
|
|
||||||
function _loadMakerAddressesFromOrderArray(uint256 orderArrayParamIndex)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (address[] memory makerAddresses)
|
|
||||||
{
|
|
||||||
uint256 orderArrayOffsetInBytes = orderArrayParamIndex * 32;
|
|
||||||
uint256 orderArrayPtr = uint256(_loadExchangeData(orderArrayOffsetInBytes));
|
|
||||||
uint256 orderArrayLength = uint256(_loadExchangeData(orderArrayPtr));
|
|
||||||
uint256 orderArrayLengthInBytes = orderArrayLength * 32;
|
|
||||||
uint256 orderArrayElementPtr = orderArrayPtr + 32;
|
|
||||||
uint256 orderArrayElementEndPtr = orderArrayElementPtr + orderArrayLengthInBytes;
|
|
||||||
for (uint orderPtrOffset = orderArrayElementPtr; orderPtrOffset < orderArrayElementEndPtr; orderPtrOffset += 32) {
|
|
||||||
uint256 orderPtr = uint256(_loadExchangeData(orderPtrOffset));
|
|
||||||
address makerAddress = address(uint256(_loadExchangeData(orderPtr + orderArrayElementPtr)));
|
|
||||||
makerAddresses = makerAddresses.append(makerAddress);
|
|
||||||
}
|
|
||||||
return makerAddresses;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
|
|
||||||
|
|
||||||
contract IBalanceThresholdFilterCore {
|
|
||||||
|
|
||||||
/// @dev Executes an Exchange transaction iff the maker and taker meet
|
|
||||||
/// the hold at least `BALANCE_THRESHOLD` of the asset `THRESHOLD_ASSET` OR
|
|
||||||
/// the exchange function is a cancellation.
|
|
||||||
/// Supported Exchange functions:
|
|
||||||
/// - batchFillOrders
|
|
||||||
/// - batchFillOrdersNoThrow
|
|
||||||
/// - batchFillOrKillOrders
|
|
||||||
/// - fillOrder
|
|
||||||
/// - fillOrderNoThrow
|
|
||||||
/// - fillOrKillOrder
|
|
||||||
/// - marketBuyOrders
|
|
||||||
/// - marketBuyOrdersNoThrow
|
|
||||||
/// - marketSellOrders
|
|
||||||
/// - marketSellOrdersNoThrow
|
|
||||||
/// - matchOrders
|
|
||||||
/// - cancelOrder
|
|
||||||
/// - batchCancelOrders
|
|
||||||
/// - cancelOrdersUpTo
|
|
||||||
/// Trying to call any other exchange function will throw.
|
|
||||||
/// @param salt Arbitrary number to ensure uniqueness of transaction hash.
|
|
||||||
/// @param signerAddress Address of transaction signer.
|
|
||||||
/// @param signedExchangeTransaction AbiV2 encoded calldata.
|
|
||||||
/// @param signature Proof of signer transaction by signer.
|
|
||||||
function executeTransaction(
|
|
||||||
uint256 salt,
|
|
||||||
address signerAddress,
|
|
||||||
bytes calldata signedExchangeTransaction,
|
|
||||||
bytes calldata signature
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
|
|
||||||
|
|
||||||
contract IThresholdAsset {
|
|
||||||
|
|
||||||
/// @param _owner The address from which the balance will be retrieved
|
|
||||||
/// @return Balance of owner
|
|
||||||
function balanceOf(address _owner)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256);
|
|
||||||
|
|
||||||
}
|
|
@ -1,199 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract DutchAuction {
|
|
||||||
|
|
||||||
using LibBytes for bytes;
|
|
||||||
using LibSafeMath for uint256;
|
|
||||||
|
|
||||||
// solhint-disable var-name-mixedcase
|
|
||||||
IExchange internal EXCHANGE;
|
|
||||||
|
|
||||||
struct AuctionDetails {
|
|
||||||
uint256 beginTimeSeconds; // Auction begin unix timestamp: sellOrder.makerAssetData
|
|
||||||
uint256 endTimeSeconds; // Auction end unix timestamp: sellOrder.expiryTimeSeconds
|
|
||||||
uint256 beginAmount; // Auction begin amount: sellOrder.makerAssetData
|
|
||||||
uint256 endAmount; // Auction end amount: sellOrder.takerAssetAmount
|
|
||||||
uint256 currentAmount; // Calculated amount given block.timestamp
|
|
||||||
uint256 currentTimeSeconds; // block.timestamp
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor (address _exchange)
|
|
||||||
public
|
|
||||||
{
|
|
||||||
EXCHANGE = IExchange(_exchange);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Matches the buy and sell orders at an amount given the following: the current block time, the auction
|
|
||||||
/// start time and the auction begin amount. The sell order is a an order at the lowest amount
|
|
||||||
/// at the end of the auction. Excess from the match is transferred to the seller.
|
|
||||||
/// Over time the price moves from beginAmount to endAmount given the current block.timestamp.
|
|
||||||
/// sellOrder.expiryTimeSeconds is the end time of the auction.
|
|
||||||
/// sellOrder.takerAssetAmount is the end amount of the auction (lowest possible amount).
|
|
||||||
/// sellOrder.makerAssetData is the ABI encoded Asset Proxy data with the following data appended
|
|
||||||
/// buyOrder.makerAssetData is the buyers bid on the auction, must meet the amount for the current block timestamp
|
|
||||||
/// (uint256 beginTimeSeconds, uint256 beginAmount).
|
|
||||||
/// This function reverts in the following scenarios:
|
|
||||||
/// * Auction has not started (auctionDetails.currentTimeSeconds < auctionDetails.beginTimeSeconds)
|
|
||||||
/// * Auction has expired (auctionDetails.endTimeSeconds < auctionDetails.currentTimeSeconds)
|
|
||||||
/// * Amount is invalid: Buy order amount is too low (buyOrder.makerAssetAmount < auctionDetails.currentAmount)
|
|
||||||
/// * Amount is invalid: Invalid begin amount (auctionDetails.beginAmount > auctionDetails.endAmount)
|
|
||||||
/// * Any failure in the 0x Match Orders
|
|
||||||
/// @param buyOrder The Buyer's order. This order is for the current expected price of the auction.
|
|
||||||
/// @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction).
|
|
||||||
/// @param buySignature Proof that order was created by the buyer.
|
|
||||||
/// @param sellSignature Proof that order was created by the seller.
|
|
||||||
/// @return matchedFillResults amounts filled and fees paid by maker and taker of matched orders.
|
|
||||||
function matchOrders(
|
|
||||||
LibOrder.Order memory buyOrder,
|
|
||||||
LibOrder.Order memory sellOrder,
|
|
||||||
bytes memory buySignature,
|
|
||||||
bytes memory sellSignature
|
|
||||||
)
|
|
||||||
public
|
|
||||||
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
|
|
||||||
{
|
|
||||||
AuctionDetails memory auctionDetails = getAuctionDetails(sellOrder);
|
|
||||||
// Ensure the auction has not yet started
|
|
||||||
require(
|
|
||||||
auctionDetails.currentTimeSeconds >= auctionDetails.beginTimeSeconds,
|
|
||||||
"AUCTION_NOT_STARTED"
|
|
||||||
);
|
|
||||||
// Ensure the auction has not expired. This will fail later in 0x but we can save gas by failing early
|
|
||||||
require(
|
|
||||||
sellOrder.expirationTimeSeconds > auctionDetails.currentTimeSeconds,
|
|
||||||
"AUCTION_EXPIRED"
|
|
||||||
);
|
|
||||||
// Validate the buyer amount is greater than the current auction amount
|
|
||||||
require(
|
|
||||||
buyOrder.makerAssetAmount >= auctionDetails.currentAmount,
|
|
||||||
"INVALID_AMOUNT"
|
|
||||||
);
|
|
||||||
// Match orders, maximally filling `buyOrder`
|
|
||||||
matchedFillResults = EXCHANGE.matchOrders(
|
|
||||||
buyOrder,
|
|
||||||
sellOrder,
|
|
||||||
buySignature,
|
|
||||||
sellSignature
|
|
||||||
);
|
|
||||||
// The difference in sellOrder.takerAssetAmount and current amount is given as spread to the matcher
|
|
||||||
// This may include additional spread from the buyOrder.makerAssetAmount and the currentAmount.
|
|
||||||
// e.g currentAmount is 30, sellOrder.takerAssetAmount is 10 and buyOrder.makerAssetamount is 40.
|
|
||||||
// 10 (40-30) is returned to the buyer, 20 (30-10) sent to the seller and 10 has previously
|
|
||||||
// been transferred to the seller during matchOrders
|
|
||||||
uint256 leftMakerAssetSpreadAmount = matchedFillResults.leftMakerAssetSpreadAmount;
|
|
||||||
if (leftMakerAssetSpreadAmount > 0) {
|
|
||||||
// ERC20 Asset data itself is encoded as follows:
|
|
||||||
//
|
|
||||||
// | Area | Offset | Length | Contents |
|
|
||||||
// |----------|--------|---------|-------------------------------------|
|
|
||||||
// | Header | 0 | 4 | function selector |
|
|
||||||
// | Params | | 1 * 32 | function parameters: |
|
|
||||||
// | | 4 | 12 | 1. token address padding |
|
|
||||||
// | | 16 | 20 | 2. token address |
|
|
||||||
bytes memory assetData = sellOrder.takerAssetData;
|
|
||||||
address token = assetData.readAddress(16);
|
|
||||||
// Calculate the excess from the buy order. This can occur if the buyer sends in a higher
|
|
||||||
// amount than the calculated current amount
|
|
||||||
uint256 buyerExcessAmount = buyOrder.makerAssetAmount.safeSub(auctionDetails.currentAmount);
|
|
||||||
uint256 sellerExcessAmount = leftMakerAssetSpreadAmount.safeSub(buyerExcessAmount);
|
|
||||||
// Return the difference between auctionDetails.currentAmount and sellOrder.takerAssetAmount
|
|
||||||
// to the seller
|
|
||||||
if (sellerExcessAmount > 0) {
|
|
||||||
IERC20Token(token).transfer(sellOrder.makerAddress, sellerExcessAmount);
|
|
||||||
}
|
|
||||||
// Return the difference between buyOrder.makerAssetAmount and auctionDetails.currentAmount
|
|
||||||
// to the buyer
|
|
||||||
if (buyerExcessAmount > 0) {
|
|
||||||
IERC20Token(token).transfer(buyOrder.makerAddress, buyerExcessAmount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matchedFillResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Calculates the Auction Details for the given order
|
|
||||||
/// @param order The sell order
|
|
||||||
/// @return AuctionDetails
|
|
||||||
function getAuctionDetails(LibOrder.Order memory order)
|
|
||||||
public
|
|
||||||
returns (AuctionDetails memory auctionDetails)
|
|
||||||
{
|
|
||||||
uint256 makerAssetDataLength = order.makerAssetData.length;
|
|
||||||
// It is unknown the encoded data of makerAssetData, we assume the last 64 bytes
|
|
||||||
// are the Auction Details encoding.
|
|
||||||
// Auction Details is encoded as follows:
|
|
||||||
//
|
|
||||||
// | Area | Offset | Length | Contents |
|
|
||||||
// |----------|--------|---------|-------------------------------------|
|
|
||||||
// | Params | | 2 * 32 | parameters: |
|
|
||||||
// | | -64 | 32 | 1. auction begin unix timestamp |
|
|
||||||
// | | -32 | 32 | 2. auction begin begin amount |
|
|
||||||
// ERC20 asset data length is 4+32, 64 for auction details results in min length 100
|
|
||||||
require(
|
|
||||||
makerAssetDataLength >= 100,
|
|
||||||
"INVALID_ASSET_DATA"
|
|
||||||
);
|
|
||||||
uint256 auctionBeginTimeSeconds = order.makerAssetData.readUint256(makerAssetDataLength - 64);
|
|
||||||
uint256 auctionBeginAmount = order.makerAssetData.readUint256(makerAssetDataLength - 32);
|
|
||||||
// Ensure the auction has a valid begin time
|
|
||||||
require(
|
|
||||||
order.expirationTimeSeconds > auctionBeginTimeSeconds,
|
|
||||||
"INVALID_BEGIN_TIME"
|
|
||||||
);
|
|
||||||
uint256 auctionDurationSeconds = order.expirationTimeSeconds-auctionBeginTimeSeconds;
|
|
||||||
// Ensure the auction goes from high to low
|
|
||||||
uint256 minAmount = order.takerAssetAmount;
|
|
||||||
require(
|
|
||||||
auctionBeginAmount > minAmount,
|
|
||||||
"INVALID_AMOUNT"
|
|
||||||
);
|
|
||||||
uint256 amountDelta = auctionBeginAmount-minAmount;
|
|
||||||
// solhint-disable-next-line not-rely-on-time
|
|
||||||
uint256 timestamp = block.timestamp;
|
|
||||||
auctionDetails.beginTimeSeconds = auctionBeginTimeSeconds;
|
|
||||||
auctionDetails.endTimeSeconds = order.expirationTimeSeconds;
|
|
||||||
auctionDetails.beginAmount = auctionBeginAmount;
|
|
||||||
auctionDetails.endAmount = minAmount;
|
|
||||||
auctionDetails.currentTimeSeconds = timestamp;
|
|
||||||
|
|
||||||
uint256 remainingDurationSeconds = order.expirationTimeSeconds-timestamp;
|
|
||||||
if (timestamp < auctionBeginTimeSeconds) {
|
|
||||||
// If the auction has not yet begun the current amount is the auctionBeginAmount
|
|
||||||
auctionDetails.currentAmount = auctionBeginAmount;
|
|
||||||
} else if (timestamp >= order.expirationTimeSeconds) {
|
|
||||||
// If the auction has ended the current amount is the minAmount.
|
|
||||||
// Auction end time is guaranteed by 0x Exchange due to the order expiration
|
|
||||||
auctionDetails.currentAmount = minAmount;
|
|
||||||
} else {
|
|
||||||
auctionDetails.currentAmount = minAmount.safeAdd(
|
|
||||||
remainingDurationSeconds.safeMul(amountDelta).safeDiv(auctionDurationSeconds)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return auctionDetails;
|
|
||||||
}
|
|
||||||
}
|
|
@ -26,7 +26,7 @@ import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
|||||||
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
||||||
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
||||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
||||||
import "./LibAssetDataTransferRichErrors.sol";
|
import "./rich-errors/LibAssetDataTransferRichErrors.sol";
|
||||||
|
|
||||||
|
|
||||||
library LibAssetDataTransfer {
|
library LibAssetDataTransfer {
|
@ -12,30 +12,57 @@
|
|||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations \under the License.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
import "./libs/LibConstants.sol";
|
import "./rich-errors/LibWethUtilsRichErrors.sol";
|
||||||
import "./libs/LibForwarderRichErrors.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinWeth is
|
contract MixinWethUtils {
|
||||||
LibConstants
|
|
||||||
{
|
uint256 constant internal MAX_UINT256 = uint256(-1);
|
||||||
|
|
||||||
|
// solhint-disable var-name-mixedcase
|
||||||
|
IEtherToken internal WETH;
|
||||||
|
// solhint-enable var-name-mixedcase
|
||||||
|
|
||||||
using LibSafeMath for uint256;
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
address exchange,
|
||||||
|
address weth
|
||||||
|
)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
WETH = IEtherToken(weth);
|
||||||
|
|
||||||
|
address proxyAddress = IExchange(exchange).getAssetProxy(IAssetData(address(0)).ERC20Token.selector);
|
||||||
|
if (proxyAddress == address(0)) {
|
||||||
|
LibRichErrors.rrevert(LibWethUtilsRichErrors.UnregisteredAssetProxyError());
|
||||||
|
}
|
||||||
|
WETH.approve(proxyAddress, MAX_UINT256);
|
||||||
|
|
||||||
|
address protocolFeeCollector = IExchange(exchange).protocolFeeCollector();
|
||||||
|
if (protocolFeeCollector != address(0)) {
|
||||||
|
WETH.approve(protocolFeeCollector, MAX_UINT256);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Default payable function, this allows us to withdraw WETH
|
/// @dev Default payable function, this allows us to withdraw WETH
|
||||||
function ()
|
function ()
|
||||||
external
|
external
|
||||||
payable
|
payable
|
||||||
{
|
{
|
||||||
if (msg.sender != address(ETHER_TOKEN)) {
|
if (msg.sender != address(WETH)) {
|
||||||
LibRichErrors.rrevert(LibForwarderRichErrors.DefaultFunctionWethContractOnlyError(
|
LibRichErrors.rrevert(LibWethUtilsRichErrors.DefaultFunctionWethContractOnlyError(
|
||||||
msg.sender
|
msg.sender
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -55,7 +82,7 @@ contract MixinWeth is
|
|||||||
uint256 feesLen = ethFeeAmounts.length;
|
uint256 feesLen = ethFeeAmounts.length;
|
||||||
// ethFeeAmounts len must equal feeRecipients len
|
// ethFeeAmounts len must equal feeRecipients len
|
||||||
if (feesLen != feeRecipients.length) {
|
if (feesLen != feeRecipients.length) {
|
||||||
LibRichErrors.rrevert(LibForwarderRichErrors.EthFeeLengthMismatchError(
|
LibRichErrors.rrevert(LibWethUtilsRichErrors.EthFeeLengthMismatchError(
|
||||||
feesLen,
|
feesLen,
|
||||||
feeRecipients.length
|
feeRecipients.length
|
||||||
));
|
));
|
||||||
@ -69,7 +96,7 @@ contract MixinWeth is
|
|||||||
uint256 ethFeeAmount = ethFeeAmounts[i];
|
uint256 ethFeeAmount = ethFeeAmounts[i];
|
||||||
// Ensure there is enough ETH to pay the fee
|
// Ensure there is enough ETH to pay the fee
|
||||||
if (ethRemaining < ethFeeAmount) {
|
if (ethRemaining < ethFeeAmount) {
|
||||||
LibRichErrors.rrevert(LibForwarderRichErrors.InsufficientEthForFeeError(
|
LibRichErrors.rrevert(LibWethUtilsRichErrors.InsufficientEthForFeeError(
|
||||||
ethFeeAmount,
|
ethFeeAmount,
|
||||||
ethRemaining
|
ethRemaining
|
||||||
));
|
));
|
||||||
@ -80,37 +107,24 @@ contract MixinWeth is
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert remaining ETH to WETH.
|
// Convert remaining ETH to WETH.
|
||||||
ETHER_TOKEN.deposit.value(ethRemaining)();
|
WETH.deposit.value(ethRemaining)();
|
||||||
|
|
||||||
return ethRemaining;
|
return ethRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Refunds any excess ETH to msg.sender.
|
/// @dev Unwraps and refunds WETH to msg.sender.
|
||||||
/// @param initialWethAmount Amount of WETH available after transferring affiliate fees.
|
/// @param refundAmount Amount of WETH balance to refund.
|
||||||
/// @param wethSpent Amount of WETH spent when filling orders.
|
|
||||||
function _transferEthRefund(
|
function _transferEthRefund(
|
||||||
uint256 initialWethAmount,
|
uint256 refundAmount
|
||||||
uint256 wethSpent
|
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
{
|
{
|
||||||
// Ensure that no extra WETH owned by this contract has been spent.
|
// Do nothing if no WETH to refund
|
||||||
if (wethSpent > initialWethAmount) {
|
if (refundAmount > 0) {
|
||||||
LibRichErrors.rrevert(LibForwarderRichErrors.OverspentWethError(
|
// Convert WETH to ETH
|
||||||
wethSpent,
|
WETH.withdraw(refundAmount);
|
||||||
msg.value
|
// Transfer ETH to sender
|
||||||
));
|
msg.sender.transfer(refundAmount);
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate amount of WETH that hasn't been spent.
|
|
||||||
uint256 wethRemaining = initialWethAmount.safeSub(wethSpent);
|
|
||||||
|
|
||||||
// Do nothing if no WETH remaining
|
|
||||||
if (wethRemaining > 0) {
|
|
||||||
// Convert remaining WETH to ETH
|
|
||||||
ETHER_TOKEN.withdraw(wethRemaining);
|
|
||||||
// Transfer remaining ETH to sender
|
|
||||||
msg.sender.transfer(wethRemaining);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,195 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
|
||||||
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
|
||||||
import "./libs/LibConstants.sol";
|
|
||||||
import "./interfaces/IAssets.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinAssets is
|
|
||||||
IAssets,
|
|
||||||
Ownable,
|
|
||||||
LibConstants
|
|
||||||
{
|
|
||||||
using LibBytes for bytes;
|
|
||||||
|
|
||||||
/// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to
|
|
||||||
/// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be
|
|
||||||
/// used to withdraw assets that were accidentally sent to this contract.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of asset to withdraw.
|
|
||||||
function withdrawAsset(
|
|
||||||
bytes calldata assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
onlyOwner
|
|
||||||
{
|
|
||||||
_transferAssetToSender(assetData, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Approves or disapproves an AssetProxy to spend asset.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of asset to approve for respective proxy.
|
|
||||||
function approveAssetProxy(
|
|
||||||
bytes calldata assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
onlyOwner
|
|
||||||
{
|
|
||||||
bytes4 proxyId = assetData.readBytes4(0);
|
|
||||||
|
|
||||||
if (proxyId == ERC20_DATA_ID) {
|
|
||||||
_approveERC20Token(assetData, amount);
|
|
||||||
} else if (proxyId == ERC721_DATA_ID) {
|
|
||||||
_approveERC721Token(assetData, amount);
|
|
||||||
} else {
|
|
||||||
revert("UNSUPPORTED_ASSET_PROXY");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Transfers given amount of asset to sender.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of asset to transfer to sender.
|
|
||||||
function _transferAssetToSender(
|
|
||||||
bytes memory assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
{
|
|
||||||
bytes4 proxyId = assetData.readBytes4(0);
|
|
||||||
|
|
||||||
if (proxyId == ERC20_DATA_ID) {
|
|
||||||
_transferERC20Token(assetData, amount);
|
|
||||||
} else if (proxyId == ERC721_DATA_ID) {
|
|
||||||
_transferERC721Token(assetData, amount);
|
|
||||||
} else {
|
|
||||||
revert("UNSUPPORTED_ASSET_PROXY");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Decodes ERC20 assetData and transfers given amount to sender.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of asset to transfer to sender.
|
|
||||||
function _transferERC20Token(
|
|
||||||
bytes memory assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
{
|
|
||||||
// 4 byte id + 12 0 bytes before ABI encoded token address.
|
|
||||||
address token = assetData.readAddress(16);
|
|
||||||
|
|
||||||
// Transfer tokens.
|
|
||||||
// We do a raw call so we can check the success separate
|
|
||||||
// from the return data.
|
|
||||||
(bool success,) = token.call(abi.encodeWithSelector(
|
|
||||||
ERC20_TRANSFER_SELECTOR,
|
|
||||||
msg.sender,
|
|
||||||
amount
|
|
||||||
));
|
|
||||||
require(
|
|
||||||
success,
|
|
||||||
"TRANSFER_FAILED"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check return data.
|
|
||||||
// If there is no return data, we assume the token incorrectly
|
|
||||||
// does not return a bool. In this case we expect it to revert
|
|
||||||
// on failure, which was handled above.
|
|
||||||
// If the token does return data, we require that it is a single
|
|
||||||
// value that evaluates to true.
|
|
||||||
assembly {
|
|
||||||
if returndatasize {
|
|
||||||
success := 0
|
|
||||||
if eq(returndatasize, 32) {
|
|
||||||
// First 64 bytes of memory are reserved scratch space
|
|
||||||
returndatacopy(0, 0, 32)
|
|
||||||
success := mload(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
require(
|
|
||||||
success,
|
|
||||||
"TRANSFER_FAILED"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Decodes ERC721 assetData and transfers given amount to sender.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of asset to transfer to sender.
|
|
||||||
function _transferERC721Token(
|
|
||||||
bytes memory assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
{
|
|
||||||
require(
|
|
||||||
amount == 1,
|
|
||||||
"INVALID_AMOUNT"
|
|
||||||
);
|
|
||||||
// Decode asset data.
|
|
||||||
// 4 byte id + 12 0 bytes before ABI encoded token address.
|
|
||||||
address token = assetData.readAddress(16);
|
|
||||||
// 4 byte id + 32 byte ABI encoded token address before token id.
|
|
||||||
uint256 tokenId = assetData.readUint256(36);
|
|
||||||
|
|
||||||
// Perform transfer.
|
|
||||||
IERC721Token(token).transferFrom(
|
|
||||||
address(this),
|
|
||||||
msg.sender,
|
|
||||||
tokenId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Sets approval for ERC20 AssetProxy.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of asset to approve for respective proxy.
|
|
||||||
function _approveERC20Token(
|
|
||||||
bytes memory assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
{
|
|
||||||
address token = assetData.readAddress(16);
|
|
||||||
require(
|
|
||||||
IERC20Token(token).approve(ERC20_PROXY_ADDRESS, amount),
|
|
||||||
"APPROVAL_FAILED"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Sets approval for ERC721 AssetProxy.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of asset to approve for respective proxy.
|
|
||||||
function _approveERC721Token(
|
|
||||||
bytes memory assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
{
|
|
||||||
address token = assetData.readAddress(16);
|
|
||||||
bool approval = amount >= 1;
|
|
||||||
IERC721Token(token).setApprovalForAll(ERC721_PROXY_ADDRESS, approval);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "./libs/LibConstants.sol";
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
|
||||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinMatchOrders is
|
|
||||||
Ownable,
|
|
||||||
LibConstants
|
|
||||||
{
|
|
||||||
/// @dev Match two complementary orders that have a profitable spread.
|
|
||||||
/// Each order is filled at their respective price point. However, the calculations are
|
|
||||||
/// carried out as though the orders are both being filled at the right order's price point.
|
|
||||||
/// The profit made by the left order is then used to fill the right order as much as possible.
|
|
||||||
/// This results in a spread being taken in terms of both assets. The spread is held within this contract.
|
|
||||||
/// @param leftOrder First order to match.
|
|
||||||
/// @param rightOrder Second order to match.
|
|
||||||
/// @param leftSignature Proof that order was created by the left maker.
|
|
||||||
/// @param rightSignature Proof that order was created by the right maker.
|
|
||||||
function matchOrders(
|
|
||||||
LibOrder.Order memory leftOrder,
|
|
||||||
LibOrder.Order memory rightOrder,
|
|
||||||
bytes memory leftSignature,
|
|
||||||
bytes memory rightSignature
|
|
||||||
)
|
|
||||||
public
|
|
||||||
onlyOwner
|
|
||||||
{
|
|
||||||
// Match orders, maximally filling `leftOrder`
|
|
||||||
LibFillResults.MatchedFillResults memory matchedFillResults = EXCHANGE.matchOrders(
|
|
||||||
leftOrder,
|
|
||||||
rightOrder,
|
|
||||||
leftSignature,
|
|
||||||
rightSignature
|
|
||||||
);
|
|
||||||
|
|
||||||
uint256 leftMakerAssetSpreadAmount = matchedFillResults.leftMakerAssetSpreadAmount;
|
|
||||||
uint256 rightOrderTakerAssetAmount = rightOrder.takerAssetAmount;
|
|
||||||
|
|
||||||
// Do not attempt to call `fillOrder` if no spread was taken or `rightOrder` has been completely filled
|
|
||||||
if (leftMakerAssetSpreadAmount == 0 || matchedFillResults.right.takerAssetFilledAmount == rightOrderTakerAssetAmount) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The `assetData` fields of the `rightOrder` could have been null for the `matchOrders` call. We reassign them before calling `fillOrder`.
|
|
||||||
rightOrder.makerAssetData = leftOrder.takerAssetData;
|
|
||||||
rightOrder.takerAssetData = leftOrder.makerAssetData;
|
|
||||||
|
|
||||||
// Query `rightOrder` info to check if it has been completely filled
|
|
||||||
// We need to make this check in case the `rightOrder` was partially filled before the `matchOrders` call
|
|
||||||
LibOrder.OrderInfo memory orderInfo = EXCHANGE.getOrderInfo(rightOrder);
|
|
||||||
|
|
||||||
// Do not attempt to call `fillOrder` if order has been completely filled
|
|
||||||
if (orderInfo.orderTakerAssetFilledAmount == rightOrderTakerAssetAmount) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We do not need to pass in a signature since it was already validated in the `matchOrders` call
|
|
||||||
EXCHANGE.fillOrder(
|
|
||||||
rightOrder,
|
|
||||||
leftMakerAssetSpreadAmount,
|
|
||||||
""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
|
||||||
import "./libs/LibConstants.sol";
|
|
||||||
import "./MixinMatchOrders.sol";
|
|
||||||
import "./MixinAssets.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
|
||||||
contract OrderMatcher is
|
|
||||||
MixinMatchOrders,
|
|
||||||
MixinAssets
|
|
||||||
{
|
|
||||||
constructor (address _exchange)
|
|
||||||
public
|
|
||||||
LibConstants(_exchange)
|
|
||||||
Ownable()
|
|
||||||
{}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
|
|
||||||
|
|
||||||
contract IAssets {
|
|
||||||
|
|
||||||
/// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to
|
|
||||||
/// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be
|
|
||||||
/// used to withdraw assets that were accidentally sent to this contract.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of asset to withdraw.
|
|
||||||
function withdrawAsset(
|
|
||||||
bytes calldata assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
|
|
||||||
/// @dev Approves or disapproves an AssetProxy to spend asset.
|
|
||||||
/// @param assetData Byte array encoded for the respective asset proxy.
|
|
||||||
/// @param amount Amount of asset to approve for respective proxy.
|
|
||||||
function approveAssetProxy(
|
|
||||||
bytes calldata assetData,
|
|
||||||
uint256 amount
|
|
||||||
)
|
|
||||||
external;
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract IMatchOrders {
|
|
||||||
|
|
||||||
/// @dev Match two complementary orders that have a profitable spread.
|
|
||||||
/// Each order is filled at their respective price point. However, the calculations are
|
|
||||||
/// carried out as though the orders are both being filled at the right order's price point.
|
|
||||||
/// The profit made by the left order is then used to fill the right order as much as possible.
|
|
||||||
/// This results in a spread being taken in terms of both assets. The spread is held within this contract.
|
|
||||||
/// @param leftOrder First order to match.
|
|
||||||
/// @param rightOrder Second order to match.
|
|
||||||
/// @param leftSignature Proof that order was created by the left maker.
|
|
||||||
/// @param rightSignature Proof that order was created by the right maker.
|
|
||||||
function matchOrders(
|
|
||||||
LibOrder.Order memory leftOrder,
|
|
||||||
LibOrder.Order memory rightOrder,
|
|
||||||
bytes memory leftSignature,
|
|
||||||
bytes memory rightSignature
|
|
||||||
)
|
|
||||||
public;
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol";
|
|
||||||
import "./IMatchOrders.sol";
|
|
||||||
import "./IAssets.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
|
||||||
contract IOrderMatcher is
|
|
||||||
IOwnable,
|
|
||||||
IMatchOrders,
|
|
||||||
IAssets
|
|
||||||
{}
|
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright 2019 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.5.9;
|
|
||||||
|
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract LibConstants {
|
|
||||||
|
|
||||||
// bytes4(keccak256("transfer(address,uint256)"))
|
|
||||||
bytes4 constant internal ERC20_TRANSFER_SELECTOR = 0xa9059cbb;
|
|
||||||
// bytes4(keccak256("ERC20Token(address)"))
|
|
||||||
bytes4 constant internal ERC20_DATA_ID = 0xf47261b0;
|
|
||||||
// bytes4(keccak256("ERC721Token(address,uint256)"))
|
|
||||||
bytes4 constant internal ERC721_DATA_ID = 0x02571792;
|
|
||||||
|
|
||||||
// solhint-disable var-name-mixedcase
|
|
||||||
IExchange internal EXCHANGE;
|
|
||||||
address internal ERC20_PROXY_ADDRESS;
|
|
||||||
address internal ERC721_PROXY_ADDRESS;
|
|
||||||
// solhint-enable var-name-mixedcase
|
|
||||||
|
|
||||||
constructor (address _exchange)
|
|
||||||
public
|
|
||||||
{
|
|
||||||
EXCHANGE = IExchange(_exchange);
|
|
||||||
|
|
||||||
ERC20_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC20_DATA_ID);
|
|
||||||
require(
|
|
||||||
ERC20_PROXY_ADDRESS != address(0),
|
|
||||||
"UNREGISTERED_ASSET_PROXY"
|
|
||||||
);
|
|
||||||
|
|
||||||
ERC721_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC721_DATA_ID);
|
|
||||||
require(
|
|
||||||
ERC721_PROXY_ADDRESS != address(0),
|
|
||||||
"UNREGISTERED_ASSET_PROXY"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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.5.9;
|
||||||
|
|
||||||
|
|
||||||
|
library LibWethUtilsRichErrors {
|
||||||
|
|
||||||
|
// bytes4(keccak256("UnregisteredAssetProxyError()"))
|
||||||
|
bytes4 internal constant UNREGISTERED_ASSET_PROXY_ERROR_SELECTOR =
|
||||||
|
0xf3b96b8d;
|
||||||
|
|
||||||
|
// bytes4(keccak256("InsufficientEthForFeeError(uint256,uint256)"))
|
||||||
|
bytes4 internal constant INSUFFICIENT_ETH_FOR_FEE_ERROR_SELECTOR =
|
||||||
|
0xecf40fd9;
|
||||||
|
|
||||||
|
// bytes4(keccak256("DefaultFunctionWethContractOnlyError(address)"))
|
||||||
|
bytes4 internal constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR =
|
||||||
|
0x08b18698;
|
||||||
|
|
||||||
|
// bytes4(keccak256("EthFeeLengthMismatchError(uint256,uint256)"))
|
||||||
|
bytes4 internal constant ETH_FEE_LENGTH_MISMATCH_ERROR_SELECTOR =
|
||||||
|
0x3ecb6ceb;
|
||||||
|
|
||||||
|
// solhint-disable func-name-mixedcase
|
||||||
|
function UnregisteredAssetProxyError()
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(UNREGISTERED_ASSET_PROXY_ERROR_SELECTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
function InsufficientEthForFeeError(
|
||||||
|
uint256 ethFeeRequired,
|
||||||
|
uint256 ethAvailable
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
INSUFFICIENT_ETH_FOR_FEE_ERROR_SELECTOR,
|
||||||
|
ethFeeRequired,
|
||||||
|
ethAvailable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DefaultFunctionWethContractOnlyError(
|
||||||
|
address senderAddress
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR,
|
||||||
|
senderAddress
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function EthFeeLengthMismatchError(
|
||||||
|
uint256 ethFeesLength,
|
||||||
|
uint256 feeRecipientsLength
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
ETH_FEE_LENGTH_MISMATCH_ERROR_SELECTOR,
|
||||||
|
ethFeesLength,
|
||||||
|
feeRecipientsLength
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -38,8 +38,7 @@
|
|||||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "DutchAuction,OrderMatcher,BalanceThresholdFilter",
|
"abis": "./test/generated-artifacts/@(LibAssetDataTransfer|LibAssetDataTransferRichErrors|LibWethUtilsRichErrors|MixinWethUtils).json",
|
||||||
"abis": "./generated-artifacts/@(BalanceThresholdFilter|DutchAuction|Exchange|ExchangeWrapper|OrderMatcher|WETH9).json",
|
|
||||||
"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."
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -5,17 +5,13 @@
|
|||||||
*/
|
*/
|
||||||
import { ContractArtifact } from 'ethereum-types';
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
import * as BalanceThresholdFilter from '../generated-artifacts/BalanceThresholdFilter.json';
|
import * as LibAssetDataTransfer from '../generated-artifacts/LibAssetDataTransfer.json';
|
||||||
import * as DutchAuction from '../generated-artifacts/DutchAuction.json';
|
import * as LibAssetDataTransferRichErrors from '../generated-artifacts/LibAssetDataTransferRichErrors.json';
|
||||||
import * as Exchange from '../generated-artifacts/Exchange.json';
|
import * as LibWethUtilsRichErrors from '../generated-artifacts/LibWethUtilsRichErrors.json';
|
||||||
import * as ExchangeWrapper from '../generated-artifacts/ExchangeWrapper.json';
|
import * as MixinWethUtils from '../generated-artifacts/MixinWethUtils.json';
|
||||||
import * as OrderMatcher from '../generated-artifacts/OrderMatcher.json';
|
|
||||||
import * as WETH9 from '../generated-artifacts/WETH9.json';
|
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
WETH9: WETH9 as ContractArtifact,
|
LibAssetDataTransfer: LibAssetDataTransfer as ContractArtifact,
|
||||||
ExchangeWrapper: ExchangeWrapper as ContractArtifact,
|
MixinWethUtils: MixinWethUtils as ContractArtifact,
|
||||||
Exchange: Exchange as ContractArtifact,
|
LibAssetDataTransferRichErrors: LibAssetDataTransferRichErrors as ContractArtifact,
|
||||||
BalanceThresholdFilter: BalanceThresholdFilter as ContractArtifact,
|
LibWethUtilsRichErrors: LibWethUtilsRichErrors as ContractArtifact,
|
||||||
DutchAuction: DutchAuction as ContractArtifact,
|
|
||||||
OrderMatcher: OrderMatcher as ContractArtifact,
|
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export { artifacts } from './artifacts';
|
export { artifacts } from './artifacts';
|
||||||
export { BalanceThresholdFilterContract, DutchAuctionContract, OrderMatcherContract } from './wrappers';
|
export { LibAssetDataTransferRevertErrors, MixinWethUtilsRevertErrors } from '@0x/utils';
|
||||||
export {
|
export {
|
||||||
ContractArtifact,
|
ContractArtifact,
|
||||||
ContractChains,
|
ContractChains,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
export * from '../generated-wrappers/balance_threshold_filter';
|
export * from '../generated-wrappers/lib_asset_data_transfer';
|
||||||
export * from '../generated-wrappers/dutch_auction';
|
export * from '../generated-wrappers/lib_asset_data_transfer_rich_errors';
|
||||||
export * from '../generated-wrappers/order_matcher';
|
export * from '../generated-wrappers/lib_weth_utils_rich_errors';
|
||||||
|
export * from '../generated-wrappers/mixin_weth_utils';
|
||||||
|
17
contracts/extensions/test/artifacts.ts
Normal file
17
contracts/extensions/test/artifacts.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
|
import * as LibAssetDataTransfer from '../test/generated-artifacts/LibAssetDataTransfer.json';
|
||||||
|
import * as LibAssetDataTransferRichErrors from '../test/generated-artifacts/LibAssetDataTransferRichErrors.json';
|
||||||
|
import * as LibWethUtilsRichErrors from '../test/generated-artifacts/LibWethUtilsRichErrors.json';
|
||||||
|
import * as MixinWethUtils from '../test/generated-artifacts/MixinWethUtils.json';
|
||||||
|
export const artifacts = {
|
||||||
|
LibAssetDataTransfer: LibAssetDataTransfer as ContractArtifact,
|
||||||
|
MixinWethUtils: MixinWethUtils as ContractArtifact,
|
||||||
|
LibAssetDataTransferRichErrors: LibAssetDataTransferRichErrors as ContractArtifact,
|
||||||
|
LibWethUtilsRichErrors: LibWethUtilsRichErrors as ContractArtifact,
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
@ -1,368 +0,0 @@
|
|||||||
import { ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy';
|
|
||||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
|
||||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
|
||||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
|
||||||
import { ExchangeContract } from '@0x/contracts-exchange';
|
|
||||||
import {
|
|
||||||
chaiSetup,
|
|
||||||
constants,
|
|
||||||
ContractName,
|
|
||||||
ERC20BalancesByOwner,
|
|
||||||
getLatestBlockTimestampAsync,
|
|
||||||
OrderFactory,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
web3Wrapper,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
|
||||||
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
|
||||||
import { RevertReason, SignedOrder } from '@0x/types';
|
|
||||||
import { BigNumber, providerUtils } from '@0x/utils';
|
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
|
||||||
import * as chai from 'chai';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { DutchAuctionContract, DutchAuctionTestWrapper, WETH9Contract } from './wrappers';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
import { encodeDutchAuctionAssetData } from './utils';
|
|
||||||
|
|
||||||
chaiSetup.configure();
|
|
||||||
const expect = chai.expect;
|
|
||||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
|
||||||
const DECIMALS_DEFAULT = 18;
|
|
||||||
|
|
||||||
describe(ContractName.DutchAuction, () => {
|
|
||||||
let chainId: number;
|
|
||||||
let makerAddress: string;
|
|
||||||
let owner: string;
|
|
||||||
let takerAddress: string;
|
|
||||||
let feeRecipientAddress: string;
|
|
||||||
let defaultMakerAssetAddress: string;
|
|
||||||
|
|
||||||
let zrxToken: DummyERC20TokenContract;
|
|
||||||
let erc20TokenA: DummyERC20TokenContract;
|
|
||||||
let erc721Token: DummyERC721TokenContract;
|
|
||||||
let dutchAuctionContract: DutchAuctionContract;
|
|
||||||
let wethContract: WETH9Contract;
|
|
||||||
|
|
||||||
let sellerOrderFactory: OrderFactory;
|
|
||||||
let buyerOrderFactory: OrderFactory;
|
|
||||||
let erc20Wrapper: ERC20Wrapper;
|
|
||||||
let erc20Balances: ERC20BalancesByOwner;
|
|
||||||
let currentBlockTimestamp: number;
|
|
||||||
let auctionBeginTimeSeconds: BigNumber;
|
|
||||||
let auctionEndTimeSeconds: BigNumber;
|
|
||||||
let auctionBeginAmount: BigNumber;
|
|
||||||
let auctionEndAmount: BigNumber;
|
|
||||||
let sellOrder: SignedOrder;
|
|
||||||
let buyOrder: SignedOrder;
|
|
||||||
let erc721MakerAssetIds: BigNumber[];
|
|
||||||
const tenMinutesInSeconds = 10 * 60;
|
|
||||||
|
|
||||||
let dutchAuctionTestWrapper: DutchAuctionTestWrapper;
|
|
||||||
let defaultERC20MakerAssetData: string;
|
|
||||||
|
|
||||||
const devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider);
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
await blockchainLifecycle.startAsync();
|
|
||||||
|
|
||||||
chainId = await providerUtils.getChainIdAsync(provider);
|
|
||||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
|
||||||
const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts);
|
|
||||||
|
|
||||||
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
|
|
||||||
|
|
||||||
const numDummyErc20ToDeploy = 2;
|
|
||||||
[erc20TokenA, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(
|
|
||||||
numDummyErc20ToDeploy,
|
|
||||||
constants.DUMMY_TOKEN_DECIMALS,
|
|
||||||
);
|
|
||||||
const erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
|
||||||
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
|
||||||
|
|
||||||
const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
|
|
||||||
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
|
|
||||||
const erc721Proxy = await erc721Wrapper.deployProxyAsync();
|
|
||||||
await erc721Wrapper.setBalancesAndAllowancesAsync();
|
|
||||||
const erc721Balances = await erc721Wrapper.getBalancesAsync();
|
|
||||||
erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
|
|
||||||
|
|
||||||
wethContract = await WETH9Contract.deployFrom0xArtifactAsync(artifacts.WETH9, provider, txDefaults, artifacts);
|
|
||||||
erc20Wrapper.addDummyTokenContract(wethContract as any);
|
|
||||||
|
|
||||||
const zrxAssetData = await devUtils.encodeERC20AssetData(zrxToken.address).callAsync();
|
|
||||||
const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.Exchange,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
zrxAssetData,
|
|
||||||
new BigNumber(chainId),
|
|
||||||
);
|
|
||||||
await exchangeInstance.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address, {
|
|
||||||
from: owner,
|
|
||||||
});
|
|
||||||
await exchangeInstance.registerAssetProxy.awaitTransactionSuccessAsync(erc721Proxy.address, {
|
|
||||||
from: owner,
|
|
||||||
});
|
|
||||||
|
|
||||||
await erc20Proxy.addAuthorizedAddress(exchangeInstance.address).sendTransactionAsync({
|
|
||||||
from: owner,
|
|
||||||
});
|
|
||||||
await erc721Proxy.addAuthorizedAddress(exchangeInstance.address).sendTransactionAsync({
|
|
||||||
from: owner,
|
|
||||||
});
|
|
||||||
|
|
||||||
const dutchAuctionInstance = await DutchAuctionContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.DutchAuction,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
exchangeInstance.address,
|
|
||||||
);
|
|
||||||
dutchAuctionContract = new DutchAuctionContract(dutchAuctionInstance.address, provider);
|
|
||||||
dutchAuctionTestWrapper = new DutchAuctionTestWrapper(dutchAuctionInstance, provider);
|
|
||||||
|
|
||||||
defaultMakerAssetAddress = erc20TokenA.address;
|
|
||||||
const defaultTakerAssetAddress = wethContract.address;
|
|
||||||
|
|
||||||
// Set up taker WETH balance and allowance
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await wethContract.deposit.sendTransactionAsync({
|
|
||||||
from: takerAddress,
|
|
||||||
value: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), DECIMALS_DEFAULT),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await wethContract
|
|
||||||
.approve(erc20Proxy.address, constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)
|
|
||||||
.sendTransactionAsync({ from: takerAddress }),
|
|
||||||
);
|
|
||||||
web3Wrapper.abiDecoder.addABI(exchangeInstance.abi);
|
|
||||||
web3Wrapper.abiDecoder.addABI(zrxToken.abi);
|
|
||||||
erc20Wrapper.addTokenOwnerAddress(dutchAuctionContract.address);
|
|
||||||
|
|
||||||
currentBlockTimestamp = await getLatestBlockTimestampAsync();
|
|
||||||
// Default auction begins 10 minutes ago
|
|
||||||
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp).minus(tenMinutesInSeconds);
|
|
||||||
// Default auction ends 10 from now
|
|
||||||
auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp).plus(tenMinutesInSeconds);
|
|
||||||
auctionBeginAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(10), DECIMALS_DEFAULT);
|
|
||||||
auctionEndAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT);
|
|
||||||
|
|
||||||
// Default sell order and buy order are exact mirrors
|
|
||||||
const sellerDefaultOrderParams = {
|
|
||||||
salt: generatePseudoRandomSalt(),
|
|
||||||
makerAddress,
|
|
||||||
feeRecipientAddress,
|
|
||||||
// taker address or sender address should be set to the ducth auction contract
|
|
||||||
takerAddress: dutchAuctionContract.address,
|
|
||||||
makerAssetData: encodeDutchAuctionAssetData(
|
|
||||||
await devUtils.encodeERC20AssetData(defaultMakerAssetAddress).callAsync(),
|
|
||||||
auctionBeginTimeSeconds,
|
|
||||||
auctionBeginAmount,
|
|
||||||
),
|
|
||||||
takerAssetData: await devUtils.encodeERC20AssetData(defaultTakerAssetAddress).callAsync(),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), DECIMALS_DEFAULT),
|
|
||||||
takerAssetAmount: auctionEndAmount,
|
|
||||||
expirationTimeSeconds: auctionEndTimeSeconds,
|
|
||||||
makerFee: constants.ZERO_AMOUNT,
|
|
||||||
takerFee: constants.ZERO_AMOUNT,
|
|
||||||
exchangeAddress: exchangeInstance.address,
|
|
||||||
chainId,
|
|
||||||
};
|
|
||||||
// Default buy order is for the auction begin price
|
|
||||||
const buyerDefaultOrderParams = {
|
|
||||||
...sellerDefaultOrderParams,
|
|
||||||
makerAddress: takerAddress,
|
|
||||||
makerAssetData: sellerDefaultOrderParams.takerAssetData,
|
|
||||||
takerAssetData: sellerDefaultOrderParams.makerAssetData,
|
|
||||||
makerAssetAmount: auctionBeginAmount,
|
|
||||||
takerAssetAmount: sellerDefaultOrderParams.makerAssetAmount,
|
|
||||||
};
|
|
||||||
const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
|
|
||||||
const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
|
|
||||||
sellerOrderFactory = new OrderFactory(makerPrivateKey, sellerDefaultOrderParams);
|
|
||||||
buyerOrderFactory = new OrderFactory(takerPrivateKey, buyerDefaultOrderParams);
|
|
||||||
defaultERC20MakerAssetData = await devUtils.encodeERC20AssetData(defaultMakerAssetAddress).callAsync();
|
|
||||||
});
|
|
||||||
after(async () => {
|
|
||||||
await blockchainLifecycle.revertAsync();
|
|
||||||
});
|
|
||||||
beforeEach(async () => {
|
|
||||||
await blockchainLifecycle.startAsync();
|
|
||||||
erc20Balances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync();
|
|
||||||
buyOrder = await buyerOrderFactory.newSignedOrderAsync();
|
|
||||||
});
|
|
||||||
afterEach(async () => {
|
|
||||||
await blockchainLifecycle.revertAsync();
|
|
||||||
});
|
|
||||||
describe('matchOrders', () => {
|
|
||||||
it('should be worth the begin price at the begining of the auction', async () => {
|
|
||||||
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp + 2);
|
|
||||||
const makerAssetData = encodeDutchAuctionAssetData(
|
|
||||||
defaultERC20MakerAssetData,
|
|
||||||
auctionBeginTimeSeconds,
|
|
||||||
auctionBeginAmount,
|
|
||||||
);
|
|
||||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({ makerAssetData });
|
|
||||||
const auctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
|
||||||
expect(auctionDetails.currentTimeSeconds).to.be.bignumber.lte(auctionBeginTimeSeconds);
|
|
||||||
expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionBeginAmount);
|
|
||||||
expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount);
|
|
||||||
});
|
|
||||||
it('should be be worth the end price at the end of the auction', async () => {
|
|
||||||
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2);
|
|
||||||
auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
|
|
||||||
const makerAssetData = encodeDutchAuctionAssetData(
|
|
||||||
defaultERC20MakerAssetData,
|
|
||||||
auctionBeginTimeSeconds,
|
|
||||||
auctionBeginAmount,
|
|
||||||
);
|
|
||||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
|
||||||
makerAssetData,
|
|
||||||
expirationTimeSeconds: auctionEndTimeSeconds,
|
|
||||||
});
|
|
||||||
const auctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
|
||||||
expect(auctionDetails.currentTimeSeconds).to.be.bignumber.gte(auctionEndTimeSeconds);
|
|
||||||
expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionEndAmount);
|
|
||||||
expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount);
|
|
||||||
});
|
|
||||||
it('should match orders at current amount and send excess to buyer', async () => {
|
|
||||||
const beforeAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
|
||||||
buyOrder = await buyerOrderFactory.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: beforeAuctionDetails.currentAmount.times(2),
|
|
||||||
});
|
|
||||||
await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
|
||||||
const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
expect(newBalances[dutchAuctionContract.address][wethContract.address]).to.be.bignumber.equal(
|
|
||||||
constants.ZERO_AMOUNT,
|
|
||||||
);
|
|
||||||
// HACK gte used here due to a bug in ganache where the timestamp can change
|
|
||||||
// between multiple calls to the same block. Which can move the amount in our case
|
|
||||||
// ref: https://github.com/trufflesuite/ganache-core/issues/111
|
|
||||||
expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte(
|
|
||||||
erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount),
|
|
||||||
);
|
|
||||||
expect(newBalances[takerAddress][wethContract.address]).to.be.bignumber.gte(
|
|
||||||
erc20Balances[takerAddress][wethContract.address].minus(beforeAuctionDetails.currentAmount),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('maker fees on sellOrder are paid to the fee receipient', async () => {
|
|
||||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
|
||||||
makerFee: new BigNumber(1),
|
|
||||||
});
|
|
||||||
await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
|
||||||
const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte(
|
|
||||||
erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount),
|
|
||||||
);
|
|
||||||
expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
|
|
||||||
erc20Balances[feeRecipientAddress][zrxToken.address].plus(sellOrder.makerFee),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('maker fees on buyOrder are paid to the fee receipient', async () => {
|
|
||||||
buyOrder = await buyerOrderFactory.newSignedOrderAsync({
|
|
||||||
makerFee: new BigNumber(1),
|
|
||||||
});
|
|
||||||
await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
|
||||||
expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte(
|
|
||||||
erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount),
|
|
||||||
);
|
|
||||||
expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
|
|
||||||
erc20Balances[feeRecipientAddress][zrxToken.address].plus(buyOrder.makerFee),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should revert when auction expires', async () => {
|
|
||||||
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2);
|
|
||||||
auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
|
|
||||||
const makerAssetData = encodeDutchAuctionAssetData(
|
|
||||||
defaultERC20MakerAssetData,
|
|
||||||
auctionBeginTimeSeconds,
|
|
||||||
auctionBeginAmount,
|
|
||||||
);
|
|
||||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
|
||||||
expirationTimeSeconds: auctionEndTimeSeconds,
|
|
||||||
makerAssetData,
|
|
||||||
});
|
|
||||||
const tx = dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
|
||||||
return expect(tx).to.revertWith(RevertReason.AuctionExpired);
|
|
||||||
});
|
|
||||||
it('cannot be filled for less than the current price', async () => {
|
|
||||||
buyOrder = await buyerOrderFactory.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: sellOrder.takerAssetAmount,
|
|
||||||
});
|
|
||||||
const tx = dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
|
||||||
return expect(tx).to.revertWith(RevertReason.AuctionInvalidAmount);
|
|
||||||
});
|
|
||||||
it('auction begin amount must be higher than final amount ', async () => {
|
|
||||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
|
||||||
takerAssetAmount: auctionBeginAmount.plus(1),
|
|
||||||
});
|
|
||||||
const tx = dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
|
||||||
return expect(tx).to.revertWith(RevertReason.AuctionInvalidAmount);
|
|
||||||
});
|
|
||||||
it('begin time is less than end time', async () => {
|
|
||||||
auctionBeginTimeSeconds = new BigNumber(auctionEndTimeSeconds).plus(tenMinutesInSeconds);
|
|
||||||
const makerAssetData = encodeDutchAuctionAssetData(
|
|
||||||
defaultERC20MakerAssetData,
|
|
||||||
auctionBeginTimeSeconds,
|
|
||||||
auctionBeginAmount,
|
|
||||||
);
|
|
||||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
|
||||||
expirationTimeSeconds: auctionEndTimeSeconds,
|
|
||||||
makerAssetData,
|
|
||||||
});
|
|
||||||
const tx = dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
|
||||||
return expect(tx).to.revertWith(RevertReason.AuctionInvalidBeginTime);
|
|
||||||
});
|
|
||||||
it('asset data contains auction parameters', async () => {
|
|
||||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
|
||||||
makerAssetData: await devUtils.encodeERC20AssetData(defaultMakerAssetAddress).callAsync(),
|
|
||||||
});
|
|
||||||
const tx = dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
|
||||||
return expect(tx).to.revertWith(RevertReason.InvalidAssetData);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('ERC721', () => {
|
|
||||||
it('should match orders when ERC721', async () => {
|
|
||||||
const makerAssetId = erc721MakerAssetIds[0];
|
|
||||||
const erc721MakerAssetData = await devUtils
|
|
||||||
.encodeERC721AssetData(erc721Token.address, makerAssetId)
|
|
||||||
.callAsync();
|
|
||||||
const makerAssetData = encodeDutchAuctionAssetData(
|
|
||||||
erc721MakerAssetData,
|
|
||||||
auctionBeginTimeSeconds,
|
|
||||||
auctionBeginAmount,
|
|
||||||
);
|
|
||||||
sellOrder = await sellerOrderFactory.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: new BigNumber(1),
|
|
||||||
makerAssetData,
|
|
||||||
});
|
|
||||||
buyOrder = await buyerOrderFactory.newSignedOrderAsync({
|
|
||||||
takerAssetAmount: new BigNumber(1),
|
|
||||||
takerAssetData: sellOrder.makerAssetData,
|
|
||||||
});
|
|
||||||
await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
|
|
||||||
const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder);
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
// HACK gte used here due to a bug in ganache where the timestamp can change
|
|
||||||
// between multiple calls to the same block. Which can move the amount in our case
|
|
||||||
// ref: https://github.com/trufflesuite/ganache-core/issues/111
|
|
||||||
expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte(
|
|
||||||
erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount),
|
|
||||||
);
|
|
||||||
const newOwner = await erc721Token.ownerOf(makerAssetId).callAsync();
|
|
||||||
expect(newOwner).to.be.bignumber.equal(takerAddress);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,19 +0,0 @@
|
|||||||
import { env, EnvVars } from '@0x/dev-utils';
|
|
||||||
|
|
||||||
import { coverage, profiler, provider } from '@0x/contracts-test-utils';
|
|
||||||
import { providerUtils } from '@0x/utils';
|
|
||||||
|
|
||||||
before('start web3 provider', () => {
|
|
||||||
providerUtils.startProviderEngine(provider);
|
|
||||||
});
|
|
||||||
after('generate coverage report', async () => {
|
|
||||||
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
|
|
||||||
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
|
|
||||||
await coverageSubprovider.writeCoverageAsync();
|
|
||||||
}
|
|
||||||
if (env.parseBoolean(EnvVars.SolidityProfiler)) {
|
|
||||||
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
|
|
||||||
await profilerSubprovider.writeProfilerOutputAsync();
|
|
||||||
}
|
|
||||||
provider.stop();
|
|
||||||
});
|
|
@ -1,796 +0,0 @@
|
|||||||
import {
|
|
||||||
artifacts as proxyArtifacts,
|
|
||||||
ERC20ProxyContract,
|
|
||||||
ERC20Wrapper,
|
|
||||||
ERC721ProxyContract,
|
|
||||||
} from '@0x/contracts-asset-proxy';
|
|
||||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
|
||||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
|
||||||
import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721';
|
|
||||||
import { ExchangeContract, ExchangeRevertErrors } from '@0x/contracts-exchange';
|
|
||||||
import {
|
|
||||||
chaiSetup,
|
|
||||||
constants,
|
|
||||||
ERC20BalancesByOwner,
|
|
||||||
expectContractCreationFailedAsync,
|
|
||||||
LogDecoder,
|
|
||||||
OrderFactory,
|
|
||||||
orderUtils,
|
|
||||||
provider,
|
|
||||||
sendTransactionResult,
|
|
||||||
txDefaults,
|
|
||||||
web3Wrapper,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
|
||||||
import { RevertReason } from '@0x/types';
|
|
||||||
import { BigNumber, providerUtils } from '@0x/utils';
|
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
|
||||||
import * as chai from 'chai';
|
|
||||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { ExchangeFillEventArgs, OrderMatcherContract } from './wrappers';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
|
||||||
chaiSetup.configure();
|
|
||||||
const expect = chai.expect;
|
|
||||||
// tslint:disable:no-unnecessary-type-assertion
|
|
||||||
describe('OrderMatcher', () => {
|
|
||||||
let chainId: number;
|
|
||||||
let makerAddressLeft: string;
|
|
||||||
let makerAddressRight: string;
|
|
||||||
let owner: string;
|
|
||||||
let takerAddress: string;
|
|
||||||
let feeRecipientAddressLeft: string;
|
|
||||||
let feeRecipientAddressRight: string;
|
|
||||||
|
|
||||||
let erc20TokenA: DummyERC20TokenContract;
|
|
||||||
let erc20TokenB: DummyERC20TokenContract;
|
|
||||||
let zrxToken: DummyERC20TokenContract;
|
|
||||||
let exchange: ExchangeContract;
|
|
||||||
let erc20Proxy: ERC20ProxyContract;
|
|
||||||
let erc721Proxy: ERC721ProxyContract;
|
|
||||||
let orderMatcher: OrderMatcherContract;
|
|
||||||
|
|
||||||
let erc20BalancesByOwner: ERC20BalancesByOwner;
|
|
||||||
let erc20Wrapper: ERC20Wrapper;
|
|
||||||
let orderFactoryLeft: OrderFactory;
|
|
||||||
let orderFactoryRight: OrderFactory;
|
|
||||||
|
|
||||||
let leftMakerAssetData: string;
|
|
||||||
let leftTakerAssetData: string;
|
|
||||||
let defaultERC20MakerAssetAddress: string;
|
|
||||||
let defaultERC20TakerAssetAddress: string;
|
|
||||||
|
|
||||||
const devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider, txDefaults);
|
|
||||||
before(async () => {
|
|
||||||
await blockchainLifecycle.startAsync();
|
|
||||||
});
|
|
||||||
after(async () => {
|
|
||||||
await blockchainLifecycle.revertAsync();
|
|
||||||
});
|
|
||||||
before(async () => {
|
|
||||||
chainId = await providerUtils.getChainIdAsync(provider);
|
|
||||||
// Create accounts
|
|
||||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
|
||||||
// Hack(albrow): Both Prettier and TSLint insert a trailing comma below
|
|
||||||
// but that is invalid syntax as of TypeScript version >= 2.8. We don't
|
|
||||||
// have the right fine-grained configuration options in TSLint,
|
|
||||||
// Prettier, or TypeScript, to reconcile this, so we will just have to
|
|
||||||
// wait for them to sort it out. We disable TSLint and Prettier for
|
|
||||||
// this part of the code for now. This occurs several times in this
|
|
||||||
// file. See https://github.com/prettier/prettier/issues/4624.
|
|
||||||
// prettier-ignore
|
|
||||||
const usedAddresses = ([
|
|
||||||
owner,
|
|
||||||
makerAddressLeft,
|
|
||||||
makerAddressRight,
|
|
||||||
takerAddress,
|
|
||||||
feeRecipientAddressLeft,
|
|
||||||
// tslint:disable-next-line:trailing-comma
|
|
||||||
feeRecipientAddressRight
|
|
||||||
] = _.slice(accounts, 0, 6));
|
|
||||||
// Create wrappers
|
|
||||||
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
|
|
||||||
// Deploy ERC20 token & ERC20 proxy
|
|
||||||
const numDummyErc20ToDeploy = 3;
|
|
||||||
[erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(
|
|
||||||
numDummyErc20ToDeploy,
|
|
||||||
constants.DUMMY_TOKEN_DECIMALS,
|
|
||||||
);
|
|
||||||
erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
|
||||||
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
|
||||||
// Deploy ERC721 proxy
|
|
||||||
erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync(
|
|
||||||
proxyArtifacts.ERC721Proxy,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
);
|
|
||||||
// Depoy exchange
|
|
||||||
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.Exchange,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
await devUtils.encodeERC20AssetData(zrxToken.address).callAsync(),
|
|
||||||
new BigNumber(chainId),
|
|
||||||
);
|
|
||||||
await exchange.registerAssetProxy(erc20Proxy.address).awaitTransactionSuccessAsync({
|
|
||||||
from: owner,
|
|
||||||
});
|
|
||||||
await exchange.registerAssetProxy(erc721Proxy.address).awaitTransactionSuccessAsync({
|
|
||||||
from: owner,
|
|
||||||
}); // Authorize ERC20 trades by exchange
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await erc20Proxy.addAuthorizedAddress(exchange.address).sendTransactionAsync({
|
|
||||||
from: owner,
|
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
// Deploy OrderMatcher
|
|
||||||
orderMatcher = await OrderMatcherContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.OrderMatcher,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
exchange.address,
|
|
||||||
);
|
|
||||||
// Set default addresses
|
|
||||||
defaultERC20MakerAssetAddress = erc20TokenA.address;
|
|
||||||
defaultERC20TakerAssetAddress = erc20TokenB.address;
|
|
||||||
leftMakerAssetData = await devUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress).callAsync();
|
|
||||||
leftTakerAssetData = await devUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress).callAsync();
|
|
||||||
// Set OrderMatcher balances and allowances
|
|
||||||
await erc20TokenA
|
|
||||||
.setBalance(orderMatcher.address, constants.INITIAL_ERC20_BALANCE)
|
|
||||||
.awaitTransactionSuccessAsync({ from: owner }, { pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS });
|
|
||||||
|
|
||||||
await erc20TokenB
|
|
||||||
.setBalance(orderMatcher.address, constants.INITIAL_ERC20_BALANCE)
|
|
||||||
.awaitTransactionSuccessAsync({ from: owner }, { pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS });
|
|
||||||
|
|
||||||
await orderMatcher
|
|
||||||
.approveAssetProxy(leftMakerAssetData, constants.INITIAL_ERC20_ALLOWANCE)
|
|
||||||
.awaitTransactionSuccessAsync({}, { pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS });
|
|
||||||
|
|
||||||
await orderMatcher
|
|
||||||
.approveAssetProxy(leftTakerAssetData, constants.INITIAL_ERC20_ALLOWANCE)
|
|
||||||
.awaitTransactionSuccessAsync({}, { pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS });
|
|
||||||
|
|
||||||
// Create default order parameters
|
|
||||||
const defaultOrderParamsLeft = {
|
|
||||||
...constants.STATIC_ORDER_PARAMS,
|
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetData: leftMakerAssetData,
|
|
||||||
takerAssetData: leftTakerAssetData,
|
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
makerFee: constants.ZERO_AMOUNT,
|
|
||||||
takerFee: constants.ZERO_AMOUNT,
|
|
||||||
exchangeAddress: exchange.address,
|
|
||||||
chainId,
|
|
||||||
};
|
|
||||||
const defaultOrderParamsRight = {
|
|
||||||
...constants.STATIC_ORDER_PARAMS,
|
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: leftTakerAssetData,
|
|
||||||
takerAssetData: leftMakerAssetData,
|
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
makerFee: constants.ZERO_AMOUNT,
|
|
||||||
takerFee: constants.ZERO_AMOUNT,
|
|
||||||
exchangeAddress: exchange.address,
|
|
||||||
chainId,
|
|
||||||
};
|
|
||||||
const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)];
|
|
||||||
orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParamsLeft);
|
|
||||||
const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)];
|
|
||||||
orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParamsRight);
|
|
||||||
});
|
|
||||||
beforeEach(async () => {
|
|
||||||
await blockchainLifecycle.startAsync();
|
|
||||||
});
|
|
||||||
afterEach(async () => {
|
|
||||||
await blockchainLifecycle.revertAsync();
|
|
||||||
});
|
|
||||||
describe('constructor', () => {
|
|
||||||
it('should revert if assetProxy is unregistered', async () => {
|
|
||||||
const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.Exchange,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
constants.NULL_BYTES,
|
|
||||||
new BigNumber(chainId),
|
|
||||||
);
|
|
||||||
return expectContractCreationFailedAsync(
|
|
||||||
(OrderMatcherContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.OrderMatcher,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
exchangeInstance.address,
|
|
||||||
) as any) as sendTransactionResult,
|
|
||||||
RevertReason.UnregisteredAssetProxy,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('matchOrders', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
erc20BalancesByOwner = await erc20Wrapper.getBalancesAsync();
|
|
||||||
});
|
|
||||||
it('should revert if not called by owner', async () => {
|
|
||||||
// Create orders to match
|
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
});
|
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
|
||||||
});
|
|
||||||
const data = exchange
|
|
||||||
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const tx = web3Wrapper.sendTransactionAsync({
|
|
||||||
data,
|
|
||||||
to: orderMatcher.address,
|
|
||||||
from: takerAddress,
|
|
||||||
gas: constants.MAX_MATCH_ORDERS_GAS,
|
|
||||||
});
|
|
||||||
return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
|
|
||||||
});
|
|
||||||
it('should transfer the correct amounts when orders completely fill each other', async () => {
|
|
||||||
// Create orders to match
|
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
});
|
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
|
||||||
});
|
|
||||||
// Match signedOrderLeft with signedOrderRight
|
|
||||||
const expectedTransferAmounts = {
|
|
||||||
// Left Maker
|
|
||||||
amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount,
|
|
||||||
amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount,
|
|
||||||
// Right Maker
|
|
||||||
amountSoldByRightMaker: signedOrderRight.makerAssetAmount,
|
|
||||||
amountBoughtByRightMaker: signedOrderRight.takerAssetAmount,
|
|
||||||
// Taker
|
|
||||||
leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount),
|
|
||||||
};
|
|
||||||
const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const data = exchange
|
|
||||||
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await web3Wrapper.sendTransactionAsync({
|
|
||||||
data,
|
|
||||||
to: orderMatcher.address,
|
|
||||||
from: owner,
|
|
||||||
gas: constants.MAX_MATCH_ORDERS_GAS,
|
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const newErc20Balances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus(
|
|
||||||
expectedTransferAmounts.amountSoldByLeftMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus(
|
|
||||||
expectedTransferAmounts.amountSoldByRightMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus(
|
|
||||||
expectedTransferAmounts.amountBoughtByLeftMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus(
|
|
||||||
expectedTransferAmounts.amountBoughtByRightMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal(
|
|
||||||
initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => {
|
|
||||||
// Create orders to match
|
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
});
|
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
|
||||||
});
|
|
||||||
// Match signedOrderLeft with signedOrderRight
|
|
||||||
const expectedTransferAmounts = {
|
|
||||||
// Left Maker
|
|
||||||
amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount,
|
|
||||||
amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount,
|
|
||||||
// Right Maker
|
|
||||||
amountSoldByRightMaker: signedOrderRight.makerAssetAmount,
|
|
||||||
amountBoughtByRightMaker: signedOrderRight.takerAssetAmount,
|
|
||||||
};
|
|
||||||
const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const data = exchange
|
|
||||||
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await web3Wrapper.sendTransactionAsync({
|
|
||||||
data,
|
|
||||||
to: orderMatcher.address,
|
|
||||||
from: owner,
|
|
||||||
gas: constants.MAX_MATCH_ORDERS_GAS,
|
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const newErc20Balances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus(
|
|
||||||
expectedTransferAmounts.amountSoldByLeftMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus(
|
|
||||||
expectedTransferAmounts.amountSoldByRightMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus(
|
|
||||||
expectedTransferAmounts.amountBoughtByLeftMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus(
|
|
||||||
expectedTransferAmounts.amountBoughtByRightMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal(initialLeftMakerAssetTakerBalance);
|
|
||||||
});
|
|
||||||
it('should transfer the correct amounts when left order is completely filled and right order would be partially filled', async () => {
|
|
||||||
// Create orders to match
|
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
});
|
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18),
|
|
||||||
});
|
|
||||||
// Match signedOrderLeft with signedOrderRight
|
|
||||||
const expectedTransferAmounts = {
|
|
||||||
// Left Maker
|
|
||||||
amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount,
|
|
||||||
amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount,
|
|
||||||
// Right Maker
|
|
||||||
amountSoldByRightMaker: signedOrderRight.makerAssetAmount,
|
|
||||||
amountBoughtByRightMaker: signedOrderRight.takerAssetAmount,
|
|
||||||
// Taker
|
|
||||||
leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount),
|
|
||||||
leftTakerAssetSpreadAmount: signedOrderRight.makerAssetAmount.minus(signedOrderLeft.takerAssetAmount),
|
|
||||||
};
|
|
||||||
const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
// Match signedOrderLeft with signedOrderRight
|
|
||||||
const data = exchange
|
|
||||||
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await web3Wrapper.sendTransactionAsync({
|
|
||||||
data,
|
|
||||||
to: orderMatcher.address,
|
|
||||||
from: owner,
|
|
||||||
gas: constants.MAX_MATCH_ORDERS_GAS,
|
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const newErc20Balances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus(
|
|
||||||
expectedTransferAmounts.amountSoldByLeftMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus(
|
|
||||||
expectedTransferAmounts.amountSoldByRightMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus(
|
|
||||||
expectedTransferAmounts.amountBoughtByLeftMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus(
|
|
||||||
expectedTransferAmounts.amountBoughtByRightMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal(
|
|
||||||
initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount),
|
|
||||||
);
|
|
||||||
expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal(
|
|
||||||
initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should not call fillOrder when rightOrder is completely filled after matchOrders call and orders were never partially filled', async () => {
|
|
||||||
// Create orders to match
|
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
});
|
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
|
||||||
});
|
|
||||||
const data = exchange
|
|
||||||
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const logDecoder = new LogDecoder(web3Wrapper, artifacts);
|
|
||||||
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
|
|
||||||
await web3Wrapper.sendTransactionAsync({
|
|
||||||
data,
|
|
||||||
to: orderMatcher.address,
|
|
||||||
from: owner,
|
|
||||||
gas: constants.MAX_MATCH_ORDERS_GAS,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const fillLogs = _.filter(
|
|
||||||
txReceipt.logs,
|
|
||||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
|
||||||
);
|
|
||||||
// Only 2 Fill logs should exist for `matchOrders` call. `fillOrder` should not have been called and should not have emitted a Fill event.
|
|
||||||
expect(fillLogs.length).to.be.equal(2);
|
|
||||||
});
|
|
||||||
it('should not call fillOrder when rightOrder is completely filled after matchOrders call and orders were initially partially filled', async () => {
|
|
||||||
// Create orders to match
|
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
});
|
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
|
||||||
});
|
|
||||||
let params = orderUtils.createFill(signedOrderLeft, signedOrderLeft.takerAssetAmount.dividedToIntegerBy(5));
|
|
||||||
await exchange
|
|
||||||
.fillOrder(params.order, params.takerAssetFillAmount, params.signature)
|
|
||||||
.awaitTransactionSuccessAsync({ from: takerAddress });
|
|
||||||
params = orderUtils.createFill(signedOrderRight, signedOrderRight.takerAssetAmount.dividedToIntegerBy(5));
|
|
||||||
await exchange
|
|
||||||
.fillOrder(params.order, params.takerAssetFillAmount, params.signature)
|
|
||||||
.awaitTransactionSuccessAsync({ from: takerAddress });
|
|
||||||
const data = exchange
|
|
||||||
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const logDecoder = new LogDecoder(web3Wrapper, artifacts);
|
|
||||||
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
|
|
||||||
await web3Wrapper.sendTransactionAsync({
|
|
||||||
data,
|
|
||||||
to: orderMatcher.address,
|
|
||||||
from: owner,
|
|
||||||
gas: constants.MAX_MATCH_ORDERS_GAS,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const fillLogs = _.filter(
|
|
||||||
txReceipt.logs,
|
|
||||||
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
|
|
||||||
);
|
|
||||||
// Only 2 Fill logs should exist for `matchOrders` call. `fillOrder` should not have been called and should not have emitted a Fill event.
|
|
||||||
expect(fillLogs.length).to.be.equal(2);
|
|
||||||
});
|
|
||||||
it('should only take a spread in rightMakerAsset if entire leftMakerAssetSpread amount can be used to fill rightOrder after matchOrders call', async () => {
|
|
||||||
// Create orders to match
|
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.9), 18),
|
|
||||||
});
|
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(990), 18),
|
|
||||||
});
|
|
||||||
const initialLeftMakerAssetSpreadAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1.09), 18);
|
|
||||||
const leftTakerAssetSpreadAmount = initialLeftMakerAssetSpreadAmount
|
|
||||||
.times(signedOrderRight.makerAssetAmount)
|
|
||||||
.dividedToIntegerBy(signedOrderRight.takerAssetAmount);
|
|
||||||
// Match signedOrderLeft with signedOrderRight
|
|
||||||
const expectedTransferAmounts = {
|
|
||||||
// Left Maker
|
|
||||||
amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount,
|
|
||||||
amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount,
|
|
||||||
// Right Maker
|
|
||||||
amountSoldByRightMaker: signedOrderLeft.takerAssetAmount.plus(leftTakerAssetSpreadAmount),
|
|
||||||
amountBoughtByRightMaker: signedOrderLeft.makerAssetAmount,
|
|
||||||
// Taker
|
|
||||||
leftMakerAssetSpreadAmount: constants.ZERO_AMOUNT,
|
|
||||||
leftTakerAssetSpreadAmount,
|
|
||||||
};
|
|
||||||
const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
// Match signedOrderLeft with signedOrderRight
|
|
||||||
const data = exchange
|
|
||||||
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await web3Wrapper.sendTransactionAsync({
|
|
||||||
data,
|
|
||||||
to: orderMatcher.address,
|
|
||||||
from: owner,
|
|
||||||
gas: constants.MAX_MATCH_ORDERS_GAS,
|
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const newErc20Balances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus(
|
|
||||||
expectedTransferAmounts.amountSoldByLeftMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus(
|
|
||||||
expectedTransferAmounts.amountSoldByRightMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus(
|
|
||||||
expectedTransferAmounts.amountBoughtByLeftMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus(
|
|
||||||
expectedTransferAmounts.amountBoughtByRightMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal(
|
|
||||||
initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount),
|
|
||||||
);
|
|
||||||
expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal(
|
|
||||||
initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it("should succeed if rightOrder's makerAssetData and takerAssetData are not provided", async () => {
|
|
||||||
// Create orders to match
|
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
});
|
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18),
|
|
||||||
});
|
|
||||||
// Match signedOrderLeft with signedOrderRight
|
|
||||||
const expectedTransferAmounts = {
|
|
||||||
// Left Maker
|
|
||||||
amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount,
|
|
||||||
amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount,
|
|
||||||
// Right Maker
|
|
||||||
amountSoldByRightMaker: signedOrderRight.makerAssetAmount,
|
|
||||||
amountBoughtByRightMaker: signedOrderRight.takerAssetAmount,
|
|
||||||
// Taker
|
|
||||||
leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount),
|
|
||||||
leftTakerAssetSpreadAmount: signedOrderRight.makerAssetAmount.minus(signedOrderLeft.takerAssetAmount),
|
|
||||||
};
|
|
||||||
const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
// Match signedOrderLeft with signedOrderRight
|
|
||||||
signedOrderRight.makerAssetData = constants.NULL_BYTES;
|
|
||||||
signedOrderRight.takerAssetData = constants.NULL_BYTES;
|
|
||||||
const data = exchange
|
|
||||||
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await web3Wrapper.sendTransactionAsync({
|
|
||||||
data,
|
|
||||||
to: orderMatcher.address,
|
|
||||||
from: owner,
|
|
||||||
gas: constants.MAX_MATCH_ORDERS_GAS,
|
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
const newErc20Balances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus(
|
|
||||||
expectedTransferAmounts.amountSoldByLeftMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus(
|
|
||||||
expectedTransferAmounts.amountSoldByRightMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus(
|
|
||||||
expectedTransferAmounts.amountBoughtByLeftMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus(
|
|
||||||
expectedTransferAmounts.amountBoughtByRightMaker,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal(
|
|
||||||
initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount),
|
|
||||||
);
|
|
||||||
expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal(
|
|
||||||
initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('should revert with the correct reason if matchOrders call reverts', async () => {
|
|
||||||
// Create orders to match
|
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
});
|
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
|
||||||
});
|
|
||||||
signedOrderRight.signature = `0xff${signedOrderRight.signature.slice(4)}`;
|
|
||||||
const data = exchange
|
|
||||||
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const expectedError = new ExchangeRevertErrors.SignatureError();
|
|
||||||
const tx = web3Wrapper.sendTransactionAsync({
|
|
||||||
data,
|
|
||||||
to: orderMatcher.address,
|
|
||||||
from: owner,
|
|
||||||
gas: constants.MAX_MATCH_ORDERS_GAS,
|
|
||||||
});
|
|
||||||
return expect(tx).to.revertWith(expectedError);
|
|
||||||
});
|
|
||||||
it('should revert with the correct reason if fillOrder call reverts', async () => {
|
|
||||||
// Create orders to match
|
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
|
||||||
});
|
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18),
|
|
||||||
});
|
|
||||||
// Matcher will not have enough allowance to fill rightOrder
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await orderMatcher.approveAssetProxy(leftMakerAssetData, constants.ZERO_AMOUNT).sendTransactionAsync({
|
|
||||||
from: owner,
|
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const data = exchange
|
|
||||||
.matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError();
|
|
||||||
const tx = web3Wrapper.sendTransactionAsync({
|
|
||||||
data,
|
|
||||||
to: orderMatcher.address,
|
|
||||||
from: owner,
|
|
||||||
gas: constants.MAX_MATCH_ORDERS_GAS,
|
|
||||||
});
|
|
||||||
return expect(tx).to.revertWith(expectedError);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('withdrawAsset', () => {
|
|
||||||
it('should allow owner to withdraw ERC20 tokens', async () => {
|
|
||||||
const erc20AWithdrawAmount = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
expect(erc20AWithdrawAmount).to.be.bignumber.gt(constants.ZERO_AMOUNT);
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await orderMatcher.withdrawAsset(leftMakerAssetData, erc20AWithdrawAmount).sendTransactionAsync({
|
|
||||||
from: owner,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const newBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
expect(newBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
|
|
||||||
});
|
|
||||||
it('should allow owner to withdraw ERC721 tokens', async () => {
|
|
||||||
const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
|
||||||
erc721Artifacts.DummyERC721Token,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
constants.DUMMY_TOKEN_NAME,
|
|
||||||
constants.DUMMY_TOKEN_SYMBOL,
|
|
||||||
);
|
|
||||||
const tokenId = new BigNumber(1);
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await erc721Token.mint(orderMatcher.address, tokenId).sendTransactionAsync({ from: owner }),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const assetData = await devUtils.encodeERC721AssetData(erc721Token.address, tokenId).callAsync();
|
|
||||||
const withdrawAmount = new BigNumber(1);
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await orderMatcher.withdrawAsset(assetData, withdrawAmount).sendTransactionAsync({ from: owner }),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const erc721Owner = await erc721Token.ownerOf(tokenId).callAsync();
|
|
||||||
expect(erc721Owner).to.be.equal(owner);
|
|
||||||
});
|
|
||||||
it('should revert if not called by owner', async () => {
|
|
||||||
const erc20AWithdrawAmount = await erc20TokenA.balanceOf(orderMatcher.address).callAsync();
|
|
||||||
expect(erc20AWithdrawAmount).to.be.bignumber.gt(constants.ZERO_AMOUNT);
|
|
||||||
const tx = orderMatcher.withdrawAsset(leftMakerAssetData, erc20AWithdrawAmount).sendTransactionAsync({
|
|
||||||
from: takerAddress,
|
|
||||||
});
|
|
||||||
return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('approveAssetProxy', () => {
|
|
||||||
it('should be able to set an allowance for ERC20 tokens', async () => {
|
|
||||||
const allowance = new BigNumber(55465465426546);
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await orderMatcher.approveAssetProxy(leftMakerAssetData, allowance).sendTransactionAsync({
|
|
||||||
from: owner,
|
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const newAllowance = await erc20TokenA.allowance(orderMatcher.address, erc20Proxy.address).callAsync();
|
|
||||||
expect(newAllowance).to.be.bignumber.equal(allowance);
|
|
||||||
});
|
|
||||||
it('should be able to approve an ERC721 token by passing in allowance = 1', async () => {
|
|
||||||
const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
|
||||||
erc721Artifacts.DummyERC721Token,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
constants.DUMMY_TOKEN_NAME,
|
|
||||||
constants.DUMMY_TOKEN_SYMBOL,
|
|
||||||
);
|
|
||||||
const assetData = await devUtils
|
|
||||||
.encodeERC721AssetData(erc721Token.address, constants.ZERO_AMOUNT)
|
|
||||||
.callAsync();
|
|
||||||
const allowance = new BigNumber(1);
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await orderMatcher.approveAssetProxy(assetData, allowance).sendTransactionAsync({ from: owner }),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const isApproved = await erc721Token
|
|
||||||
.isApprovedForAll(orderMatcher.address, erc721Proxy.address)
|
|
||||||
.callAsync();
|
|
||||||
expect(isApproved).to.be.equal(true);
|
|
||||||
});
|
|
||||||
it('should be able to approve an ERC721 token by passing in allowance > 1', async () => {
|
|
||||||
const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
|
|
||||||
erc721Artifacts.DummyERC721Token,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
artifacts,
|
|
||||||
constants.DUMMY_TOKEN_NAME,
|
|
||||||
constants.DUMMY_TOKEN_SYMBOL,
|
|
||||||
);
|
|
||||||
const assetData = await devUtils
|
|
||||||
.encodeERC721AssetData(erc721Token.address, constants.ZERO_AMOUNT)
|
|
||||||
.callAsync();
|
|
||||||
const allowance = new BigNumber(2);
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await orderMatcher.approveAssetProxy(assetData, allowance).sendTransactionAsync({ from: owner }),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
const isApproved = await erc721Token
|
|
||||||
.isApprovedForAll(orderMatcher.address, erc721Proxy.address)
|
|
||||||
.callAsync();
|
|
||||||
expect(isApproved).to.be.equal(true);
|
|
||||||
});
|
|
||||||
it('should revert if not called by owner', async () => {
|
|
||||||
const approval = new BigNumber(1);
|
|
||||||
const tx = orderMatcher.approveAssetProxy(leftMakerAssetData, approval).sendTransactionAsync({
|
|
||||||
from: takerAddress,
|
|
||||||
});
|
|
||||||
return expect(tx).to.revertWith(RevertReason.OnlyContractOwner);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// tslint:disable:max-file-line-count
|
|
||||||
// tslint:enable:no-unnecessary-type-assertion
|
|
@ -1,253 +0,0 @@
|
|||||||
import { ExchangeContract } from '@0x/contracts-exchange';
|
|
||||||
import {
|
|
||||||
FillResults,
|
|
||||||
formatters,
|
|
||||||
LogDecoder,
|
|
||||||
OrderInfo,
|
|
||||||
orderUtils,
|
|
||||||
TransactionFactory,
|
|
||||||
Web3ProviderEngine,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { SignedOrder } from '@0x/types';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
|
||||||
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
|
||||||
|
|
||||||
import { BalanceThresholdFilterContract } from './wrappers';
|
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
|
||||||
|
|
||||||
export class BalanceThresholdWrapper {
|
|
||||||
private readonly _balanceThresholdFilter: BalanceThresholdFilterContract;
|
|
||||||
private readonly _signerTransactionFactory: TransactionFactory;
|
|
||||||
private readonly _exchange: ExchangeContract;
|
|
||||||
private readonly _web3Wrapper: Web3Wrapper;
|
|
||||||
private readonly _logDecoder: LogDecoder;
|
|
||||||
constructor(
|
|
||||||
balanceThresholdFilter: BalanceThresholdFilterContract,
|
|
||||||
exchangeContract: ExchangeContract,
|
|
||||||
signerTransactionFactory: TransactionFactory,
|
|
||||||
provider: Web3ProviderEngine,
|
|
||||||
) {
|
|
||||||
this._balanceThresholdFilter = balanceThresholdFilter;
|
|
||||||
this._exchange = exchangeContract;
|
|
||||||
this._signerTransactionFactory = signerTransactionFactory;
|
|
||||||
this._web3Wrapper = new Web3Wrapper(provider);
|
|
||||||
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
|
|
||||||
}
|
|
||||||
public async fillOrderAsync(
|
|
||||||
signedOrder: SignedOrder,
|
|
||||||
from: string,
|
|
||||||
opts: { takerAssetFillAmount?: BigNumber } = {},
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
|
|
||||||
const data = this._exchange
|
|
||||||
.fillOrder(params.order, params.takerAssetFillAmount, params.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async fillOrKillOrderAsync(
|
|
||||||
signedOrder: SignedOrder,
|
|
||||||
from: string,
|
|
||||||
opts: { takerAssetFillAmount?: BigNumber } = {},
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
|
|
||||||
const data = this._exchange
|
|
||||||
.fillOrKillOrder(params.order, params.takerAssetFillAmount, params.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async fillOrderNoThrowAsync(
|
|
||||||
signedOrder: SignedOrder,
|
|
||||||
from: string,
|
|
||||||
opts: { takerAssetFillAmount?: BigNumber; gas?: number } = {},
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
|
|
||||||
const data = this._exchange
|
|
||||||
.fillOrderNoThrow(params.order, params.takerAssetFillAmount, params.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from, opts.gas);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async batchFillOrdersAsync(
|
|
||||||
orders: SignedOrder[],
|
|
||||||
from: string,
|
|
||||||
opts: { takerAssetFillAmounts?: BigNumber[] } = {},
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts);
|
|
||||||
const data = this._exchange
|
|
||||||
.batchFillOrders(params.orders, params.takerAssetFillAmounts, params.signatures)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async batchFillOrKillOrdersAsync(
|
|
||||||
orders: SignedOrder[],
|
|
||||||
from: string,
|
|
||||||
opts: { takerAssetFillAmounts?: BigNumber[] } = {},
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts);
|
|
||||||
const data = this._exchange
|
|
||||||
.batchFillOrKillOrders(params.orders, params.takerAssetFillAmounts, params.signatures)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async batchFillOrdersNoThrowAsync(
|
|
||||||
orders: SignedOrder[],
|
|
||||||
from: string,
|
|
||||||
opts: { takerAssetFillAmounts?: BigNumber[]; gas?: number } = {},
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts);
|
|
||||||
const data = this._exchange
|
|
||||||
.batchFillOrKillOrders(params.orders, params.takerAssetFillAmounts, params.signatures)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from, opts.gas);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async marketSellOrdersAsync(
|
|
||||||
orders: SignedOrder[],
|
|
||||||
from: string,
|
|
||||||
opts: { takerAssetFillAmount: BigNumber },
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount);
|
|
||||||
const data = this._exchange
|
|
||||||
.marketSellOrders(params.orders, params.takerAssetFillAmount, params.signatures)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async marketSellOrdersNoThrowAsync(
|
|
||||||
orders: SignedOrder[],
|
|
||||||
from: string,
|
|
||||||
opts: { takerAssetFillAmount: BigNumber; gas?: number },
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount);
|
|
||||||
const data = this._exchange
|
|
||||||
.marketSellOrdersNoThrow(params.orders, params.takerAssetFillAmount, params.signatures)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from, opts.gas);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async marketBuyOrdersAsync(
|
|
||||||
orders: SignedOrder[],
|
|
||||||
from: string,
|
|
||||||
opts: { makerAssetFillAmount: BigNumber },
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = formatters.createMarketBuyOrders(orders, opts.makerAssetFillAmount);
|
|
||||||
const data = this._exchange
|
|
||||||
.marketBuyOrders(params.orders, params.makerAssetFillAmount, params.signatures)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async marketBuyOrdersNoThrowAsync(
|
|
||||||
orders: SignedOrder[],
|
|
||||||
from: string,
|
|
||||||
opts: { makerAssetFillAmount: BigNumber; gas?: number },
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = formatters.createMarketBuyOrders(orders, opts.makerAssetFillAmount);
|
|
||||||
const data = this._exchange
|
|
||||||
.marketBuyOrdersNoThrow(params.orders, params.makerAssetFillAmount, params.signatures)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from, opts.gas);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async cancelOrderAsync(signedOrder: SignedOrder, from: string): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = orderUtils.createCancel(signedOrder);
|
|
||||||
const data = this._exchange.cancelOrder(params.order).getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async batchCancelOrdersAsync(
|
|
||||||
orders: SignedOrder[],
|
|
||||||
from: string,
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = formatters.createBatchCancel(orders);
|
|
||||||
const data = this._exchange.batchCancelOrders(params.orders).getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async cancelOrdersUpToAsync(salt: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const data = this._exchange.cancelOrdersUpTo(salt).getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> {
|
|
||||||
const filledAmount = await this._exchange.filled(orderHashHex).callAsync();
|
|
||||||
return filledAmount;
|
|
||||||
}
|
|
||||||
public async isCancelledAsync(orderHashHex: string): Promise<boolean> {
|
|
||||||
const isCancelled = await this._exchange.cancelled(orderHashHex).callAsync();
|
|
||||||
return isCancelled;
|
|
||||||
}
|
|
||||||
public async getOrderEpochAsync(makerAddress: string, senderAddress: string): Promise<BigNumber> {
|
|
||||||
const orderEpoch = await this._exchange.orderEpoch(makerAddress, senderAddress).callAsync();
|
|
||||||
return orderEpoch;
|
|
||||||
}
|
|
||||||
public async getOrderInfoAsync(signedOrder: SignedOrder): Promise<OrderInfo> {
|
|
||||||
const orderInfo = await this._exchange.getOrderInfo(signedOrder).callAsync();
|
|
||||||
return orderInfo;
|
|
||||||
}
|
|
||||||
public async getOrdersInfoAsync(signedOrders: SignedOrder[]): Promise<OrderInfo[]> {
|
|
||||||
const ordersInfo = (await this._exchange.getOrdersInfo(signedOrders)).callAsync() as OrderInfo[];
|
|
||||||
return ordersInfo;
|
|
||||||
}
|
|
||||||
public async matchOrdersAsync(
|
|
||||||
signedOrderLeft: SignedOrder,
|
|
||||||
signedOrderRight: SignedOrder,
|
|
||||||
from: string,
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = orderUtils.createMatchOrders(signedOrderLeft, signedOrderRight);
|
|
||||||
const data = this._exchange
|
|
||||||
.matchOrders(params.left, params.right, params.leftSignature, params.rightSignature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
const txReceipt = this._executeTransactionAsync(data, from);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
public async getFillOrderResultsAsync(
|
|
||||||
signedOrder: SignedOrder,
|
|
||||||
from: string,
|
|
||||||
opts: { takerAssetFillAmount?: BigNumber } = {},
|
|
||||||
): Promise<FillResults> {
|
|
||||||
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
|
|
||||||
const fillResults = await this._exchange
|
|
||||||
.fillOrder(params.order, params.takerAssetFillAmount, params.signature)
|
|
||||||
.callAsync({ from });
|
|
||||||
return fillResults;
|
|
||||||
}
|
|
||||||
public abiEncodeFillOrder(signedOrder: SignedOrder, opts: { takerAssetFillAmount?: BigNumber } = {}): string {
|
|
||||||
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
|
|
||||||
const data = this._exchange
|
|
||||||
.fillOrder(params.order, params.takerAssetFillAmount, params.signature)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
public getBalanceThresholdAddress(): string {
|
|
||||||
return this._balanceThresholdFilter.address;
|
|
||||||
}
|
|
||||||
public getExchangeAddress(): string {
|
|
||||||
return this._exchange.address;
|
|
||||||
}
|
|
||||||
private async _executeTransactionAsync(
|
|
||||||
abiEncodedExchangeTxData: string,
|
|
||||||
from: string,
|
|
||||||
gas?: number,
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const signedExchangeTx = this._signerTransactionFactory.newSignedTransaction(abiEncodedExchangeTxData);
|
|
||||||
const txOpts = gas === undefined ? { from } : { from, gas };
|
|
||||||
const txHash = await this._balanceThresholdFilter
|
|
||||||
.executeTransaction(
|
|
||||||
signedExchangeTx.salt,
|
|
||||||
signedExchangeTx.signerAddress,
|
|
||||||
signedExchangeTx.data,
|
|
||||||
signedExchangeTx.signature,
|
|
||||||
txOpts,
|
|
||||||
)
|
|
||||||
.sendTransactionAsync();
|
|
||||||
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
|
||||||
return txReceipt;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
import { artifacts as erc721Artifacts } from '@0x/contracts-erc721';
|
|
||||||
import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange';
|
|
||||||
import { LogDecoder, Web3ProviderEngine } from '@0x/contracts-test-utils';
|
|
||||||
import { DutchAuctionDetails, SignedOrder } from '@0x/types';
|
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
|
||||||
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction';
|
|
||||||
import { artifacts } from '../../src/artifacts';
|
|
||||||
|
|
||||||
export class DutchAuctionTestWrapper {
|
|
||||||
private readonly _dutchAuctionContract: DutchAuctionContract;
|
|
||||||
private readonly _web3Wrapper: Web3Wrapper;
|
|
||||||
private readonly _logDecoder: LogDecoder;
|
|
||||||
|
|
||||||
constructor(contractInstance: DutchAuctionContract, provider: Web3ProviderEngine) {
|
|
||||||
this._dutchAuctionContract = contractInstance;
|
|
||||||
this._web3Wrapper = new Web3Wrapper(provider);
|
|
||||||
this._logDecoder = new LogDecoder(this._web3Wrapper, {
|
|
||||||
...artifacts,
|
|
||||||
...exchangeArtifacts,
|
|
||||||
...erc721Artifacts,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Matches the buy and sell orders at an amount given the following: the current block time, the auction
|
|
||||||
* start time and the auction begin amount. The sell order is a an order at the lowest amount
|
|
||||||
* at the end of the auction. Excess from the match is transferred to the seller.
|
|
||||||
* Over time the price moves from beginAmount to endAmount given the current block.timestamp.
|
|
||||||
* @param buyOrder The Buyer's order. This order is for the current expected price of the auction.
|
|
||||||
* @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction).
|
|
||||||
* @param from Address the transaction is being sent from.
|
|
||||||
* @return Transaction receipt with decoded logs.
|
|
||||||
*/
|
|
||||||
public async matchOrdersAsync(
|
|
||||||
buyOrder: SignedOrder,
|
|
||||||
sellOrder: SignedOrder,
|
|
||||||
from: string,
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const txHash = await this._dutchAuctionContract
|
|
||||||
.matchOrders(buyOrder, sellOrder, buyOrder.signature, sellOrder.signature)
|
|
||||||
.sendTransactionAsync({
|
|
||||||
from,
|
|
||||||
});
|
|
||||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
|
||||||
return tx;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Calculates the Auction Details for the given order
|
|
||||||
* @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction).
|
|
||||||
* @return The dutch auction details.
|
|
||||||
*/
|
|
||||||
public async getAuctionDetailsAsync(sellOrder: SignedOrder): Promise<DutchAuctionDetails> {
|
|
||||||
const auctionDetails = await this._dutchAuctionContract.getAuctionDetails(sellOrder).callAsync();
|
|
||||||
return auctionDetails;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
import { BigNumber } from '@0x/utils';
|
|
||||||
import * as ethAbi from 'ethereumjs-abi';
|
|
||||||
import * as ethUtil from 'ethereumjs-util';
|
|
||||||
|
|
||||||
export * from './balance_threshold_wrapper';
|
|
||||||
export * from './dutch_auction_test_wrapper';
|
|
||||||
|
|
||||||
// tslint:disable-next-line:completed-docs
|
|
||||||
export function encodeDutchAuctionAssetData(
|
|
||||||
assetData: string,
|
|
||||||
beginTimeSeconds: BigNumber,
|
|
||||||
beginAmount: BigNumber,
|
|
||||||
): string {
|
|
||||||
const assetDataBuffer = ethUtil.toBuffer(assetData);
|
|
||||||
const abiEncodedAuctionData = (ethAbi as any).rawEncode(
|
|
||||||
['uint256', 'uint256'],
|
|
||||||
[beginTimeSeconds.toString(), beginAmount.toString()],
|
|
||||||
);
|
|
||||||
const abiEncodedAuctionDataBuffer = ethUtil.toBuffer(abiEncodedAuctionData);
|
|
||||||
const dutchAuctionDataBuffer = Buffer.concat([assetDataBuffer, abiEncodedAuctionDataBuffer]);
|
|
||||||
const dutchAuctionData = ethUtil.bufferToHex(dutchAuctionDataBuffer);
|
|
||||||
return dutchAuctionData;
|
|
||||||
}
|
|
9
contracts/extensions/test/wrappers.ts
Normal file
9
contracts/extensions/test/wrappers.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
export * from '../test/generated-wrappers/lib_asset_data_transfer';
|
||||||
|
export * from '../test/generated-wrappers/lib_asset_data_transfer_rich_errors';
|
||||||
|
export * from '../test/generated-wrappers/lib_weth_utils_rich_errors';
|
||||||
|
export * from '../test/generated-wrappers/mixin_weth_utils';
|
@ -3,12 +3,14 @@
|
|||||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||||
"files": [
|
"files": [
|
||||||
"generated-artifacts/BalanceThresholdFilter.json",
|
"generated-artifacts/LibAssetDataTransfer.json",
|
||||||
"generated-artifacts/DutchAuction.json",
|
"generated-artifacts/LibAssetDataTransferRichErrors.json",
|
||||||
"generated-artifacts/Exchange.json",
|
"generated-artifacts/LibWethUtilsRichErrors.json",
|
||||||
"generated-artifacts/ExchangeWrapper.json",
|
"generated-artifacts/MixinWethUtils.json",
|
||||||
"generated-artifacts/OrderMatcher.json",
|
"test/generated-artifacts/LibAssetDataTransfer.json",
|
||||||
"generated-artifacts/WETH9.json"
|
"test/generated-artifacts/LibAssetDataTransferRichErrors.json",
|
||||||
|
"test/generated-artifacts/LibWethUtilsRichErrors.json",
|
||||||
|
"test/generated-artifacts/MixinWethUtils.json"
|
||||||
],
|
],
|
||||||
"exclude": ["./deploy/solc/solc_bin"]
|
"exclude": ["./deploy/solc/solc_bin"]
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
"@0x/contracts-dev-utils": "^1.0.5",
|
"@0x/contracts-dev-utils": "^1.0.5",
|
||||||
"@0x/contracts-exchange-forwarder": "^4.0.5",
|
"@0x/contracts-exchange-forwarder": "^4.0.5",
|
||||||
"@0x/contracts-exchange-libs": "^4.1.1",
|
"@0x/contracts-exchange-libs": "^4.1.1",
|
||||||
|
"@0x/contracts-extensions": "^5.1.4",
|
||||||
"@0x/contracts-gen": "^2.0.5",
|
"@0x/contracts-gen": "^2.0.5",
|
||||||
"@0x/contracts-utils": "^4.2.0",
|
"@0x/contracts-utils": "^4.2.0",
|
||||||
"@0x/coordinator-server": "^1.0.5",
|
"@0x/coordinator-server": "^1.0.5",
|
||||||
|
@ -3,6 +3,7 @@ import { DummyERC20TokenContract } from '@0x/contracts-erc20';
|
|||||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||||
import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange';
|
import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange';
|
||||||
import { artifacts, ExchangeForwarderRevertErrors, ForwarderContract } from '@0x/contracts-exchange-forwarder';
|
import { artifacts, ExchangeForwarderRevertErrors, ForwarderContract } from '@0x/contracts-exchange-forwarder';
|
||||||
|
import { MixinWethUtilsRevertErrors } from '@0x/contracts-extensions';
|
||||||
import {
|
import {
|
||||||
blockchainTests,
|
blockchainTests,
|
||||||
constants,
|
constants,
|
||||||
@ -426,7 +427,7 @@ blockchainTests('Forwarder integration tests', env => {
|
|||||||
});
|
});
|
||||||
const forwarderFeeAmounts = [toBaseUnitAmount(0.2)];
|
const forwarderFeeAmounts = [toBaseUnitAmount(0.2)];
|
||||||
const forwarderFeeRecipientAddresses: string[] = [];
|
const forwarderFeeRecipientAddresses: string[] = [];
|
||||||
const revertError = new ExchangeForwarderRevertErrors.EthFeeLengthMismatchError(
|
const revertError = new MixinWethUtilsRevertErrors.EthFeeLengthMismatchError(
|
||||||
new BigNumber(forwarderFeeAmounts.length),
|
new BigNumber(forwarderFeeAmounts.length),
|
||||||
new BigNumber(forwarderFeeRecipientAddresses.length),
|
new BigNumber(forwarderFeeRecipientAddresses.length),
|
||||||
);
|
);
|
||||||
@ -443,7 +444,7 @@ blockchainTests('Forwarder integration tests', env => {
|
|||||||
});
|
});
|
||||||
const forwarderFeeAmounts: BigNumber[] = [];
|
const forwarderFeeAmounts: BigNumber[] = [];
|
||||||
const forwarderFeeRecipientAddresses = [randomAddress()];
|
const forwarderFeeRecipientAddresses = [randomAddress()];
|
||||||
const revertError = new ExchangeForwarderRevertErrors.EthFeeLengthMismatchError(
|
const revertError = new MixinWethUtilsRevertErrors.EthFeeLengthMismatchError(
|
||||||
new BigNumber(forwarderFeeAmounts.length),
|
new BigNumber(forwarderFeeAmounts.length),
|
||||||
new BigNumber(forwarderFeeRecipientAddresses.length),
|
new BigNumber(forwarderFeeRecipientAddresses.length),
|
||||||
);
|
);
|
||||||
@ -768,7 +769,7 @@ blockchainTests('Forwarder integration tests', env => {
|
|||||||
const order = await maker.signOrderAsync();
|
const order = await maker.signOrderAsync();
|
||||||
const forwarderFeeAmounts = [toBaseUnitAmount(1)];
|
const forwarderFeeAmounts = [toBaseUnitAmount(1)];
|
||||||
const value = forwarderFeeAmounts[0].minus(1);
|
const value = forwarderFeeAmounts[0].minus(1);
|
||||||
const revertError = new ExchangeForwarderRevertErrors.InsufficientEthForFeeError(
|
const revertError = new MixinWethUtilsRevertErrors.InsufficientEthForFeeError(
|
||||||
forwarderFeeAmounts[0],
|
forwarderFeeAmounts[0],
|
||||||
value,
|
value,
|
||||||
);
|
);
|
||||||
|
@ -25,9 +25,9 @@
|
|||||||
"install:all": "yarn install",
|
"install:all": "yarn install",
|
||||||
"wsrun": "wsrun",
|
"wsrun": "wsrun",
|
||||||
"lerna": "lerna",
|
"lerna": "lerna",
|
||||||
"build": "lerna link && wsrun build $PKG -r --stages --fast-exit --exclude-missing --exclude @0x/contracts-extensions",
|
"build": "lerna link && wsrun build $PKG -r --stages --fast-exit --exclude-missing",
|
||||||
"build:ci": "lerna link && wsrun build:ci $PKG --fast-exit -r --stages --exclude-missing --exclude @0x/contracts-extensions",
|
"build:ci": "lerna link && wsrun build:ci $PKG --fast-exit -r --stages --exclude-missing",
|
||||||
"build:contracts": "lerna link && wsrun build -p ${npm_package_config_contractsPackages} -c --fast-exit -r --stages --exclude-missing --exclude @0x/contracts-extensions",
|
"build:contracts": "lerna link && wsrun build -p ${npm_package_config_contractsPackages} -c --fast-exit -r --stages --exclude-missing",
|
||||||
"build:monorepo_scripts": "PKG=@0x/monorepo-scripts yarn build",
|
"build:monorepo_scripts": "PKG=@0x/monorepo-scripts yarn build",
|
||||||
"build:ts": "tsc -b",
|
"build:ts": "tsc -b",
|
||||||
"watch:ts": "tsc -b -w",
|
"watch:ts": "tsc -b -w",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"contracts:watch": "wsrun watch $PKG --parallel --exclude-missing",
|
"contracts:watch": "wsrun watch $PKG --parallel --exclude-missing",
|
||||||
"remove_node_modules": "lerna clean --yes; rm -rf node_modules",
|
"remove_node_modules": "lerna clean --yes; rm -rf node_modules",
|
||||||
"rebuild": "run-s clean build",
|
"rebuild": "run-s clean build",
|
||||||
"test": "wsrun test $PKG --fast-exit --serial --exclude-missing --exclude @0x/asset-swapper --exclude @0x/orderbook --exclude @0x/contracts-extensions",
|
"test": "wsrun test $PKG --fast-exit --serial --exclude-missing --exclude @0x/asset-swapper --exclude @0x/orderbook",
|
||||||
"test:contracts": "wsrun test -p ${npm_package_config_contractsPackages} -c --fast-exit --exclude-missing",
|
"test:contracts": "wsrun test -p ${npm_package_config_contractsPackages} -c --fast-exit --exclude-missing",
|
||||||
"generate_doc": "node ./packages/monorepo-scripts/lib/doc_generate.js",
|
"generate_doc": "node ./packages/monorepo-scripts/lib/doc_generate.js",
|
||||||
"upload_md_docs": "aws s3 rm --recursive s3://docs-markdown; wsrun s3:sync_md_docs --exclude-missing",
|
"upload_md_docs": "aws s3 rm --recursive s3://docs-markdown; wsrun s3:sync_md_docs --exclude-missing",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
{
|
{
|
||||||
"note": "Added Broker revert errors",
|
"note": "Added Broker, MixinWethUtils revert errors",
|
||||||
"pr": 2455
|
"pr": 2455
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -30,9 +30,10 @@ export {
|
|||||||
export import BrokerRevertErrors = require('./revert_errors/broker/revert_errors');
|
export import BrokerRevertErrors = require('./revert_errors/broker/revert_errors');
|
||||||
export import CoordinatorRevertErrors = require('./revert_errors/coordinator/revert_errors');
|
export import CoordinatorRevertErrors = require('./revert_errors/coordinator/revert_errors');
|
||||||
export import ExchangeForwarderRevertErrors = require('./revert_errors/exchange-forwarder/revert_errors');
|
export import ExchangeForwarderRevertErrors = require('./revert_errors/exchange-forwarder/revert_errors');
|
||||||
export import LibAssetDataTransferRevertErrors = require('./revert_errors/exchange-libs/lib_asset_data_transfer_revert_errors');
|
|
||||||
export import LibMathRevertErrors = require('./revert_errors/exchange-libs/lib_math_revert_errors');
|
export import LibMathRevertErrors = require('./revert_errors/exchange-libs/lib_math_revert_errors');
|
||||||
export import ExchangeRevertErrors = require('./revert_errors/exchange/revert_errors');
|
export import ExchangeRevertErrors = require('./revert_errors/exchange/revert_errors');
|
||||||
|
export import LibAssetDataTransferRevertErrors = require('./revert_errors/extensions/lib_asset_data_transfer_revert_errors');
|
||||||
|
export import MixinWethUtilsRevertErrors = require('./revert_errors/extensions/mixin_weth_utils_revert_errors');
|
||||||
export import FixedMathRevertErrors = require('./revert_errors/staking/fixed_math_revert_errors');
|
export import FixedMathRevertErrors = require('./revert_errors/staking/fixed_math_revert_errors');
|
||||||
export import StakingRevertErrors = require('./revert_errors/staking/staking_revert_errors');
|
export import StakingRevertErrors = require('./revert_errors/staking/staking_revert_errors');
|
||||||
export import AuthorizableRevertErrors = require('./revert_errors/utils/authorizable_revert_errors');
|
export import AuthorizableRevertErrors = require('./revert_errors/utils/authorizable_revert_errors');
|
||||||
|
@ -28,16 +28,6 @@ export class UnsupportedFeeError extends RevertError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InsufficientEthForFeeError extends RevertError {
|
|
||||||
constructor(ethFeeRequired?: BigNumber | number | string, ethAvailable?: BigNumber | number | string) {
|
|
||||||
super(
|
|
||||||
'InsufficientEthForFeeError',
|
|
||||||
'InsufficientEthForFeeError(uint256 ethFeeRequired, uint256 ethAvailable)',
|
|
||||||
{ ethFeeRequired, ethAvailable },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class OverspentWethError extends RevertError {
|
export class OverspentWethError extends RevertError {
|
||||||
constructor(wethSpent?: BigNumber | number | string, msgValue?: BigNumber | number | string) {
|
constructor(wethSpent?: BigNumber | number | string, msgValue?: BigNumber | number | string) {
|
||||||
super('OverspentWethError', 'OverspentWethError(uint256 wethSpent, uint256 msgValue)', {
|
super('OverspentWethError', 'OverspentWethError(uint256 wethSpent, uint256 msgValue)', {
|
||||||
@ -47,42 +37,18 @@ export class OverspentWethError extends RevertError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DefaultFunctionWethContractOnlyError extends RevertError {
|
|
||||||
constructor(senderAddress?: string) {
|
|
||||||
super('DefaultFunctionWethContractOnlyError', 'DefaultFunctionWethContractOnlyError(address senderAddress)', {
|
|
||||||
senderAddress,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MsgValueCannotEqualZeroError extends RevertError {
|
export class MsgValueCannotEqualZeroError extends RevertError {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('MsgValueCannotEqualZeroError', 'MsgValueCannotEqualZeroError()', {});
|
super('MsgValueCannotEqualZeroError', 'MsgValueCannotEqualZeroError()', {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EthFeeLengthMismatchError extends RevertError {
|
|
||||||
constructor(ethFeesLength?: BigNumber | number | string, feeRecipientsLength?: BigNumber | number | string) {
|
|
||||||
super(
|
|
||||||
'EthFeeLengthMismatchError',
|
|
||||||
'EthFeeLengthMismatchError(uint256 ethFeesLength, uint256 feeRecipientsLength)',
|
|
||||||
{
|
|
||||||
ethFeesLength,
|
|
||||||
feeRecipientsLength,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const types = [
|
const types = [
|
||||||
UnregisteredAssetProxyError,
|
UnregisteredAssetProxyError,
|
||||||
CompleteBuyFailedError,
|
CompleteBuyFailedError,
|
||||||
UnsupportedFeeError,
|
UnsupportedFeeError,
|
||||||
InsufficientEthForFeeError,
|
|
||||||
OverspentWethError,
|
OverspentWethError,
|
||||||
DefaultFunctionWethContractOnlyError,
|
|
||||||
MsgValueCannotEqualZeroError,
|
MsgValueCannotEqualZeroError,
|
||||||
EthFeeLengthMismatchError,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Register the types we've defined.
|
// Register the types we've defined.
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
import { BigNumber } from '../../configured_bignumber';
|
||||||
|
import { RevertError } from '../../revert_error';
|
||||||
|
|
||||||
|
// tslint:disable:max-classes-per-file
|
||||||
|
|
||||||
|
export class UnregisteredAssetProxyError extends RevertError {
|
||||||
|
constructor() {
|
||||||
|
super('UnregisteredAssetProxyError', 'UnregisteredAssetProxyError()', {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InsufficientEthForFeeError extends RevertError {
|
||||||
|
constructor(ethFeeRequired?: BigNumber | number | string, ethAvailable?: BigNumber | number | string) {
|
||||||
|
super(
|
||||||
|
'InsufficientEthForFeeError',
|
||||||
|
'InsufficientEthForFeeError(uint256 ethFeeRequired, uint256 ethAvailable)',
|
||||||
|
{ ethFeeRequired, ethAvailable },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DefaultFunctionWethContractOnlyError extends RevertError {
|
||||||
|
constructor(senderAddress?: string) {
|
||||||
|
super('DefaultFunctionWethContractOnlyError', 'DefaultFunctionWethContractOnlyError(address senderAddress)', {
|
||||||
|
senderAddress,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EthFeeLengthMismatchError extends RevertError {
|
||||||
|
constructor(ethFeesLength?: BigNumber | number | string, feeRecipientsLength?: BigNumber | number | string) {
|
||||||
|
super(
|
||||||
|
'EthFeeLengthMismatchError',
|
||||||
|
'EthFeeLengthMismatchError(uint256 ethFeesLength, uint256 feeRecipientsLength)',
|
||||||
|
{
|
||||||
|
ethFeesLength,
|
||||||
|
feeRecipientsLength,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const types = [InsufficientEthForFeeError, DefaultFunctionWethContractOnlyError, EthFeeLengthMismatchError];
|
||||||
|
|
||||||
|
// Register the types we've defined.
|
||||||
|
for (const type of types) {
|
||||||
|
RevertError.registerType(type);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user