Merge pull request #1383 from 0xProject/feature/contracts/balanceThresholdFilter

Balance Threshold Filter
This commit is contained in:
Greg Hysen 2018-12-18 13:56:18 -08:00 committed by GitHub
commit 38346a9aae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 2675 additions and 3 deletions

View File

@ -1,4 +1,13 @@
[
{
"version": "1.1.0",
"changes": [
{
"note": "Added Balance Threshold Filter",
"pr": 1383
}
]
},
{
"timestamp": 1544741676,
"version": "1.0.2",

View File

@ -18,5 +18,5 @@
}
}
},
"contracts": ["DutchAuction", "Forwarder"]
"contracts": ["BalanceThresholdFilter", "DutchAuction", "Forwarder"]
}

View File

@ -0,0 +1,45 @@
/*
Copyright 2018 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.4.24;
import "@0x/contracts-interfaces/contracts/protocol/Exchange/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;
}
}

View File

@ -0,0 +1,135 @@
/*
Copyright 2018 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.4.24;
import "@0x/contracts-libs/contracts/libs/LibExchangeSelectors.sol";
import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
import "./mixins/MBalanceThresholdFilterCore.sol";
import "./MixinExchangeCalldata.sol";
contract MixinBalanceThresholdFilterCore is
MBalanceThresholdFilterCore,
MixinExchangeCalldata,
LibOrder,
LibExchangeSelectors
{
/// @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 signedExchangeTransaction,
bytes 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);
// All addresses are valid. Execute exchange function.
EXCHANGE.executeTransaction(
salt,
signerAddress,
signedExchangeTransaction,
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 == BATCH_FILL_ORDERS_SELECTOR ||
exchangeFunctionSelector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR ||
exchangeFunctionSelector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR ||
exchangeFunctionSelector == MARKET_BUY_ORDERS_SELECTOR ||
exchangeFunctionSelector == MARKET_BUY_ORDERS_NO_THROW_SELECTOR ||
exchangeFunctionSelector == MARKET_SELL_ORDERS_SELECTOR ||
exchangeFunctionSelector == MARKET_SELL_ORDERS_NO_THROW_SELECTOR
) {
addressesToValidate = loadMakerAddressesFromOrderArray(0);
addressesToValidate = addressesToValidate.append(signerAddress);
} else if (
exchangeFunctionSelector == FILL_ORDER_SELECTOR ||
exchangeFunctionSelector == FILL_ORDER_NO_THROW_SELECTOR ||
exchangeFunctionSelector == FILL_OR_KILL_ORDER_SELECTOR
) {
address makerAddress = loadMakerAddressFromOrder(0);
addressesToValidate = addressesToValidate.append(makerAddress);
addressesToValidate = addressesToValidate.append(signerAddress);
} else if (exchangeFunctionSelector == MATCH_ORDERS_SELECTOR) {
address leftMakerAddress = loadMakerAddressFromOrder(0);
addressesToValidate = addressesToValidate.append(leftMakerAddress);
address rightMakerAddress = loadMakerAddressFromOrder(1);
addressesToValidate = addressesToValidate.append(rightMakerAddress);
addressesToValidate = addressesToValidate.append(signerAddress);
} else if (
exchangeFunctionSelector != CANCEL_ORDER_SELECTOR &&
exchangeFunctionSelector != BATCH_CANCEL_ORDERS_SELECTOR &&
exchangeFunctionSelector != CANCEL_ORDERS_UP_TO_SELECTOR
) {
revert("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR");
}
// solhint-enable expression-indent
return addressesToValidate;
}
}

View File

@ -0,0 +1,103 @@
/*
Copyright 2018 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.4.24;
import "./mixins/MExchangeCalldata.sol";
import "@0x/contracts-libs/contracts/libs/LibAddressArray.sol";
contract MixinExchangeCalldata is
MExchangeCalldata
{
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(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[] 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(loadExchangeData(orderPtr + orderArrayElementPtr));
makerAddresses = makerAddresses.append(makerAddress);
}
return makerAddresses;
}
}

View File

@ -0,0 +1,55 @@
/*
Copyright 2018 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.4.24;
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 signedExchangeTransaction,
bytes signature
)
external;
}

View File

@ -0,0 +1,31 @@
/*
Copyright 2018 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.4.24;
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);
}

View File

@ -0,0 +1,54 @@
/*
Copyright 2018 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.4.24;
import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol";
import "../interfaces/IThresholdAsset.sol";
import "../interfaces/IBalanceThresholdFilterCore.sol";
contract MBalanceThresholdFilterCore is
IBalanceThresholdFilterCore
{
// Points to 0x exchange contract
// solhint-disable var-name-mixedcase
IExchange internal EXCHANGE;
// The asset that must be held by makers/takers
IThresholdAsset internal THRESHOLD_ASSET;
// The minimum balance of `THRESHOLD_ASSET` that must be held by makers/takers
uint256 internal BALANCE_THRESHOLD;
// solhint-enable var-name-mixedcase
// Addresses that hold at least `BALANCE_THRESHOLD` of `THRESHOLD_ASSET`
event ValidatedAddresses (
address[] addresses
);
/// @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);
}

View File

@ -0,0 +1,56 @@
/*
Copyright 2018 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.4.24;
contract MExchangeCalldata {
/// @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);
/// @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);
/// @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);
/// @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[] makerAddresses);
}

View File

@ -31,7 +31,7 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "generated-artifacts/@(DutchAuction|Forwarder).json"
"abis": "generated-artifacts/@(BalanceThresholdFilter|DutchAuction|Forwarder).json"
},
"repository": {
"type": "git",

View File

@ -1,9 +1,11 @@
import { ContractArtifact } from 'ethereum-types';
import * as BalanceThresholdFilter from '../../generated-artifacts/BalanceThresholdFilter.json';
import * as DutchAuction from '../../generated-artifacts/DutchAuction.json';
import * as Forwarder from '../../generated-artifacts/Forwarder.json';
export const artifacts = {
BalanceThresholdFilter: BalanceThresholdFilter as ContractArtifact,
DutchAuction: DutchAuction as ContractArtifact,
Forwarder: Forwarder as ContractArtifact,
};

View File

@ -1,2 +1,3 @@
export * from '../../generated-wrappers/balance_threshold_filter';
export * from '../../generated-wrappers/dutch_auction';
export * from '../../generated-wrappers/forwarder';

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,283 @@
import { artifacts as protocolArtifacts, ExchangeContract } from '@0x/contracts-protocol';
import {
FillResults,
formatters,
LogDecoder,
OrderInfo,
orderUtils,
TransactionFactory,
} from '@0x/contracts-test-utils';
import { artifacts as tokensArtifacts } from '@0x/contracts-tokens';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { BalanceThresholdFilterContract } from '../../generated-wrappers/balance_threshold_filter';
import { artifacts } from '../../src/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: Provider,
) {
this._balanceThresholdFilter = balanceThresholdFilter;
this._exchange = exchangeContract;
this._signerTransactionFactory = signerTransactionFactory;
this._web3Wrapper = new Web3Wrapper(provider);
this._logDecoder = new LogDecoder(this._web3Wrapper, {
...artifacts,
...tokensArtifacts,
...protocolArtifacts,
});
}
public async fillOrderAsync(
signedOrder: SignedOrder,
from: string,
opts: { takerAssetFillAmount?: BigNumber } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
const data = this._exchange.fillOrder.getABIEncodedTransactionData(
params.order,
params.takerAssetFillAmount,
params.signature,
);
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.getABIEncodedTransactionData(
params.order,
params.takerAssetFillAmount,
params.signature,
);
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.getABIEncodedTransactionData(
params.order,
params.takerAssetFillAmount,
params.signature,
);
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.getABIEncodedTransactionData(
params.orders,
params.takerAssetFillAmounts,
params.signatures,
);
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.getABIEncodedTransactionData(
params.orders,
params.takerAssetFillAmounts,
params.signatures,
);
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.getABIEncodedTransactionData(
params.orders,
params.takerAssetFillAmounts,
params.signatures,
);
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.getABIEncodedTransactionData(
params.orders,
params.takerAssetFillAmount,
params.signatures,
);
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.getABIEncodedTransactionData(
params.orders,
params.takerAssetFillAmount,
params.signatures,
);
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.getABIEncodedTransactionData(
params.orders,
params.makerAssetFillAmount,
params.signatures,
);
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.getABIEncodedTransactionData(
params.orders,
params.makerAssetFillAmount,
params.signatures,
);
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.getABIEncodedTransactionData(params.order);
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.getABIEncodedTransactionData(params.orders);
const txReceipt = this._executeTransactionAsync(data, from);
return txReceipt;
}
public async cancelOrdersUpToAsync(salt: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> {
const data = this._exchange.cancelOrdersUpTo.getABIEncodedTransactionData(salt);
const txReceipt = this._executeTransactionAsync(data, from);
return txReceipt;
}
public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> {
const filledAmount = await this._exchange.filled.callAsync(orderHashHex);
return filledAmount;
}
public async isCancelledAsync(orderHashHex: string): Promise<boolean> {
const isCancelled = await this._exchange.cancelled.callAsync(orderHashHex);
return isCancelled;
}
public async getOrderEpochAsync(makerAddress: string, senderAddress: string): Promise<BigNumber> {
const orderEpoch = await this._exchange.orderEpoch.callAsync(makerAddress, senderAddress);
return orderEpoch;
}
public async getOrderInfoAsync(signedOrder: SignedOrder): Promise<OrderInfo> {
const orderInfo = await this._exchange.getOrderInfo.callAsync(signedOrder);
return orderInfo;
}
public async getOrdersInfoAsync(signedOrders: SignedOrder[]): Promise<OrderInfo[]> {
const ordersInfo = (await this._exchange.getOrdersInfo.callAsync(signedOrders)) as OrderInfo[];
return ordersInfo;
}
public async matchOrdersAsync(
signedOrderLeft: SignedOrder,
signedOrderRight: SignedOrder,
from: string,
): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createMatchOrders(signedOrderLeft, signedOrderRight);
const data = await this._exchange.matchOrders.getABIEncodedTransactionData(
params.left,
params.right,
params.leftSignature,
params.rightSignature,
);
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.callAsync(
params.order,
params.takerAssetFillAmount,
params.signature,
{ from },
);
return fillResults;
}
public abiEncodeFillOrder(signedOrder: SignedOrder, opts: { takerAssetFillAmount?: BigNumber } = {}): string {
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
const data = this._exchange.fillOrder.getABIEncodedTransactionData(
params.order,
params.takerAssetFillAmount,
params.signature,
);
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 = _.isUndefined(gas) ? { from } : { from, gas };
const txHash = await this._balanceThresholdFilter.executeTransaction.sendTransactionAsync(
signedExchangeTx.salt,
signedExchangeTx.signerAddress,
signedExchangeTx.data,
signedExchangeTx.signature,
txOpts,
);
const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return txReceipt;
}
}

View File

@ -6,6 +6,10 @@
"resolveJsonModule": true
},
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": ["./generated-artifacts/DutchAuction.json", "./generated-artifacts/Forwarder.json"],
"files": [
"./generated-artifacts/BalanceThresholdFilter.json",
"./generated-artifacts/DutchAuction.json",
"./generated-artifacts/Forwarder.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@ -0,0 +1,84 @@
/*
Copyright 2018 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.4.24;
import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
library LibAddressArray {
/// @dev Append a new address to an array of addresses.
/// The `addressArray` may need to be reallocated to make space
/// for the new address. Because of this we return the resulting
/// memory location of `addressArray`.
/// @param addressToAppend Address to append.
/// @return Array of addresses: [... addressArray, addressToAppend]
function append(address[] memory addressArray, address addressToAppend)
internal pure
returns (address[])
{
// Get stats on address array and free memory
uint256 freeMemPtr = 0;
uint256 addressArrayBeginPtr = 0;
uint256 addressArrayEndPtr = 0;
uint256 addressArrayLength = addressArray.length;
uint256 addressArrayMemSizeInBytes = 32 + (32 * addressArrayLength);
assembly {
freeMemPtr := mload(0x40)
addressArrayBeginPtr := addressArray
addressArrayEndPtr := add(addressArray, addressArrayMemSizeInBytes)
}
// Cases for `freeMemPtr`:
// `freeMemPtr` == `addressArrayEndPtr`: Nothing occupies memory after `addressArray`
// `freeMemPtr` > `addressArrayEndPtr`: Some value occupies memory after `addressArray`
// `freeMemPtr` < `addressArrayEndPtr`: Memory has not been managed properly.
require(
freeMemPtr >= addressArrayEndPtr,
"INVALID_FREE_MEMORY_PTR"
);
// If free memory begins at the end of `addressArray`
// then we can append `addressToAppend` directly.
// Otherwise, we must copy the array to free memory
// before appending new values to it.
if (freeMemPtr > addressArrayEndPtr) {
LibBytes.memCopy(freeMemPtr, addressArrayBeginPtr, addressArrayMemSizeInBytes);
assembly {
addressArray := freeMemPtr
addressArrayBeginPtr := addressArray
}
}
// Append `addressToAppend`
addressArrayLength += 1;
addressArrayMemSizeInBytes += 32;
addressArrayEndPtr = addressArrayBeginPtr + addressArrayMemSizeInBytes;
freeMemPtr = addressArrayEndPtr;
assembly {
// Store new array length
mstore(addressArray, addressArrayLength)
// Update `freeMemPtr`
mstore(0x40, freeMemPtr)
}
addressArray[addressArrayLength - 1] = addressToAppend;
return addressArray;
}
}

View File

@ -0,0 +1,152 @@
/*
Copyright 2018 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.4.24;
contract LibExchangeSelectors {
// solhint-disable max-line-length
// allowedValidators
bytes4 constant public ALLOWED_VALIDATORS_SELECTOR = 0x7b8e3514;
bytes4 constant public ALLOWED_VALIDATORS_SELECTOR_GENERATOR = bytes4(keccak256("allowedValidators(address,address)"));
// assetProxies
bytes4 constant public ASSET_PROXIES_SELECTOR = 0x3fd3c997;
bytes4 constant public ASSET_PROXIES_SELECTOR_GENERATOR = bytes4(keccak256("assetProxies(bytes4)"));
// batchCancelOrders
bytes4 constant public BATCH_CANCEL_ORDERS_SELECTOR = 0x4ac14782;
bytes4 constant public BATCH_CANCEL_ORDERS_SELECTOR_GENERATOR = bytes4(keccak256("batchCancelOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[])"));
// batchFillOrKillOrders
bytes4 constant public BATCH_FILL_OR_KILL_ORDERS_SELECTOR = 0x4d0ae546;
bytes4 constant public BATCH_FILL_OR_KILL_ORDERS_SELECTOR_GENERATOR = bytes4(keccak256("batchFillOrKillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])"));
// batchFillOrders
bytes4 constant public BATCH_FILL_ORDERS_SELECTOR = 0x297bb70b;
bytes4 constant public BATCH_FILL_ORDERS_SELECTOR_GENERATOR = bytes4(keccak256("batchFillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])"));
// batchFillOrdersNoThrow
bytes4 constant public BATCH_FILL_ORDERS_NO_THROW_SELECTOR = 0x50dde190;
bytes4 constant public BATCH_FILL_ORDERS_NO_THROW_SELECTOR_GENERATOR = bytes4(keccak256("batchFillOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])"));
// cancelOrder
bytes4 constant public CANCEL_ORDER_SELECTOR = 0xd46b02c3;
bytes4 constant public CANCEL_ORDER_SELECTOR_GENERATOR = bytes4(keccak256("cancelOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes))"));
// cancelOrdersUpTo
bytes4 constant public CANCEL_ORDERS_UP_TO_SELECTOR = 0x4f9559b1;
bytes4 constant public CANCEL_ORDERS_UP_TO_SELECTOR_GENERATOR = bytes4(keccak256("cancelOrdersUpTo(uint256)"));
// cancelled
bytes4 constant public CANCELLED_SELECTOR = 0x2ac12622;
bytes4 constant public CANCELLED_SELECTOR_GENERATOR = bytes4(keccak256("cancelled(bytes32)"));
// currentContextAddress
bytes4 constant public CURRENT_CONTEXT_ADDRESS_SELECTOR = 0xeea086ba;
bytes4 constant public CURRENT_CONTEXT_ADDRESS_SELECTOR_GENERATOR = bytes4(keccak256("currentContextAddress()"));
// executeTransaction
bytes4 constant public EXECUTE_TRANSACTION_SELECTOR = 0xbfc8bfce;
bytes4 constant public EXECUTE_TRANSACTION_SELECTOR_GENERATOR = bytes4(keccak256("executeTransaction(uint256,address,bytes,bytes)"));
// fillOrKillOrder
bytes4 constant public FILL_OR_KILL_ORDER_SELECTOR = 0x64a3bc15;
bytes4 constant public FILL_OR_KILL_ORDER_SELECTOR_GENERATOR = bytes4(keccak256("fillOrKillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)"));
// fillOrder
bytes4 constant public FILL_ORDER_SELECTOR = 0xb4be83d5;
bytes4 constant public FILL_ORDER_SELECTOR_GENERATOR = bytes4(keccak256("fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)"));
// fillOrderNoThrow
bytes4 constant public FILL_ORDER_NO_THROW_SELECTOR = 0x3e228bae;
bytes4 constant public FILL_ORDER_NO_THROW_SELECTOR_GENERATOR = bytes4(keccak256("fillOrderNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)"));
// filled
bytes4 constant public FILLED_SELECTOR = 0x288cdc91;
bytes4 constant public FILLED_SELECTOR_GENERATOR = bytes4(keccak256("filled(bytes32)"));
// getAssetProxy
bytes4 constant public GET_ASSET_PROXY_SELECTOR = 0x60704108;
bytes4 constant public GET_ASSET_PROXY_SELECTOR_GENERATOR = bytes4(keccak256("getAssetProxy(bytes4)"));
// getOrderInfo
bytes4 constant public GET_ORDER_INFO_SELECTOR = 0xc75e0a81;
bytes4 constant public GET_ORDER_INFO_SELECTOR_GENERATOR = bytes4(keccak256("getOrderInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes))"));
// getOrdersInfo
bytes4 constant public GET_ORDERS_INFO_SELECTOR = 0x7e9d74dc;
bytes4 constant public GET_ORDERS_INFO_SELECTOR_GENERATOR = bytes4(keccak256("getOrdersInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[])"));
// isValidSignature
bytes4 constant public IS_VALID_SIGNATURE_SELECTOR = 0x93634702;
bytes4 constant public IS_VALID_SIGNATURE_SELECTOR_GENERATOR = bytes4(keccak256("isValidSignature(bytes32,address,bytes)"));
// marketBuyOrders
bytes4 constant public MARKET_BUY_ORDERS_SELECTOR = 0xe5fa431b;
bytes4 constant public MARKET_BUY_ORDERS_SELECTOR_GENERATOR = bytes4(keccak256("marketBuyOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])"));
// marketBuyOrdersNoThrow
bytes4 constant public MARKET_BUY_ORDERS_NO_THROW_SELECTOR = 0xa3e20380;
bytes4 constant public MARKET_BUY_ORDERS_NO_THROW_SELECTOR_GENERATOR = bytes4(keccak256("marketBuyOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])"));
// marketSellOrders
bytes4 constant public MARKET_SELL_ORDERS_SELECTOR = 0x7e1d9808;
bytes4 constant public MARKET_SELL_ORDERS_SELECTOR_GENERATOR = bytes4(keccak256("marketSellOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])"));
// marketSellOrdersNoThrow
bytes4 constant public MARKET_SELL_ORDERS_NO_THROW_SELECTOR = 0xdd1c7d18;
bytes4 constant public MARKET_SELL_ORDERS_NO_THROW_SELECTOR_GENERATOR = bytes4(keccak256("marketSellOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])"));
// matchOrders
bytes4 constant public MATCH_ORDERS_SELECTOR = 0x3c28d861;
bytes4 constant public MATCH_ORDERS_SELECTOR_GENERATOR = bytes4(keccak256("matchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),bytes,bytes)"));
// orderEpoch
bytes4 constant public ORDER_EPOCH_SELECTOR = 0xd9bfa73e;
bytes4 constant public ORDER_EPOCH_SELECTOR_GENERATOR = bytes4(keccak256("orderEpoch(address,address)"));
// owner
bytes4 constant public OWNER_SELECTOR = 0x8da5cb5b;
bytes4 constant public OWNER_SELECTOR_GENERATOR = bytes4(keccak256("owner()"));
// preSign
bytes4 constant public PRE_SIGN_SELECTOR = 0x3683ef8e;
bytes4 constant public PRE_SIGN_SELECTOR_GENERATOR = bytes4(keccak256("preSign(bytes32,address,bytes)"));
// preSigned
bytes4 constant public PRE_SIGNED_SELECTOR = 0x82c174d0;
bytes4 constant public PRE_SIGNED_SELECTOR_GENERATOR = bytes4(keccak256("preSigned(bytes32,address)"));
// registerAssetProxy
bytes4 constant public REGISTER_ASSET_PROXY_SELECTOR = 0xc585bb93;
bytes4 constant public REGISTER_ASSET_PROXY_SELECTOR_GENERATOR = bytes4(keccak256("registerAssetProxy(address)"));
// setSignatureValidatorApproval
bytes4 constant public SET_SIGNATURE_VALIDATOR_APPROVAL_SELECTOR = 0x77fcce68;
bytes4 constant public SET_SIGNATURE_VALIDATOR_APPROVAL_SELECTOR_GENERATOR = bytes4(keccak256("setSignatureValidatorApproval(address,bool)"));
// transactions
bytes4 constant public TRANSACTIONS_SELECTOR = 0x642f2eaf;
bytes4 constant public TRANSACTIONS_SELECTOR_GENERATOR = bytes4(keccak256("transactions(bytes32)"));
// transferOwnership
bytes4 constant public TRANSFER_OWNERSHIP_SELECTOR = 0xf2fde38b;
bytes4 constant public TRANSFER_OWNERSHIP_SELECTOR_GENERATOR = bytes4(keccak256("transferOwnership(address)"));
}

View File

@ -1,4 +1,13 @@
[
{
"version": "2.2.0",
"changes": [
{
"note": "Added LibAddressArray",
"pr": 1383
}
]
},
{
"timestamp": 1544741676,
"version": "2.1.59",

View File

@ -104,6 +104,7 @@ export enum ContractName {
Authorizable = 'Authorizable',
Whitelist = 'Whitelist',
Forwarder = 'Forwarder',
BalanceThresholdFilter = 'BalanceThresholdFilter',
}
export interface SignedTransaction {

View File

@ -243,6 +243,10 @@ export enum RevertReason {
AuctionNotStarted = 'AUCTION_NOT_STARTED',
AuctionInvalidBeginTime = 'INVALID_BEGIN_TIME',
InvalidAssetData = 'INVALID_ASSET_DATA',
// Balance Threshold Filter
InvalidOrBlockedExchangeSelector = 'INVALID_OR_BLOCKED_EXCHANGE_SELECTOR',
BalanceQueryFailed = 'BALANCE_QUERY_FAILED',
AtLeastOneAddressDoesNotMeetBalanceThreshold = 'AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD',
}
export enum StatusCodes {