Ported Balance Threshold Filter to new contract directory structure
This commit is contained in:
@@ -18,5 +18,5 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"contracts": ["DutchAuction", "Forwarder"]
|
||||
"contracts": ["BalanceThresholdFilter", "DutchAuction", "Forwarder"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
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 thresholdBalance The minimum balance of `thresholdAsset` that must be held by makers/takers.
|
||||
constructor(
|
||||
address exchange,
|
||||
address thresholdAsset,
|
||||
uint256 thresholdBalance
|
||||
)
|
||||
public
|
||||
{
|
||||
EXCHANGE = IExchange(exchange);
|
||||
THRESHOLD_ASSET = IThresholdAsset(thresholdAsset);
|
||||
THRESHOLD_BALANCE = thresholdBalance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./mixins/MBalanceThresholdFilterCore.sol";
|
||||
|
||||
|
||||
contract MixinBalanceThresholdFilterCore is MBalanceThresholdFilterCore {
|
||||
|
||||
/// @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
|
||||
{
|
||||
// Validate addresses.
|
||||
validateBalanceThresholdsOrRevert();
|
||||
|
||||
// All addresses are valid. Execute fillOrder.
|
||||
EXCHANGE.executeTransaction(
|
||||
salt,
|
||||
signerAddress,
|
||||
signedExchangeTransaction,
|
||||
signature
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Validates addresses meet the balance threshold specified by `BALANCE_THRESHOLD`
|
||||
/// for the asset `THRESHOLD_ASSET`. If one address does not meet the thresold
|
||||
/// then this function will revert. Which addresses are validated depends on
|
||||
/// which Exchange function is to be called (defined by `signedExchangeTransaction` above).
|
||||
/// No parameters are taken as this function reads arguments directly from calldata, to save gas.
|
||||
/// If all addresses are valid then this function emits a ValidatedAddresses event, listing all
|
||||
/// of the addresses whose balance thresholds it checked.
|
||||
function validateBalanceThresholdsOrRevert()
|
||||
internal
|
||||
{
|
||||
// Addresses that are validated below.
|
||||
address[] memory validatedAddresses;
|
||||
|
||||
///// Do not add variables after this point. /////
|
||||
///// The assembly block may overwrite their values. /////
|
||||
|
||||
// Validate addresses
|
||||
assembly {
|
||||
/// @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(offset) -> value {
|
||||
// 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)
|
||||
}
|
||||
|
||||
/// @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(offset) -> value {
|
||||
value := exchangeCalldataload(add(offset, 0x4))
|
||||
}
|
||||
|
||||
/// @dev A running list is maintained of addresses to validate.
|
||||
/// This function records an address in this array.
|
||||
/// @param addressToValidate - Address to record for validation.
|
||||
function recordAddressToValidate(addressToValidate) {
|
||||
// Compute `addressesToValidate` memory offset
|
||||
let addressesToValidate_ := mload(0x40)
|
||||
let nAddressesToValidate_ := mload(addressesToValidate_)
|
||||
|
||||
// Increment length
|
||||
nAddressesToValidate_ := add(mload(addressesToValidate_), 0x01)
|
||||
mstore(addressesToValidate_, nAddressesToValidate_)
|
||||
|
||||
// Append address to validate
|
||||
let offset := mul(nAddressesToValidate_, 0x20)
|
||||
mstore(add(addressesToValidate_, offset), addressToValidate)
|
||||
}
|
||||
|
||||
/// @dev Extracts the maker address from an order stored in the Exchange calldata
|
||||
/// (which is embedded in `signedExchangeTransaction`), and records it in
|
||||
/// the running list of addresses to validate.
|
||||
/// @param orderParamIndex - Index of the order in the Exchange function's signature
|
||||
function recordMakerAddressFromOrder(orderParamIndex) {
|
||||
let orderPtr := loadExchangeData(mul(orderParamIndex, 0x20))
|
||||
let makerAddress := loadExchangeData(orderPtr)
|
||||
recordAddressToValidate(makerAddress)
|
||||
}
|
||||
|
||||
/// @dev Extracts the maker addresses from an array of orders stored in the Exchange calldata
|
||||
/// (which is embedded in `signedExchangeTransaction`), and records them in
|
||||
/// the running list of addresses to validate.
|
||||
/// @param orderArrayParamIndex - Index of the order array in the Exchange function's signature
|
||||
function recordMakerAddressesFromOrderArray(orderArrayParamIndex) {
|
||||
let orderArrayPtr := loadExchangeData(mul(orderArrayParamIndex, 0x20))
|
||||
let orderArrayLength := loadExchangeData(orderArrayPtr)
|
||||
let orderArrayElementPtr := add(orderArrayPtr, 0x20)
|
||||
let orderArrayElementEndPtr := add(orderArrayElementPtr, mul(orderArrayLength, 0x20))
|
||||
for {let orderPtrOffset := orderArrayElementPtr} lt(orderPtrOffset, orderArrayElementEndPtr) {orderPtrOffset := add(orderPtrOffset, 0x20)} {
|
||||
let orderPtr := loadExchangeData(orderPtrOffset)
|
||||
let makerAddress := loadExchangeData(add(orderPtr, orderArrayElementPtr))
|
||||
recordAddressToValidate(makerAddress)
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Records address of signer in the running list of addresses to validate.
|
||||
/// @note: We cannot access `signerAddress` directly from within the asm function,
|
||||
/// so it is loaded from the calldata.
|
||||
function recordSignerAddress() {
|
||||
// Load the signer address from calldata
|
||||
// 0x04 for selector
|
||||
// 0x20 to access `signerAddress`, which is the second parameter.
|
||||
let signerAddress_ := calldataload(0x24)
|
||||
recordAddressToValidate(signerAddress_)
|
||||
}
|
||||
|
||||
/// @dev Records addresses to be validated when Exchange transaction is a batch fill variant.
|
||||
/// This is one of: batchFillOrders, batchFillOrKillOrders, batchFillNoThrow
|
||||
/// Reference signature<T>: <batchFillVariant>(Order[],uint256[],bytes[])
|
||||
function recordAddressesForBatchFillVariant() {
|
||||
// Record maker addresses from order array (parameter index 0)
|
||||
// The signer is the taker for these orders and must also be validated.
|
||||
recordMakerAddressesFromOrderArray(0)
|
||||
recordSignerAddress()
|
||||
}
|
||||
|
||||
/// @dev Records addresses to be validated when Exchange transaction is a fill order variant.
|
||||
/// This is one of: fillOrder, fillOrKillOrder, fillOrderNoThrow
|
||||
/// Reference signature<T>: <fillOrderVariant>(Order,uint256,bytes)
|
||||
function recordAddressesForFillOrderVariant() {
|
||||
// Record maker address from the order (param index 0)
|
||||
// The signer is the taker for this order and must also be validated.
|
||||
recordMakerAddressFromOrder(0)
|
||||
recordSignerAddress()
|
||||
}
|
||||
|
||||
/// @dev Records addresses to be validated when Exchange transaction is a market fill variant.
|
||||
/// This is one of: marketBuyOrders, marketBuyOrdersNoThrow, marketSellOrders, marketSellOrdersNoThrow
|
||||
/// Reference signature<T>: <marketFillInvariant>(Order[],uint256,bytes[])
|
||||
function recordAddressesForMarketFillVariant() {
|
||||
// Record maker addresses from order array (parameter index 0)
|
||||
// The signer is the taker for these orders and must also be validated.
|
||||
recordMakerAddressesFromOrderArray(0)
|
||||
recordSignerAddress()
|
||||
}
|
||||
|
||||
/// @dev Records addresses to be validated when Exchange transaction is matchOrders.
|
||||
/// Reference signature: matchOrders(Order,Order)
|
||||
function recordAddressesForMatchOrders() {
|
||||
// Record maker address from both orders (param indices 0 & 1).
|
||||
// The signer is the taker and must also be validated.
|
||||
recordMakerAddressFromOrder(0)
|
||||
recordMakerAddressFromOrder(1)
|
||||
recordSignerAddress()
|
||||
}
|
||||
|
||||
///// Record Addresses to Validate /////
|
||||
|
||||
// Addresses needing validation depends on which Exchange function is being called.
|
||||
// Step 1/2 Read the exchange function selector.
|
||||
let exchangeFunctionSelector := and(
|
||||
exchangeCalldataload(0x0),
|
||||
0xffffffff00000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
|
||||
// Step 2/2 Extract addresses to validate based on this selector.
|
||||
// See ../../utils/ExchangeSelectors/ExchangeSelectors.sol for selectors
|
||||
switch exchangeFunctionSelector
|
||||
case 0x297bb70b00000000000000000000000000000000000000000000000000000000 { recordAddressesForBatchFillVariant() } // batchFillOrders
|
||||
case 0x50dde19000000000000000000000000000000000000000000000000000000000 { recordAddressesForBatchFillVariant() } // batchFillOrdersNoThrow
|
||||
case 0x4d0ae54600000000000000000000000000000000000000000000000000000000 { recordAddressesForBatchFillVariant() } // batchFillOrKillOrders
|
||||
case 0xb4be83d500000000000000000000000000000000000000000000000000000000 { recordAddressesForFillOrderVariant() } // fillOrder
|
||||
case 0x3e228bae00000000000000000000000000000000000000000000000000000000 { recordAddressesForFillOrderVariant() } // fillOrderNoThrow
|
||||
case 0x64a3bc1500000000000000000000000000000000000000000000000000000000 { recordAddressesForFillOrderVariant() } // fillOrKillOrder
|
||||
case 0xe5fa431b00000000000000000000000000000000000000000000000000000000 { recordAddressesForMarketFillVariant() } // marketBuyOrders
|
||||
case 0xa3e2038000000000000000000000000000000000000000000000000000000000 { recordAddressesForMarketFillVariant() } // marketBuyOrdersNoThrow
|
||||
case 0x7e1d980800000000000000000000000000000000000000000000000000000000 { recordAddressesForMarketFillVariant() } // marketSellOrders
|
||||
case 0xdd1c7d1800000000000000000000000000000000000000000000000000000000 { recordAddressesForMarketFillVariant() } // marketSellOrdersNoThrow
|
||||
case 0x3c28d86100000000000000000000000000000000000000000000000000000000 { recordAddressesForMatchOrders() } // matchOrders
|
||||
case 0xd46b02c300000000000000000000000000000000000000000000000000000000 {} // cancelOrder
|
||||
case 0x4ac1478200000000000000000000000000000000000000000000000000000000 {} // batchCancelOrders
|
||||
case 0x4f9559b100000000000000000000000000000000000000000000000000000000 {} // cancelOrdersUpTo
|
||||
default {
|
||||
// Revert with `Error("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR")`
|
||||
mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(0x20, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(0x40, 0x00000024494e56414c49445f4f525f424c4f434b45445f45584348414e47455f)
|
||||
mstore(0x60, 0x53454c4543544f52000000000000000000000000000000000000000000000000)
|
||||
mstore(0x80, 0x00000000)
|
||||
// Revert length calculation:
|
||||
// 4 -- error selector
|
||||
// 32 -- offset to string
|
||||
// 32 -- string length field
|
||||
// 64 -- strlen(INVALID_OR_BLOCKED_EXCHANGE_SELECTOR) rounded up to nearest 32-byte word.
|
||||
revert(0, 132)
|
||||
}
|
||||
|
||||
///// Validate Recorded Addresses /////
|
||||
|
||||
// Load from memory the addresses to validate
|
||||
let addressesToValidate := mload(0x40)
|
||||
let addressesToValidateLength := mload(addressesToValidate)
|
||||
let addressesToValidateElementPtr := add(addressesToValidate, 0x20)
|
||||
let addressesToValidateElementEndPtr := add(addressesToValidateElementPtr, mul(addressesToValidateLength, 0x20))
|
||||
|
||||
// Set free memory pointer to after `addressesToValidate` array.
|
||||
// This is to avoid corruption when making calls in the loop below.
|
||||
let freeMemPtr := addressesToValidateElementEndPtr
|
||||
mstore(0x40, freeMemPtr)
|
||||
|
||||
// Validate addresses
|
||||
let thresholdAssetAddress := sload(THRESHOLD_ASSET_slot)
|
||||
let thresholdBalance := sload(THRESHOLD_BALANCE_slot)
|
||||
for {let addressToValidate := addressesToValidateElementPtr} lt(addressToValidate, addressesToValidateElementEndPtr) {addressToValidate := add(addressToValidate, 0x20)} {
|
||||
// Construct calldata for `THRESHOLD_ASSET.balanceOf`
|
||||
mstore(freeMemPtr, 0x70a0823100000000000000000000000000000000000000000000000000000000)
|
||||
mstore(add(freeMemPtr, 0x04), mload(addressToValidate))
|
||||
|
||||
// call `THRESHOLD_ASSET.balanceOf`
|
||||
let success := call(
|
||||
gas, // forward all gas
|
||||
thresholdAssetAddress, // call address of asset proxy
|
||||
0, // don't send any ETH
|
||||
freeMemPtr, // pointer to start of input
|
||||
0x24, // length of input (one padded address)
|
||||
freeMemPtr, // write output to next free memory offset
|
||||
0x20 // reserve space for return balance (0x20 bytes)
|
||||
)
|
||||
if eq(success, 0) {
|
||||
// @TODO Revert with `Error("BALANCE_QUERY_FAILED")`
|
||||
mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(0x20, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(0x40, 0x0000001442414c414e43455f51554552595f4641494c45440000000000000000)
|
||||
mstore(0x60, 0x00000000)
|
||||
// Revert length calculation:
|
||||
// 4 -- error selector
|
||||
// 32 -- offset to string
|
||||
// 32 -- string length field
|
||||
// 32 -- strlen(BALANCE_QUERY_FAILED) rounded up to nearest 32-byte word.
|
||||
revert(0, 100)
|
||||
}
|
||||
|
||||
// Revert if balance not held
|
||||
let addressBalance := mload(freeMemPtr)
|
||||
if lt(addressBalance, thresholdBalance) {
|
||||
// Revert with `Error("AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD")`
|
||||
mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(0x20, 0x0000002000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(0x40, 0x0000003441545f4c454153545f4f4e455f414444524553535f444f45535f4e4f)
|
||||
mstore(0x60, 0x545f4d4545545f42414c414e43455f5448524553484f4c440000000000000000)
|
||||
mstore(0x80, 0x00000000)
|
||||
// Revert length calculation:
|
||||
// 4 -- error selector
|
||||
// 32 -- offset to string
|
||||
// 32 -- string length field
|
||||
// 64 -- strlen(AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD) rounded up to nearest 32-byte word.
|
||||
revert(0, 132)
|
||||
}
|
||||
}
|
||||
|
||||
// Record validated addresses
|
||||
validatedAddresses := addressesToValidate
|
||||
}
|
||||
|
||||
///// If we hit this point then all addresses are valid /////
|
||||
emit ValidatedAddresses(validatedAddresses);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
|
||||
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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol";
|
||||
import "../interfaces/IThresholdAsset.sol";
|
||||
|
||||
|
||||
contract MBalanceThresholdFilterCore {
|
||||
|
||||
// Points to 0x exchange contract
|
||||
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 THRESHOLD_BALANCE;
|
||||
|
||||
// Addresses that hold at least `THRESHOLD_BALANCE` of `THRESHOLD_ASSET`
|
||||
event ValidatedAddresses (
|
||||
address[] addresses
|
||||
);
|
||||
|
||||
/// @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;
|
||||
|
||||
/// @dev Validates addresses meet the balance threshold specified by `BALANCE_THRESHOLD`
|
||||
/// for the asset `THRESHOLD_ASSET`. If one address does not meet the thresold
|
||||
/// then this function will revert. Which addresses are validated depends on
|
||||
/// which Exchange function is to be called (defined by `signedExchangeTransaction` above).
|
||||
/// No parameters are taken as this function reads arguments directly from calldata, to save gas.
|
||||
/// If all addresses are valid then this function emits a ValidatedAddresses event, listing all
|
||||
/// of the addresses whose balance thresholds it checked.
|
||||
function validateBalanceThresholdsOrRevert() internal;
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from '../../generated-wrappers/balance_threshold_filter';
|
||||
export * from '../../generated-wrappers/dutch_auction';
|
||||
export * from '../../generated-wrappers/forwarder';
|
||||
|
||||
1530
contracts/extensions/test/extensions/balance_threshold_filter.ts
Normal file
1530
contracts/extensions/test/extensions/balance_threshold_filter.ts
Normal file
File diff suppressed because it is too large
Load Diff
246
contracts/extensions/test/utils/balance_threshold_wrapper.ts
Normal file
246
contracts/extensions/test/utils/balance_threshold_wrapper.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
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 { ExchangeContract } from '@0x/contracts-protocol';
|
||||
import { BalanceThresholdFilterContract } from '../../generated-wrappers/balance_threshold_filter';
|
||||
|
||||
import { formatters, LogDecoder, orderUtils, OrderInfo, TransactionFactory } from '@0x/contracts-test-utils';
|
||||
import { artifacts } from '../../src/artifacts';
|
||||
import {artifacts as protocolArtifacts} from '@0x/contracts-protocol';
|
||||
import { artifacts as tokensArtifacts } from '@0x/contracts-tokens';
|
||||
|
||||
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._executeTransaction(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._executeTransaction(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._executeTransaction(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._executeTransaction(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._executeTransaction(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._executeTransaction(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._executeTransaction(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._executeTransaction(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._executeTransaction(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._executeTransaction(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._executeTransaction(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._executeTransaction(data, from);
|
||||
return txReceipt;
|
||||
}
|
||||
public async cancelOrdersUpToAsync(salt: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const data = this._exchange.cancelOrdersUpTo.getABIEncodedTransactionData(salt);
|
||||
const txReceipt = this._executeTransaction(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)) as OrderInfo;
|
||||
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._executeTransaction(data, from);
|
||||
return txReceipt;
|
||||
}
|
||||
public getBalanceThresholdAddress(): string {
|
||||
return this._balanceThresholdFilter.address;
|
||||
}
|
||||
public getExchangeAddress(): string {
|
||||
return this._exchange.address;
|
||||
}
|
||||
// Exchange functions
|
||||
//abiEncodeFillOrder
|
||||
//getFillOrderResultsAsync
|
||||
//
|
||||
private async _executeTransaction(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;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,6 @@
|
||||
"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"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user