Merge pull request #2055 from 0xProject/feat/3.0/optimizeConstants
Refactor library usage
This commit is contained in:
commit
4c78b7d4bb
@ -90,13 +90,15 @@ jobs:
|
|||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-coordinator @0x/contracts-dev-utils @0x/contracts-staking
|
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-exchange @0x/contracts-dev-utils @0x/contracts-staking
|
||||||
# TODO(dorothy-zbornak): Re-enable after updating this package for 3.0.
|
# TODO(dorothy-zbornak): Re-enable after updating this package for 3.0.
|
||||||
# - run: yarn wsrun test:circleci @0x/contracts-extensions
|
# - run: yarn wsrun test:circleci @0x/contracts-extensions
|
||||||
# TODO(dorothy-zbornak): Re-enable after updating this package for 3.0.
|
# TODO(dorothy-zbornak): Re-enable after updating this package for 3.0.
|
||||||
# - run: yarn wsrun test:circleci @0x/contracts-exchange-forwarder
|
# - run: yarn wsrun test:circleci @0x/contracts-exchange-forwarder
|
||||||
# TODO(dorothy-zbornak): Re-enable after this package is complete.
|
# TODO(dorothy-zbornak): Re-enable after this package is complete.
|
||||||
# - run: yarn wsrun test:circleci @0x/contracts-staking
|
# - run: yarn wsrun test:circleci @0x/contracts-staking
|
||||||
|
# TODO(abandeali): Re-enable after this package is complete.
|
||||||
|
# - run: yarn wsrun test:circleci @0x/contracts-coordinator
|
||||||
test-contracts-geth:
|
test-contracts-geth:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:9-browsers
|
- image: circleci/node:9-browsers
|
||||||
|
@ -5,6 +5,14 @@
|
|||||||
{
|
{
|
||||||
"note": "Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable",
|
"note": "Disallow the zero address from being made an authorized address in MixinAuthorizable, and created an archive directory that includes an old version of Ownable",
|
||||||
"pr": 2019
|
"pr": 2019
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove `LibAssetProxyIds` contract",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
||||||
|
"pr": 2055
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -22,17 +22,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": [
|
|
||||||
"archive/MixinAuthorizable.sol",
|
|
||||||
"src/ERC1155Proxy.sol",
|
|
||||||
"src/ERC20Proxy.sol",
|
|
||||||
"src/ERC721Proxy.sol",
|
|
||||||
"src/MultiAssetProxy.sol",
|
|
||||||
"src/StaticCallProxy.sol",
|
|
||||||
"src/interfaces/IAssetData.sol",
|
|
||||||
"src/interfaces/IAssetProxy.sol",
|
|
||||||
"src/interfaces/IAuthorizable.sol",
|
|
||||||
"test/TestStaticCallTarget.sol"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -1,40 +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.5;
|
|
||||||
|
|
||||||
|
|
||||||
contract LibAssetProxyIds {
|
|
||||||
|
|
||||||
// AssetProxy Ids are equiavalent the first 4 bytes of the keccak256 hash of the function signature assigned to each AssetProxy.
|
|
||||||
|
|
||||||
// ERC20Token(address)
|
|
||||||
bytes4 constant public ERC20_PROXY_ID = 0xf47261b0;
|
|
||||||
|
|
||||||
// ERC721Token(address,uint256)
|
|
||||||
bytes4 constant public ERC721_PROXY_ID = 0x02571792;
|
|
||||||
|
|
||||||
// ERC1155Assets(address,uint256[],uint256[],bytes)
|
|
||||||
bytes4 constant public ERC1155_PROXY_ID = 0xa7cb5fb7;
|
|
||||||
|
|
||||||
// MultiAsset(uint256[],bytes[])
|
|
||||||
bytes4 constant public MULTI_ASSET_PROXY_ID = 0x94cfcdd7;
|
|
||||||
|
|
||||||
// StaticCall(address,bytes,bytes32)
|
|
||||||
bytes4 constant public STATIC_CALL_PROXY_ID = 0xc339d10a;
|
|
||||||
}
|
|
@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
@ -34,7 +34,7 @@
|
|||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy|StaticCallProxy|TestStaticCallTarget).json",
|
"abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestStaticCallTarget).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": {
|
||||||
|
@ -10,20 +10,26 @@ import * as ERC20Proxy from '../generated-artifacts/ERC20Proxy.json';
|
|||||||
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
|
import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
|
||||||
import * as IAssetData from '../generated-artifacts/IAssetData.json';
|
import * as IAssetData from '../generated-artifacts/IAssetData.json';
|
||||||
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
||||||
|
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
|
||||||
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
||||||
|
import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json';
|
||||||
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
|
import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json';
|
||||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||||
|
import * as Ownable from '../generated-artifacts/Ownable.json';
|
||||||
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
||||||
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
|
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
|
||||||
|
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
||||||
|
Ownable: Ownable as ContractArtifact,
|
||||||
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
|
ERC1155Proxy: ERC1155Proxy as ContractArtifact,
|
||||||
ERC20Proxy: ERC20Proxy as ContractArtifact,
|
ERC20Proxy: ERC20Proxy as ContractArtifact,
|
||||||
ERC721Proxy: ERC721Proxy as ContractArtifact,
|
ERC721Proxy: ERC721Proxy as ContractArtifact,
|
||||||
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
|
|
||||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||||
IAssetData: IAssetData as ContractArtifact,
|
IAssetData: IAssetData as ContractArtifact,
|
||||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||||
|
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
|
||||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
@ -8,8 +8,11 @@ export * from '../generated-wrappers/erc20_proxy';
|
|||||||
export * from '../generated-wrappers/erc721_proxy';
|
export * from '../generated-wrappers/erc721_proxy';
|
||||||
export * from '../generated-wrappers/i_asset_data';
|
export * from '../generated-wrappers/i_asset_data';
|
||||||
export * from '../generated-wrappers/i_asset_proxy';
|
export * from '../generated-wrappers/i_asset_proxy';
|
||||||
|
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
|
||||||
export * from '../generated-wrappers/i_authorizable';
|
export * from '../generated-wrappers/i_authorizable';
|
||||||
|
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
|
||||||
export * from '../generated-wrappers/mixin_authorizable';
|
export * from '../generated-wrappers/mixin_authorizable';
|
||||||
export * from '../generated-wrappers/multi_asset_proxy';
|
export * from '../generated-wrappers/multi_asset_proxy';
|
||||||
|
export * from '../generated-wrappers/ownable';
|
||||||
export * from '../generated-wrappers/static_call_proxy';
|
export * from '../generated-wrappers/static_call_proxy';
|
||||||
export * from '../generated-wrappers/test_static_call_target';
|
export * from '../generated-wrappers/test_static_call_target';
|
||||||
|
@ -8,9 +8,12 @@
|
|||||||
"generated-artifacts/ERC721Proxy.json",
|
"generated-artifacts/ERC721Proxy.json",
|
||||||
"generated-artifacts/IAssetData.json",
|
"generated-artifacts/IAssetData.json",
|
||||||
"generated-artifacts/IAssetProxy.json",
|
"generated-artifacts/IAssetProxy.json",
|
||||||
|
"generated-artifacts/IAssetProxyDispatcher.json",
|
||||||
"generated-artifacts/IAuthorizable.json",
|
"generated-artifacts/IAuthorizable.json",
|
||||||
|
"generated-artifacts/MixinAssetProxyDispatcher.json",
|
||||||
"generated-artifacts/MixinAuthorizable.json",
|
"generated-artifacts/MixinAuthorizable.json",
|
||||||
"generated-artifacts/MultiAssetProxy.json",
|
"generated-artifacts/MultiAssetProxy.json",
|
||||||
|
"generated-artifacts/Ownable.json",
|
||||||
"generated-artifacts/StaticCallProxy.json",
|
"generated-artifacts/StaticCallProxy.json",
|
||||||
"generated-artifacts/TestStaticCallTarget.json"
|
"generated-artifacts/TestStaticCallTarget.json"
|
||||||
],
|
],
|
||||||
|
@ -33,6 +33,14 @@
|
|||||||
{
|
{
|
||||||
"note": "Update for new `marketXOrders` consolidation.",
|
"note": "Update for new `marketXOrders` consolidation.",
|
||||||
"pr": 2042
|
"pr": 2042
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Use built in selectors instead of hard coded constants",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
||||||
|
"pr": 2055
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -21,6 +21,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": ["src/Coordinator.sol", "src/registry/CoordinatorRegistry.sol"]
|
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,11 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.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/LibZeroExTransaction.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
|
import "@0x/contracts-utils/contracts/src/LibAddressArray.sol";
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
import "./libs/LibCoordinatorApproval.sol";
|
import "./libs/LibCoordinatorApproval.sol";
|
||||||
import "./interfaces/ISignatureValidator.sol";
|
import "./interfaces/ISignatureValidator.sol";
|
||||||
import "./interfaces/ICoordinatorApprovalVerifier.sol";
|
import "./interfaces/ICoordinatorApprovalVerifier.sol";
|
||||||
@ -31,7 +31,6 @@ import "./interfaces/ICoordinatorApprovalVerifier.sol";
|
|||||||
|
|
||||||
// solhint-disable avoid-tx-origin
|
// solhint-disable avoid-tx-origin
|
||||||
contract MixinCoordinatorApprovalVerifier is
|
contract MixinCoordinatorApprovalVerifier is
|
||||||
LibExchangeSelectors,
|
|
||||||
LibCoordinatorApproval,
|
LibCoordinatorApproval,
|
||||||
LibZeroExTransaction,
|
LibZeroExTransaction,
|
||||||
ISignatureValidator,
|
ISignatureValidator,
|
||||||
@ -84,9 +83,9 @@ contract MixinCoordinatorApprovalVerifier is
|
|||||||
{
|
{
|
||||||
bytes4 selector = data.readBytes4(0);
|
bytes4 selector = data.readBytes4(0);
|
||||||
if (
|
if (
|
||||||
selector == FILL_ORDER_SELECTOR ||
|
selector == IExchange(address(0)).fillOrder.selector ||
|
||||||
selector == FILL_ORDER_NO_THROW_SELECTOR ||
|
selector == IExchange(address(0)).fillOrderNoThrow.selector ||
|
||||||
selector == FILL_OR_KILL_ORDER_SELECTOR
|
selector == IExchange(address(0)).fillOrKillOrder.selector
|
||||||
) {
|
) {
|
||||||
// Decode single order
|
// Decode single order
|
||||||
(LibOrder.Order memory order) = abi.decode(
|
(LibOrder.Order memory order) = abi.decode(
|
||||||
@ -96,11 +95,11 @@ contract MixinCoordinatorApprovalVerifier is
|
|||||||
orders = new LibOrder.Order[](1);
|
orders = new LibOrder.Order[](1);
|
||||||
orders[0] = order;
|
orders[0] = order;
|
||||||
} else if (
|
} else if (
|
||||||
selector == BATCH_FILL_ORDERS_SELECTOR ||
|
selector == IExchange(address(0)).batchFillOrders.selector ||
|
||||||
selector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR ||
|
selector == IExchange(address(0)).batchFillOrdersNoThrow.selector ||
|
||||||
selector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR ||
|
selector == IExchange(address(0)).batchFillOrKillOrders.selector ||
|
||||||
selector == MARKET_BUY_ORDERS_SELECTOR ||
|
selector == IExchange(address(0)).marketBuyOrders.selector ||
|
||||||
selector == MARKET_SELL_ORDERS_SELECTOR
|
selector == IExchange(address(0)).marketSellOrders.selector
|
||||||
) {
|
) {
|
||||||
// Decode all orders
|
// Decode all orders
|
||||||
// solhint-disable indent
|
// solhint-disable indent
|
||||||
@ -108,7 +107,7 @@ contract MixinCoordinatorApprovalVerifier is
|
|||||||
data.slice(4, data.length),
|
data.slice(4, data.length),
|
||||||
(LibOrder.Order[])
|
(LibOrder.Order[])
|
||||||
);
|
);
|
||||||
} else if (selector == MATCH_ORDERS_SELECTOR) {
|
} else if (selector == IExchange(address(0)).matchOrders.selector) {
|
||||||
// Decode left and right orders
|
// Decode left and right orders
|
||||||
(LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder) = abi.decode(
|
(LibOrder.Order memory leftOrder, LibOrder.Order memory rightOrder) = abi.decode(
|
||||||
data.slice(4, data.length),
|
data.slice(4, data.length),
|
||||||
|
@ -34,7 +34,7 @@ contract LibCoordinatorApproval is
|
|||||||
// "uint256 approvalExpirationTimeSeconds",
|
// "uint256 approvalExpirationTimeSeconds",
|
||||||
// ")"
|
// ")"
|
||||||
// ));
|
// ));
|
||||||
bytes32 constant internal EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x2fbcdbaa76bc7589916958ae919dfbef04d23f6bbf26de6ff317b32c6cc01e05;
|
bytes32 constant public EIP712_COORDINATOR_APPROVAL_SCHEMA_HASH = 0x2fbcdbaa76bc7589916958ae919dfbef04d23f6bbf26de6ff317b32c6cc01e05;
|
||||||
|
|
||||||
struct CoordinatorApproval {
|
struct CoordinatorApproval {
|
||||||
address txOrigin; // Required signer of Ethereum transaction that is submitting approval.
|
address txOrigin; // Required signer of Ethereum transaction that is submitting approval.
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
|
@ -1,4 +1,17 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Use built in selectors instead of hard coded constants",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
||||||
|
"pr": 2055
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1563193019,
|
"timestamp": 1563193019,
|
||||||
"version": "0.0.4",
|
"version": "0.0.4",
|
||||||
|
@ -22,12 +22,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": [
|
|
||||||
"src/DevUtils.sol",
|
|
||||||
"src/EthBalanceChecker.sol",
|
|
||||||
"src/LibAssetData.sol",
|
|
||||||
"src/LibTransactionDecoder.sol",
|
|
||||||
"src/OrderTransferSimulationUtils.sol"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -20,32 +20,19 @@ pragma solidity ^0.5.5;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetProxyIds.sol";
|
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
|
||||||
|
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetProxy.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol";
|
||||||
|
import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol";
|
||||||
|
|
||||||
|
|
||||||
contract LibAssetData is
|
contract LibAssetData {
|
||||||
LibAssetProxyIds
|
|
||||||
{
|
|
||||||
// 2^256 - 1
|
// 2^256 - 1
|
||||||
uint256 constant internal _MAX_UINT256 = uint256(-1);
|
uint256 constant internal _MAX_UINT256 = uint256(-1);
|
||||||
|
|
||||||
// ERC20 selectors
|
|
||||||
bytes4 constant internal _ERC20_BALANCE_OF_SELECTOR = 0x70a08231;
|
|
||||||
bytes4 constant internal _ERC20_ALLOWANCE_SELECTOR = 0xdd62ed3e;
|
|
||||||
|
|
||||||
// ERC721 selectors
|
|
||||||
bytes4 constant internal _ERC721_OWNER_OF_SELECTOR = 0x6352211e;
|
|
||||||
bytes4 constant internal _ERC721_IS_APPROVED_FOR_ALL_SELECTOR = 0xe985e9c5;
|
|
||||||
bytes4 constant internal _ERC721_GET_APPROVED_SELECTOR = 0x081812fc;
|
|
||||||
|
|
||||||
// ERC1155 selectors
|
|
||||||
bytes4 constant internal _ERC1155_BALANCE_OF_SELECTOR = 0x00fdd58e;
|
|
||||||
bytes4 constant internal _ERC1155_IS_APPROVED_FOR_ALL_SELECTOR = 0xe985e9c5;
|
|
||||||
|
|
||||||
// `transferFrom` selector for all AssetProxy contracts
|
|
||||||
bytes4 constant internal _ASSET_PROXY_TRANSFER_FROM_SELECTOR = 0xa85e59e4;
|
|
||||||
|
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
|
|
||||||
// solhint-disable var-name-mixedcase
|
// solhint-disable var-name-mixedcase
|
||||||
@ -60,10 +47,10 @@ contract LibAssetData is
|
|||||||
public
|
public
|
||||||
{
|
{
|
||||||
_EXCHANGE = IExchange(_exchange);
|
_EXCHANGE = IExchange(_exchange);
|
||||||
_ERC20_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC20_PROXY_ID);
|
_ERC20_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC20Token.selector);
|
||||||
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC721_PROXY_ID);
|
_ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC721Token.selector);
|
||||||
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC1155_PROXY_ID);
|
_ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC1155Assets.selector);
|
||||||
_STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(STATIC_CALL_PROXY_ID);
|
_STATIC_CALL_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(IAssetData(address(0)).StaticCall.selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Returns the owner's balance of the assets(s) specified in
|
/// @dev Returns the owner's balance of the assets(s) specified in
|
||||||
@ -81,23 +68,33 @@ contract LibAssetData is
|
|||||||
// Get id of AssetProxy contract
|
// Get id of AssetProxy contract
|
||||||
bytes4 assetProxyId = assetData.readBytes4(0);
|
bytes4 assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
if (assetProxyId == ERC20_PROXY_ID) {
|
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
|
||||||
// Get ERC20 token address
|
// Get ERC20 token address
|
||||||
address tokenAddress = assetData.readAddress(16);
|
address tokenAddress = assetData.readAddress(16);
|
||||||
|
|
||||||
// Encode data for `balanceOf(ownerAddress)`
|
// Encode data for `balanceOf(ownerAddress)`
|
||||||
bytes memory balanceOfData = abi.encodeWithSelector(_ERC20_BALANCE_OF_SELECTOR, ownerAddress);
|
bytes memory balanceOfData = abi.encodeWithSelector(
|
||||||
|
IERC20Token(address(0)).balanceOf.selector,
|
||||||
|
ownerAddress
|
||||||
|
);
|
||||||
|
|
||||||
// Query balance
|
// Query balance
|
||||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData);
|
||||||
balance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
balance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||||
} else if (assetProxyId == ERC721_PROXY_ID) {
|
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||||
// Get ERC721 token address and id
|
// Get ERC721 token address and id
|
||||||
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
||||||
|
|
||||||
// Check if id is owned by ownerAddress
|
// Check if id is owned by ownerAddress
|
||||||
balance = getERC721TokenOwner(tokenAddress, tokenId) == ownerAddress ? 1 : 0;
|
bytes memory ownerOfCalldata = abi.encodeWithSelector(
|
||||||
} else if (assetProxyId == ERC1155_PROXY_ID) {
|
IERC721Token(address(0)).ownerOf.selector,
|
||||||
|
tokenId
|
||||||
|
);
|
||||||
|
|
||||||
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
|
||||||
|
address currentOwnerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
|
||||||
|
balance = currentOwnerAddress == ownerAddress ? 1 : 0;
|
||||||
|
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
||||||
// Get ERC1155 token address, array of ids, and array of values
|
// Get ERC1155 token address, array of ids, and array of values
|
||||||
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData);
|
(, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData);
|
||||||
|
|
||||||
@ -105,7 +102,7 @@ contract LibAssetData is
|
|||||||
for (uint256 i = 0; i != length; i++) {
|
for (uint256 i = 0; i != length; i++) {
|
||||||
// Encode data for `balanceOf(ownerAddress, tokenIds[i])
|
// Encode data for `balanceOf(ownerAddress, tokenIds[i])
|
||||||
bytes memory balanceOfData = abi.encodeWithSelector(
|
bytes memory balanceOfData = abi.encodeWithSelector(
|
||||||
_ERC1155_BALANCE_OF_SELECTOR,
|
IERC1155(address(0)).balanceOf.selector,
|
||||||
ownerAddress,
|
ownerAddress,
|
||||||
tokenIds[i]
|
tokenIds[i]
|
||||||
);
|
);
|
||||||
@ -120,10 +117,10 @@ contract LibAssetData is
|
|||||||
balance = scaledBalance;
|
balance = scaledBalance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (assetProxyId == STATIC_CALL_PROXY_ID) {
|
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
||||||
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
|
// Encode data for `staticCallProxy.transferFrom(assetData,...)`
|
||||||
bytes memory transferFromData = abi.encodeWithSelector(
|
bytes memory transferFromData = abi.encodeWithSelector(
|
||||||
_ASSET_PROXY_TRANSFER_FROM_SELECTOR,
|
IAssetProxy(address(0)).transferFrom.selector,
|
||||||
assetData,
|
assetData,
|
||||||
address(0), // `from` address is not used
|
address(0), // `from` address is not used
|
||||||
address(0), // `to` address is not used
|
address(0), // `to` address is not used
|
||||||
@ -135,7 +132,7 @@ contract LibAssetData is
|
|||||||
|
|
||||||
// Success means that the staticcall can be made an unlimited amount of times
|
// Success means that the staticcall can be made an unlimited amount of times
|
||||||
balance = success ? _MAX_UINT256 : 0;
|
balance = success ? _MAX_UINT256 : 0;
|
||||||
} else if (assetProxyId == MULTI_ASSET_PROXY_ID) {
|
} else if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
|
||||||
// Get array of values and array of assetDatas
|
// Get array of values and array of assetDatas
|
||||||
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
||||||
|
|
||||||
@ -190,7 +187,7 @@ contract LibAssetData is
|
|||||||
// Get id of AssetProxy contract
|
// Get id of AssetProxy contract
|
||||||
bytes4 assetProxyId = assetData.readBytes4(0);
|
bytes4 assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
if (assetProxyId == MULTI_ASSET_PROXY_ID) {
|
if (assetProxyId == IAssetData(address(0)).MultiAsset.selector) {
|
||||||
// Get array of values and array of assetDatas
|
// Get array of values and array of assetDatas
|
||||||
(, uint256[] memory amounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
(, uint256[] memory amounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData);
|
||||||
|
|
||||||
@ -208,13 +205,13 @@ contract LibAssetData is
|
|||||||
return allowance;
|
return allowance;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (assetProxyId == ERC20_PROXY_ID) {
|
if (assetProxyId == IAssetData(address(0)).ERC20Token.selector) {
|
||||||
// Get ERC20 token address
|
// Get ERC20 token address
|
||||||
address tokenAddress = assetData.readAddress(16);
|
address tokenAddress = assetData.readAddress(16);
|
||||||
|
|
||||||
// Encode data for `allowance(ownerAddress, _ERC20_PROXY_ADDRESS)`
|
// Encode data for `allowance(ownerAddress, _ERC20_PROXY_ADDRESS)`
|
||||||
bytes memory allowanceData = abi.encodeWithSelector(
|
bytes memory allowanceData = abi.encodeWithSelector(
|
||||||
_ERC20_ALLOWANCE_SELECTOR,
|
IERC20Token(address(0)).allowance.selector,
|
||||||
ownerAddress,
|
ownerAddress,
|
||||||
_ERC20_PROXY_ADDRESS
|
_ERC20_PROXY_ADDRESS
|
||||||
);
|
);
|
||||||
@ -222,13 +219,13 @@ contract LibAssetData is
|
|||||||
// Query allowance
|
// Query allowance
|
||||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(allowanceData);
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(allowanceData);
|
||||||
allowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
allowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0;
|
||||||
} else if (assetProxyId == ERC721_PROXY_ID) {
|
} else if (assetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||||
// Get ERC721 token address and id
|
// Get ERC721 token address and id
|
||||||
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
(, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData);
|
||||||
|
|
||||||
// Encode data for `isApprovedForAll(ownerAddress, _ERC721_PROXY_ADDRESS)`
|
// Encode data for `isApprovedForAll(ownerAddress, _ERC721_PROXY_ADDRESS)`
|
||||||
bytes memory isApprovedForAllData = abi.encodeWithSelector(
|
bytes memory isApprovedForAllData = abi.encodeWithSelector(
|
||||||
_ERC721_IS_APPROVED_FOR_ALL_SELECTOR,
|
IERC721Token(address(0)).isApprovedForAll.selector,
|
||||||
ownerAddress,
|
ownerAddress,
|
||||||
_ERC721_PROXY_ADDRESS
|
_ERC721_PROXY_ADDRESS
|
||||||
);
|
);
|
||||||
@ -238,7 +235,7 @@ contract LibAssetData is
|
|||||||
// If not approved for all, call `getApproved(tokenId)`
|
// If not approved for all, call `getApproved(tokenId)`
|
||||||
if (!success || returnData.length != 32 || returnData.readUint256(0) != 1) {
|
if (!success || returnData.length != 32 || returnData.readUint256(0) != 1) {
|
||||||
// Encode data for `getApproved(tokenId)`
|
// Encode data for `getApproved(tokenId)`
|
||||||
bytes memory getApprovedData = abi.encodeWithSelector(_ERC721_GET_APPROVED_SELECTOR, tokenId);
|
bytes memory getApprovedData = abi.encodeWithSelector(IERC721Token(address(0)).getApproved.selector, tokenId);
|
||||||
(success, returnData) = tokenAddress.staticcall(getApprovedData);
|
(success, returnData) = tokenAddress.staticcall(getApprovedData);
|
||||||
|
|
||||||
// Allowance is 1 if successful and the approved address is the ERC721Proxy
|
// Allowance is 1 if successful and the approved address is the ERC721Proxy
|
||||||
@ -247,13 +244,13 @@ contract LibAssetData is
|
|||||||
// Allowance is 2^256 - 1 if `isApprovedForAll` returned true
|
// Allowance is 2^256 - 1 if `isApprovedForAll` returned true
|
||||||
allowance = _MAX_UINT256;
|
allowance = _MAX_UINT256;
|
||||||
}
|
}
|
||||||
} else if (assetProxyId == ERC1155_PROXY_ID) {
|
} else if (assetProxyId == IAssetData(address(0)).ERC1155Assets.selector) {
|
||||||
// Get ERC1155 token address
|
// Get ERC1155 token address
|
||||||
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
|
(, address tokenAddress, , , ) = decodeERC1155AssetData(assetData);
|
||||||
|
|
||||||
// Encode data for `isApprovedForAll(ownerAddress, _ERC1155_PROXY_ADDRESS)`
|
// Encode data for `isApprovedForAll(ownerAddress, _ERC1155_PROXY_ADDRESS)`
|
||||||
bytes memory isApprovedForAllData = abi.encodeWithSelector(
|
bytes memory isApprovedForAllData = abi.encodeWithSelector(
|
||||||
_ERC1155_IS_APPROVED_FOR_ALL_SELECTOR,
|
IERC1155(address(0)).isApprovedForAll.selector,
|
||||||
ownerAddress,
|
ownerAddress,
|
||||||
_ERC1155_PROXY_ADDRESS
|
_ERC1155_PROXY_ADDRESS
|
||||||
);
|
);
|
||||||
@ -261,7 +258,7 @@ contract LibAssetData is
|
|||||||
// Query allowance
|
// Query allowance
|
||||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
|
(bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData);
|
||||||
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
|
allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0;
|
||||||
} else if (assetProxyId == STATIC_CALL_PROXY_ID) {
|
} else if (assetProxyId == IAssetData(address(0)).StaticCall.selector) {
|
||||||
// The StaticCallProxy does not require any approvals
|
// The StaticCallProxy does not require any approvals
|
||||||
allowance = _MAX_UINT256;
|
allowance = _MAX_UINT256;
|
||||||
}
|
}
|
||||||
@ -327,7 +324,7 @@ contract LibAssetData is
|
|||||||
pure
|
pure
|
||||||
returns (bytes memory assetData)
|
returns (bytes memory assetData)
|
||||||
{
|
{
|
||||||
assetData = abi.encodeWithSelector(ERC20_PROXY_ID, tokenAddress);
|
assetData = abi.encodeWithSelector(IAssetData(address(0)).ERC20Token.selector, tokenAddress);
|
||||||
return assetData;
|
return assetData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,7 +343,7 @@ contract LibAssetData is
|
|||||||
assetProxyId = assetData.readBytes4(0);
|
assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
require(
|
require(
|
||||||
assetProxyId == ERC20_PROXY_ID,
|
assetProxyId == IAssetData(address(0)).ERC20Token.selector,
|
||||||
"WRONG_PROXY_ID"
|
"WRONG_PROXY_ID"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -364,7 +361,7 @@ contract LibAssetData is
|
|||||||
returns (bytes memory assetData)
|
returns (bytes memory assetData)
|
||||||
{
|
{
|
||||||
assetData = abi.encodeWithSelector(
|
assetData = abi.encodeWithSelector(
|
||||||
ERC721_PROXY_ID,
|
IAssetData(address(0)).ERC721Token.selector,
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
tokenId
|
tokenId
|
||||||
);
|
);
|
||||||
@ -388,7 +385,7 @@ contract LibAssetData is
|
|||||||
assetProxyId = assetData.readBytes4(0);
|
assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
require(
|
require(
|
||||||
assetProxyId == ERC721_PROXY_ID,
|
assetProxyId == IAssetData(address(0)).ERC721Token.selector,
|
||||||
"WRONG_PROXY_ID"
|
"WRONG_PROXY_ID"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -414,7 +411,7 @@ contract LibAssetData is
|
|||||||
returns (bytes memory assetData)
|
returns (bytes memory assetData)
|
||||||
{
|
{
|
||||||
assetData = abi.encodeWithSelector(
|
assetData = abi.encodeWithSelector(
|
||||||
ERC1155_PROXY_ID,
|
IAssetData(address(0)).ERC1155Assets.selector,
|
||||||
tokenAddress,
|
tokenAddress,
|
||||||
tokenIds,
|
tokenIds,
|
||||||
tokenValues,
|
tokenValues,
|
||||||
@ -446,7 +443,7 @@ contract LibAssetData is
|
|||||||
assetProxyId = assetData.readBytes4(0);
|
assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
require(
|
require(
|
||||||
assetProxyId == ERC1155_PROXY_ID,
|
assetProxyId == IAssetData(address(0)).ERC1155Assets.selector,
|
||||||
"WRONG_PROXY_ID"
|
"WRONG_PROXY_ID"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -482,7 +479,7 @@ contract LibAssetData is
|
|||||||
returns (bytes memory assetData)
|
returns (bytes memory assetData)
|
||||||
{
|
{
|
||||||
assetData = abi.encodeWithSelector(
|
assetData = abi.encodeWithSelector(
|
||||||
MULTI_ASSET_PROXY_ID,
|
IAssetData(address(0)).MultiAsset.selector,
|
||||||
amounts,
|
amounts,
|
||||||
nestedAssetData
|
nestedAssetData
|
||||||
);
|
);
|
||||||
@ -507,7 +504,7 @@ contract LibAssetData is
|
|||||||
assetProxyId = assetData.readBytes4(0);
|
assetProxyId = assetData.readBytes4(0);
|
||||||
|
|
||||||
require(
|
require(
|
||||||
assetProxyId == MULTI_ASSET_PROXY_ID,
|
assetProxyId == IAssetData(address(0)).MultiAsset.selector,
|
||||||
"WRONG_PROXY_ID"
|
"WRONG_PROXY_ID"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -518,24 +515,4 @@ contract LibAssetData is
|
|||||||
);
|
);
|
||||||
// solhint-enable indent
|
// solhint-enable indent
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Calls `asset.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned asset.
|
|
||||||
/// @param tokenAddress Address of ERC721 asset.
|
|
||||||
/// @param tokenId The identifier for the specific NFT.
|
|
||||||
/// @return Owner of tokenId or null address if unowned.
|
|
||||||
function getERC721TokenOwner(address tokenAddress, uint256 tokenId)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (address ownerAddress)
|
|
||||||
{
|
|
||||||
bytes memory ownerOfCalldata = abi.encodeWithSelector(
|
|
||||||
_ERC721_OWNER_OF_SELECTOR,
|
|
||||||
tokenId
|
|
||||||
);
|
|
||||||
|
|
||||||
(bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata);
|
|
||||||
|
|
||||||
ownerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0);
|
|
||||||
return ownerAddress;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,14 +19,13 @@
|
|||||||
pragma solidity ^0.5.5;
|
pragma solidity ^0.5.5;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.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-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
|
||||||
|
|
||||||
contract LibTransactionDecoder is
|
contract LibTransactionDecoder {
|
||||||
LibExchangeSelectors
|
|
||||||
{
|
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
|
|
||||||
/// @dev Decodes the call data for an Exchange contract method call.
|
/// @dev Decodes the call data for an Exchange contract method call.
|
||||||
@ -47,66 +46,65 @@ contract LibTransactionDecoder is
|
|||||||
{
|
{
|
||||||
bytes4 functionSelector = transactionData.readBytes4(0);
|
bytes4 functionSelector = transactionData.readBytes4(0);
|
||||||
|
|
||||||
if (functionSelector == BATCH_CANCEL_ORDERS_SELECTOR) {
|
if (functionSelector == IExchange(address(0)).batchCancelOrders.selector) {
|
||||||
functionName = "batchCancelOrders";
|
functionName = "batchCancelOrders";
|
||||||
} else if (functionSelector == BATCH_FILL_ORDERS_SELECTOR) {
|
} else if (functionSelector == IExchange(address(0)).batchFillOrders.selector) {
|
||||||
functionName = "batchFillOrders";
|
functionName = "batchFillOrders";
|
||||||
} else if (functionSelector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR) {
|
} else if (functionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector) {
|
||||||
functionName = "batchFillOrdersNoThrow";
|
functionName = "batchFillOrdersNoThrow";
|
||||||
} else if (functionSelector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR) {
|
} else if (functionSelector == IExchange(address(0)).batchFillOrKillOrders.selector) {
|
||||||
functionName = "batchFillOrKillOrders";
|
functionName = "batchFillOrKillOrders";
|
||||||
} else if (functionSelector == CANCEL_ORDER_SELECTOR) {
|
} else if (functionSelector == IExchange(address(0)).cancelOrder.selector) {
|
||||||
functionName = "cancelOrder";
|
functionName = "cancelOrder";
|
||||||
} else if (functionSelector == FILL_ORDER_SELECTOR) {
|
} else if (functionSelector == IExchange(address(0)).fillOrder.selector) {
|
||||||
functionName = "fillOrder";
|
functionName = "fillOrder";
|
||||||
} else if (functionSelector == FILL_ORDER_NO_THROW_SELECTOR) {
|
} else if (functionSelector == IExchange(address(0)).fillOrderNoThrow.selector) {
|
||||||
functionName = "fillOrderNoThrow";
|
functionName = "fillOrderNoThrow";
|
||||||
} else if (functionSelector == FILL_OR_KILL_ORDER_SELECTOR) {
|
} else if (functionSelector == IExchange(address(0)).fillOrKillOrder.selector) {
|
||||||
functionName = "fillOrKillOrder";
|
functionName = "fillOrKillOrder";
|
||||||
} else if (functionSelector == MARKET_BUY_ORDERS_SELECTOR) {
|
} else if (functionSelector == IExchange(address(0)).marketBuyOrders.selector) {
|
||||||
functionName = "marketBuyOrders";
|
functionName = "marketBuyOrders";
|
||||||
} else if (functionSelector == MARKET_SELL_ORDERS_SELECTOR) {
|
} else if (functionSelector == IExchange(address(0)).marketSellOrders.selector) {
|
||||||
functionName = "marketSellOrders";
|
functionName = "marketSellOrders";
|
||||||
} else if (functionSelector == MATCH_ORDERS_SELECTOR) {
|
} else if (functionSelector == IExchange(address(0)).matchOrders.selector) {
|
||||||
functionName = "matchOrders";
|
functionName = "matchOrders";
|
||||||
} else if (
|
} else if (
|
||||||
functionSelector == CANCEL_ORDERS_UP_TO_SELECTOR ||
|
functionSelector == IExchange(address(0)).cancelOrdersUpTo.selector ||
|
||||||
functionSelector == EXECUTE_TRANSACTION_SELECTOR
|
functionSelector == IExchange(address(0)).executeTransaction.selector
|
||||||
// TODO: add new noThrow cancel functions when https://github.com/0xProject/ZEIPs/issues/35 is merged.
|
|
||||||
) {
|
) {
|
||||||
revert("UNIMPLEMENTED");
|
revert("UNIMPLEMENTED");
|
||||||
} else {
|
} else {
|
||||||
revert("UNKNOWN_FUNCTION_SELECTOR");
|
revert("UNKNOWN_FUNCTION_SELECTOR");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (functionSelector == BATCH_CANCEL_ORDERS_SELECTOR) {
|
if (functionSelector == IExchange(address(0)).batchCancelOrders.selector) {
|
||||||
// solhint-disable-next-line indent
|
// solhint-disable-next-line indent
|
||||||
orders = abi.decode(transactionData.slice(4, transactionData.length), (LibOrder.Order[]));
|
orders = abi.decode(transactionData.slice(4, transactionData.length), (LibOrder.Order[]));
|
||||||
takerAssetFillAmounts = new uint256[](0);
|
takerAssetFillAmounts = new uint256[](0);
|
||||||
signatures = new bytes[](0);
|
signatures = new bytes[](0);
|
||||||
} else if (
|
} else if (
|
||||||
functionSelector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR ||
|
functionSelector == IExchange(address(0)).batchFillOrKillOrders.selector ||
|
||||||
functionSelector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR ||
|
functionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector ||
|
||||||
functionSelector == BATCH_FILL_ORDERS_SELECTOR
|
functionSelector == IExchange(address(0)).batchFillOrders.selector
|
||||||
) {
|
) {
|
||||||
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForBatchFill(transactionData);
|
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForBatchFill(transactionData);
|
||||||
} else if (functionSelector == CANCEL_ORDER_SELECTOR) {
|
} else if (functionSelector == IExchange(address(0)).cancelOrder.selector) {
|
||||||
orders = new LibOrder.Order[](1);
|
orders = new LibOrder.Order[](1);
|
||||||
orders[0] = abi.decode(transactionData.slice(4, transactionData.length), (LibOrder.Order));
|
orders[0] = abi.decode(transactionData.slice(4, transactionData.length), (LibOrder.Order));
|
||||||
takerAssetFillAmounts = new uint256[](0);
|
takerAssetFillAmounts = new uint256[](0);
|
||||||
signatures = new bytes[](0);
|
signatures = new bytes[](0);
|
||||||
} else if (
|
} else if (
|
||||||
functionSelector == FILL_OR_KILL_ORDER_SELECTOR ||
|
functionSelector == IExchange(address(0)).fillOrKillOrder.selector ||
|
||||||
functionSelector == FILL_ORDER_SELECTOR ||
|
functionSelector == IExchange(address(0)).fillOrder.selector ||
|
||||||
functionSelector == FILL_ORDER_NO_THROW_SELECTOR
|
functionSelector == IExchange(address(0)).fillOrderNoThrow.selector
|
||||||
) {
|
) {
|
||||||
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForSingleOrderFill(transactionData);
|
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForSingleOrderFill(transactionData);
|
||||||
} else if (
|
} else if (
|
||||||
functionSelector == MARKET_BUY_ORDERS_SELECTOR ||
|
functionSelector == IExchange(address(0)).marketBuyOrders.selector ||
|
||||||
functionSelector == MARKET_SELL_ORDERS_SELECTOR
|
functionSelector == IExchange(address(0)).marketSellOrders.selector
|
||||||
) {
|
) {
|
||||||
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForMarketFill(transactionData);
|
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForMarketFill(transactionData);
|
||||||
} else if (functionSelector == MATCH_ORDERS_SELECTOR) {
|
} else if (functionSelector == IExchange(address(0)).matchOrders.selector) {
|
||||||
(
|
(
|
||||||
LibOrder.Order memory leftOrder,
|
LibOrder.Order memory leftOrder,
|
||||||
LibOrder.Order memory rightOrder,
|
LibOrder.Order memory rightOrder,
|
||||||
|
@ -21,8 +21,8 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
|
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
import "@0x/contracts-exchange/contracts/src/LibExchangeRichErrors.sol";
|
|
||||||
import "@0x/contracts-exchange/contracts/src/libs/LibExchangeRichErrorDecoder.sol";
|
import "@0x/contracts-exchange/contracts/src/libs/LibExchangeRichErrorDecoder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
|
||||||
@ -40,9 +40,6 @@ contract OrderTransferSimulationUtils is
|
|||||||
TransfersSuccessful // All transfers in the order were successful
|
TransfersSuccessful // All transfers in the order were successful
|
||||||
}
|
}
|
||||||
|
|
||||||
// simulateDispatchTransferFromCalls(bytes[],address[],address[],uint256[])
|
|
||||||
bytes4 constant internal _SIMULATE_DISPATCH_TRANSFER_FROM_CALLS_SELECTOR = 0xb04fbddd;
|
|
||||||
|
|
||||||
// keccak256(abi.encodeWithSignature("Error(string)", "TRANSFERS_SUCCESSFUL"));
|
// keccak256(abi.encodeWithSignature("Error(string)", "TRANSFERS_SUCCESSFUL"));
|
||||||
bytes32 constant internal _TRANSFERS_SUCCESSFUL_RESULT_HASH = 0xf43f26ea5a94b478394a975e856464913dc1a8a1ca70939d974aa7c238aa0ce0;
|
bytes32 constant internal _TRANSFERS_SUCCESSFUL_RESULT_HASH = 0xf43f26ea5a94b478394a975e856464913dc1a8a1ca70939d974aa7c238aa0ce0;
|
||||||
|
|
||||||
@ -101,7 +98,7 @@ contract OrderTransferSimulationUtils is
|
|||||||
|
|
||||||
// Encode data for `simulateDispatchTransferFromCalls(assetData, fromAddresses, toAddresses, amounts)`
|
// Encode data for `simulateDispatchTransferFromCalls(assetData, fromAddresses, toAddresses, amounts)`
|
||||||
bytes memory simulateDispatchTransferFromCallsData = abi.encodeWithSelector(
|
bytes memory simulateDispatchTransferFromCallsData = abi.encodeWithSelector(
|
||||||
_SIMULATE_DISPATCH_TRANSFER_FROM_CALLS_SELECTOR,
|
IExchange(address(0)).simulateDispatchTransferFromCalls.selector,
|
||||||
assetData,
|
assetData,
|
||||||
fromAddresses,
|
fromAddresses,
|
||||||
toAddresses,
|
toAddresses,
|
||||||
|
@ -23,14 +23,15 @@ 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/LibMath.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
import "./LibAssetData.sol";
|
import "./LibAssetData.sol";
|
||||||
|
|
||||||
|
|
||||||
contract OrderValidationUtils is
|
contract OrderValidationUtils is
|
||||||
LibAssetData,
|
LibAssetData
|
||||||
LibMath
|
|
||||||
{
|
{
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
constructor (address _exchange)
|
constructor (address _exchange)
|
||||||
public
|
public
|
||||||
@ -79,9 +80,9 @@ contract OrderValidationUtils is
|
|||||||
if (order.makerAssetData.equals(order.makerFeeAssetData)) {
|
if (order.makerAssetData.equals(order.makerFeeAssetData)) {
|
||||||
// If `makerAsset` equals `makerFeeAsset`, the % that can be filled is
|
// If `makerAsset` equals `makerFeeAsset`, the % that can be filled is
|
||||||
// transferableMakerAssetAmount / (makerAssetAmount + makerFee)
|
// transferableMakerAssetAmount / (makerAssetAmount + makerFee)
|
||||||
transferableTakerAssetAmount = _getPartialAmountFloor(
|
transferableTakerAssetAmount = LibMath.getPartialAmountFloor(
|
||||||
transferableMakerAssetAmount,
|
transferableMakerAssetAmount,
|
||||||
_safeAdd(order.makerAssetAmount, makerFee),
|
order.makerAssetAmount.safeAdd(makerFee),
|
||||||
takerAssetAmount
|
takerAssetAmount
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -90,7 +91,7 @@ contract OrderValidationUtils is
|
|||||||
|
|
||||||
// If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount)
|
// If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount)
|
||||||
if (makerFee == 0) {
|
if (makerFee == 0) {
|
||||||
transferableTakerAssetAmount = _getPartialAmountFloor(
|
transferableTakerAssetAmount = LibMath.getPartialAmountFloor(
|
||||||
transferableMakerAssetAmount,
|
transferableMakerAssetAmount,
|
||||||
order.makerAssetAmount,
|
order.makerAssetAmount,
|
||||||
takerAssetAmount
|
takerAssetAmount
|
||||||
@ -99,23 +100,23 @@ contract OrderValidationUtils is
|
|||||||
// If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of
|
// If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of
|
||||||
// (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee)
|
// (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee)
|
||||||
} else {
|
} else {
|
||||||
uint256 transferableMakerToTakerAmount = _getPartialAmountFloor(
|
uint256 transferableMakerToTakerAmount = LibMath.getPartialAmountFloor(
|
||||||
transferableMakerAssetAmount,
|
transferableMakerAssetAmount,
|
||||||
order.makerAssetAmount,
|
order.makerAssetAmount,
|
||||||
takerAssetAmount
|
takerAssetAmount
|
||||||
);
|
);
|
||||||
uint256 transferableMakerFeeToTakerAmount = _getPartialAmountFloor(
|
uint256 transferableMakerFeeToTakerAmount = LibMath.getPartialAmountFloor(
|
||||||
transferableMakerFeeAssetAmount,
|
transferableMakerFeeAssetAmount,
|
||||||
makerFee,
|
makerFee,
|
||||||
takerAssetAmount
|
takerAssetAmount
|
||||||
);
|
);
|
||||||
transferableTakerAssetAmount = _min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount);
|
transferableTakerAssetAmount = LibSafeMath.min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount`
|
// `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount`
|
||||||
fillableTakerAssetAmount = _min256(
|
fillableTakerAssetAmount = LibSafeMath.min256(
|
||||||
_safeSub(takerAssetAmount, orderInfo.orderTakerAssetFilledAmount),
|
takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount),
|
||||||
transferableTakerAssetAmount
|
transferableTakerAssetAmount
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -170,7 +171,7 @@ contract OrderValidationUtils is
|
|||||||
returns (uint256 transferableAssetAmount)
|
returns (uint256 transferableAssetAmount)
|
||||||
{
|
{
|
||||||
(uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData);
|
(uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData);
|
||||||
transferableAssetAmount = _min256(balance, allowance);
|
transferableAssetAmount = LibSafeMath.min256(balance, allowance);
|
||||||
return transferableAssetAmount;
|
return transferableAssetAmount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
@ -34,7 +34,7 @@
|
|||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"abis": "./generated-artifacts/@(DevUtils|EthBalanceChecker|LibAssetData|LibTransactionDecoder|OrderTransferSimulationUtils).json",
|
"abis": "./generated-artifacts/@(DevUtils|EthBalanceChecker|LibAssetData|LibTransactionDecoder|OrderTransferSimulationUtils|OrderValidationUtils).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": {
|
||||||
|
@ -10,10 +10,12 @@ import * as EthBalanceChecker from '../generated-artifacts/EthBalanceChecker.jso
|
|||||||
import * as LibAssetData from '../generated-artifacts/LibAssetData.json';
|
import * as LibAssetData from '../generated-artifacts/LibAssetData.json';
|
||||||
import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json';
|
import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json';
|
||||||
import * as OrderTransferSimulationUtils from '../generated-artifacts/OrderTransferSimulationUtils.json';
|
import * as OrderTransferSimulationUtils from '../generated-artifacts/OrderTransferSimulationUtils.json';
|
||||||
|
import * as OrderValidationUtils from '../generated-artifacts/OrderValidationUtils.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
DevUtils: DevUtils as ContractArtifact,
|
DevUtils: DevUtils as ContractArtifact,
|
||||||
|
EthBalanceChecker: EthBalanceChecker as ContractArtifact,
|
||||||
LibAssetData: LibAssetData as ContractArtifact,
|
LibAssetData: LibAssetData as ContractArtifact,
|
||||||
LibTransactionDecoder: LibTransactionDecoder as ContractArtifact,
|
LibTransactionDecoder: LibTransactionDecoder as ContractArtifact,
|
||||||
EthBalanceChecker: EthBalanceChecker as ContractArtifact,
|
|
||||||
OrderTransferSimulationUtils: OrderTransferSimulationUtils as ContractArtifact,
|
OrderTransferSimulationUtils: OrderTransferSimulationUtils as ContractArtifact,
|
||||||
|
OrderValidationUtils: OrderValidationUtils as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
@ -8,3 +8,4 @@ export * from '../generated-wrappers/eth_balance_checker';
|
|||||||
export * from '../generated-wrappers/lib_asset_data';
|
export * from '../generated-wrappers/lib_asset_data';
|
||||||
export * from '../generated-wrappers/lib_transaction_decoder';
|
export * from '../generated-wrappers/lib_transaction_decoder';
|
||||||
export * from '../generated-wrappers/order_transfer_simulation_utils';
|
export * from '../generated-wrappers/order_transfer_simulation_utils';
|
||||||
|
export * from '../generated-wrappers/order_validation_utils';
|
||||||
|
@ -465,20 +465,6 @@ describe('LibAssetData', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getERC721TokenOwner', async () => {
|
|
||||||
it('should return the null address when tokenId is not owned', async () => {
|
|
||||||
const nonexistentTokenId = new BigNumber(1234567890);
|
|
||||||
expect(
|
|
||||||
await libAssetData.getERC721TokenOwner.callAsync(erc721Token.address, nonexistentTokenId),
|
|
||||||
).to.be.equal(constants.NULL_ADDRESS);
|
|
||||||
});
|
|
||||||
it('should return the owner address when tokenId is owned', async () => {
|
|
||||||
expect(
|
|
||||||
await libAssetData.getERC721TokenOwner.callAsync(erc721Token.address, firstERC721TokenId),
|
|
||||||
).to.be.equal(tokenOwnerAddress);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getBalanceAndAllowance', () => {
|
describe('getBalanceAndAllowance', () => {
|
||||||
it('should query balance and allowance together, from asset data', async () => {
|
it('should query balance and allowance together, from asset data', async () => {
|
||||||
const allowance = new BigNumber(1);
|
const allowance = new BigNumber(1);
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
"generated-artifacts/EthBalanceChecker.json",
|
"generated-artifacts/EthBalanceChecker.json",
|
||||||
"generated-artifacts/LibAssetData.json",
|
"generated-artifacts/LibAssetData.json",
|
||||||
"generated-artifacts/LibTransactionDecoder.json",
|
"generated-artifacts/LibTransactionDecoder.json",
|
||||||
"generated-artifacts/OrderTransferSimulationUtils.json"
|
"generated-artifacts/OrderTransferSimulationUtils.json",
|
||||||
|
"generated-artifacts/OrderValidationUtils.json"
|
||||||
],
|
],
|
||||||
"exclude": ["./deploy/solc/solc_bin"]
|
"exclude": ["./deploy/solc/solc_bin"]
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": [
|
|
||||||
"src/ERC1155.sol",
|
|
||||||
"src/ERC1155Mintable.sol",
|
|
||||||
"src/MixinNonFungibleToken.sol",
|
|
||||||
"src/interfaces/IERC1155.sol",
|
|
||||||
"src/interfaces/IERC1155Mintable.sol",
|
|
||||||
"src/interfaces/IERC1155Receiver.sol",
|
|
||||||
"test/DummyERC1155Receiver.sol"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
|
@ -23,18 +23,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": [
|
|
||||||
"src/ERC20Token.sol",
|
|
||||||
"src/MintableERC20Token.sol",
|
|
||||||
"src/UnlimitedAllowanceERC20Token.sol",
|
|
||||||
"src/WETH9.sol",
|
|
||||||
"src/ZRXToken.sol",
|
|
||||||
"src/interfaces/IERC20Token.sol",
|
|
||||||
"src/interfaces/IEtherToken.sol",
|
|
||||||
"test/DummyERC20Token.sol",
|
|
||||||
"test/DummyMultipleReturnERC20Token.sol",
|
|
||||||
"test/DummyNoReturnERC20Token.sol",
|
|
||||||
"test/UntransferrableDummyERC20Token.sol"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
|
@ -22,14 +22,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": [
|
|
||||||
"src/ERC721Token.sol",
|
|
||||||
"src/MintableERC721Token.sol",
|
|
||||||
"src/interfaces/IERC721Receiver.sol",
|
|
||||||
"src/interfaces/IERC721Token.sol",
|
|
||||||
"test/DummyERC721Receiver.sol",
|
|
||||||
"test/DummyERC721Token.sol",
|
|
||||||
"test/InvalidERC721Receiver.sol"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
|
@ -22,6 +22,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": ["src/Forwarder.sol"]
|
|
||||||
}
|
}
|
||||||
|
@ -23,14 +23,13 @@ import "./libs/LibConstants.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-libs/contracts/src/LibExchangeSelectors.sol";
|
import "@0x/contracts/exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
|
|
||||||
|
|
||||||
contract MixinExchangeWrapper is
|
contract MixinExchangeWrapper is
|
||||||
LibFillResults,
|
LibFillResults,
|
||||||
LibMath,
|
LibMath,
|
||||||
LibConstants,
|
LibConstants
|
||||||
LibExchangeSelectors
|
|
||||||
{
|
{
|
||||||
/// @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.
|
||||||
@ -48,9 +47,7 @@ contract MixinExchangeWrapper is
|
|||||||
{
|
{
|
||||||
// ABI encode calldata for `fillOrder`
|
// ABI encode calldata for `fillOrder`
|
||||||
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
||||||
// bytes4(keccak256("fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),uint256,bytes)"))
|
IExchange(address(0)).fillOrder.selector,
|
||||||
// = 0x9b44d556
|
|
||||||
0x9b44d556,
|
|
||||||
order,
|
order,
|
||||||
takerAssetFillAmount,
|
takerAssetFillAmount,
|
||||||
signature
|
signature
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
|
@ -77,6 +77,30 @@
|
|||||||
{
|
{
|
||||||
"note": "Regenerate selectors.",
|
"note": "Regenerate selectors.",
|
||||||
"pr": 2042
|
"pr": 2042
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Convert `LibFillResults`, `LibOrder`, `LibZeroExTransaction`, and `LibMath` to libraries",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove `LibExchangeSelectors`",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `LibExchangeRichErrors`",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `calculateFillResults` and `calculateMatchedFillResults` to `LibFillResults`",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove `_hashEIP712ExchangeMessage` from `LibEIP712ExchangeDomain`",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
||||||
|
"pr": 2055
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -22,13 +22,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": [
|
|
||||||
"src/LibEIP712ExchangeDomain.sol",
|
|
||||||
"src/LibFillResults.sol",
|
|
||||||
"src/LibMath.sol",
|
|
||||||
"src/LibOrder.sol",
|
|
||||||
"src/LibZeroExTransaction.sol",
|
|
||||||
"test/TestLibs.sol"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,8 @@ pragma solidity ^0.5.9;
|
|||||||
import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
|
import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
|
||||||
|
|
||||||
|
|
||||||
contract LibEIP712ExchangeDomain is
|
contract LibEIP712ExchangeDomain {
|
||||||
LibEIP712
|
|
||||||
{
|
|
||||||
// EIP712 Exchange Domain Name value
|
// EIP712 Exchange Domain Name value
|
||||||
string constant public EIP712_EXCHANGE_DOMAIN_NAME = "0x Protocol";
|
string constant public EIP712_EXCHANGE_DOMAIN_NAME = "0x Protocol";
|
||||||
|
|
||||||
@ -43,23 +42,11 @@ contract LibEIP712ExchangeDomain is
|
|||||||
public
|
public
|
||||||
{
|
{
|
||||||
address verifyingContractAddress = verifyingContractAddressIfExists == address(0) ? address(this) : verifyingContractAddressIfExists;
|
address verifyingContractAddress = verifyingContractAddressIfExists == address(0) ? address(this) : verifyingContractAddressIfExists;
|
||||||
EIP712_EXCHANGE_DOMAIN_HASH = _hashEIP712Domain(
|
EIP712_EXCHANGE_DOMAIN_HASH = LibEIP712.hashEIP712Domain(
|
||||||
EIP712_EXCHANGE_DOMAIN_NAME,
|
EIP712_EXCHANGE_DOMAIN_NAME,
|
||||||
EIP712_EXCHANGE_DOMAIN_VERSION,
|
EIP712_EXCHANGE_DOMAIN_VERSION,
|
||||||
chainId,
|
chainId,
|
||||||
verifyingContractAddress
|
verifyingContractAddress
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Calculates EIP712 encoding for a hash struct in the EIP712 domain
|
|
||||||
/// of the Exchange contract.
|
|
||||||
/// @param hashStruct The EIP712 hash struct.
|
|
||||||
/// @return EIP712 hash applied to the Exchange EIP712 Domain.
|
|
||||||
function _hashEIP712ExchangeMessage(bytes32 hashStruct)
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (bytes32 result)
|
|
||||||
{
|
|
||||||
return _hashEIP712Message(EIP712_EXCHANGE_DOMAIN_HASH, hashStruct);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,45 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
import "./LibOrder.sol";
|
||||||
import "./interfaces/IExchangeRichErrors.sol";
|
|
||||||
|
|
||||||
|
|
||||||
library LibExchangeRichErrors {
|
library LibExchangeRichErrors {
|
||||||
|
|
||||||
|
enum AssetProxyDispatchErrorCodes {
|
||||||
|
INVALID_ASSET_DATA_LENGTH,
|
||||||
|
UNKNOWN_ASSET_PROXY
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BatchMatchOrdersErrorCodes {
|
||||||
|
ZERO_LEFT_ORDERS,
|
||||||
|
ZERO_RIGHT_ORDERS,
|
||||||
|
INVALID_LENGTH_LEFT_SIGNATURES,
|
||||||
|
INVALID_LENGTH_RIGHT_SIGNATURES
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FillErrorCodes {
|
||||||
|
INVALID_TAKER_AMOUNT,
|
||||||
|
TAKER_OVERPAY,
|
||||||
|
OVERFILL,
|
||||||
|
INVALID_FILL_PRICE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SignatureErrorCodes {
|
||||||
|
BAD_SIGNATURE,
|
||||||
|
INVALID_LENGTH,
|
||||||
|
UNSUPPORTED,
|
||||||
|
ILLEGAL,
|
||||||
|
INAPPROPRIATE_SIGNATURE_TYPE,
|
||||||
|
INVALID_SIGNER
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TransactionErrorCodes {
|
||||||
|
NO_REENTRANCY,
|
||||||
|
ALREADY_EXECUTED,
|
||||||
|
EXPIRED
|
||||||
|
}
|
||||||
|
|
||||||
// bytes4(keccak256("SignatureError(uint8,bytes32,address,bytes)"))
|
// bytes4(keccak256("SignatureError(uint8,bytes32,address,bytes)"))
|
||||||
bytes4 internal constant SIGNATURE_ERROR_SELECTOR =
|
bytes4 internal constant SIGNATURE_ERROR_SELECTOR =
|
||||||
0x7e5a2318;
|
0x7e5a2318;
|
||||||
@ -255,7 +288,7 @@ library LibExchangeRichErrors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function BatchMatchOrdersError(
|
function BatchMatchOrdersError(
|
||||||
IExchangeRichErrors.BatchMatchOrdersErrorCodes errorCode
|
BatchMatchOrdersErrorCodes errorCode
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
pure
|
pure
|
||||||
@ -268,7 +301,7 @@ library LibExchangeRichErrors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SignatureError(
|
function SignatureError(
|
||||||
IExchangeRichErrors.SignatureErrorCodes errorCode,
|
SignatureErrorCodes errorCode,
|
||||||
bytes32 hash,
|
bytes32 hash,
|
||||||
address signerAddress,
|
address signerAddress,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
@ -387,7 +420,7 @@ library LibExchangeRichErrors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function FillError(
|
function FillError(
|
||||||
IExchangeRichErrors.FillErrorCodes errorCode,
|
FillErrorCodes errorCode,
|
||||||
bytes32 orderHash
|
bytes32 orderHash
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
@ -447,7 +480,7 @@ library LibExchangeRichErrors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function AssetProxyDispatchError(
|
function AssetProxyDispatchError(
|
||||||
IExchangeRichErrors.AssetProxyDispatchErrorCodes errorCode,
|
AssetProxyDispatchErrorCodes errorCode,
|
||||||
bytes32 orderHash,
|
bytes32 orderHash,
|
||||||
bytes memory assetData
|
bytes memory assetData
|
||||||
)
|
)
|
||||||
@ -496,7 +529,7 @@ library LibExchangeRichErrors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function TransactionError(
|
function TransactionError(
|
||||||
IExchangeRichErrors.TransactionErrorCodes errorCode,
|
TransactionErrorCodes errorCode,
|
||||||
bytes32 transactionHash
|
bytes32 transactionHash
|
||||||
)
|
)
|
||||||
internal
|
internal
|
@ -1,186 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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.5.9;
|
|
||||||
|
|
||||||
|
|
||||||
contract LibExchangeSelectors {
|
|
||||||
// solhint-disable max-line-length
|
|
||||||
|
|
||||||
// function allowedValidators(address,address)
|
|
||||||
bytes4 constant internal ALLOWED_VALIDATORS_SELECTOR = 0x7b8e3514;
|
|
||||||
|
|
||||||
// function assetProxies(bytes4)
|
|
||||||
bytes4 constant internal ASSET_PROXIES_SELECTOR = 0x3fd3c997;
|
|
||||||
|
|
||||||
// function batchCancelOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[])
|
|
||||||
bytes4 constant internal BATCH_CANCEL_ORDERS_SELECTOR = 0xdedfc1f1;
|
|
||||||
|
|
||||||
// function batchExecuteTransactions((uint256,uint256,address,bytes)[],bytes[])
|
|
||||||
bytes4 constant internal BATCH_EXECUTE_TRANSACTIONS_SELECTOR = 0x3f80f0ee;
|
|
||||||
|
|
||||||
// function batchFillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],uint256[],bytes[])
|
|
||||||
bytes4 constant internal BATCH_FILL_ORDERS_SELECTOR = 0x9694a402;
|
|
||||||
|
|
||||||
// function batchFillOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],uint256[],bytes[])
|
|
||||||
bytes4 constant internal BATCH_FILL_ORDERS_NO_THROW_SELECTOR = 0x8ea8dfe4;
|
|
||||||
|
|
||||||
// function batchFillOrKillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],uint256[],bytes[])
|
|
||||||
bytes4 constant internal BATCH_FILL_OR_KILL_ORDERS_SELECTOR = 0xbeee2e14;
|
|
||||||
|
|
||||||
// function batchMatchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],bytes[],bytes[])
|
|
||||||
bytes4 constant internal BATCH_MATCH_ORDERS_SELECTOR = 0x6fcf3e9e;
|
|
||||||
|
|
||||||
// function batchMatchOrdersWithMaximalFill((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],bytes[],bytes[])
|
|
||||||
bytes4 constant internal BATCH_MATCH_ORDERS_WITH_MAXIMAL_FILL_SELECTOR = 0x6a1a80fd;
|
|
||||||
|
|
||||||
// function calculateMatchedFillResults((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),uint256,uint256,bool)
|
|
||||||
bytes4 constant internal CALCULATE_MATCHED_FILL_RESULTS_SELECTOR = 0x38f9eb3b;
|
|
||||||
|
|
||||||
// function cancelled(bytes32)
|
|
||||||
bytes4 constant internal CANCELLED_SELECTOR = 0x2ac12622;
|
|
||||||
|
|
||||||
// function cancelOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes))
|
|
||||||
bytes4 constant internal CANCEL_ORDER_SELECTOR = 0x2da62987;
|
|
||||||
|
|
||||||
// function cancelOrdersUpTo(uint256)
|
|
||||||
bytes4 constant internal CANCEL_ORDERS_UP_TO_SELECTOR = 0x4f9559b1;
|
|
||||||
|
|
||||||
// function currentContextAddress()
|
|
||||||
bytes4 constant internal CURRENT_CONTEXT_ADDRESS_SELECTOR = 0xeea086ba;
|
|
||||||
|
|
||||||
// function doesSignatureRequireRegularValidation(bytes32,address,bytes)
|
|
||||||
bytes4 constant internal DOES_SIGNATURE_REQUIRE_REGULAR_VALIDATION_SELECTOR = 0xc17f8ccc;
|
|
||||||
|
|
||||||
// function EIP1271_MAGIC_VALUE()
|
|
||||||
bytes4 constant internal EIP_1271_MAGIC_VALUE_SELECTOR = 0xdd885e2d;
|
|
||||||
|
|
||||||
// function EIP712_EXCHANGE_DOMAIN_HASH()
|
|
||||||
bytes4 constant internal EIP_712_EXCHANGE_DOMAIN_HASH_SELECTOR = 0xc26cfecd;
|
|
||||||
|
|
||||||
// function EIP712_EXCHANGE_DOMAIN_NAME()
|
|
||||||
bytes4 constant internal EIP_712_EXCHANGE_DOMAIN_NAME_SELECTOR = 0x63c4e8cc;
|
|
||||||
|
|
||||||
// function EIP712_EXCHANGE_DOMAIN_VERSION()
|
|
||||||
bytes4 constant internal EIP_712_EXCHANGE_DOMAIN_VERSION_SELECTOR = 0x0f01323b;
|
|
||||||
|
|
||||||
// function EIP712_ORDER_SCHEMA_HASH()
|
|
||||||
bytes4 constant internal EIP_712_ORDER_SCHEMA_HASH_SELECTOR = 0xe4588b64;
|
|
||||||
|
|
||||||
// function EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH()
|
|
||||||
bytes4 constant internal EIP_712_ZEROEX_TRANSACTION_SCHEMA_HASH_SELECTOR = 0xc148c58a;
|
|
||||||
|
|
||||||
// function executeTransaction((uint256,uint256,address,bytes),bytes)
|
|
||||||
bytes4 constant internal EXECUTE_TRANSACTION_SELECTOR = 0xcba0648a;
|
|
||||||
|
|
||||||
// function filled(bytes32)
|
|
||||||
bytes4 constant internal FILLED_SELECTOR = 0x288cdc91;
|
|
||||||
|
|
||||||
// function fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),uint256,bytes)
|
|
||||||
bytes4 constant internal FILL_ORDER_SELECTOR = 0x9b44d556;
|
|
||||||
|
|
||||||
// function fillOrderNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),uint256,bytes)
|
|
||||||
bytes4 constant internal FILL_ORDER_NO_THROW_SELECTOR = 0x01da61ae;
|
|
||||||
|
|
||||||
// function fillOrKillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),uint256,bytes)
|
|
||||||
bytes4 constant internal FILL_OR_KILL_ORDER_SELECTOR = 0xe14b58c4;
|
|
||||||
|
|
||||||
// function getAssetProxy(bytes4)
|
|
||||||
bytes4 constant internal GET_ASSET_PROXY_SELECTOR = 0x60704108;
|
|
||||||
|
|
||||||
// function getOrderHash((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes))
|
|
||||||
bytes4 constant internal GET_ORDER_HASH_SELECTOR = 0xad3449bd;
|
|
||||||
|
|
||||||
// function getOrderInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes))
|
|
||||||
bytes4 constant internal GET_ORDER_INFO_SELECTOR = 0x9d3fa4b9;
|
|
||||||
|
|
||||||
// function getOrdersInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[])
|
|
||||||
bytes4 constant internal GET_ORDERS_INFO_SELECTOR = 0x9dfac06d;
|
|
||||||
|
|
||||||
// function getTransactionHash((uint256,uint256,address,bytes))
|
|
||||||
bytes4 constant internal GET_TRANSACTION_HASH_SELECTOR = 0xe0456690;
|
|
||||||
|
|
||||||
// function isValidHashSignature(bytes32,address,bytes)
|
|
||||||
bytes4 constant internal IS_VALID_HASH_SIGNATURE_SELECTOR = 0x8171c407;
|
|
||||||
|
|
||||||
// function isValidOrderSignature((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),address,bytes)
|
|
||||||
bytes4 constant internal IS_VALID_ORDER_SIGNATURE_SELECTOR = 0xf813e384;
|
|
||||||
|
|
||||||
// function isValidTransactionSignature((uint256,uint256,address,bytes),address,bytes)
|
|
||||||
bytes4 constant internal IS_VALID_TRANSACTION_SIGNATURE_SELECTOR = 0xfaa8b882;
|
|
||||||
|
|
||||||
// function marketBuyOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],uint256,bytes[])
|
|
||||||
bytes4 constant internal MARKET_BUY_ORDERS_SELECTOR = 0xdb702a9c;
|
|
||||||
|
|
||||||
// function marketSellOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],uint256,bytes[])
|
|
||||||
bytes4 constant internal MARKET_SELL_ORDERS_SELECTOR = 0x52b3ca9e;
|
|
||||||
|
|
||||||
// function matchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),bytes,bytes)
|
|
||||||
bytes4 constant internal MATCH_ORDERS_SELECTOR = 0x88ec79fb;
|
|
||||||
|
|
||||||
// function matchOrdersWithMaximalFill((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes),bytes,bytes)
|
|
||||||
bytes4 constant internal MATCH_ORDERS_WITH_MAXIMAL_FILL_SELECTOR = 0xb718e292;
|
|
||||||
|
|
||||||
// function orderEpoch(address,address)
|
|
||||||
bytes4 constant internal ORDER_EPOCH_SELECTOR = 0xd9bfa73e;
|
|
||||||
|
|
||||||
// function owner()
|
|
||||||
bytes4 constant internal OWNER_SELECTOR = 0x8da5cb5b;
|
|
||||||
|
|
||||||
// function preSign(bytes32)
|
|
||||||
bytes4 constant internal PRE_SIGN_SELECTOR = 0x46c02d7a;
|
|
||||||
|
|
||||||
// function preSigned(bytes32,address)
|
|
||||||
bytes4 constant internal PRE_SIGNED_SELECTOR = 0x82c174d0;
|
|
||||||
|
|
||||||
// function registerAssetProxy(address)
|
|
||||||
bytes4 constant internal REGISTER_ASSET_PROXY_SELECTOR = 0xc585bb93;
|
|
||||||
|
|
||||||
// function setSignatureValidatorApproval(address,bool)
|
|
||||||
bytes4 constant internal SET_SIGNATURE_VALIDATOR_APPROVAL_SELECTOR = 0x77fcce68;
|
|
||||||
|
|
||||||
// function simulateDispatchTransferFromCalls(bytes[],address[],address[],uint256[])
|
|
||||||
bytes4 constant internal SIMULATE_DISPATCH_TRANSFER_FROM_CALLS_SELECTOR = 0xb04fbddd;
|
|
||||||
|
|
||||||
// function transactionsExecuted(bytes32)
|
|
||||||
bytes4 constant internal TRANSACTIONS_EXECUTED_SELECTOR = 0x0228e168;
|
|
||||||
|
|
||||||
// function transferOwnership(address)
|
|
||||||
bytes4 constant internal TRANSFER_OWNERSHIP_SELECTOR = 0xf2fde38b;
|
|
||||||
|
|
||||||
// function VERSION()
|
|
||||||
bytes4 constant internal VERSION_SELECTOR = 0xffa1ad74;
|
|
||||||
|
|
||||||
// event AssetProxyRegistered(bytes4,address)
|
|
||||||
bytes32 constant internal EVENT_ASSET_PROXY_REGISTERED_SELECTOR = 0xd2c6b762299c609bdb96520b58a49bfb80186934d4f71a86a367571a15c03194;
|
|
||||||
|
|
||||||
// event Cancel(address,address,address,bytes32,bytes,bytes)
|
|
||||||
bytes32 constant internal EVENT_CANCEL_SELECTOR = 0xdc47b3613d9fe400085f6dbdc99453462279057e6207385042827ed6b1a62cf7;
|
|
||||||
|
|
||||||
// event CancelUpTo(address,address,uint256)
|
|
||||||
bytes32 constant internal EVENT_CANCEL_UP_TO_SELECTOR = 0x82af639571738f4ebd4268fb0363d8957ebe1bbb9e78dba5ebd69eed39b154f0;
|
|
||||||
|
|
||||||
// event Fill(address,address,bytes,bytes,bytes,bytes,uint256,uint256,uint256,uint256,address,address,bytes32)
|
|
||||||
bytes32 constant internal EVENT_FILL_SELECTOR = 0xa5a8f3e79ee70e3be6330220296f9075863b936f4098d942ab107367d193a197;
|
|
||||||
|
|
||||||
// event SignatureValidatorApproval(address,address,bool)
|
|
||||||
bytes32 constant internal EVENT_SIGNATURE_VALIDATOR_APPROVAL_SELECTOR = 0xa8656e308026eeabce8f0bc18048433252318ab80ac79da0b3d3d8697dfba891;
|
|
||||||
|
|
||||||
// event TransactionExecution(bytes32)
|
|
||||||
bytes32 constant internal EVENT_TRANSACTION_EXECUTION_SELECTOR = 0xa4a7329f1dd821363067e07d359e347b4af9b1efe4b6cccf13240228af3c800d;
|
|
||||||
}
|
|
@ -18,12 +18,15 @@
|
|||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
|
import "./LibMath.sol";
|
||||||
|
import "./LibOrder.sol";
|
||||||
|
|
||||||
|
|
||||||
contract LibFillResults is
|
library LibFillResults {
|
||||||
SafeMath
|
|
||||||
{
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
struct BatchMatchedFillResults {
|
struct BatchMatchedFillResults {
|
||||||
FillResults[] left; // Fill results for left orders
|
FillResults[] left; // Fill results for left orders
|
||||||
FillResults[] right; // Fill results for right orders
|
FillResults[] right; // Fill results for right orders
|
||||||
@ -45,17 +48,358 @@ contract LibFillResults is
|
|||||||
uint256 profitInRightMakerAsset; // Profit taken from the right maker
|
uint256 profitInRightMakerAsset; // Profit taken from the right maker
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Adds properties of both FillResults instances.
|
/// @dev Calculates amounts filled and fees paid by maker and taker.
|
||||||
/// Modifies the first FillResults instance specified.
|
/// @param order to be filled.
|
||||||
/// @param totalFillResults Fill results instance that will be added onto.
|
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
|
||||||
/// @param singleFillResults Fill results instance that will be added to totalFillResults.
|
/// @return fillResults Amounts filled and fees paid by maker and taker.
|
||||||
function _addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults)
|
function calculateFillResults(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
uint256 takerAssetFilledAmount
|
||||||
|
)
|
||||||
internal
|
internal
|
||||||
pure
|
pure
|
||||||
|
returns (FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
totalFillResults.makerAssetFilledAmount = _safeAdd(totalFillResults.makerAssetFilledAmount, singleFillResults.makerAssetFilledAmount);
|
// Compute proportional transfer amounts
|
||||||
totalFillResults.takerAssetFilledAmount = _safeAdd(totalFillResults.takerAssetFilledAmount, singleFillResults.takerAssetFilledAmount);
|
fillResults.takerAssetFilledAmount = takerAssetFilledAmount;
|
||||||
totalFillResults.makerFeePaid = _safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid);
|
fillResults.makerAssetFilledAmount = LibMath.safeGetPartialAmountFloor(
|
||||||
totalFillResults.takerFeePaid = _safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid);
|
takerAssetFilledAmount,
|
||||||
|
order.takerAssetAmount,
|
||||||
|
order.makerAssetAmount
|
||||||
|
);
|
||||||
|
fillResults.makerFeePaid = LibMath.safeGetPartialAmountFloor(
|
||||||
|
fillResults.makerAssetFilledAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
order.makerFee
|
||||||
|
);
|
||||||
|
fillResults.takerFeePaid = LibMath.safeGetPartialAmountFloor(
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
order.takerAssetAmount,
|
||||||
|
order.takerFee
|
||||||
|
);
|
||||||
|
|
||||||
|
return fillResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Calculates fill amounts for the matched orders.
|
||||||
|
/// 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 leftOrder order goes to the taker (who matched the two orders).
|
||||||
|
/// @param leftOrder First order to match.
|
||||||
|
/// @param rightOrder Second order to match.
|
||||||
|
/// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.
|
||||||
|
/// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.
|
||||||
|
/// @param shouldMaximallyFillOrders A value that indicates whether or not this calculation should use
|
||||||
|
/// the maximal fill order matching strategy.
|
||||||
|
/// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
|
||||||
|
function calculateMatchedFillResults(
|
||||||
|
LibOrder.Order memory leftOrder,
|
||||||
|
LibOrder.Order memory rightOrder,
|
||||||
|
uint256 leftOrderTakerAssetFilledAmount,
|
||||||
|
uint256 rightOrderTakerAssetFilledAmount,
|
||||||
|
bool shouldMaximallyFillOrders
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (MatchedFillResults memory matchedFillResults)
|
||||||
|
{
|
||||||
|
// Derive maker asset amounts for left & right orders, given store taker assert amounts
|
||||||
|
uint256 leftTakerAssetAmountRemaining = leftOrder.takerAssetAmount.safeSub(leftOrderTakerAssetFilledAmount);
|
||||||
|
uint256 leftMakerAssetAmountRemaining = LibMath.safeGetPartialAmountFloor(
|
||||||
|
leftOrder.makerAssetAmount,
|
||||||
|
leftOrder.takerAssetAmount,
|
||||||
|
leftTakerAssetAmountRemaining
|
||||||
|
);
|
||||||
|
uint256 rightTakerAssetAmountRemaining = rightOrder.takerAssetAmount.safeSub(rightOrderTakerAssetFilledAmount);
|
||||||
|
uint256 rightMakerAssetAmountRemaining = LibMath.safeGetPartialAmountFloor(
|
||||||
|
rightOrder.makerAssetAmount,
|
||||||
|
rightOrder.takerAssetAmount,
|
||||||
|
rightTakerAssetAmountRemaining
|
||||||
|
);
|
||||||
|
|
||||||
|
// Maximally fill the orders and pay out profits to the matcher in one or both of the maker assets.
|
||||||
|
if (shouldMaximallyFillOrders) {
|
||||||
|
matchedFillResults = _calculateMatchedFillResultsWithMaximalFill(
|
||||||
|
leftOrder,
|
||||||
|
rightOrder,
|
||||||
|
leftMakerAssetAmountRemaining,
|
||||||
|
leftTakerAssetAmountRemaining,
|
||||||
|
rightMakerAssetAmountRemaining,
|
||||||
|
rightTakerAssetAmountRemaining
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
matchedFillResults = _calculateMatchedFillResults(
|
||||||
|
leftOrder,
|
||||||
|
rightOrder,
|
||||||
|
leftMakerAssetAmountRemaining,
|
||||||
|
leftTakerAssetAmountRemaining,
|
||||||
|
rightMakerAssetAmountRemaining,
|
||||||
|
rightTakerAssetAmountRemaining
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute fees for left order
|
||||||
|
matchedFillResults.left.makerFeePaid = LibMath.safeGetPartialAmountFloor(
|
||||||
|
matchedFillResults.left.makerAssetFilledAmount,
|
||||||
|
leftOrder.makerAssetAmount,
|
||||||
|
leftOrder.makerFee
|
||||||
|
);
|
||||||
|
matchedFillResults.left.takerFeePaid = LibMath.safeGetPartialAmountFloor(
|
||||||
|
matchedFillResults.left.takerAssetFilledAmount,
|
||||||
|
leftOrder.takerAssetAmount,
|
||||||
|
leftOrder.takerFee
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compute fees for right order
|
||||||
|
matchedFillResults.right.makerFeePaid = LibMath.safeGetPartialAmountFloor(
|
||||||
|
matchedFillResults.right.makerAssetFilledAmount,
|
||||||
|
rightOrder.makerAssetAmount,
|
||||||
|
rightOrder.makerFee
|
||||||
|
);
|
||||||
|
matchedFillResults.right.takerFeePaid = LibMath.safeGetPartialAmountFloor(
|
||||||
|
matchedFillResults.right.takerAssetFilledAmount,
|
||||||
|
rightOrder.takerAssetAmount,
|
||||||
|
rightOrder.takerFee
|
||||||
|
);
|
||||||
|
|
||||||
|
// Return fill results
|
||||||
|
return matchedFillResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Adds properties of both FillResults instances.
|
||||||
|
/// @param fillResults1 The first FillResults.
|
||||||
|
/// @param fillResults2 The second FillResults.
|
||||||
|
/// @return The sum of both fill results.
|
||||||
|
function addFillResults(
|
||||||
|
FillResults memory fillResults1,
|
||||||
|
FillResults memory fillResults2
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (FillResults memory totalFillResults)
|
||||||
|
{
|
||||||
|
totalFillResults.makerAssetFilledAmount = fillResults1.makerAssetFilledAmount.safeAdd(fillResults2.makerAssetFilledAmount);
|
||||||
|
totalFillResults.takerAssetFilledAmount = fillResults1.takerAssetFilledAmount.safeAdd(fillResults2.takerAssetFilledAmount);
|
||||||
|
totalFillResults.makerFeePaid = fillResults1.makerFeePaid.safeAdd(fillResults2.makerFeePaid);
|
||||||
|
totalFillResults.takerFeePaid = fillResults1.takerFeePaid.safeAdd(fillResults2.takerFeePaid);
|
||||||
|
|
||||||
|
return totalFillResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Calculates part of the matched fill results for a given situation using the fill strategy that only
|
||||||
|
/// awards profit denominated in the left maker asset.
|
||||||
|
/// @param leftOrder The left order in the order matching situation.
|
||||||
|
/// @param rightOrder The right order in the order matching situation.
|
||||||
|
/// @param leftMakerAssetAmountRemaining The amount of the left order maker asset that can still be filled.
|
||||||
|
/// @param leftTakerAssetAmountRemaining The amount of the left order taker asset that can still be filled.
|
||||||
|
/// @param rightMakerAssetAmountRemaining The amount of the right order maker asset that can still be filled.
|
||||||
|
/// @param rightTakerAssetAmountRemaining The amount of the right order taker asset that can still be filled.
|
||||||
|
/// @return MatchFillResults struct that does not include fees paid.
|
||||||
|
function _calculateMatchedFillResults(
|
||||||
|
LibOrder.Order memory leftOrder,
|
||||||
|
LibOrder.Order memory rightOrder,
|
||||||
|
uint256 leftMakerAssetAmountRemaining,
|
||||||
|
uint256 leftTakerAssetAmountRemaining,
|
||||||
|
uint256 rightMakerAssetAmountRemaining,
|
||||||
|
uint256 rightTakerAssetAmountRemaining
|
||||||
|
)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
returns (MatchedFillResults memory matchedFillResults)
|
||||||
|
{
|
||||||
|
// Calculate fill results for maker and taker assets: at least one order will be fully filled.
|
||||||
|
// The maximum amount the left maker can buy is `leftTakerAssetAmountRemaining`
|
||||||
|
// The maximum amount the right maker can sell is `rightMakerAssetAmountRemaining`
|
||||||
|
// We have two distinct cases for calculating the fill results:
|
||||||
|
// Case 1.
|
||||||
|
// If the left maker can buy more than the right maker can sell, then only the right order is fully filled.
|
||||||
|
// If the left maker can buy exactly what the right maker can sell, then both orders are fully filled.
|
||||||
|
// Case 2.
|
||||||
|
// If the left maker cannot buy more than the right maker can sell, then only the left order is fully filled.
|
||||||
|
// Case 3.
|
||||||
|
// If the left maker can buy exactly as much as the right maker can sell, then both orders are fully filled.
|
||||||
|
if (leftTakerAssetAmountRemaining > rightMakerAssetAmountRemaining) {
|
||||||
|
// Case 1: Right order is fully filled
|
||||||
|
matchedFillResults = _calculateCompleteRightFill(
|
||||||
|
leftOrder,
|
||||||
|
rightMakerAssetAmountRemaining,
|
||||||
|
rightTakerAssetAmountRemaining
|
||||||
|
);
|
||||||
|
} else if (leftTakerAssetAmountRemaining < rightMakerAssetAmountRemaining) {
|
||||||
|
// Case 2: Left order is fully filled
|
||||||
|
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
|
||||||
|
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
|
||||||
|
matchedFillResults.right.makerAssetFilledAmount = leftTakerAssetAmountRemaining;
|
||||||
|
// Round up to ensure the maker's exchange rate does not exceed the price specified by the order.
|
||||||
|
// We favor the maker when the exchange rate must be rounded.
|
||||||
|
matchedFillResults.right.takerAssetFilledAmount = LibMath.safeGetPartialAmountCeil(
|
||||||
|
rightOrder.takerAssetAmount,
|
||||||
|
rightOrder.makerAssetAmount,
|
||||||
|
leftTakerAssetAmountRemaining // matchedFillResults.right.makerAssetFilledAmount
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// leftTakerAssetAmountRemaining == rightMakerAssetAmountRemaining
|
||||||
|
// Case 3: Both orders are fully filled. Technically, this could be captured by the above cases, but
|
||||||
|
// this calculation will be more precise since it does not include rounding.
|
||||||
|
matchedFillResults = _calculateCompleteFillBoth(
|
||||||
|
leftMakerAssetAmountRemaining,
|
||||||
|
leftTakerAssetAmountRemaining,
|
||||||
|
rightMakerAssetAmountRemaining,
|
||||||
|
rightTakerAssetAmountRemaining
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate amount given to taker
|
||||||
|
matchedFillResults.profitInLeftMakerAsset = matchedFillResults.left.makerAssetFilledAmount.safeSub(
|
||||||
|
matchedFillResults.right.takerAssetFilledAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
return matchedFillResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Calculates part of the matched fill results for a given situation using the maximal fill order matching
|
||||||
|
/// strategy.
|
||||||
|
/// @param leftOrder The left order in the order matching situation.
|
||||||
|
/// @param rightOrder The right order in the order matching situation.
|
||||||
|
/// @param leftMakerAssetAmountRemaining The amount of the left order maker asset that can still be filled.
|
||||||
|
/// @param leftTakerAssetAmountRemaining The amount of the left order taker asset that can still be filled.
|
||||||
|
/// @param rightMakerAssetAmountRemaining The amount of the right order maker asset that can still be filled.
|
||||||
|
/// @param rightTakerAssetAmountRemaining The amount of the right order taker asset that can still be filled.
|
||||||
|
/// @return MatchFillResults struct that does not include fees paid.
|
||||||
|
function _calculateMatchedFillResultsWithMaximalFill(
|
||||||
|
LibOrder.Order memory leftOrder,
|
||||||
|
LibOrder.Order memory rightOrder,
|
||||||
|
uint256 leftMakerAssetAmountRemaining,
|
||||||
|
uint256 leftTakerAssetAmountRemaining,
|
||||||
|
uint256 rightMakerAssetAmountRemaining,
|
||||||
|
uint256 rightTakerAssetAmountRemaining
|
||||||
|
)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
returns (MatchedFillResults memory matchedFillResults)
|
||||||
|
{
|
||||||
|
// If a maker asset is greater than the opposite taker asset, than there will be a spread denominated in that maker asset.
|
||||||
|
bool doesLeftMakerAssetProfitExist = leftMakerAssetAmountRemaining > rightTakerAssetAmountRemaining;
|
||||||
|
bool doesRightMakerAssetProfitExist = rightMakerAssetAmountRemaining > leftTakerAssetAmountRemaining;
|
||||||
|
|
||||||
|
// Calculate the maximum fill results for the maker and taker assets. At least one of the orders will be fully filled.
|
||||||
|
//
|
||||||
|
// The maximum that the left maker can possibly buy is the amount that the right order can sell.
|
||||||
|
// The maximum that the right maker can possibly buy is the amount that the left order can sell.
|
||||||
|
//
|
||||||
|
// If the left order is fully filled, profit will be paid out in the left maker asset. If the right order is fully filled,
|
||||||
|
// the profit will be out in the right maker asset.
|
||||||
|
//
|
||||||
|
// There are three cases to consider:
|
||||||
|
// Case 1.
|
||||||
|
// If the left maker can buy more than the right maker can sell, then only the right order is fully filled.
|
||||||
|
// Case 2.
|
||||||
|
// If the right maker can buy more than the left maker can sell, then only the right order is fully filled.
|
||||||
|
// Case 3.
|
||||||
|
// If the right maker can sell the max of what the left maker can buy and the left maker can sell the max of
|
||||||
|
// what the right maker can buy, then both orders are fully filled.
|
||||||
|
if (leftTakerAssetAmountRemaining > rightMakerAssetAmountRemaining) {
|
||||||
|
// Case 1: Right order is fully filled with the profit paid in the left makerAsset
|
||||||
|
matchedFillResults = _calculateCompleteRightFill(
|
||||||
|
leftOrder,
|
||||||
|
rightMakerAssetAmountRemaining,
|
||||||
|
rightTakerAssetAmountRemaining
|
||||||
|
);
|
||||||
|
} else if (rightTakerAssetAmountRemaining > leftMakerAssetAmountRemaining) {
|
||||||
|
// Case 2: Left order is fully filled with the profit paid in the right makerAsset.
|
||||||
|
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
|
||||||
|
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
|
||||||
|
// Round down to ensure the right maker's exchange rate does not exceed the price specified by the order.
|
||||||
|
// We favor the right maker when the exchange rate must be rounded and the profit is being paid in the
|
||||||
|
// right maker asset.
|
||||||
|
matchedFillResults.right.makerAssetFilledAmount = LibMath.safeGetPartialAmountFloor(
|
||||||
|
rightOrder.makerAssetAmount,
|
||||||
|
rightOrder.takerAssetAmount,
|
||||||
|
leftMakerAssetAmountRemaining
|
||||||
|
);
|
||||||
|
matchedFillResults.right.takerAssetFilledAmount = leftMakerAssetAmountRemaining;
|
||||||
|
} else {
|
||||||
|
// Case 3: The right and left orders are fully filled
|
||||||
|
matchedFillResults = _calculateCompleteFillBoth(
|
||||||
|
leftMakerAssetAmountRemaining,
|
||||||
|
leftTakerAssetAmountRemaining,
|
||||||
|
rightMakerAssetAmountRemaining,
|
||||||
|
rightTakerAssetAmountRemaining
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate amount given to taker in the left order's maker asset if the left spread will be part of the profit.
|
||||||
|
if (doesLeftMakerAssetProfitExist) {
|
||||||
|
matchedFillResults.profitInLeftMakerAsset = matchedFillResults.left.makerAssetFilledAmount.safeSub(
|
||||||
|
matchedFillResults.right.takerAssetFilledAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate amount given to taker in the right order's maker asset if the right spread will be part of the profit.
|
||||||
|
if (doesRightMakerAssetProfitExist) {
|
||||||
|
matchedFillResults.profitInRightMakerAsset = matchedFillResults.right.makerAssetFilledAmount.safeSub(
|
||||||
|
matchedFillResults.left.takerAssetFilledAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchedFillResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Calculates the fill results for the maker and taker in the order matching and writes the results
|
||||||
|
/// to the fillResults that are being collected on the order. Both orders will be fully filled in this
|
||||||
|
/// case.
|
||||||
|
/// @param leftMakerAssetAmountRemaining The amount of the left maker asset that is remaining to be filled.
|
||||||
|
/// @param leftTakerAssetAmountRemaining The amount of the left taker asset that is remaining to be filled.
|
||||||
|
/// @param rightMakerAssetAmountRemaining The amount of the right maker asset that is remaining to be filled.
|
||||||
|
/// @param rightTakerAssetAmountRemaining The amount of the right taker asset that is remaining to be filled.
|
||||||
|
/// @return MatchFillResults struct that does not include fees paid or spreads taken.
|
||||||
|
function _calculateCompleteFillBoth(
|
||||||
|
uint256 leftMakerAssetAmountRemaining,
|
||||||
|
uint256 leftTakerAssetAmountRemaining,
|
||||||
|
uint256 rightMakerAssetAmountRemaining,
|
||||||
|
uint256 rightTakerAssetAmountRemaining
|
||||||
|
)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
returns (MatchedFillResults memory matchedFillResults)
|
||||||
|
{
|
||||||
|
// Calculate the fully filled results for both orders.
|
||||||
|
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
|
||||||
|
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
|
||||||
|
matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;
|
||||||
|
matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;
|
||||||
|
|
||||||
|
return matchedFillResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Calculates the fill results for the maker and taker in the order matching and writes the results
|
||||||
|
/// to the fillResults that are being collected on the order.
|
||||||
|
/// @param leftOrder The left order that is being maximally filled. All of the information about fill amounts
|
||||||
|
/// can be derived from this order and the right asset remaining fields.
|
||||||
|
/// @param rightMakerAssetAmountRemaining The amount of the right maker asset that is remaining to be filled.
|
||||||
|
/// @param rightTakerAssetAmountRemaining The amount of the right taker asset that is remaining to be filled.
|
||||||
|
/// @return MatchFillResults struct that does not include fees paid or spreads taken.
|
||||||
|
function _calculateCompleteRightFill(
|
||||||
|
LibOrder.Order memory leftOrder,
|
||||||
|
uint256 rightMakerAssetAmountRemaining,
|
||||||
|
uint256 rightTakerAssetAmountRemaining
|
||||||
|
)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
returns (MatchedFillResults memory matchedFillResults)
|
||||||
|
{
|
||||||
|
matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;
|
||||||
|
matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;
|
||||||
|
matchedFillResults.left.takerAssetFilledAmount = rightMakerAssetAmountRemaining;
|
||||||
|
// Round down to ensure the left maker's exchange rate does not exceed the price specified by the order.
|
||||||
|
// We favor the left maker when the exchange rate must be rounded and the profit is being paid in the
|
||||||
|
// left maker asset.
|
||||||
|
matchedFillResults.left.makerAssetFilledAmount = LibMath.safeGetPartialAmountFloor(
|
||||||
|
leftOrder.makerAssetAmount,
|
||||||
|
leftOrder.takerAssetAmount,
|
||||||
|
rightMakerAssetAmountRemaining
|
||||||
|
);
|
||||||
|
|
||||||
|
return matchedFillResults;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,21 +18,22 @@
|
|||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
import "./LibMathRichErrors.sol";
|
import "./LibMathRichErrors.sol";
|
||||||
|
|
||||||
|
|
||||||
contract LibMath is
|
library LibMath {
|
||||||
SafeMath
|
|
||||||
{
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
/// @dev Calculates partial value given a numerator and denominator rounded down.
|
/// @dev Calculates partial value given a numerator and denominator rounded down.
|
||||||
/// Reverts if rounding error is >= 0.1%
|
/// Reverts if rounding error is >= 0.1%
|
||||||
/// @param numerator Numerator.
|
/// @param numerator Numerator.
|
||||||
/// @param denominator Denominator.
|
/// @param denominator Denominator.
|
||||||
/// @param target Value to calculate partial of.
|
/// @param target Value to calculate partial of.
|
||||||
/// @return Partial value of target rounded down.
|
/// @return Partial value of target rounded down.
|
||||||
function _safeGetPartialAmountFloor(
|
function safeGetPartialAmountFloor(
|
||||||
uint256 numerator,
|
uint256 numerator,
|
||||||
uint256 denominator,
|
uint256 denominator,
|
||||||
uint256 target
|
uint256 target
|
||||||
@ -41,22 +42,19 @@ contract LibMath is
|
|||||||
pure
|
pure
|
||||||
returns (uint256 partialAmount)
|
returns (uint256 partialAmount)
|
||||||
{
|
{
|
||||||
if (_isRoundingErrorFloor(
|
if (isRoundingErrorFloor(
|
||||||
numerator,
|
numerator,
|
||||||
denominator,
|
denominator,
|
||||||
target
|
target
|
||||||
)) {
|
)) {
|
||||||
LibRichErrors._rrevert(LibMathRichErrors.RoundingError(
|
LibRichErrors.rrevert(LibMathRichErrors.RoundingError(
|
||||||
numerator,
|
numerator,
|
||||||
denominator,
|
denominator,
|
||||||
target
|
target
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
partialAmount = _safeDiv(
|
partialAmount = numerator.safeMul(target).safeDiv(denominator);
|
||||||
_safeMul(numerator, target),
|
|
||||||
denominator
|
|
||||||
);
|
|
||||||
return partialAmount;
|
return partialAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +64,7 @@ contract LibMath is
|
|||||||
/// @param denominator Denominator.
|
/// @param denominator Denominator.
|
||||||
/// @param target Value to calculate partial of.
|
/// @param target Value to calculate partial of.
|
||||||
/// @return Partial value of target rounded up.
|
/// @return Partial value of target rounded up.
|
||||||
function _safeGetPartialAmountCeil(
|
function safeGetPartialAmountCeil(
|
||||||
uint256 numerator,
|
uint256 numerator,
|
||||||
uint256 denominator,
|
uint256 denominator,
|
||||||
uint256 target
|
uint256 target
|
||||||
@ -75,12 +73,12 @@ contract LibMath is
|
|||||||
pure
|
pure
|
||||||
returns (uint256 partialAmount)
|
returns (uint256 partialAmount)
|
||||||
{
|
{
|
||||||
if (_isRoundingErrorCeil(
|
if (isRoundingErrorCeil(
|
||||||
numerator,
|
numerator,
|
||||||
denominator,
|
denominator,
|
||||||
target
|
target
|
||||||
)) {
|
)) {
|
||||||
LibRichErrors._rrevert(LibMathRichErrors.RoundingError(
|
LibRichErrors.rrevert(LibMathRichErrors.RoundingError(
|
||||||
numerator,
|
numerator,
|
||||||
denominator,
|
denominator,
|
||||||
target
|
target
|
||||||
@ -90,13 +88,10 @@ contract LibMath is
|
|||||||
// _safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
|
// _safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
|
||||||
// ceil(a / b) = floor((a + b - 1) / b)
|
// ceil(a / b) = floor((a + b - 1) / b)
|
||||||
// To implement `ceil(a / b)` using _safeDiv.
|
// To implement `ceil(a / b)` using _safeDiv.
|
||||||
partialAmount = _safeDiv(
|
partialAmount = numerator.safeMul(target)
|
||||||
_safeAdd(
|
.safeAdd(denominator.safeSub(1))
|
||||||
_safeMul(numerator, target),
|
.safeDiv(denominator);
|
||||||
_safeSub(denominator, 1)
|
|
||||||
),
|
|
||||||
denominator
|
|
||||||
);
|
|
||||||
return partialAmount;
|
return partialAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +100,7 @@ contract LibMath is
|
|||||||
/// @param denominator Denominator.
|
/// @param denominator Denominator.
|
||||||
/// @param target Value to calculate partial of.
|
/// @param target Value to calculate partial of.
|
||||||
/// @return Partial value of target rounded down.
|
/// @return Partial value of target rounded down.
|
||||||
function _getPartialAmountFloor(
|
function getPartialAmountFloor(
|
||||||
uint256 numerator,
|
uint256 numerator,
|
||||||
uint256 denominator,
|
uint256 denominator,
|
||||||
uint256 target
|
uint256 target
|
||||||
@ -114,10 +109,7 @@ contract LibMath is
|
|||||||
pure
|
pure
|
||||||
returns (uint256 partialAmount)
|
returns (uint256 partialAmount)
|
||||||
{
|
{
|
||||||
partialAmount = _safeDiv(
|
partialAmount = numerator.safeMul(target).safeDiv(denominator);
|
||||||
_safeMul(numerator, target),
|
|
||||||
denominator
|
|
||||||
);
|
|
||||||
return partialAmount;
|
return partialAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +118,7 @@ contract LibMath is
|
|||||||
/// @param denominator Denominator.
|
/// @param denominator Denominator.
|
||||||
/// @param target Value to calculate partial of.
|
/// @param target Value to calculate partial of.
|
||||||
/// @return Partial value of target rounded up.
|
/// @return Partial value of target rounded up.
|
||||||
function _getPartialAmountCeil(
|
function getPartialAmountCeil(
|
||||||
uint256 numerator,
|
uint256 numerator,
|
||||||
uint256 denominator,
|
uint256 denominator,
|
||||||
uint256 target
|
uint256 target
|
||||||
@ -138,13 +130,10 @@ contract LibMath is
|
|||||||
// _safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
|
// _safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
|
||||||
// ceil(a / b) = floor((a + b - 1) / b)
|
// ceil(a / b) = floor((a + b - 1) / b)
|
||||||
// To implement `ceil(a / b)` using _safeDiv.
|
// To implement `ceil(a / b)` using _safeDiv.
|
||||||
partialAmount = _safeDiv(
|
partialAmount = numerator.safeMul(target)
|
||||||
_safeAdd(
|
.safeAdd(denominator.safeSub(1))
|
||||||
_safeMul(numerator, target),
|
.safeDiv(denominator);
|
||||||
_safeSub(denominator, 1)
|
|
||||||
),
|
|
||||||
denominator
|
|
||||||
);
|
|
||||||
return partialAmount;
|
return partialAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +142,7 @@ contract LibMath is
|
|||||||
/// @param denominator Denominator.
|
/// @param denominator Denominator.
|
||||||
/// @param target Value to multiply with numerator/denominator.
|
/// @param target Value to multiply with numerator/denominator.
|
||||||
/// @return Rounding error is present.
|
/// @return Rounding error is present.
|
||||||
function _isRoundingErrorFloor(
|
function isRoundingErrorFloor(
|
||||||
uint256 numerator,
|
uint256 numerator,
|
||||||
uint256 denominator,
|
uint256 denominator,
|
||||||
uint256 target
|
uint256 target
|
||||||
@ -163,7 +152,7 @@ contract LibMath is
|
|||||||
returns (bool isError)
|
returns (bool isError)
|
||||||
{
|
{
|
||||||
if (denominator == 0) {
|
if (denominator == 0) {
|
||||||
LibRichErrors._rrevert(LibMathRichErrors.DivisionByZeroError());
|
LibRichErrors.rrevert(LibMathRichErrors.DivisionByZeroError());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The absolute rounding error is the difference between the rounded
|
// The absolute rounding error is the difference between the rounded
|
||||||
@ -197,7 +186,7 @@ contract LibMath is
|
|||||||
numerator,
|
numerator,
|
||||||
denominator
|
denominator
|
||||||
);
|
);
|
||||||
isError = _safeMul(1000, remainder) >= _safeMul(numerator, target);
|
isError = remainder.safeMul(1000) >= numerator.safeMul(target);
|
||||||
return isError;
|
return isError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +195,7 @@ contract LibMath is
|
|||||||
/// @param denominator Denominator.
|
/// @param denominator Denominator.
|
||||||
/// @param target Value to multiply with numerator/denominator.
|
/// @param target Value to multiply with numerator/denominator.
|
||||||
/// @return Rounding error is present.
|
/// @return Rounding error is present.
|
||||||
function _isRoundingErrorCeil(
|
function isRoundingErrorCeil(
|
||||||
uint256 numerator,
|
uint256 numerator,
|
||||||
uint256 denominator,
|
uint256 denominator,
|
||||||
uint256 target
|
uint256 target
|
||||||
@ -216,7 +205,7 @@ contract LibMath is
|
|||||||
returns (bool isError)
|
returns (bool isError)
|
||||||
{
|
{
|
||||||
if (denominator == 0) {
|
if (denominator == 0) {
|
||||||
LibRichErrors._rrevert(LibMathRichErrors.DivisionByZeroError());
|
LibRichErrors.rrevert(LibMathRichErrors.DivisionByZeroError());
|
||||||
}
|
}
|
||||||
|
|
||||||
// See the comments in `isRoundingError`.
|
// See the comments in `isRoundingError`.
|
||||||
@ -232,8 +221,8 @@ contract LibMath is
|
|||||||
numerator,
|
numerator,
|
||||||
denominator
|
denominator
|
||||||
);
|
);
|
||||||
remainder = _safeSub(denominator, remainder) % denominator;
|
remainder = denominator.safeSub(remainder) % denominator;
|
||||||
isError = _safeMul(1000, remainder) >= _safeMul(numerator, target);
|
isError = remainder.safeMul(1000) >= numerator.safeMul(target);
|
||||||
return isError;
|
return isError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "./LibEIP712ExchangeDomain.sol";
|
import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
|
||||||
|
|
||||||
|
|
||||||
contract LibOrder is
|
library LibOrder {
|
||||||
LibEIP712ExchangeDomain
|
|
||||||
{
|
using LibOrder for Order;
|
||||||
|
|
||||||
// Hash for the EIP712 Order Schema:
|
// Hash for the EIP712 Order Schema:
|
||||||
// keccak256(abi.encodePacked(
|
// keccak256(abi.encodePacked(
|
||||||
// "Order(",
|
// "Order(",
|
||||||
@ -44,11 +44,11 @@ contract LibOrder is
|
|||||||
// "bytes takerFeeAssetData",
|
// "bytes takerFeeAssetData",
|
||||||
// ")"
|
// ")"
|
||||||
// ))
|
// ))
|
||||||
bytes32 constant public EIP712_ORDER_SCHEMA_HASH =
|
bytes32 constant internal _EIP712_ORDER_SCHEMA_HASH =
|
||||||
0xf80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a7534;
|
0xf80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a7534;
|
||||||
|
|
||||||
// A valid order remains fillable until it is expired, fully filled, or cancelled.
|
// A valid order remains fillable until it is expired, fully filled, or cancelled.
|
||||||
// An order's state is unaffected by external factors, like account balances.
|
// An order's status is unaffected by external factors, like account balances.
|
||||||
enum OrderStatus {
|
enum OrderStatus {
|
||||||
INVALID, // Default value
|
INVALID, // Default value
|
||||||
INVALID_MAKER_ASSET_AMOUNT, // Order does not have a valid maker asset amount
|
INVALID_MAKER_ASSET_AMOUNT, // Order does not have a valid maker asset amount
|
||||||
@ -67,44 +67,47 @@ contract LibOrder is
|
|||||||
address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods.
|
address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods.
|
||||||
uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0.
|
uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0.
|
||||||
uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0.
|
uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0.
|
||||||
uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted.
|
uint256 makerFee; // Fee paid to feeRecipient by maker when order is filled.
|
||||||
uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted.
|
uint256 takerFee; // Fee paid to feeRecipient by taker when order is filled.
|
||||||
uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires.
|
uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires.
|
||||||
uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash.
|
uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash.
|
||||||
bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The leading bytes4 references the id of the asset proxy.
|
bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The leading bytes4 references the id of the asset proxy.
|
||||||
bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The leading bytes4 references the id of the asset proxy.
|
bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The leading bytes4 references the id of the asset proxy.
|
||||||
bytes makerFeeAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset fees. The leading bytes4 references the id of the asset proxy.
|
bytes makerFeeAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerFeeAsset. The leading bytes4 references the id of the asset proxy.
|
||||||
bytes takerFeeAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset fees. The leading bytes4 references the id of the asset proxy.
|
bytes takerFeeAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerFeeAsset. The leading bytes4 references the id of the asset proxy.
|
||||||
}
|
}
|
||||||
// solhint-enable max-line-length
|
// solhint-enable max-line-length
|
||||||
|
|
||||||
struct OrderInfo {
|
struct OrderInfo {
|
||||||
uint8 orderStatus; // Status that describes order's validity and fillability.
|
uint8 orderStatus; // Status that describes order's validity and fillability.
|
||||||
bytes32 orderHash; // EIP712 hash of the order (see LibOrder.getOrderHash).
|
bytes32 orderHash; // EIP712 typed data hash of the order (see LibOrder.getTypedDataHash).
|
||||||
uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.
|
uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Calculates Keccak-256 hash of the order.
|
/// @dev Calculates the EIP712 typed data hash of an order with a given domain separator.
|
||||||
/// @param order The order structure.
|
/// @param order The order structure.
|
||||||
/// @return Keccak-256 EIP712 hash of the order.
|
/// @return EIP712 typed data hash of the order.
|
||||||
function getOrderHash(Order memory order)
|
function getTypedDataHash(Order memory order, bytes32 eip712ExchangeDomainHash)
|
||||||
public
|
internal
|
||||||
view
|
pure
|
||||||
returns (bytes32 orderHash)
|
returns (bytes32 orderHash)
|
||||||
{
|
{
|
||||||
orderHash = _hashEIP712ExchangeMessage(_hashOrder(order));
|
orderHash = LibEIP712.hashEIP712Message(
|
||||||
|
eip712ExchangeDomainHash,
|
||||||
|
order.getStructHash()
|
||||||
|
);
|
||||||
return orderHash;
|
return orderHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Calculates EIP712 hash of the order.
|
/// @dev Calculates EIP712 hash of the order struct.
|
||||||
/// @param order The order structure.
|
/// @param order The order structure.
|
||||||
/// @return EIP712 hash of the order.
|
/// @return EIP712 hash of the order struct.
|
||||||
function _hashOrder(Order memory order)
|
function getStructHash(Order memory order)
|
||||||
internal
|
internal
|
||||||
pure
|
pure
|
||||||
returns (bytes32 result)
|
returns (bytes32 result)
|
||||||
{
|
{
|
||||||
bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH;
|
bytes32 schemaHash = _EIP712_ORDER_SCHEMA_HASH;
|
||||||
bytes memory makerAssetData = order.makerAssetData;
|
bytes memory makerAssetData = order.makerAssetData;
|
||||||
bytes memory takerAssetData = order.takerAssetData;
|
bytes memory takerAssetData = order.takerAssetData;
|
||||||
bytes memory makerFeeAssetData = order.makerFeeAssetData;
|
bytes memory makerFeeAssetData = order.makerFeeAssetData;
|
||||||
@ -113,10 +116,10 @@ contract LibOrder is
|
|||||||
// Assembly for more efficiently computing:
|
// Assembly for more efficiently computing:
|
||||||
// keccak256(abi.encodePacked(
|
// keccak256(abi.encodePacked(
|
||||||
// EIP712_ORDER_SCHEMA_HASH,
|
// EIP712_ORDER_SCHEMA_HASH,
|
||||||
// bytes32(order.makerAddress),
|
// uint256(order.makerAddress),
|
||||||
// bytes32(order.takerAddress),
|
// uint256(order.takerAddress),
|
||||||
// bytes32(order.feeRecipientAddress),
|
// uint256(order.feeRecipientAddress),
|
||||||
// bytes32(order.senderAddress),
|
// uint256(order.senderAddress),
|
||||||
// order.makerAssetAmount,
|
// order.makerAssetAmount,
|
||||||
// order.takerAssetAmount,
|
// order.takerAssetAmount,
|
||||||
// order.makerFee,
|
// order.makerFee,
|
||||||
|
@ -19,12 +19,13 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "./LibEIP712ExchangeDomain.sol";
|
import "@0x/contracts-utils/contracts/src/LibEIP712.sol";
|
||||||
|
|
||||||
|
|
||||||
contract LibZeroExTransaction is
|
library LibZeroExTransaction {
|
||||||
LibEIP712ExchangeDomain
|
|
||||||
{
|
using LibZeroExTransaction for ZeroExTransaction;
|
||||||
|
|
||||||
// Hash for the EIP712 0x transaction schema
|
// Hash for the EIP712 0x transaction schema
|
||||||
// keccak256(abi.encodePacked(
|
// keccak256(abi.encodePacked(
|
||||||
// "ZeroExTransaction(",
|
// "ZeroExTransaction(",
|
||||||
@ -34,7 +35,7 @@ contract LibZeroExTransaction is
|
|||||||
// "bytes data",
|
// "bytes data",
|
||||||
// ")"
|
// ")"
|
||||||
// ));
|
// ));
|
||||||
bytes32 constant public EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = 0x6b4c70d217b44d0ff0d3bf7aeb18eb8604c5cd06f615a4b497aeefa4f01d2775;
|
bytes32 constant internal _EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = 0x6b4c70d217b44d0ff0d3bf7aeb18eb8604c5cd06f615a4b497aeefa4f01d2775;
|
||||||
|
|
||||||
struct ZeroExTransaction {
|
struct ZeroExTransaction {
|
||||||
uint256 salt; // Arbitrary number to ensure uniqueness of transaction hash.
|
uint256 salt; // Arbitrary number to ensure uniqueness of transaction hash.
|
||||||
@ -43,28 +44,31 @@ contract LibZeroExTransaction is
|
|||||||
bytes data; // AbiV2 encoded calldata.
|
bytes data; // AbiV2 encoded calldata.
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Calculates the EIP712 hash of a 0x transaction using the domain separator of the Exchange contract.
|
/// @dev Calculates the EIP712 typed data hash of a transaction with a given domain separator.
|
||||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
/// @param transaction 0x transaction structure.
|
||||||
/// @return EIP712 hash of the transaction with the domain separator of this contract.
|
/// @return EIP712 typed data hash of the transaction.
|
||||||
function getTransactionHash(ZeroExTransaction memory transaction)
|
function getTypedDataHash(ZeroExTransaction memory transaction, bytes32 eip712ExchangeDomainHash)
|
||||||
public
|
internal
|
||||||
view
|
pure
|
||||||
returns (bytes32 transactionHash)
|
returns (bytes32 transactionHash)
|
||||||
{
|
{
|
||||||
// Hash the transaction with the domain separator of the Exchange contract.
|
// Hash the transaction with the domain separator of the Exchange contract.
|
||||||
transactionHash = _hashEIP712ExchangeMessage(_hashZeroExTransaction(transaction));
|
transactionHash = LibEIP712.hashEIP712Message(
|
||||||
|
eip712ExchangeDomainHash,
|
||||||
|
transaction.getStructHash()
|
||||||
|
);
|
||||||
return transactionHash;
|
return transactionHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Calculates EIP712 hash of the 0x transaction with no domain separator.
|
/// @dev Calculates EIP712 hash of the 0x transaction struct.
|
||||||
/// @param transaction 0x transaction containing salt, signerAddress, and data.
|
/// @param transaction 0x transaction structure.
|
||||||
/// @return EIP712 hash of the transaction with no domain separator.
|
/// @return EIP712 hash of the transaction struct.
|
||||||
function _hashZeroExTransaction(ZeroExTransaction memory transaction)
|
function getStructHash(ZeroExTransaction memory transaction)
|
||||||
internal
|
internal
|
||||||
pure
|
pure
|
||||||
returns (bytes32 result)
|
returns (bytes32 result)
|
||||||
{
|
{
|
||||||
bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;
|
bytes32 schemaHash = _EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;
|
||||||
bytes memory data = transaction.data;
|
bytes memory data = transaction.data;
|
||||||
uint256 salt = transaction.salt;
|
uint256 salt = transaction.salt;
|
||||||
uint256 expirationTimeSeconds = transaction.expirationTimeSeconds;
|
uint256 expirationTimeSeconds = transaction.expirationTimeSeconds;
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../src/LibEIP712ExchangeDomain.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestLibEIP712ExchangeDomain is
|
||||||
|
LibEIP712ExchangeDomain
|
||||||
|
{
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
uint256 chainId,
|
||||||
|
address verifyingContractAddressIfExists
|
||||||
|
)
|
||||||
|
public
|
||||||
|
LibEIP712ExchangeDomain(chainId, verifyingContractAddressIfExists)
|
||||||
|
{}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../src/LibOrder.sol";
|
||||||
|
import "../src/LibFillResults.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestLibFillResults {
|
||||||
|
|
||||||
|
using LibFillResults for *;
|
||||||
|
|
||||||
|
function calculateFillResults(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
uint256 takerAssetFilledAmount
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
|
{
|
||||||
|
fillResults = LibFillResults.calculateFillResults(order, takerAssetFilledAmount);
|
||||||
|
return fillResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateMatchedFillResults(
|
||||||
|
LibOrder.Order memory leftOrder,
|
||||||
|
LibOrder.Order memory rightOrder,
|
||||||
|
uint256 leftOrderTakerAssetFilledAmount,
|
||||||
|
uint256 rightOrderTakerAssetFilledAmount,
|
||||||
|
bool shouldMaximallyFillOrders
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
|
||||||
|
{
|
||||||
|
matchedFillResults = LibFillResults.calculateMatchedFillResults(
|
||||||
|
leftOrder,
|
||||||
|
rightOrder,
|
||||||
|
leftOrderTakerAssetFilledAmount,
|
||||||
|
rightOrderTakerAssetFilledAmount,
|
||||||
|
shouldMaximallyFillOrders
|
||||||
|
);
|
||||||
|
return matchedFillResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addFillResults(
|
||||||
|
LibFillResults.FillResults memory fillResults1,
|
||||||
|
LibFillResults.FillResults memory fillResults2
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (LibFillResults.FillResults memory totalFillResults)
|
||||||
|
{
|
||||||
|
totalFillResults = LibFillResults.addFillResults(fillResults1, fillResults2);
|
||||||
|
return totalFillResults;
|
||||||
|
}
|
||||||
|
}
|
130
contracts/exchange-libs/contracts/test/TestLibMath.sol
Normal file
130
contracts/exchange-libs/contracts/test/TestLibMath.sol
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../src/LibMath.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestLibMath {
|
||||||
|
|
||||||
|
/// @dev Calculates partial value given a numerator and denominator.
|
||||||
|
/// Reverts if rounding error is >= 0.1%
|
||||||
|
/// @param numerator Numerator.
|
||||||
|
/// @param denominator Denominator.
|
||||||
|
/// @param target Value to calculate partial of.
|
||||||
|
/// @return Partial value of target.
|
||||||
|
function safeGetPartialAmountFloor(
|
||||||
|
uint256 numerator,
|
||||||
|
uint256 denominator,
|
||||||
|
uint256 target
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (uint256 partialAmount)
|
||||||
|
{
|
||||||
|
return LibMath.safeGetPartialAmountFloor(numerator, denominator, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Calculates partial value given a numerator and denominator.
|
||||||
|
/// Reverts if rounding error is >= 0.1%
|
||||||
|
/// @param numerator Numerator.
|
||||||
|
/// @param denominator Denominator.
|
||||||
|
/// @param target Value to calculate partial of.
|
||||||
|
/// @return Partial value of target.
|
||||||
|
function safeGetPartialAmountCeil(
|
||||||
|
uint256 numerator,
|
||||||
|
uint256 denominator,
|
||||||
|
uint256 target
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (uint256 partialAmount)
|
||||||
|
{
|
||||||
|
return LibMath.safeGetPartialAmountCeil(numerator, denominator, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Calculates partial value given a numerator and denominator.
|
||||||
|
/// @param numerator Numerator.
|
||||||
|
/// @param denominator Denominator.
|
||||||
|
/// @param target Value to calculate partial of.
|
||||||
|
/// @return Partial value of target.
|
||||||
|
function getPartialAmountFloor(
|
||||||
|
uint256 numerator,
|
||||||
|
uint256 denominator,
|
||||||
|
uint256 target
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (uint256 partialAmount)
|
||||||
|
{
|
||||||
|
return LibMath.getPartialAmountFloor(numerator, denominator, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Calculates partial value given a numerator and denominator.
|
||||||
|
/// @param numerator Numerator.
|
||||||
|
/// @param denominator Denominator.
|
||||||
|
/// @param target Value to calculate partial of.
|
||||||
|
/// @return Partial value of target.
|
||||||
|
function getPartialAmountCeil(
|
||||||
|
uint256 numerator,
|
||||||
|
uint256 denominator,
|
||||||
|
uint256 target
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (uint256 partialAmount)
|
||||||
|
{
|
||||||
|
return LibMath.getPartialAmountCeil(numerator, denominator, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Checks if rounding error >= 0.1%.
|
||||||
|
/// @param numerator Numerator.
|
||||||
|
/// @param denominator Denominator.
|
||||||
|
/// @param target Value to multiply with numerator/denominator.
|
||||||
|
/// @return Rounding error is present.
|
||||||
|
function isRoundingErrorFloor(
|
||||||
|
uint256 numerator,
|
||||||
|
uint256 denominator,
|
||||||
|
uint256 target
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bool isError)
|
||||||
|
{
|
||||||
|
return LibMath.isRoundingErrorFloor(numerator, denominator, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Checks if rounding error >= 0.1%.
|
||||||
|
/// @param numerator Numerator.
|
||||||
|
/// @param denominator Denominator.
|
||||||
|
/// @param target Value to multiply with numerator/denominator.
|
||||||
|
/// @return Rounding error is present.
|
||||||
|
function isRoundingErrorCeil(
|
||||||
|
uint256 numerator,
|
||||||
|
uint256 denominator,
|
||||||
|
uint256 target
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bool isError)
|
||||||
|
{
|
||||||
|
return LibMath.isRoundingErrorCeil(numerator, denominator, target);
|
||||||
|
}
|
||||||
|
}
|
44
contracts/exchange-libs/contracts/test/TestLibOrder.sol
Normal file
44
contracts/exchange-libs/contracts/test/TestLibOrder.sol
Normal file
@ -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.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../src/LibOrder.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestLibOrder {
|
||||||
|
|
||||||
|
function getTypedDataHash(LibOrder.Order memory order, bytes32 eip712ExchangeDomainHash)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bytes32 orderHash)
|
||||||
|
{
|
||||||
|
orderHash = LibOrder.getTypedDataHash(order, eip712ExchangeDomainHash);
|
||||||
|
return orderHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStructHash(LibOrder.Order memory order)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bytes32 result)
|
||||||
|
{
|
||||||
|
result = LibOrder.getStructHash(order);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -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.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../src/LibZeroExTransaction.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestLibZeroExTransaction {
|
||||||
|
|
||||||
|
function getTypedDataHash(LibZeroExTransaction.ZeroExTransaction memory transaction, bytes32 eip712ExchangeDomainHash)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bytes32 transactionHash)
|
||||||
|
{
|
||||||
|
transactionHash = LibZeroExTransaction.getTypedDataHash(transaction, eip712ExchangeDomainHash);
|
||||||
|
return transactionHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStructHash(LibZeroExTransaction.ZeroExTransaction memory transaction)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (bytes32 result)
|
||||||
|
{
|
||||||
|
result = LibZeroExTransaction.getStructHash(transaction);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -1,200 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
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.5.9;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "../src/LibEIP712ExchangeDomain.sol";
|
|
||||||
import "../src/LibMath.sol";
|
|
||||||
import "../src/LibOrder.sol";
|
|
||||||
import "../src/LibZeroExTransaction.sol";
|
|
||||||
import "../src/LibFillResults.sol";
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
|
||||||
contract TestLibs is
|
|
||||||
LibEIP712ExchangeDomain,
|
|
||||||
LibMath,
|
|
||||||
LibOrder,
|
|
||||||
LibZeroExTransaction,
|
|
||||||
LibFillResults
|
|
||||||
{
|
|
||||||
constructor (uint256 chainId)
|
|
||||||
public
|
|
||||||
LibEIP712ExchangeDomain(chainId, address(0))
|
|
||||||
{}
|
|
||||||
|
|
||||||
function getPartialAmountFloor(
|
|
||||||
uint256 numerator,
|
|
||||||
uint256 denominator,
|
|
||||||
uint256 target
|
|
||||||
)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (uint256 partialAmount)
|
|
||||||
{
|
|
||||||
partialAmount = _getPartialAmountFloor(
|
|
||||||
numerator,
|
|
||||||
denominator,
|
|
||||||
target
|
|
||||||
);
|
|
||||||
return partialAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPartialAmountCeil(
|
|
||||||
uint256 numerator,
|
|
||||||
uint256 denominator,
|
|
||||||
uint256 target
|
|
||||||
)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (uint256 partialAmount)
|
|
||||||
{
|
|
||||||
partialAmount = _getPartialAmountCeil(
|
|
||||||
numerator,
|
|
||||||
denominator,
|
|
||||||
target
|
|
||||||
);
|
|
||||||
return partialAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
function safeGetPartialAmountFloor(
|
|
||||||
uint256 numerator,
|
|
||||||
uint256 denominator,
|
|
||||||
uint256 target
|
|
||||||
)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (uint256 partialAmount)
|
|
||||||
{
|
|
||||||
partialAmount = _safeGetPartialAmountFloor(
|
|
||||||
numerator,
|
|
||||||
denominator,
|
|
||||||
target
|
|
||||||
);
|
|
||||||
return partialAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
function safeGetPartialAmountCeil(
|
|
||||||
uint256 numerator,
|
|
||||||
uint256 denominator,
|
|
||||||
uint256 target
|
|
||||||
)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (uint256 partialAmount)
|
|
||||||
{
|
|
||||||
partialAmount = _safeGetPartialAmountCeil(
|
|
||||||
numerator,
|
|
||||||
denominator,
|
|
||||||
target
|
|
||||||
);
|
|
||||||
return partialAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isRoundingErrorFloor(
|
|
||||||
uint256 numerator,
|
|
||||||
uint256 denominator,
|
|
||||||
uint256 target
|
|
||||||
)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (bool isError)
|
|
||||||
{
|
|
||||||
isError = _isRoundingErrorFloor(
|
|
||||||
numerator,
|
|
||||||
denominator,
|
|
||||||
target
|
|
||||||
);
|
|
||||||
return isError;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isRoundingErrorCeil(
|
|
||||||
uint256 numerator,
|
|
||||||
uint256 denominator,
|
|
||||||
uint256 target
|
|
||||||
)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (bool isError)
|
|
||||||
{
|
|
||||||
isError = _isRoundingErrorCeil(
|
|
||||||
numerator,
|
|
||||||
denominator,
|
|
||||||
target
|
|
||||||
);
|
|
||||||
return isError;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getOrderSchemaHash()
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (bytes32)
|
|
||||||
{
|
|
||||||
return EIP712_ORDER_SCHEMA_HASH;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDomainSeparatorSchemaHash()
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (bytes32)
|
|
||||||
{
|
|
||||||
return EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDomainSeparator()
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (bytes32)
|
|
||||||
{
|
|
||||||
return EIP712_EXCHANGE_DOMAIN_HASH;
|
|
||||||
}
|
|
||||||
|
|
||||||
function addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (FillResults memory)
|
|
||||||
{
|
|
||||||
_addFillResults(totalFillResults, singleFillResults);
|
|
||||||
return totalFillResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hashOrder(Order memory order)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (bytes32)
|
|
||||||
{
|
|
||||||
return _hashOrder(order);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hashZeroExTransaction(ZeroExTransaction memory transaction)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (bytes32)
|
|
||||||
{
|
|
||||||
return _hashZeroExTransaction(transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hashEIP712ExchangeMessage(bytes32 hashStruct)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (bytes32)
|
|
||||||
{
|
|
||||||
return _hashEIP712ExchangeMessage(hashStruct);
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
@ -31,11 +31,10 @@
|
|||||||
"coverage:report:lcov": "istanbul report lcov",
|
"coverage:report:lcov": "istanbul report lcov",
|
||||||
"test:circleci": "yarn test",
|
"test:circleci": "yarn test",
|
||||||
"contracts:gen": "contracts-gen",
|
"contracts:gen": "contracts-gen",
|
||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||||
"generate-exchange-selectors": "node lib/scripts/generate-exchange-selectors.js ../../../exchange/generated-artifacts/Exchange.json ./contracts/src/LibExchangeSelectors.sol"
|
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"abis": "./generated-artifacts/@(LibEIP712ExchangeDomain|LibFillResults|LibMath|LibOrder|LibZeroExTransaction|TestLibs).json",
|
"abis": "./generated-artifacts/@(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": {
|
||||||
|
@ -1,162 +0,0 @@
|
|||||||
import { AbiDefinition, ContractAbi, DataItem, EventAbi, MethodAbi } from 'ethereum-types';
|
|
||||||
import * as ethUtil from 'ethereumjs-util';
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as process from 'process';
|
|
||||||
|
|
||||||
const keccak256 = ethUtil.sha3;
|
|
||||||
const ARGS = process.argv.slice(2);
|
|
||||||
const INDENT = ' ';
|
|
||||||
const LINEBREAK = '\n';
|
|
||||||
const VISIBILITY = 'internal';
|
|
||||||
|
|
||||||
interface ParsedContract {
|
|
||||||
methods: {
|
|
||||||
[functionName: string]: Array<{
|
|
||||||
selector: string;
|
|
||||||
signature: string;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
events: {
|
|
||||||
[eventName: string]: {
|
|
||||||
selector: string;
|
|
||||||
signature: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable: no-console
|
|
||||||
(() => {
|
|
||||||
const [exchangeArtifactsFile, outputFile] = ARGS;
|
|
||||||
const exchangeArtifacts = require(exchangeArtifactsFile);
|
|
||||||
const contractName = path.basename(outputFile, '.sol');
|
|
||||||
const parsedContract = parseContract(exchangeArtifacts.compilerOutput.abi);
|
|
||||||
const contractDefinition = defineContract(contractName, parsedContract);
|
|
||||||
const preamble = extractOutputFilePreamble(outputFile);
|
|
||||||
const outputFileContents = `${preamble}${contractDefinition}${LINEBREAK}`;
|
|
||||||
fs.writeFileSync(outputFile, outputFileContents);
|
|
||||||
console.log(`Wrote exchange selectors to "${path.resolve(outputFile)}."`);
|
|
||||||
})();
|
|
||||||
|
|
||||||
function parseContract(abi: ContractAbi): ParsedContract {
|
|
||||||
const parsedContract: ParsedContract = {
|
|
||||||
methods: {},
|
|
||||||
events: {},
|
|
||||||
};
|
|
||||||
for (const abiItem of abi) {
|
|
||||||
if (isMethodAbi(abiItem)) {
|
|
||||||
const name = abiItem.name;
|
|
||||||
const signature = `${name}(${encodeMethodInputs(abiItem.inputs)})`;
|
|
||||||
const selector = `0x${keccak256(signature)
|
|
||||||
.slice(0, 4)
|
|
||||||
.toString('hex')}`;
|
|
||||||
if (parsedContract.methods[name] === undefined) {
|
|
||||||
parsedContract.methods[name] = [];
|
|
||||||
}
|
|
||||||
parsedContract.methods[name].push({
|
|
||||||
selector,
|
|
||||||
signature,
|
|
||||||
});
|
|
||||||
} else if (isEventAbi(abiItem)) {
|
|
||||||
const name = abiItem.name;
|
|
||||||
const signature = `${name}(${encodeMethodInputs(abiItem.inputs)})`;
|
|
||||||
const selector = `0x${keccak256(signature).toString('hex')}`;
|
|
||||||
parsedContract.events[name] = {
|
|
||||||
selector,
|
|
||||||
signature,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return parsedContract;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isMethodAbi(abiItem: AbiDefinition): abiItem is MethodAbi {
|
|
||||||
return abiItem.type === 'function';
|
|
||||||
}
|
|
||||||
|
|
||||||
function isEventAbi(abiItem: AbiDefinition): abiItem is EventAbi {
|
|
||||||
return abiItem.type === 'event';
|
|
||||||
}
|
|
||||||
|
|
||||||
function defineContract(contractName: string, parsedContract: ParsedContract): string {
|
|
||||||
const constantDefinitions = [];
|
|
||||||
// Define function selectors.
|
|
||||||
const sortedMethodNames = _.sortBy(_.keys(parsedContract.methods), name => name.toLowerCase());
|
|
||||||
for (const name of sortedMethodNames) {
|
|
||||||
const methods = parsedContract.methods[name];
|
|
||||||
for (let idx = 0; idx < methods.length; idx++) {
|
|
||||||
const constantLines = generateFunctionSelectorConstantDefinition(
|
|
||||||
name,
|
|
||||||
methods[idx].signature,
|
|
||||||
methods[idx].selector,
|
|
||||||
idx,
|
|
||||||
methods.length,
|
|
||||||
);
|
|
||||||
constantDefinitions.push(constantLines);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Define event selectors.
|
|
||||||
const sortedEventNames = _.sortBy(_.keys(parsedContract.events), name => name.toLowerCase());
|
|
||||||
for (const name of sortedEventNames) {
|
|
||||||
const event = parsedContract.events[name];
|
|
||||||
const constantLines = generateEventSelectorConstantDefinition(name, event.signature, event.selector);
|
|
||||||
constantDefinitions.push(constantLines);
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
`contract ${contractName} {`,
|
|
||||||
`${INDENT}// solhint-disable max-line-length`,
|
|
||||||
'',
|
|
||||||
constantDefinitions
|
|
||||||
.map(lines => lines.map(line => `${INDENT}${line}`))
|
|
||||||
.map(lines => lines.join(LINEBREAK))
|
|
||||||
.join(`${LINEBREAK}${LINEBREAK}`),
|
|
||||||
`}`,
|
|
||||||
].join(LINEBREAK);
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractOutputFilePreamble(outputFile: string): string {
|
|
||||||
const preambleLines = [];
|
|
||||||
const outputFileLines = fs.readFileSync(outputFile, 'utf-8').split(/\r?\n/);
|
|
||||||
for (const line of outputFileLines) {
|
|
||||||
if (/^\s*contract\s+[a-zA-Z][a-zA-Z0-9_]+/.test(line)) {
|
|
||||||
preambleLines.push('');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
preambleLines.push(line);
|
|
||||||
}
|
|
||||||
return preambleLines.join(LINEBREAK);
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateFunctionSelectorConstantDefinition(
|
|
||||||
name: string,
|
|
||||||
signature: string,
|
|
||||||
selector: string,
|
|
||||||
idx: number,
|
|
||||||
total: number,
|
|
||||||
): string[] {
|
|
||||||
const varName = _.snakeCase(total === 1 ? name : `${name}_${idx + 1}`).toUpperCase();
|
|
||||||
return [`// function ${signature}`, `bytes4 constant ${VISIBILITY} ${varName}_SELECTOR = ${selector};`];
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateEventSelectorConstantDefinition(name: string, signature: string, selector: string): string[] {
|
|
||||||
const varName = _.snakeCase(name).toUpperCase();
|
|
||||||
return [`// event ${signature}`, `bytes32 constant ${VISIBILITY} EVENT_${varName}_SELECTOR = ${selector};`];
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeMethodInputs(inputs?: DataItem[]): string {
|
|
||||||
if (inputs === undefined) {
|
|
||||||
throw new Error('encodeMethodInputs: inputs are undefined');
|
|
||||||
}
|
|
||||||
const types = [];
|
|
||||||
for (const input of inputs) {
|
|
||||||
if (input.type === 'tuple') {
|
|
||||||
types.push(`(${encodeMethodInputs(input.components)})`);
|
|
||||||
} else if (input.type === 'tuple[]') {
|
|
||||||
types.push(`(${encodeMethodInputs(input.components)})[]`);
|
|
||||||
} else {
|
|
||||||
types.push(input.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return types.join(',');
|
|
||||||
}
|
|
@ -6,16 +6,28 @@
|
|||||||
import { ContractArtifact } from 'ethereum-types';
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
import * as LibEIP712ExchangeDomain from '../generated-artifacts/LibEIP712ExchangeDomain.json';
|
import * as LibEIP712ExchangeDomain from '../generated-artifacts/LibEIP712ExchangeDomain.json';
|
||||||
|
import * as LibExchangeRichErrors from '../generated-artifacts/LibExchangeRichErrors.json';
|
||||||
import * as LibFillResults from '../generated-artifacts/LibFillResults.json';
|
import * as LibFillResults from '../generated-artifacts/LibFillResults.json';
|
||||||
import * as LibMath from '../generated-artifacts/LibMath.json';
|
import * as LibMath from '../generated-artifacts/LibMath.json';
|
||||||
|
import * as LibMathRichErrors from '../generated-artifacts/LibMathRichErrors.json';
|
||||||
import * as LibOrder from '../generated-artifacts/LibOrder.json';
|
import * as LibOrder from '../generated-artifacts/LibOrder.json';
|
||||||
import * as LibZeroExTransaction from '../generated-artifacts/LibZeroExTransaction.json';
|
import * as LibZeroExTransaction from '../generated-artifacts/LibZeroExTransaction.json';
|
||||||
import * as TestLibs from '../generated-artifacts/TestLibs.json';
|
import * as TestLibEIP712ExchangeDomain from '../generated-artifacts/TestLibEIP712ExchangeDomain.json';
|
||||||
|
import * as TestLibFillResults from '../generated-artifacts/TestLibFillResults.json';
|
||||||
|
import * as TestLibMath from '../generated-artifacts/TestLibMath.json';
|
||||||
|
import * as TestLibOrder from '../generated-artifacts/TestLibOrder.json';
|
||||||
|
import * as TestLibZeroExTransaction from '../generated-artifacts/TestLibZeroExTransaction.json';
|
||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
LibEIP712ExchangeDomain: LibEIP712ExchangeDomain as ContractArtifact,
|
LibEIP712ExchangeDomain: LibEIP712ExchangeDomain as ContractArtifact,
|
||||||
|
LibExchangeRichErrors: LibExchangeRichErrors as ContractArtifact,
|
||||||
LibFillResults: LibFillResults as ContractArtifact,
|
LibFillResults: LibFillResults as ContractArtifact,
|
||||||
LibMath: LibMath as ContractArtifact,
|
LibMath: LibMath as ContractArtifact,
|
||||||
|
LibMathRichErrors: LibMathRichErrors as ContractArtifact,
|
||||||
LibOrder: LibOrder as ContractArtifact,
|
LibOrder: LibOrder as ContractArtifact,
|
||||||
LibZeroExTransaction: LibZeroExTransaction as ContractArtifact,
|
LibZeroExTransaction: LibZeroExTransaction as ContractArtifact,
|
||||||
TestLibs: TestLibs as ContractArtifact,
|
TestLibEIP712ExchangeDomain: TestLibEIP712ExchangeDomain as ContractArtifact,
|
||||||
|
TestLibFillResults: TestLibFillResults as ContractArtifact,
|
||||||
|
TestLibMath: TestLibMath as ContractArtifact,
|
||||||
|
TestLibOrder: TestLibOrder as ContractArtifact,
|
||||||
|
TestLibZeroExTransaction: TestLibZeroExTransaction as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ReferenceFunctions } from '@0x/contracts-utils';
|
import { ReferenceFunctions } from '@0x/contracts-utils';
|
||||||
import { LibMathRevertErrors } from '@0x/order-utils';
|
import { LibMathRevertErrors } from '@0x/order-utils';
|
||||||
import { FillResults } from '@0x/types';
|
import { FillResults, OrderWithoutDomain } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
const { safeAdd, safeSub, safeMul, safeDiv } = ReferenceFunctions;
|
const { safeAdd, safeSub, safeMul, safeDiv } = ReferenceFunctions;
|
||||||
@ -87,3 +87,22 @@ export function addFillResults(a: FillResults, b: FillResults): FillResults {
|
|||||||
takerFeePaid: safeAdd(a.takerFeePaid, b.takerFeePaid),
|
takerFeePaid: safeAdd(a.takerFeePaid, b.takerFeePaid),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates amounts filled and fees paid by maker and taker.
|
||||||
|
*/
|
||||||
|
export function calculateFillResults(order: OrderWithoutDomain, takerAssetFilledAmount: BigNumber): FillResults {
|
||||||
|
const makerAssetFilledAmount = safeGetPartialAmountFloor(
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
order.takerAssetAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
);
|
||||||
|
const makerFeePaid = safeGetPartialAmountFloor(makerAssetFilledAmount, order.makerAssetAmount, order.makerFee);
|
||||||
|
const takerFeePaid = safeGetPartialAmountFloor(takerAssetFilledAmount, order.takerAssetAmount, order.takerFee);
|
||||||
|
return {
|
||||||
|
makerAssetFilledAmount,
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
makerFeePaid,
|
||||||
|
takerFeePaid,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -4,8 +4,14 @@
|
|||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
export * from '../generated-wrappers/lib_e_i_p712_exchange_domain';
|
export * from '../generated-wrappers/lib_e_i_p712_exchange_domain';
|
||||||
|
export * from '../generated-wrappers/lib_exchange_rich_errors';
|
||||||
export * from '../generated-wrappers/lib_fill_results';
|
export * from '../generated-wrappers/lib_fill_results';
|
||||||
export * from '../generated-wrappers/lib_math';
|
export * from '../generated-wrappers/lib_math';
|
||||||
|
export * from '../generated-wrappers/lib_math_rich_errors';
|
||||||
export * from '../generated-wrappers/lib_order';
|
export * from '../generated-wrappers/lib_order';
|
||||||
export * from '../generated-wrappers/lib_zero_ex_transaction';
|
export * from '../generated-wrappers/lib_zero_ex_transaction';
|
||||||
export * from '../generated-wrappers/test_libs';
|
export * from '../generated-wrappers/test_lib_e_i_p712_exchange_domain';
|
||||||
|
export * from '../generated-wrappers/test_lib_fill_results';
|
||||||
|
export * from '../generated-wrappers/test_lib_math';
|
||||||
|
export * from '../generated-wrappers/test_lib_order';
|
||||||
|
export * from '../generated-wrappers/test_lib_zero_ex_transaction';
|
||||||
|
@ -1,212 +0,0 @@
|
|||||||
import { addressUtils, chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
|
|
||||||
import { BlockchainLifecycle } from '@0x/dev-utils';
|
|
||||||
import { assetDataUtils, orderHashUtils, transactionHashUtils } from '@0x/order-utils';
|
|
||||||
import { constants as orderConstants } from '@0x/order-utils/lib/src/constants';
|
|
||||||
import { Order, ZeroExTransaction } from '@0x/types';
|
|
||||||
import { BigNumber, providerUtils } from '@0x/utils';
|
|
||||||
import * as chai from 'chai';
|
|
||||||
import * as ethUtil from 'ethereumjs-util';
|
|
||||||
|
|
||||||
import { TestLibsContract } from '../src';
|
|
||||||
import { artifacts } from '../src/artifacts';
|
|
||||||
|
|
||||||
import { stringifySchema } from './utils';
|
|
||||||
|
|
||||||
chaiSetup.configure();
|
|
||||||
const expect = chai.expect;
|
|
||||||
|
|
||||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
|
||||||
|
|
||||||
describe('Exchange libs', () => {
|
|
||||||
let chainId: number;
|
|
||||||
let order: Order;
|
|
||||||
let transaction: ZeroExTransaction;
|
|
||||||
let libs: TestLibsContract;
|
|
||||||
let libsAlternateChain: TestLibsContract;
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
await blockchainLifecycle.startAsync();
|
|
||||||
});
|
|
||||||
after(async () => {
|
|
||||||
await blockchainLifecycle.revertAsync();
|
|
||||||
});
|
|
||||||
before(async () => {
|
|
||||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
|
||||||
const [makerAddress, takerAddress, senderAddress, feeRecipientAddress, signerAddress] = accounts.slice(0, 5);
|
|
||||||
chainId = await providerUtils.getChainIdAsync(provider);
|
|
||||||
libs = await TestLibsContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestLibs,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
new BigNumber(chainId),
|
|
||||||
);
|
|
||||||
// Deploy a version with a different chain ID.
|
|
||||||
const alternateChainId = chainId + 1;
|
|
||||||
libsAlternateChain = await TestLibsContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestLibs,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
new BigNumber(alternateChainId),
|
|
||||||
);
|
|
||||||
const domain = {
|
|
||||||
verifyingContractAddress: libs.address,
|
|
||||||
chainId,
|
|
||||||
};
|
|
||||||
order = {
|
|
||||||
...constants.STATIC_ORDER_PARAMS,
|
|
||||||
makerAddress,
|
|
||||||
takerAddress,
|
|
||||||
senderAddress,
|
|
||||||
feeRecipientAddress,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
|
|
||||||
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
|
|
||||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
|
|
||||||
salt: new BigNumber(0),
|
|
||||||
expirationTimeSeconds: new BigNumber(0),
|
|
||||||
domain,
|
|
||||||
};
|
|
||||||
transaction = {
|
|
||||||
signerAddress,
|
|
||||||
salt: new BigNumber(0),
|
|
||||||
expirationTimeSeconds: new BigNumber(0),
|
|
||||||
data: constants.NULL_BYTES,
|
|
||||||
domain,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
beforeEach(async () => {
|
|
||||||
await blockchainLifecycle.startAsync();
|
|
||||||
});
|
|
||||||
afterEach(async () => {
|
|
||||||
await blockchainLifecycle.revertAsync();
|
|
||||||
});
|
|
||||||
// Note(albrow): These tests are designed to be supplemental to the
|
|
||||||
// combinatorial tests in test/exchange/internal. They test specific edge
|
|
||||||
// cases that are not covered by the combinatorial tests.
|
|
||||||
describe('LibMath', () => {
|
|
||||||
describe('isRoundingError', () => {
|
|
||||||
it('should return true if there is a rounding error of 0.1%', async () => {
|
|
||||||
const numerator = new BigNumber(20);
|
|
||||||
const denominator = new BigNumber(999);
|
|
||||||
const target = new BigNumber(50);
|
|
||||||
// rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
|
|
||||||
const isRoundingError = await libs.isRoundingErrorFloor.callAsync(numerator, denominator, target);
|
|
||||||
expect(isRoundingError).to.be.true();
|
|
||||||
});
|
|
||||||
it('should return false if there is a rounding of 0.09%', async () => {
|
|
||||||
const numerator = new BigNumber(20);
|
|
||||||
const denominator = new BigNumber(9991);
|
|
||||||
const target = new BigNumber(500);
|
|
||||||
// rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09%
|
|
||||||
const isRoundingError = await libs.isRoundingErrorFloor.callAsync(numerator, denominator, target);
|
|
||||||
expect(isRoundingError).to.be.false();
|
|
||||||
});
|
|
||||||
it('should return true if there is a rounding error of 0.11%', async () => {
|
|
||||||
const numerator = new BigNumber(20);
|
|
||||||
const denominator = new BigNumber(9989);
|
|
||||||
const target = new BigNumber(500);
|
|
||||||
// rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011%
|
|
||||||
const isRoundingError = await libs.isRoundingErrorFloor.callAsync(numerator, denominator, target);
|
|
||||||
expect(isRoundingError).to.be.true();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('isRoundingErrorCeil', () => {
|
|
||||||
it('should return true if there is a rounding error of 0.1%', async () => {
|
|
||||||
const numerator = new BigNumber(20);
|
|
||||||
const denominator = new BigNumber(1001);
|
|
||||||
const target = new BigNumber(50);
|
|
||||||
// rounding error = (ceil(20*50/1001) - (20*50/1001)) / (20*50/1001) = 0.1%
|
|
||||||
const isRoundingError = await libs.isRoundingErrorCeil.callAsync(numerator, denominator, target);
|
|
||||||
expect(isRoundingError).to.be.true();
|
|
||||||
});
|
|
||||||
it('should return false if there is a rounding of 0.09%', async () => {
|
|
||||||
const numerator = new BigNumber(20);
|
|
||||||
const denominator = new BigNumber(10009);
|
|
||||||
const target = new BigNumber(500);
|
|
||||||
// rounding error = (ceil(20*500/10009) - (20*500/10009)) / (20*500/10009) = 0.09%
|
|
||||||
const isRoundingError = await libs.isRoundingErrorCeil.callAsync(numerator, denominator, target);
|
|
||||||
expect(isRoundingError).to.be.false();
|
|
||||||
});
|
|
||||||
it('should return true if there is a rounding error of 0.11%', async () => {
|
|
||||||
const numerator = new BigNumber(20);
|
|
||||||
const denominator = new BigNumber(10011);
|
|
||||||
const target = new BigNumber(500);
|
|
||||||
// rounding error = (ceil(20*500/10011) - (20*500/10011)) / (20*500/10011) = 0.11%
|
|
||||||
const isRoundingError = await libs.isRoundingErrorCeil.callAsync(numerator, denominator, target);
|
|
||||||
expect(isRoundingError).to.be.true();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('LibOrder', () => {
|
|
||||||
describe('getOrderHash', () => {
|
|
||||||
it('should return the correct orderHash', async () => {
|
|
||||||
const orderHashHex = await libs.getOrderHash.callAsync(order);
|
|
||||||
expect(orderHashUtils.getOrderHashHex(order)).to.be.equal(orderHashHex);
|
|
||||||
});
|
|
||||||
it('orderHash should differ if chainId is different', async () => {
|
|
||||||
const orderHashHex1 = await libsAlternateChain.getOrderHash.callAsync(order);
|
|
||||||
const orderHashHex2 = await libs.getOrderHash.callAsync(order);
|
|
||||||
expect(orderHashHex1).to.be.not.equal(orderHashHex2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('LibZeroExTransaction', () => {
|
|
||||||
describe('EIP712ZeroExTransactionSchemaHash', () => {
|
|
||||||
it('should return the correct schema hash', async () => {
|
|
||||||
const schemaHash = await libs.EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH.callAsync();
|
|
||||||
const schemaString =
|
|
||||||
'ZeroExTransaction(uint256 salt,uint256 expirationTimeSeconds,address signerAddress,bytes data)';
|
|
||||||
const expectedSchemaHash = ethUtil.addHexPrefix(ethUtil.bufferToHex(ethUtil.sha3(schemaString)));
|
|
||||||
expect(schemaHash).to.equal(expectedSchemaHash);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('getTransactionHash', () => {
|
|
||||||
it('should return the correct transactionHash', async () => {
|
|
||||||
const transactionHash = await libs.getTransactionHash.callAsync(transaction);
|
|
||||||
const expectedTransactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
|
||||||
expect(transactionHash).to.equal(expectedTransactionHash);
|
|
||||||
});
|
|
||||||
it('transactionHash should differ if chainId is different', async () => {
|
|
||||||
const transactionHash1 = await libsAlternateChain.getTransactionHash.callAsync(transaction);
|
|
||||||
const transactionHash2 = await libs.getTransactionHash.callAsync(transaction);
|
|
||||||
expect(transactionHash1).to.not.equal(transactionHash2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('LibEIP712', () => {
|
|
||||||
it('should return the correct domain separator schema hash', async () => {
|
|
||||||
const schema = stringifySchema(orderConstants.DEFAULT_DOMAIN_SCHEMA);
|
|
||||||
const expectedSchemaHash = ethUtil.bufferToHex(ethUtil.sha3(Buffer.from(schema)));
|
|
||||||
const actualSchemaHash = await libs.getDomainSeparatorSchemaHash.callAsync();
|
|
||||||
expect(actualSchemaHash).to.be.equal(expectedSchemaHash);
|
|
||||||
});
|
|
||||||
it('should return the correct order schema hash', async () => {
|
|
||||||
const schema = stringifySchema(orderConstants.EXCHANGE_ORDER_SCHEMA);
|
|
||||||
const expectedSchemaHash = ethUtil.bufferToHex(ethUtil.sha3(Buffer.from(schema)));
|
|
||||||
const actualSchemaHash = await libs.getOrderSchemaHash.callAsync();
|
|
||||||
expect(actualSchemaHash).to.be.equal(expectedSchemaHash);
|
|
||||||
});
|
|
||||||
it('should return the correct domain separator', async () => {
|
|
||||||
const schema = stringifySchema(orderConstants.DEFAULT_DOMAIN_SCHEMA);
|
|
||||||
const schemaHash = ethUtil.sha3(Buffer.from(schema));
|
|
||||||
const payload = Buffer.concat([
|
|
||||||
schemaHash,
|
|
||||||
ethUtil.sha3(Buffer.from(orderConstants.EXCHANGE_DOMAIN_NAME)),
|
|
||||||
ethUtil.sha3(Buffer.from(orderConstants.EXCHANGE_DOMAIN_VERSION)),
|
|
||||||
ethUtil.setLengthLeft(ethUtil.toBuffer(chainId), 32),
|
|
||||||
ethUtil.setLengthLeft(ethUtil.toBuffer(libs.address), 32),
|
|
||||||
]);
|
|
||||||
const expectedDomain = ethUtil.bufferToHex(ethUtil.sha3(payload));
|
|
||||||
const actualDomain = await libs.getDomainSeparator.callAsync();
|
|
||||||
expect(actualDomain).to.be.equal(expectedDomain);
|
|
||||||
});
|
|
||||||
it('should return a different domain separator if chainId is different', async () => {
|
|
||||||
const domain1 = await libsAlternateChain.getDomainSeparator.callAsync();
|
|
||||||
const domain2 = await libs.getDomainSeparator.callAsync();
|
|
||||||
expect(domain1).to.be.not.equal(domain2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,65 +1,49 @@
|
|||||||
import { blockchainTests, constants, describe, expect, hexRandom } from '@0x/contracts-test-utils';
|
import { addressUtils, blockchainTests, constants, expect } from '@0x/contracts-test-utils';
|
||||||
import { BigNumber, signTypedDataUtils } from '@0x/utils';
|
import { BigNumber, signTypedDataUtils } from '@0x/utils';
|
||||||
import * as ethUtil from 'ethereumjs-util';
|
import * as ethUtil from 'ethereumjs-util';
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { artifacts, TestLibsContract } from '../src';
|
import { artifacts, TestLibEIP712ExchangeDomainContract } from '../src';
|
||||||
|
|
||||||
blockchainTests('LibEIP712ExchangeDomain', env => {
|
blockchainTests('LibEIP712ExchangeDomain', env => {
|
||||||
let libsContract: TestLibsContract;
|
describe('constructor', () => {
|
||||||
let exchangeDomainHash: string;
|
it('should calculate the correct domain hash when verifyingContractAddressIfExists is set to null', async () => {
|
||||||
const CHAIN_ID = 1337;
|
const chainId = 1;
|
||||||
|
const libEIP712ExchangeDomainContract = await TestLibEIP712ExchangeDomainContract.deployFrom0xArtifactAsync(
|
||||||
// Random generator functions
|
artifacts.TestLibEIP712ExchangeDomain,
|
||||||
const randomHash = () => hexRandom(constants.WORD_LENGTH);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests a specific instance of EIP712 message hashing.
|
|
||||||
* @param lib The LibEIP712 contract to call.
|
|
||||||
* @param domainHash The hash of the EIP712 domain of this instance.
|
|
||||||
* @param hashStruct The hash of the struct of this instance.
|
|
||||||
*/
|
|
||||||
async function testHashEIP712MessageAsync(hashStruct: string): Promise<void> {
|
|
||||||
// Remove the hex-prefix from the exchangeDomainHash and the hashStruct
|
|
||||||
const unprefixedHashStruct = hashStruct.slice(2, hashStruct.length);
|
|
||||||
|
|
||||||
// Hash the provided input to get the expected hash
|
|
||||||
const input = '0x1901'.concat(exchangeDomainHash, unprefixedHashStruct);
|
|
||||||
const expectedHash = '0x'.concat(ethUtil.sha3(input).toString('hex'));
|
|
||||||
|
|
||||||
// Get the actual hash by calling the smart contract
|
|
||||||
const actualHash = await libsContract.hashEIP712ExchangeMessage.callAsync(hashStruct);
|
|
||||||
|
|
||||||
// Verify that the actual hash matches the expected hash
|
|
||||||
expect(actualHash).to.be.eq(expectedHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
before(async () => {
|
|
||||||
libsContract = await TestLibsContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.TestLibs,
|
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
new BigNumber(CHAIN_ID),
|
new BigNumber(chainId),
|
||||||
|
constants.NULL_ADDRESS,
|
||||||
);
|
);
|
||||||
|
const domain = {
|
||||||
// Generate the domain hash of 0x Exchange V3
|
verifyingContractAddress: libEIP712ExchangeDomainContract.address,
|
||||||
exchangeDomainHash = signTypedDataUtils
|
chainId,
|
||||||
.generateDomainHash({
|
name: constants.EIP712_DOMAIN_NAME,
|
||||||
name: '0x Protocol',
|
version: constants.EIP712_DOMAIN_VERSION,
|
||||||
version: '3.0.0',
|
};
|
||||||
chainId: CHAIN_ID,
|
const expectedDomainHash = ethUtil.bufferToHex(signTypedDataUtils.generateDomainHash(domain));
|
||||||
verifyingContractAddress: libsContract.address,
|
const actualDomainHash = await libEIP712ExchangeDomainContract.EIP712_EXCHANGE_DOMAIN_HASH.callAsync();
|
||||||
})
|
expect(actualDomainHash).to.be.equal(expectedDomainHash);
|
||||||
.toString('hex');
|
|
||||||
});
|
});
|
||||||
|
it('should calculate the correct domain hash when verifyingContractAddressIfExists is set to a non-null address', async () => {
|
||||||
describe('hashEIP712ExchangeMessage', () => {
|
const chainId = 1;
|
||||||
it('should correctly match an empty hash', async () => {
|
const verifyingContractAddress = addressUtils.generatePseudoRandomAddress();
|
||||||
await testHashEIP712MessageAsync(constants.NULL_BYTES32);
|
const libEIP712ExchangeDomainContract = await TestLibEIP712ExchangeDomainContract.deployFrom0xArtifactAsync(
|
||||||
});
|
artifacts.TestLibEIP712ExchangeDomain,
|
||||||
|
env.provider,
|
||||||
it('should correctly match a non-empty hash', async () => {
|
env.txDefaults,
|
||||||
await testHashEIP712MessageAsync(randomHash());
|
new BigNumber(chainId),
|
||||||
|
verifyingContractAddress,
|
||||||
|
);
|
||||||
|
const domain = {
|
||||||
|
verifyingContractAddress,
|
||||||
|
chainId,
|
||||||
|
name: constants.EIP712_DOMAIN_NAME,
|
||||||
|
version: constants.EIP712_DOMAIN_VERSION,
|
||||||
|
};
|
||||||
|
const expectedDomainHash = ethUtil.bufferToHex(signTypedDataUtils.generateDomainHash(domain));
|
||||||
|
const actualDomainHash = await libEIP712ExchangeDomainContract.EIP712_EXCHANGE_DOMAIN_HASH.callAsync();
|
||||||
|
expect(actualDomainHash).to.be.equal(expectedDomainHash);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -9,19 +9,17 @@ import {
|
|||||||
import { LibMathRevertErrors } from '@0x/order-utils';
|
import { LibMathRevertErrors } from '@0x/order-utils';
|
||||||
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
|
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
|
||||||
|
|
||||||
import { artifacts, ReferenceFunctions, TestLibsContract } from '../src';
|
import { artifacts, ReferenceFunctions, TestLibMathContract } from '../src';
|
||||||
|
|
||||||
blockchainTests('LibMath', env => {
|
blockchainTests('LibMath', env => {
|
||||||
const CHAIN_ID = 1337;
|
|
||||||
const { ONE_ETHER, MAX_UINT256, MAX_UINT256_ROOT, ZERO_AMOUNT } = constants;
|
const { ONE_ETHER, MAX_UINT256, MAX_UINT256_ROOT, ZERO_AMOUNT } = constants;
|
||||||
let libsContract: TestLibsContract;
|
let libsContract: TestLibMathContract;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
libsContract = await TestLibsContract.deployFrom0xArtifactAsync(
|
libsContract = await TestLibMathContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestLibs,
|
artifacts.TestLibMath,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
new BigNumber(CHAIN_ID),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,13 +2,13 @@ import { blockchainTests, constants, describe, expect, hexRandom } from '@0x/con
|
|||||||
import { eip712Utils, orderHashUtils } from '@0x/order-utils';
|
import { eip712Utils, orderHashUtils } from '@0x/order-utils';
|
||||||
import { Order } from '@0x/types';
|
import { Order } from '@0x/types';
|
||||||
import { BigNumber, signTypedDataUtils } from '@0x/utils';
|
import { BigNumber, signTypedDataUtils } from '@0x/utils';
|
||||||
|
import * as ethUtil from 'ethereumjs-util';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts, TestLibsContract } from '../src';
|
import { artifacts, TestLibOrderContract } from '../src';
|
||||||
|
|
||||||
blockchainTests('LibOrder', env => {
|
blockchainTests('LibOrder', env => {
|
||||||
const CHAIN_ID = 1337;
|
let libOrderContract: TestLibOrderContract;
|
||||||
let libsContract: TestLibsContract;
|
|
||||||
|
|
||||||
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
|
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
|
||||||
const randomHash = () => hexRandom(constants.WORD_LENGTH);
|
const randomHash = () => hexRandom(constants.WORD_LENGTH);
|
||||||
@ -37,38 +37,44 @@ blockchainTests('LibOrder', env => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
libsContract = await TestLibsContract.deployFrom0xArtifactAsync(
|
libOrderContract = await TestLibOrderContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestLibs,
|
artifacts.TestLibOrder,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
new BigNumber(CHAIN_ID),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the `getOrderHash()` function against a reference hash.
|
* Tests the `getTypedDataHash()` function against a reference hash.
|
||||||
*/
|
*/
|
||||||
async function testGetOrderHashAsync(order: Order): Promise<void> {
|
async function testGetTypedDataHashAsync(order: Order): Promise<void> {
|
||||||
const expectedHash = orderHashUtils.getOrderHashHex(order);
|
const expectedHash = orderHashUtils.getOrderHashHex(order);
|
||||||
const actualHash = await libsContract.getOrderHash.callAsync(order);
|
const domainHash = ethUtil.bufferToHex(
|
||||||
|
signTypedDataUtils.generateDomainHash({
|
||||||
|
...order.domain,
|
||||||
|
name: constants.EIP712_DOMAIN_NAME,
|
||||||
|
version: constants.EIP712_DOMAIN_VERSION,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const actualHash = await libOrderContract.getTypedDataHash.callAsync(order, domainHash);
|
||||||
expect(actualHash).to.be.eq(expectedHash);
|
expect(actualHash).to.be.eq(expectedHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('getOrderHash', () => {
|
describe('getTypedDataHash', () => {
|
||||||
it('should correctly hash an empty order', async () => {
|
it('should correctly hash an empty order', async () => {
|
||||||
await testGetOrderHashAsync({
|
await testGetTypedDataHashAsync({
|
||||||
...EMPTY_ORDER,
|
...EMPTY_ORDER,
|
||||||
domain: {
|
domain: {
|
||||||
verifyingContractAddress: libsContract.address,
|
...EMPTY_ORDER.domain,
|
||||||
chainId: 1337,
|
verifyingContractAddress: libOrderContract.address,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly hash a non-empty order', async () => {
|
it('should correctly hash a non-empty order', async () => {
|
||||||
await testGetOrderHashAsync({
|
await testGetTypedDataHashAsync({
|
||||||
domain: {
|
domain: {
|
||||||
verifyingContractAddress: libsContract.address,
|
verifyingContractAddress: libOrderContract.address,
|
||||||
chainId: 1337,
|
chainId: 1337,
|
||||||
},
|
},
|
||||||
senderAddress: randomAddress(),
|
senderAddress: randomAddress(),
|
||||||
@ -87,27 +93,46 @@ blockchainTests('LibOrder', env => {
|
|||||||
expirationTimeSeconds: randomUint256(),
|
expirationTimeSeconds: randomUint256(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('orderHash should differ if the domain hash is different', async () => {
|
||||||
|
const domainHash1 = ethUtil.bufferToHex(
|
||||||
|
signTypedDataUtils.generateDomainHash({
|
||||||
|
...EMPTY_ORDER.domain,
|
||||||
|
name: constants.EIP712_DOMAIN_NAME,
|
||||||
|
version: constants.EIP712_DOMAIN_VERSION,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const domainHash2 = ethUtil.bufferToHex(
|
||||||
|
signTypedDataUtils.generateDomainHash({
|
||||||
|
...EMPTY_ORDER.domain,
|
||||||
|
name: constants.EIP712_DOMAIN_NAME,
|
||||||
|
version: constants.EIP712_DOMAIN_VERSION,
|
||||||
|
chainId: 1337,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const orderHashHex1 = await libOrderContract.getTypedDataHash.callAsync(EMPTY_ORDER, domainHash1);
|
||||||
|
const orderHashHex2 = await libOrderContract.getTypedDataHash.callAsync(EMPTY_ORDER, domainHash2);
|
||||||
|
expect(orderHashHex1).to.be.not.equal(orderHashHex2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the `_hashOrder()` function against a reference hash.
|
* Tests the `getStructHash()` function against a reference hash.
|
||||||
*/
|
*/
|
||||||
async function testHashOrderAsync(order: Order): Promise<void> {
|
async function testGetStructHashAsync(order: Order): Promise<void> {
|
||||||
const typedData = eip712Utils.createOrderTypedData(order);
|
const typedData = eip712Utils.createOrderTypedData(order);
|
||||||
const expectedHash = '0x'.concat(
|
const expectedHash = ethUtil.bufferToHex(signTypedDataUtils.generateTypedDataHashWithoutDomain(typedData));
|
||||||
signTypedDataUtils.generateTypedDataHashWithoutDomain(typedData).toString('hex'),
|
const actualHash = await libOrderContract.getStructHash.callAsync(order);
|
||||||
);
|
|
||||||
const actualHash = await libsContract.hashOrder.callAsync(order);
|
|
||||||
expect(actualHash).to.be.eq(expectedHash);
|
expect(actualHash).to.be.eq(expectedHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('hashOrder', () => {
|
describe('getStructHash', () => {
|
||||||
it('should correctly hash an empty order', async () => {
|
it('should correctly hash an empty order', async () => {
|
||||||
await testHashOrderAsync(EMPTY_ORDER);
|
await testGetStructHashAsync(EMPTY_ORDER);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly hash a non-empty order', async () => {
|
it('should correctly hash a non-empty order', async () => {
|
||||||
await testHashOrderAsync({
|
await testGetStructHashAsync({
|
||||||
// The domain is not used in this test, so it's okay if it is left empty.
|
// The domain is not used in this test, so it's okay if it is left empty.
|
||||||
domain: {
|
domain: {
|
||||||
verifyingContractAddress: constants.NULL_ADDRESS,
|
verifyingContractAddress: constants.NULL_ADDRESS,
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { blockchainTests, constants, describe, expect, hexRandom } from '@0x/contracts-test-utils';
|
import { blockchainTests, constants, describe, expect, hexRandom } from '@0x/contracts-test-utils';
|
||||||
import { eip712Utils } from '@0x/order-utils';
|
import { eip712Utils, transactionHashUtils } from '@0x/order-utils';
|
||||||
import { ZeroExTransaction } from '@0x/types';
|
import { ZeroExTransaction } from '@0x/types';
|
||||||
import { BigNumber, signTypedDataUtils } from '@0x/utils';
|
import { BigNumber, signTypedDataUtils } from '@0x/utils';
|
||||||
|
import * as ethUtil from 'ethereumjs-util';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts, TestLibsContract } from '../src';
|
import { artifacts, TestLibZeroExTransactionContract } from '../src';
|
||||||
|
|
||||||
blockchainTests('LibZeroExTransaction', env => {
|
blockchainTests('LibZeroExTransaction', env => {
|
||||||
const CHAIN_ID = 1337;
|
let libZeroExTransactionContract: TestLibZeroExTransactionContract;
|
||||||
let libsContract: TestLibsContract;
|
|
||||||
|
|
||||||
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
|
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
|
||||||
const randomHash = () => hexRandom(constants.WORD_LENGTH);
|
const randomHash = () => hexRandom(constants.WORD_LENGTH);
|
||||||
@ -27,68 +27,97 @@ blockchainTests('LibZeroExTransaction', env => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
libsContract = await TestLibsContract.deployFrom0xArtifactAsync(
|
libZeroExTransactionContract = await TestLibZeroExTransactionContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestLibs,
|
artifacts.TestLibZeroExTransaction,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
new BigNumber(CHAIN_ID),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the `getTransactionHash()` function against a reference hash.
|
* Tests the `getTypedDataHash()` function against a reference hash.
|
||||||
*/
|
*/
|
||||||
async function testGetTransactionHashAsync(transaction: ZeroExTransaction): Promise<void> {
|
async function testGetTypedDataHashAsync(transaction: ZeroExTransaction): Promise<void> {
|
||||||
const typedData = eip712Utils.createZeroExTransactionTypedData(transaction);
|
const expectedHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||||
const expectedHash = '0x'.concat(signTypedDataUtils.generateTypedDataHash(typedData).toString('hex'));
|
const domainHash = ethUtil.bufferToHex(
|
||||||
const actualHash = await libsContract.getTransactionHash.callAsync(transaction);
|
signTypedDataUtils.generateDomainHash({
|
||||||
|
...transaction.domain,
|
||||||
|
name: constants.EIP712_DOMAIN_NAME,
|
||||||
|
version: constants.EIP712_DOMAIN_VERSION,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const actualHash = await libZeroExTransactionContract.getTypedDataHash.callAsync(transaction, domainHash);
|
||||||
expect(actualHash).to.be.eq(expectedHash);
|
expect(actualHash).to.be.eq(expectedHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('getTransactionHash', () => {
|
describe('getTypedDataHash', () => {
|
||||||
it('should correctly hash an empty transaction', async () => {
|
it('should correctly hash an empty transaction', async () => {
|
||||||
await testGetTransactionHashAsync({
|
await testGetTypedDataHashAsync({
|
||||||
...EMPTY_TRANSACTION,
|
...EMPTY_TRANSACTION,
|
||||||
domain: {
|
domain: {
|
||||||
verifyingContractAddress: libsContract.address,
|
...EMPTY_TRANSACTION.domain,
|
||||||
chainId: 1337,
|
verifyingContractAddress: libZeroExTransactionContract.address,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly hash a non-empty transaction', async () => {
|
it('should correctly hash a non-empty transaction', async () => {
|
||||||
await testGetTransactionHashAsync({
|
await testGetTypedDataHashAsync({
|
||||||
salt: randomUint256(),
|
salt: randomUint256(),
|
||||||
expirationTimeSeconds: randomUint256(),
|
expirationTimeSeconds: randomUint256(),
|
||||||
signerAddress: randomAddress(),
|
signerAddress: randomAddress(),
|
||||||
data: randomAssetData(),
|
data: randomAssetData(),
|
||||||
domain: {
|
domain: {
|
||||||
verifyingContractAddress: libsContract.address,
|
...EMPTY_TRANSACTION.domain,
|
||||||
chainId: 1337,
|
verifyingContractAddress: libZeroExTransactionContract.address,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('transactionHash should differ if the domain hash is different', async () => {
|
||||||
|
const domainHash1 = ethUtil.bufferToHex(
|
||||||
|
signTypedDataUtils.generateDomainHash({
|
||||||
|
...EMPTY_TRANSACTION.domain,
|
||||||
|
name: constants.EIP712_DOMAIN_NAME,
|
||||||
|
version: constants.EIP712_DOMAIN_VERSION,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const domainHash2 = ethUtil.bufferToHex(
|
||||||
|
signTypedDataUtils.generateDomainHash({
|
||||||
|
...EMPTY_TRANSACTION.domain,
|
||||||
|
name: constants.EIP712_DOMAIN_NAME,
|
||||||
|
version: constants.EIP712_DOMAIN_VERSION,
|
||||||
|
chainId: 1337,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const transactionHashHex1 = await libZeroExTransactionContract.getTypedDataHash.callAsync(
|
||||||
|
EMPTY_TRANSACTION,
|
||||||
|
domainHash1,
|
||||||
|
);
|
||||||
|
const transactionHashHex2 = await libZeroExTransactionContract.getTypedDataHash.callAsync(
|
||||||
|
EMPTY_TRANSACTION,
|
||||||
|
domainHash2,
|
||||||
|
);
|
||||||
|
expect(transactionHashHex1).to.be.not.equal(transactionHashHex2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the `_hashZeroExTransaction()` function against a reference hash.
|
* Tests the `getStructHash()` function against a reference hash.
|
||||||
*/
|
*/
|
||||||
async function testHashZeroExTransactionAsync(transaction: ZeroExTransaction): Promise<void> {
|
async function testGetStructHashAsync(transaction: ZeroExTransaction): Promise<void> {
|
||||||
const typedData = eip712Utils.createZeroExTransactionTypedData(transaction);
|
const typedData = eip712Utils.createZeroExTransactionTypedData(transaction);
|
||||||
const expectedHash = '0x'.concat(
|
const expectedHash = ethUtil.bufferToHex(signTypedDataUtils.generateTypedDataHashWithoutDomain(typedData));
|
||||||
signTypedDataUtils.generateTypedDataHashWithoutDomain(typedData).toString('hex'),
|
const actualHash = await libZeroExTransactionContract.getStructHash.callAsync(transaction);
|
||||||
);
|
|
||||||
const actualHash = await libsContract.hashZeroExTransaction.callAsync(transaction);
|
|
||||||
expect(actualHash).to.be.eq(expectedHash);
|
expect(actualHash).to.be.eq(expectedHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('hashOrder', () => {
|
describe('getStructHash', () => {
|
||||||
it('should correctly hash an empty transaction', async () => {
|
it('should correctly hash an empty transaction', async () => {
|
||||||
await testHashZeroExTransactionAsync(EMPTY_TRANSACTION);
|
await testGetStructHashAsync(EMPTY_TRANSACTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly hash a non-empty transaction', async () => {
|
it('should correctly hash a non-empty transaction', async () => {
|
||||||
await testHashZeroExTransactionAsync({
|
await testGetStructHashAsync({
|
||||||
salt: randomUint256(),
|
salt: randomUint256(),
|
||||||
expirationTimeSeconds: randomUint256(),
|
expirationTimeSeconds: randomUint256(),
|
||||||
signerAddress: randomAddress(),
|
signerAddress: randomAddress(),
|
||||||
|
@ -4,11 +4,17 @@
|
|||||||
"include": ["./src/**/*", "./test/**/*", "./scripts/**/*", "./generated-wrappers/**/*"],
|
"include": ["./src/**/*", "./test/**/*", "./scripts/**/*", "./generated-wrappers/**/*"],
|
||||||
"files": [
|
"files": [
|
||||||
"generated-artifacts/LibEIP712ExchangeDomain.json",
|
"generated-artifacts/LibEIP712ExchangeDomain.json",
|
||||||
|
"generated-artifacts/LibExchangeRichErrors.json",
|
||||||
"generated-artifacts/LibFillResults.json",
|
"generated-artifacts/LibFillResults.json",
|
||||||
"generated-artifacts/LibMath.json",
|
"generated-artifacts/LibMath.json",
|
||||||
|
"generated-artifacts/LibMathRichErrors.json",
|
||||||
"generated-artifacts/LibOrder.json",
|
"generated-artifacts/LibOrder.json",
|
||||||
"generated-artifacts/LibZeroExTransaction.json",
|
"generated-artifacts/LibZeroExTransaction.json",
|
||||||
"generated-artifacts/TestLibs.json"
|
"generated-artifacts/TestLibEIP712ExchangeDomain.json",
|
||||||
|
"generated-artifacts/TestLibFillResults.json",
|
||||||
|
"generated-artifacts/TestLibMath.json",
|
||||||
|
"generated-artifacts/TestLibOrder.json",
|
||||||
|
"generated-artifacts/TestLibZeroExTransaction.json"
|
||||||
],
|
],
|
||||||
"exclude": ["./deploy/solc/solc_bin"]
|
"exclude": ["./deploy/solc/solc_bin"]
|
||||||
}
|
}
|
||||||
|
@ -153,6 +153,26 @@
|
|||||||
{
|
{
|
||||||
"note": "Add (semi) automated reentrancy tests and remove manual ones",
|
"note": "Add (semi) automated reentrancy tests and remove manual ones",
|
||||||
"pr": 2042
|
"pr": 2042
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Refactor to use new `LibFillResults`, `LibOrder`, `LibZeroExTransaction`, and `LibMath` to libraries",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove `LibExchangeRichErrors` and `IExchangeRichErrors`",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Use built in selectors instead of `LibExchangeSelectors` constants",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Move `calculateFillResults` and `calculateMatchedFillResults` to `LibFillResults` in `exchange-libs` package",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
||||||
|
"pr": 2055
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -22,27 +22,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": [
|
|
||||||
"examples/ExchangeWrapper.sol",
|
|
||||||
"examples/Whitelist.sol",
|
|
||||||
"src/Exchange.sol",
|
|
||||||
"src/interfaces/IAssetProxyDispatcher.sol",
|
|
||||||
"src/interfaces/IEIP1271Wallet.sol",
|
|
||||||
"src/interfaces/IExchange.sol",
|
|
||||||
"src/interfaces/IExchangeCore.sol",
|
|
||||||
"src/interfaces/IMatchOrders.sol",
|
|
||||||
"src/interfaces/ISignatureValidator.sol",
|
|
||||||
"src/interfaces/ITransactions.sol",
|
|
||||||
"src/interfaces/IWallet.sol",
|
|
||||||
"src/interfaces/IWrapperFunctions.sol",
|
|
||||||
"test/IsolatedExchange.sol",
|
|
||||||
"test/ReentrancyTester.sol",
|
|
||||||
"test/TestAssetProxyDispatcher.sol",
|
|
||||||
"test/TestExchangeInternals.sol",
|
|
||||||
"test/TestLibExchangeRichErrorDecoder.sol",
|
|
||||||
"test/TestSignatureValidator.sol",
|
|
||||||
"test/TestValidatorWallet.sol",
|
|
||||||
"test/TestWrapperFunctions.sol"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -19,18 +19,18 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||||
import "./MixinMatchOrders.sol";
|
import "./MixinMatchOrders.sol";
|
||||||
import "./MixinSignatureValidator.sol";
|
|
||||||
import "./MixinWrapperFunctions.sol";
|
import "./MixinWrapperFunctions.sol";
|
||||||
import "./MixinTransferSimulator.sol";
|
import "./MixinTransferSimulator.sol";
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
// solhint-disable no-empty-blocks
|
||||||
// MixinAssetProxyDispatcher, MixinExchangeCore, MixinExchangeRichErrors,
|
// MixinAssetProxyDispatcher, MixinExchangeCore, MixinSignatureValidator,
|
||||||
// and MixinTransactions are all inherited via the other Mixins that are
|
// and MixinTransactions are all inherited via the other Mixins that are
|
||||||
// used.
|
// used.
|
||||||
contract Exchange is
|
contract Exchange is
|
||||||
MixinSignatureValidator,
|
LibEIP712ExchangeDomain,
|
||||||
MixinMatchOrders,
|
MixinMatchOrders,
|
||||||
MixinWrapperFunctions,
|
MixinWrapperFunctions,
|
||||||
MixinTransferSimulator
|
MixinTransferSimulator
|
||||||
@ -42,12 +42,5 @@ contract Exchange is
|
|||||||
constructor (uint256 chainId)
|
constructor (uint256 chainId)
|
||||||
public
|
public
|
||||||
LibEIP712ExchangeDomain(chainId, address(0))
|
LibEIP712ExchangeDomain(chainId, address(0))
|
||||||
MixinExchangeCore()
|
|
||||||
MixinMatchOrders()
|
|
||||||
MixinSignatureValidator()
|
|
||||||
MixinTransactions()
|
|
||||||
MixinAssetProxyDispatcher()
|
|
||||||
MixinTransferSimulator()
|
|
||||||
MixinWrapperFunctions()
|
|
||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,9 @@ pragma solidity ^0.5.9;
|
|||||||
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
import "@0x/contracts-utils/contracts/src/Ownable.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
|
||||||
import "./interfaces/IAssetProxy.sol";
|
import "./interfaces/IAssetProxy.sol";
|
||||||
import "./interfaces/IAssetProxyDispatcher.sol";
|
import "./interfaces/IAssetProxyDispatcher.sol";
|
||||||
import "./interfaces/IExchangeRichErrors.sol";
|
|
||||||
import "./LibExchangeRichErrors.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinAssetProxyDispatcher is
|
contract MixinAssetProxyDispatcher is
|
||||||
@ -47,7 +46,7 @@ contract MixinAssetProxyDispatcher is
|
|||||||
bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId();
|
bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId();
|
||||||
address currentAssetProxy = assetProxies[assetProxyId];
|
address currentAssetProxy = assetProxies[assetProxyId];
|
||||||
if (currentAssetProxy != address(0)) {
|
if (currentAssetProxy != address(0)) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.AssetProxyExistsError(currentAssetProxy));
|
LibRichErrors.rrevert(LibExchangeRichErrors.AssetProxyExistsError(currentAssetProxy));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add asset proxy and log registration.
|
// Add asset proxy and log registration.
|
||||||
@ -88,8 +87,8 @@ contract MixinAssetProxyDispatcher is
|
|||||||
if (amount > 0 && from != to) {
|
if (amount > 0 && from != to) {
|
||||||
// Ensure assetData length is valid
|
// Ensure assetData length is valid
|
||||||
if (assetData.length <= 3) {
|
if (assetData.length <= 3) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.AssetProxyDispatchError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.AssetProxyDispatchError(
|
||||||
IExchangeRichErrors.AssetProxyDispatchErrorCodes.INVALID_ASSET_DATA_LENGTH,
|
LibExchangeRichErrors.AssetProxyDispatchErrorCodes.INVALID_ASSET_DATA_LENGTH,
|
||||||
orderHash,
|
orderHash,
|
||||||
assetData
|
assetData
|
||||||
));
|
));
|
||||||
@ -101,8 +100,8 @@ contract MixinAssetProxyDispatcher is
|
|||||||
|
|
||||||
// Ensure that assetProxy exists
|
// Ensure that assetProxy exists
|
||||||
if (assetProxy == address(0)) {
|
if (assetProxy == address(0)) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.AssetProxyDispatchError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.AssetProxyDispatchError(
|
||||||
IExchangeRichErrors.AssetProxyDispatchErrorCodes.UNKNOWN_ASSET_PROXY,
|
LibExchangeRichErrors.AssetProxyDispatchErrorCodes.UNKNOWN_ASSET_PROXY,
|
||||||
orderHash,
|
orderHash,
|
||||||
assetData
|
assetData
|
||||||
));
|
));
|
||||||
@ -122,7 +121,7 @@ contract MixinAssetProxyDispatcher is
|
|||||||
|
|
||||||
// If the transaction did not succeed, revert with the returned data.
|
// If the transaction did not succeed, revert with the returned data.
|
||||||
if (!didSucceed) {
|
if (!didSucceed) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.AssetProxyTransferError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.AssetProxyTransferError(
|
||||||
orderHash,
|
orderHash,
|
||||||
assetData,
|
assetData,
|
||||||
revertData
|
revertData
|
||||||
|
@ -18,27 +18,26 @@
|
|||||||
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-utils/contracts/src/LibRichErrors.sol";
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.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-libs/contracts/src/LibOrder.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
|
||||||
import "./interfaces/IExchangeCore.sol";
|
import "./interfaces/IExchangeCore.sol";
|
||||||
import "./interfaces/IExchangeRichErrors.sol";
|
|
||||||
import "./LibExchangeRichErrors.sol";
|
|
||||||
import "./MixinAssetProxyDispatcher.sol";
|
import "./MixinAssetProxyDispatcher.sol";
|
||||||
import "./MixinSignatureValidator.sol";
|
import "./MixinSignatureValidator.sol";
|
||||||
|
|
||||||
|
|
||||||
contract MixinExchangeCore is
|
contract MixinExchangeCore is
|
||||||
|
LibEIP712ExchangeDomain,
|
||||||
IExchangeCore,
|
IExchangeCore,
|
||||||
IExchangeRichErrors,
|
|
||||||
LibMath,
|
|
||||||
LibFillResults,
|
|
||||||
MixinAssetProxyDispatcher,
|
MixinAssetProxyDispatcher,
|
||||||
MixinSignatureValidator
|
MixinSignatureValidator
|
||||||
{
|
{
|
||||||
using LibBytes for bytes;
|
using LibOrder for LibOrder.Order;
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
// Mapping of orderHash => amount of takerAsset already bought by maker
|
// Mapping of orderHash => amount of takerAsset already bought by maker
|
||||||
mapping (bytes32 => uint256) public filled;
|
mapping (bytes32 => uint256) public filled;
|
||||||
@ -68,7 +67,7 @@ contract MixinExchangeCore is
|
|||||||
|
|
||||||
// Ensure orderEpoch is monotonically increasing
|
// Ensure orderEpoch is monotonically increasing
|
||||||
if (newOrderEpoch <= oldOrderEpoch) {
|
if (newOrderEpoch <= oldOrderEpoch) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.OrderEpochError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.OrderEpochError(
|
||||||
makerAddress,
|
makerAddress,
|
||||||
orderSenderAddress,
|
orderSenderAddress,
|
||||||
oldOrderEpoch
|
oldOrderEpoch
|
||||||
@ -90,13 +89,13 @@ contract MixinExchangeCore is
|
|||||||
/// @param signature Proof that order has been created by maker.
|
/// @param signature Proof that order has been created by maker.
|
||||||
/// @return Amounts filled and fees paid by maker and taker.
|
/// @return Amounts filled and fees paid by maker and taker.
|
||||||
function fillOrder(
|
function fillOrder(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
uint256 takerAssetFillAmount,
|
uint256 takerAssetFillAmount,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
nonReentrant
|
nonReentrant
|
||||||
returns (FillResults memory fillResults)
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
fillResults = _fillOrder(
|
fillResults = _fillOrder(
|
||||||
order,
|
order,
|
||||||
@ -109,7 +108,7 @@ contract MixinExchangeCore is
|
|||||||
/// @dev After calling, the order can not be filled anymore.
|
/// @dev After calling, the order can not be filled anymore.
|
||||||
/// Throws if order is invalid or sender does not have permission to cancel.
|
/// Throws if order is invalid or sender does not have permission to cancel.
|
||||||
/// @param order Order to cancel. Order must be OrderStatus.FILLABLE.
|
/// @param order Order to cancel. Order must be OrderStatus.FILLABLE.
|
||||||
function cancelOrder(Order memory order)
|
function cancelOrder(LibOrder.Order memory order)
|
||||||
public
|
public
|
||||||
nonReentrant
|
nonReentrant
|
||||||
{
|
{
|
||||||
@ -120,13 +119,13 @@ contract MixinExchangeCore is
|
|||||||
/// @param order Order to gather information on.
|
/// @param order Order to gather information on.
|
||||||
/// @return OrderInfo Information about the order and its state.
|
/// @return OrderInfo Information about the order and its state.
|
||||||
/// See LibOrder.OrderInfo for a complete description.
|
/// See LibOrder.OrderInfo for a complete description.
|
||||||
function getOrderInfo(Order memory order)
|
function getOrderInfo(LibOrder.Order memory order)
|
||||||
public
|
public
|
||||||
view
|
view
|
||||||
returns (OrderInfo memory orderInfo)
|
returns (LibOrder.OrderInfo memory orderInfo)
|
||||||
{
|
{
|
||||||
// Compute the order hash
|
// Compute the order hash
|
||||||
orderInfo.orderHash = getOrderHash(order);
|
orderInfo.orderHash = order.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
|
||||||
|
|
||||||
// Fetch filled amount
|
// Fetch filled amount
|
||||||
orderInfo.orderTakerAssetFilledAmount = filled[orderInfo.orderHash];
|
orderInfo.orderTakerAssetFilledAmount = filled[orderInfo.orderHash];
|
||||||
@ -136,7 +135,7 @@ contract MixinExchangeCore is
|
|||||||
// edge cases in the supporting infrastructure because they have
|
// edge cases in the supporting infrastructure because they have
|
||||||
// an 'infinite' price when computed by a simple division.
|
// an 'infinite' price when computed by a simple division.
|
||||||
if (order.makerAssetAmount == 0) {
|
if (order.makerAssetAmount == 0) {
|
||||||
orderInfo.orderStatus = uint8(OrderStatus.INVALID_MAKER_ASSET_AMOUNT);
|
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.INVALID_MAKER_ASSET_AMOUNT);
|
||||||
return orderInfo;
|
return orderInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,35 +144,35 @@ contract MixinExchangeCore is
|
|||||||
// Instead of distinguishing between unfilled and filled zero taker
|
// Instead of distinguishing between unfilled and filled zero taker
|
||||||
// amount orders, we choose not to support them.
|
// amount orders, we choose not to support them.
|
||||||
if (order.takerAssetAmount == 0) {
|
if (order.takerAssetAmount == 0) {
|
||||||
orderInfo.orderStatus = uint8(OrderStatus.INVALID_TAKER_ASSET_AMOUNT);
|
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.INVALID_TAKER_ASSET_AMOUNT);
|
||||||
return orderInfo;
|
return orderInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate order availability
|
// Validate order availability
|
||||||
if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) {
|
if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) {
|
||||||
orderInfo.orderStatus = uint8(OrderStatus.FULLY_FILLED);
|
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.FULLY_FILLED);
|
||||||
return orderInfo;
|
return orderInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate order expiration
|
// Validate order expiration
|
||||||
// solhint-disable-next-line not-rely-on-time
|
// solhint-disable-next-line not-rely-on-time
|
||||||
if (block.timestamp >= order.expirationTimeSeconds) {
|
if (block.timestamp >= order.expirationTimeSeconds) {
|
||||||
orderInfo.orderStatus = uint8(OrderStatus.EXPIRED);
|
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.EXPIRED);
|
||||||
return orderInfo;
|
return orderInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if order has been cancelled
|
// Check if order has been cancelled
|
||||||
if (cancelled[orderInfo.orderHash]) {
|
if (cancelled[orderInfo.orderHash]) {
|
||||||
orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);
|
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.CANCELLED);
|
||||||
return orderInfo;
|
return orderInfo;
|
||||||
}
|
}
|
||||||
if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) {
|
if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) {
|
||||||
orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);
|
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.CANCELLED);
|
||||||
return orderInfo;
|
return orderInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other statuses are ruled out: order is Fillable
|
// All other statuses are ruled out: order is Fillable
|
||||||
orderInfo.orderStatus = uint8(OrderStatus.FILLABLE);
|
orderInfo.orderStatus = uint8(LibOrder.OrderStatus.FILLABLE);
|
||||||
return orderInfo;
|
return orderInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,15 +182,15 @@ contract MixinExchangeCore is
|
|||||||
/// @param signature Proof that order has been created by maker.
|
/// @param signature Proof that order has been created by maker.
|
||||||
/// @return Amounts filled and fees paid by maker and taker.
|
/// @return Amounts filled and fees paid by maker and taker.
|
||||||
function _fillOrder(
|
function _fillOrder(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
uint256 takerAssetFillAmount,
|
uint256 takerAssetFillAmount,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
returns (FillResults memory fillResults)
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
// Fetch order info
|
// Fetch order info
|
||||||
OrderInfo memory orderInfo = getOrderInfo(order);
|
LibOrder.OrderInfo memory orderInfo = getOrderInfo(order);
|
||||||
|
|
||||||
// Fetch taker address
|
// Fetch taker address
|
||||||
address takerAddress = _getCurrentContextAddress();
|
address takerAddress = _getCurrentContextAddress();
|
||||||
@ -205,11 +204,11 @@ contract MixinExchangeCore is
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Get amount of takerAsset to fill
|
// Get amount of takerAsset to fill
|
||||||
uint256 remainingTakerAssetAmount = _safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);
|
uint256 remainingTakerAssetAmount = order.takerAssetAmount.safeSub(orderInfo.orderTakerAssetFilledAmount);
|
||||||
uint256 takerAssetFilledAmount = _min256(takerAssetFillAmount, remainingTakerAssetAmount);
|
uint256 takerAssetFilledAmount = LibSafeMath.min256(takerAssetFillAmount, remainingTakerAssetAmount);
|
||||||
|
|
||||||
// Compute proportional fill amounts
|
// Compute proportional fill amounts
|
||||||
fillResults = _calculateFillResults(order, takerAssetFilledAmount);
|
fillResults = LibFillResults.calculateFillResults(order, takerAssetFilledAmount);
|
||||||
|
|
||||||
bytes32 orderHash = orderInfo.orderHash;
|
bytes32 orderHash = orderInfo.orderHash;
|
||||||
|
|
||||||
@ -236,17 +235,17 @@ contract MixinExchangeCore is
|
|||||||
/// @dev After calling, the order can not be filled anymore.
|
/// @dev After calling, the order can not be filled anymore.
|
||||||
/// Throws if order is invalid or sender does not have permission to cancel.
|
/// Throws if order is invalid or sender does not have permission to cancel.
|
||||||
/// @param order Order to cancel. Order must be OrderStatus.FILLABLE.
|
/// @param order Order to cancel. Order must be OrderStatus.FILLABLE.
|
||||||
function _cancelOrder(Order memory order)
|
function _cancelOrder(LibOrder.Order memory order)
|
||||||
internal
|
internal
|
||||||
{
|
{
|
||||||
// Fetch current order status
|
// Fetch current order status
|
||||||
OrderInfo memory orderInfo = getOrderInfo(order);
|
LibOrder.OrderInfo memory orderInfo = getOrderInfo(order);
|
||||||
|
|
||||||
// Validate context
|
// Validate context
|
||||||
_assertValidCancel(order, orderInfo);
|
_assertValidCancel(order, orderInfo);
|
||||||
|
|
||||||
// Noop if order is already unfillable
|
// Noop if order is already unfillable
|
||||||
if (orderInfo.orderStatus != uint8(OrderStatus.FILLABLE)) {
|
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,16 +258,16 @@ contract MixinExchangeCore is
|
|||||||
/// @param takerAddress Address of taker who filled the order.
|
/// @param takerAddress Address of taker who filled the order.
|
||||||
/// @param orderTakerAssetFilledAmount Amount of order already filled.
|
/// @param orderTakerAssetFilledAmount Amount of order already filled.
|
||||||
function _updateFilledState(
|
function _updateFilledState(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
address takerAddress,
|
address takerAddress,
|
||||||
bytes32 orderHash,
|
bytes32 orderHash,
|
||||||
uint256 orderTakerAssetFilledAmount,
|
uint256 orderTakerAssetFilledAmount,
|
||||||
FillResults memory fillResults
|
LibFillResults.FillResults memory fillResults
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
{
|
{
|
||||||
// Update state
|
// Update state
|
||||||
filled[orderHash] = _safeAdd(orderTakerAssetFilledAmount, fillResults.takerAssetFilledAmount);
|
filled[orderHash] = orderTakerAssetFilledAmount.safeAdd(fillResults.takerAssetFilledAmount);
|
||||||
|
|
||||||
// Emit a Fill() event THE HARD WAY to avoid a stack overflow.
|
// Emit a Fill() event THE HARD WAY to avoid a stack overflow.
|
||||||
// All this logic is equivalent to:
|
// All this logic is equivalent to:
|
||||||
@ -295,7 +294,7 @@ contract MixinExchangeCore is
|
|||||||
/// @param order that was cancelled.
|
/// @param order that was cancelled.
|
||||||
/// @param orderHash Hash of order that was cancelled.
|
/// @param orderHash Hash of order that was cancelled.
|
||||||
function _updateCancelledState(
|
function _updateCancelledState(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
bytes32 orderHash
|
bytes32 orderHash
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
@ -320,8 +319,8 @@ contract MixinExchangeCore is
|
|||||||
/// @param takerAddress Address of order taker.
|
/// @param takerAddress Address of order taker.
|
||||||
/// @param signature Proof that the orders was created by its maker.
|
/// @param signature Proof that the orders was created by its maker.
|
||||||
function _assertFillableOrder(
|
function _assertFillableOrder(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
OrderInfo memory orderInfo,
|
LibOrder.OrderInfo memory orderInfo,
|
||||||
address takerAddress,
|
address takerAddress,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
@ -329,17 +328,17 @@ contract MixinExchangeCore is
|
|||||||
view
|
view
|
||||||
{
|
{
|
||||||
// An order can only be filled if its status is FILLABLE.
|
// An order can only be filled if its status is FILLABLE.
|
||||||
if (orderInfo.orderStatus != uint8(OrderStatus.FILLABLE)) {
|
if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE)) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.OrderStatusError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.OrderStatusError(
|
||||||
orderInfo.orderHash,
|
orderInfo.orderHash,
|
||||||
OrderStatus(orderInfo.orderStatus)
|
LibOrder.OrderStatus(orderInfo.orderStatus)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate sender is allowed to fill this order
|
// Validate sender is allowed to fill this order
|
||||||
if (order.senderAddress != address(0)) {
|
if (order.senderAddress != address(0)) {
|
||||||
if (order.senderAddress != msg.sender) {
|
if (order.senderAddress != msg.sender) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.InvalidSenderError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.InvalidSenderError(
|
||||||
orderInfo.orderHash, msg.sender
|
orderInfo.orderHash, msg.sender
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -348,7 +347,7 @@ contract MixinExchangeCore is
|
|||||||
// Validate taker is allowed to fill this order
|
// Validate taker is allowed to fill this order
|
||||||
if (order.takerAddress != address(0)) {
|
if (order.takerAddress != address(0)) {
|
||||||
if (order.takerAddress != takerAddress) {
|
if (order.takerAddress != takerAddress) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.InvalidTakerError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.InvalidTakerError(
|
||||||
orderInfo.orderHash, takerAddress
|
orderInfo.orderHash, takerAddress
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -367,8 +366,8 @@ contract MixinExchangeCore is
|
|||||||
order,
|
order,
|
||||||
orderInfo.orderHash,
|
orderInfo.orderHash,
|
||||||
signature)) {
|
signature)) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
|
||||||
SignatureErrorCodes.BAD_SIGNATURE,
|
LibExchangeRichErrors.SignatureErrorCodes.BAD_SIGNATURE,
|
||||||
orderInfo.orderHash,
|
orderInfo.orderHash,
|
||||||
makerAddress,
|
makerAddress,
|
||||||
signature
|
signature
|
||||||
@ -381,8 +380,8 @@ contract MixinExchangeCore is
|
|||||||
/// @param order to be cancelled.
|
/// @param order to be cancelled.
|
||||||
/// @param orderInfo OrderStatus, orderHash, and amount already filled of order.
|
/// @param orderInfo OrderStatus, orderHash, and amount already filled of order.
|
||||||
function _assertValidCancel(
|
function _assertValidCancel(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
OrderInfo memory orderInfo
|
LibOrder.OrderInfo memory orderInfo
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
view
|
view
|
||||||
@ -390,50 +389,17 @@ contract MixinExchangeCore is
|
|||||||
// Validate sender is allowed to cancel this order
|
// Validate sender is allowed to cancel this order
|
||||||
if (order.senderAddress != address(0)) {
|
if (order.senderAddress != address(0)) {
|
||||||
if (order.senderAddress != msg.sender) {
|
if (order.senderAddress != msg.sender) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.InvalidSenderError(orderInfo.orderHash, msg.sender));
|
LibRichErrors.rrevert(LibExchangeRichErrors.InvalidSenderError(orderInfo.orderHash, msg.sender));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate transaction signed by maker
|
// Validate transaction signed by maker
|
||||||
address makerAddress = _getCurrentContextAddress();
|
address makerAddress = _getCurrentContextAddress();
|
||||||
if (order.makerAddress != makerAddress) {
|
if (order.makerAddress != makerAddress) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.InvalidMakerError(orderInfo.orderHash, makerAddress));
|
LibRichErrors.rrevert(LibExchangeRichErrors.InvalidMakerError(orderInfo.orderHash, makerAddress));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Calculates amounts filled and fees paid by maker and taker.
|
|
||||||
/// @param order to be filled.
|
|
||||||
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
|
|
||||||
/// @return fillResults Amounts filled and fees paid by maker and taker.
|
|
||||||
function _calculateFillResults(
|
|
||||||
Order memory order,
|
|
||||||
uint256 takerAssetFilledAmount
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (FillResults memory fillResults)
|
|
||||||
{
|
|
||||||
// Compute proportional transfer amounts
|
|
||||||
fillResults.takerAssetFilledAmount = takerAssetFilledAmount;
|
|
||||||
fillResults.makerAssetFilledAmount = _safeGetPartialAmountFloor(
|
|
||||||
takerAssetFilledAmount,
|
|
||||||
order.takerAssetAmount,
|
|
||||||
order.makerAssetAmount
|
|
||||||
);
|
|
||||||
fillResults.makerFeePaid = _safeGetPartialAmountFloor(
|
|
||||||
fillResults.makerAssetFilledAmount,
|
|
||||||
order.makerAssetAmount,
|
|
||||||
order.makerFee
|
|
||||||
);
|
|
||||||
fillResults.takerFeePaid = _safeGetPartialAmountFloor(
|
|
||||||
takerAssetFilledAmount,
|
|
||||||
order.takerAssetAmount,
|
|
||||||
order.takerFee
|
|
||||||
);
|
|
||||||
|
|
||||||
return fillResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Settles an order by transferring assets between counterparties.
|
/// @dev Settles an order by transferring assets between counterparties.
|
||||||
/// @param orderHash The order hash.
|
/// @param orderHash The order hash.
|
||||||
/// @param order Order struct containing order specifications.
|
/// @param order Order struct containing order specifications.
|
||||||
|
@ -16,14 +16,10 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/ReentrancyGuard.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 "./interfaces/IAssetProxyDispatcher.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
|
||||||
import "./interfaces/IExchangeRichErrors.sol";
|
|
||||||
import "./interfaces/IMatchOrders.sol";
|
import "./interfaces/IMatchOrders.sol";
|
||||||
import "./interfaces/ITransactions.sol";
|
|
||||||
import "./LibExchangeRichErrors.sol";
|
|
||||||
import "./MixinExchangeCore.sol";
|
import "./MixinExchangeCore.sol";
|
||||||
|
|
||||||
|
|
||||||
@ -32,6 +28,7 @@ contract MixinMatchOrders is
|
|||||||
IMatchOrders
|
IMatchOrders
|
||||||
{
|
{
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
/// @dev Match complementary orders that have a profitable spread.
|
/// @dev Match complementary orders that have a profitable spread.
|
||||||
/// Each order is filled at their respective price point, and
|
/// Each order is filled at their respective price point, and
|
||||||
@ -88,93 +85,6 @@ contract MixinMatchOrders is
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Calculates fill amounts for the matched orders.
|
|
||||||
/// 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 leftOrder order goes to the taker (who matched the two orders).
|
|
||||||
/// @param leftOrder First order to match.
|
|
||||||
/// @param rightOrder Second order to match.
|
|
||||||
/// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.
|
|
||||||
/// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.
|
|
||||||
/// @param shouldMaximallyFillOrders A value that indicates whether or not this calculation should use
|
|
||||||
/// the maximal fill order matching strategy.
|
|
||||||
/// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
|
|
||||||
function calculateMatchedFillResults(
|
|
||||||
LibOrder.Order memory leftOrder,
|
|
||||||
LibOrder.Order memory rightOrder,
|
|
||||||
uint256 leftOrderTakerAssetFilledAmount,
|
|
||||||
uint256 rightOrderTakerAssetFilledAmount,
|
|
||||||
bool shouldMaximallyFillOrders
|
|
||||||
)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
|
|
||||||
{
|
|
||||||
// Derive maker asset amounts for left & right orders, given store taker assert amounts
|
|
||||||
uint256 leftTakerAssetAmountRemaining = _safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount);
|
|
||||||
uint256 leftMakerAssetAmountRemaining = _safeGetPartialAmountFloor(
|
|
||||||
leftOrder.makerAssetAmount,
|
|
||||||
leftOrder.takerAssetAmount,
|
|
||||||
leftTakerAssetAmountRemaining
|
|
||||||
);
|
|
||||||
uint256 rightTakerAssetAmountRemaining = _safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount);
|
|
||||||
uint256 rightMakerAssetAmountRemaining = _safeGetPartialAmountFloor(
|
|
||||||
rightOrder.makerAssetAmount,
|
|
||||||
rightOrder.takerAssetAmount,
|
|
||||||
rightTakerAssetAmountRemaining
|
|
||||||
);
|
|
||||||
|
|
||||||
// Maximally fill the orders and pay out profits to the matcher in one or both of the maker assets.
|
|
||||||
if (shouldMaximallyFillOrders) {
|
|
||||||
_calculateMatchedFillResultsWithMaximalFill(
|
|
||||||
matchedFillResults,
|
|
||||||
leftOrder,
|
|
||||||
rightOrder,
|
|
||||||
leftMakerAssetAmountRemaining,
|
|
||||||
leftTakerAssetAmountRemaining,
|
|
||||||
rightMakerAssetAmountRemaining,
|
|
||||||
rightTakerAssetAmountRemaining
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
_calculateMatchedFillResults(
|
|
||||||
matchedFillResults,
|
|
||||||
leftOrder,
|
|
||||||
rightOrder,
|
|
||||||
leftMakerAssetAmountRemaining,
|
|
||||||
leftTakerAssetAmountRemaining,
|
|
||||||
rightMakerAssetAmountRemaining,
|
|
||||||
rightTakerAssetAmountRemaining
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute fees for left order
|
|
||||||
matchedFillResults.left.makerFeePaid = _safeGetPartialAmountFloor(
|
|
||||||
matchedFillResults.left.makerAssetFilledAmount,
|
|
||||||
leftOrder.makerAssetAmount,
|
|
||||||
leftOrder.makerFee
|
|
||||||
);
|
|
||||||
matchedFillResults.left.takerFeePaid = _safeGetPartialAmountFloor(
|
|
||||||
matchedFillResults.left.takerAssetFilledAmount,
|
|
||||||
leftOrder.takerAssetAmount,
|
|
||||||
leftOrder.takerFee
|
|
||||||
);
|
|
||||||
|
|
||||||
// Compute fees for right order
|
|
||||||
matchedFillResults.right.makerFeePaid = _safeGetPartialAmountFloor(
|
|
||||||
matchedFillResults.right.makerAssetFilledAmount,
|
|
||||||
rightOrder.makerAssetAmount,
|
|
||||||
rightOrder.makerFee
|
|
||||||
);
|
|
||||||
matchedFillResults.right.takerFeePaid = _safeGetPartialAmountFloor(
|
|
||||||
matchedFillResults.right.takerAssetFilledAmount,
|
|
||||||
rightOrder.takerAssetAmount,
|
|
||||||
rightOrder.takerFee
|
|
||||||
);
|
|
||||||
|
|
||||||
// Return fill results
|
|
||||||
return matchedFillResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Match two complementary orders that have a profitable spread.
|
/// @dev Match two complementary orders that have a profitable spread.
|
||||||
/// Each order is filled at their respective price point. However, the calculations are
|
/// 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.
|
/// carried out as though the orders are both being filled at the right order's price point.
|
||||||
@ -234,9 +144,13 @@ contract MixinMatchOrders is
|
|||||||
/// @dev Validates context for matchOrders. Succeeds or throws.
|
/// @dev Validates context for matchOrders. Succeeds or throws.
|
||||||
/// @param leftOrder First order to match.
|
/// @param leftOrder First order to match.
|
||||||
/// @param rightOrder Second order to match.
|
/// @param rightOrder Second order to match.
|
||||||
|
/// @param leftOrderInfo OrderStatus, orderHash, and amount already filled of leftOrder.
|
||||||
|
/// @param rightOrderInfo OrderStatus, orderHash, and amount already filled of rightOrder.
|
||||||
function _assertValidMatch(
|
function _assertValidMatch(
|
||||||
LibOrder.Order memory leftOrder,
|
LibOrder.Order memory leftOrder,
|
||||||
LibOrder.Order memory rightOrder
|
LibOrder.Order memory rightOrder,
|
||||||
|
LibOrder.OrderInfo memory leftOrderInfo,
|
||||||
|
LibOrder.OrderInfo memory rightOrderInfo
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
view
|
view
|
||||||
@ -249,340 +163,15 @@ contract MixinMatchOrders is
|
|||||||
// AND
|
// AND
|
||||||
// <rightOrder.makerAssetAmount> / <rightOrder.takerAssetAmount> >= <leftOrder.takerAssetAmount> / <leftOrder.makerAssetAmount>
|
// <rightOrder.makerAssetAmount> / <rightOrder.takerAssetAmount> >= <leftOrder.takerAssetAmount> / <leftOrder.makerAssetAmount>
|
||||||
// These equations can be combined to get the following:
|
// These equations can be combined to get the following:
|
||||||
if (_safeMul(leftOrder.makerAssetAmount, rightOrder.makerAssetAmount) <
|
if (leftOrder.makerAssetAmount.safeMul(rightOrder.makerAssetAmount) <
|
||||||
_safeMul(leftOrder.takerAssetAmount, rightOrder.takerAssetAmount)) {
|
leftOrder.takerAssetAmount.safeMul(rightOrder.takerAssetAmount)) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.NegativeSpreadError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.NegativeSpreadError(
|
||||||
getOrderHash(leftOrder),
|
leftOrderInfo.orderHash,
|
||||||
getOrderHash(rightOrder)
|
rightOrderInfo.orderHash
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Calculates part of the matched fill results for a given situation using the fill strategy that only
|
|
||||||
/// awards profit denominated in the left maker asset.
|
|
||||||
/// @param matchedFillResults The MatchedFillResults struct to update with fill result calculations.
|
|
||||||
/// @param leftOrder The left order in the order matching situation.
|
|
||||||
/// @param rightOrder The right order in the order matching situation.
|
|
||||||
/// @param leftMakerAssetAmountRemaining The amount of the left order maker asset that can still be filled.
|
|
||||||
/// @param leftTakerAssetAmountRemaining The amount of the left order taker asset that can still be filled.
|
|
||||||
/// @param rightMakerAssetAmountRemaining The amount of the right order maker asset that can still be filled.
|
|
||||||
/// @param rightTakerAssetAmountRemaining The amount of the right order taker asset that can still be filled.
|
|
||||||
function _calculateMatchedFillResults(
|
|
||||||
MatchedFillResults memory matchedFillResults,
|
|
||||||
LibOrder.Order memory leftOrder,
|
|
||||||
LibOrder.Order memory rightOrder,
|
|
||||||
uint256 leftMakerAssetAmountRemaining,
|
|
||||||
uint256 leftTakerAssetAmountRemaining,
|
|
||||||
uint256 rightMakerAssetAmountRemaining,
|
|
||||||
uint256 rightTakerAssetAmountRemaining
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
{
|
|
||||||
// Calculate fill results for maker and taker assets: at least one order will be fully filled.
|
|
||||||
// The maximum amount the left maker can buy is `leftTakerAssetAmountRemaining`
|
|
||||||
// The maximum amount the right maker can sell is `rightMakerAssetAmountRemaining`
|
|
||||||
// We have two distinct cases for calculating the fill results:
|
|
||||||
// Case 1.
|
|
||||||
// If the left maker can buy more than the right maker can sell, then only the right order is fully filled.
|
|
||||||
// If the left maker can buy exactly what the right maker can sell, then both orders are fully filled.
|
|
||||||
// Case 2.
|
|
||||||
// If the left maker cannot buy more than the right maker can sell, then only the left order is fully filled.
|
|
||||||
// Case 3.
|
|
||||||
// If the left maker can buy exactly as much as the right maker can sell, then both orders are fully filled.
|
|
||||||
if (leftTakerAssetAmountRemaining > rightMakerAssetAmountRemaining) {
|
|
||||||
// Case 1: Right order is fully filled
|
|
||||||
_calculateCompleteRightFill(
|
|
||||||
matchedFillResults,
|
|
||||||
leftOrder,
|
|
||||||
rightMakerAssetAmountRemaining,
|
|
||||||
rightTakerAssetAmountRemaining
|
|
||||||
);
|
|
||||||
} else if (leftTakerAssetAmountRemaining < rightMakerAssetAmountRemaining) {
|
|
||||||
// Case 2: Left order is fully filled
|
|
||||||
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
|
|
||||||
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
|
|
||||||
matchedFillResults.right.makerAssetFilledAmount = leftTakerAssetAmountRemaining;
|
|
||||||
// Round up to ensure the maker's exchange rate does not exceed the price specified by the order.
|
|
||||||
// We favor the maker when the exchange rate must be rounded.
|
|
||||||
matchedFillResults.right.takerAssetFilledAmount = _safeGetPartialAmountCeil(
|
|
||||||
rightOrder.takerAssetAmount,
|
|
||||||
rightOrder.makerAssetAmount,
|
|
||||||
leftTakerAssetAmountRemaining // matchedFillResults.right.makerAssetFilledAmount
|
|
||||||
);
|
|
||||||
} else { // leftTakerAssetAmountRemaining == rightMakerAssetAmountRemaining
|
|
||||||
// Case 3: Both orders are fully filled. Technically, this could be captured by the above cases, but
|
|
||||||
// this calculation will be more precise since it does not include rounding.
|
|
||||||
_calculateCompleteFillBoth(
|
|
||||||
matchedFillResults,
|
|
||||||
leftMakerAssetAmountRemaining,
|
|
||||||
leftTakerAssetAmountRemaining,
|
|
||||||
rightMakerAssetAmountRemaining,
|
|
||||||
rightTakerAssetAmountRemaining
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate amount given to taker
|
|
||||||
matchedFillResults.profitInLeftMakerAsset = _safeSub(
|
|
||||||
matchedFillResults.left.makerAssetFilledAmount,
|
|
||||||
matchedFillResults.right.takerAssetFilledAmount
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Calculates part of the matched fill results for a given situation using the maximal fill order matching
|
|
||||||
/// strategy.
|
|
||||||
/// @param matchedFillResults The MatchedFillResults struct to update with fill result calculations.
|
|
||||||
/// @param leftOrder The left order in the order matching situation.
|
|
||||||
/// @param rightOrder The right order in the order matching situation.
|
|
||||||
/// @param leftMakerAssetAmountRemaining The amount of the left order maker asset that can still be filled.
|
|
||||||
/// @param leftTakerAssetAmountRemaining The amount of the left order taker asset that can still be filled.
|
|
||||||
/// @param rightMakerAssetAmountRemaining The amount of the right order maker asset that can still be filled.
|
|
||||||
/// @param rightTakerAssetAmountRemaining The amount of the right order taker asset that can still be filled.
|
|
||||||
function _calculateMatchedFillResultsWithMaximalFill(
|
|
||||||
MatchedFillResults memory matchedFillResults,
|
|
||||||
LibOrder.Order memory leftOrder,
|
|
||||||
LibOrder.Order memory rightOrder,
|
|
||||||
uint256 leftMakerAssetAmountRemaining,
|
|
||||||
uint256 leftTakerAssetAmountRemaining,
|
|
||||||
uint256 rightMakerAssetAmountRemaining,
|
|
||||||
uint256 rightTakerAssetAmountRemaining
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
{
|
|
||||||
// If a maker asset is greater than the opposite taker asset, than there will be a spread denominated in that maker asset.
|
|
||||||
bool doesLeftMakerAssetProfitExist = leftMakerAssetAmountRemaining > rightTakerAssetAmountRemaining;
|
|
||||||
bool doesRightMakerAssetProfitExist = rightMakerAssetAmountRemaining > leftTakerAssetAmountRemaining;
|
|
||||||
|
|
||||||
// Calculate the maximum fill results for the maker and taker assets. At least one of the orders will be fully filled.
|
|
||||||
//
|
|
||||||
// The maximum that the left maker can possibly buy is the amount that the right order can sell.
|
|
||||||
// The maximum that the right maker can possibly buy is the amount that the left order can sell.
|
|
||||||
//
|
|
||||||
// If the left order is fully filled, profit will be paid out in the left maker asset. If the right order is fully filled,
|
|
||||||
// the profit will be out in the right maker asset.
|
|
||||||
//
|
|
||||||
// There are three cases to consider:
|
|
||||||
// Case 1.
|
|
||||||
// If the left maker can buy more than the right maker can sell, then only the right order is fully filled.
|
|
||||||
// Case 2.
|
|
||||||
// If the right maker can buy more than the left maker can sell, then only the right order is fully filled.
|
|
||||||
// Case 3.
|
|
||||||
// If the right maker can sell the max of what the left maker can buy and the left maker can sell the max of
|
|
||||||
// what the right maker can buy, then both orders are fully filled.
|
|
||||||
if (leftTakerAssetAmountRemaining > rightMakerAssetAmountRemaining) {
|
|
||||||
// Case 1: Right order is fully filled with the profit paid in the left makerAsset
|
|
||||||
_calculateCompleteRightFill(
|
|
||||||
matchedFillResults,
|
|
||||||
leftOrder,
|
|
||||||
rightMakerAssetAmountRemaining,
|
|
||||||
rightTakerAssetAmountRemaining
|
|
||||||
);
|
|
||||||
} else if (rightTakerAssetAmountRemaining > leftMakerAssetAmountRemaining) {
|
|
||||||
// Case 2: Left order is fully filled with the profit paid in the right makerAsset.
|
|
||||||
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
|
|
||||||
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
|
|
||||||
// Round down to ensure the right maker's exchange rate does not exceed the price specified by the order.
|
|
||||||
// We favor the right maker when the exchange rate must be rounded and the profit is being paid in the
|
|
||||||
// right maker asset.
|
|
||||||
matchedFillResults.right.makerAssetFilledAmount = _safeGetPartialAmountFloor(
|
|
||||||
rightOrder.makerAssetAmount,
|
|
||||||
rightOrder.takerAssetAmount,
|
|
||||||
leftMakerAssetAmountRemaining
|
|
||||||
);
|
|
||||||
matchedFillResults.right.takerAssetFilledAmount = leftMakerAssetAmountRemaining;
|
|
||||||
} else {
|
|
||||||
// Case 3: The right and left orders are fully filled
|
|
||||||
_calculateCompleteFillBoth(
|
|
||||||
matchedFillResults,
|
|
||||||
leftMakerAssetAmountRemaining,
|
|
||||||
leftTakerAssetAmountRemaining,
|
|
||||||
rightMakerAssetAmountRemaining,
|
|
||||||
rightTakerAssetAmountRemaining
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate amount given to taker in the left order's maker asset if the left spread will be part of the profit.
|
|
||||||
if (doesLeftMakerAssetProfitExist) {
|
|
||||||
matchedFillResults.profitInLeftMakerAsset = _safeSub(
|
|
||||||
matchedFillResults.left.makerAssetFilledAmount,
|
|
||||||
matchedFillResults.right.takerAssetFilledAmount
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate amount given to taker in the right order's maker asset if the right spread will be part of the profit.
|
|
||||||
if (doesRightMakerAssetProfitExist) {
|
|
||||||
matchedFillResults.profitInRightMakerAsset = _safeSub(
|
|
||||||
matchedFillResults.right.makerAssetFilledAmount,
|
|
||||||
matchedFillResults.left.takerAssetFilledAmount
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Calculates the fill results for the maker and taker in the order matching and writes the results
|
|
||||||
/// to the fillResults that are being collected on the order. Both orders will be fully filled in this
|
|
||||||
/// case.
|
|
||||||
/// @param matchedFillResults The fill results object to populate with calculations.
|
|
||||||
/// @param leftMakerAssetAmountRemaining The amount of the left maker asset that is remaining to be filled.
|
|
||||||
/// @param leftTakerAssetAmountRemaining The amount of the left taker asset that is remaining to be filled.
|
|
||||||
/// @param rightMakerAssetAmountRemaining The amount of the right maker asset that is remaining to be filled.
|
|
||||||
/// @param rightTakerAssetAmountRemaining The amount of the right taker asset that is remaining to be filled.
|
|
||||||
function _calculateCompleteFillBoth(
|
|
||||||
MatchedFillResults memory matchedFillResults,
|
|
||||||
uint256 leftMakerAssetAmountRemaining,
|
|
||||||
uint256 leftTakerAssetAmountRemaining,
|
|
||||||
uint256 rightMakerAssetAmountRemaining,
|
|
||||||
uint256 rightTakerAssetAmountRemaining
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
{
|
|
||||||
// Calculate the fully filled results for both orders.
|
|
||||||
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
|
|
||||||
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
|
|
||||||
matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;
|
|
||||||
matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Calculates the fill results for the maker and taker in the order matching and writes the results
|
|
||||||
/// to the fillResults that are being collected on the order.
|
|
||||||
/// @param matchedFillResults The fill results object to populate with calculations.
|
|
||||||
/// @param leftOrder The left order that is being maximally filled. All of the information about fill amounts
|
|
||||||
/// can be derived from this order and the right asset remaining fields.
|
|
||||||
/// @param rightMakerAssetAmountRemaining The amount of the right maker asset that is remaining to be filled.
|
|
||||||
/// @param rightTakerAssetAmountRemaining The amount of the right taker asset that is remaining to be filled.
|
|
||||||
function _calculateCompleteRightFill(
|
|
||||||
MatchedFillResults memory matchedFillResults,
|
|
||||||
LibOrder.Order memory leftOrder,
|
|
||||||
uint256 rightMakerAssetAmountRemaining,
|
|
||||||
uint256 rightTakerAssetAmountRemaining
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
{
|
|
||||||
matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;
|
|
||||||
matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;
|
|
||||||
matchedFillResults.left.takerAssetFilledAmount = rightMakerAssetAmountRemaining;
|
|
||||||
// Round down to ensure the left maker's exchange rate does not exceed the price specified by the order.
|
|
||||||
// We favor the left maker when the exchange rate must be rounded and the profit is being paid in the
|
|
||||||
// left maker asset.
|
|
||||||
matchedFillResults.left.makerAssetFilledAmount = _safeGetPartialAmountFloor(
|
|
||||||
leftOrder.makerAssetAmount,
|
|
||||||
leftOrder.takerAssetAmount,
|
|
||||||
rightMakerAssetAmountRemaining
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient.
|
|
||||||
/// @param leftOrderHash First matched order hash.
|
|
||||||
/// @param rightOrderHash Second matched order hash.
|
|
||||||
/// @param leftOrder First matched order.
|
|
||||||
/// @param rightOrder Second matched order.
|
|
||||||
/// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit.
|
|
||||||
/// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients.
|
|
||||||
function _settleMatchedOrders(
|
|
||||||
bytes32 leftOrderHash,
|
|
||||||
bytes32 rightOrderHash,
|
|
||||||
LibOrder.Order memory leftOrder,
|
|
||||||
LibOrder.Order memory rightOrder,
|
|
||||||
address takerAddress,
|
|
||||||
LibFillResults.MatchedFillResults memory matchedFillResults
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
{
|
|
||||||
address leftFeeRecipientAddress = leftOrder.feeRecipientAddress;
|
|
||||||
address rightFeeRecipientAddress = rightOrder.feeRecipientAddress;
|
|
||||||
|
|
||||||
// Right maker asset -> left maker
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
rightOrderHash,
|
|
||||||
rightOrder.makerAssetData,
|
|
||||||
rightOrder.makerAddress,
|
|
||||||
leftOrder.makerAddress,
|
|
||||||
matchedFillResults.left.takerAssetFilledAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
// Left maker asset -> right maker
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
leftOrderHash,
|
|
||||||
leftOrder.makerAssetData,
|
|
||||||
leftOrder.makerAddress,
|
|
||||||
rightOrder.makerAddress,
|
|
||||||
matchedFillResults.right.takerAssetFilledAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
// Right maker fee -> right fee recipient
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
rightOrderHash,
|
|
||||||
rightOrder.makerFeeAssetData,
|
|
||||||
rightOrder.makerAddress,
|
|
||||||
rightFeeRecipientAddress,
|
|
||||||
matchedFillResults.right.makerFeePaid
|
|
||||||
);
|
|
||||||
|
|
||||||
// Left maker fee -> left fee recipient
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
leftOrderHash,
|
|
||||||
leftOrder.makerFeeAssetData,
|
|
||||||
leftOrder.makerAddress,
|
|
||||||
leftFeeRecipientAddress,
|
|
||||||
matchedFillResults.left.makerFeePaid
|
|
||||||
);
|
|
||||||
|
|
||||||
// Settle taker profits.
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
leftOrderHash,
|
|
||||||
leftOrder.makerAssetData,
|
|
||||||
leftOrder.makerAddress,
|
|
||||||
takerAddress,
|
|
||||||
matchedFillResults.profitInLeftMakerAsset
|
|
||||||
);
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
rightOrderHash,
|
|
||||||
rightOrder.makerAssetData,
|
|
||||||
rightOrder.makerAddress,
|
|
||||||
takerAddress,
|
|
||||||
matchedFillResults.profitInRightMakerAsset
|
|
||||||
);
|
|
||||||
|
|
||||||
// Settle taker fees.
|
|
||||||
if (
|
|
||||||
leftFeeRecipientAddress == rightFeeRecipientAddress &&
|
|
||||||
leftOrder.takerFeeAssetData.equals(rightOrder.takerFeeAssetData)
|
|
||||||
) {
|
|
||||||
// Fee recipients and taker fee assets are identical, so we can
|
|
||||||
// transfer them in one go.
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
leftOrderHash,
|
|
||||||
leftOrder.takerFeeAssetData,
|
|
||||||
takerAddress,
|
|
||||||
leftFeeRecipientAddress,
|
|
||||||
_safeAdd(
|
|
||||||
matchedFillResults.left.takerFeePaid,
|
|
||||||
matchedFillResults.right.takerFeePaid
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Right taker fee -> right fee recipient
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
rightOrderHash,
|
|
||||||
rightOrder.takerFeeAssetData,
|
|
||||||
takerAddress,
|
|
||||||
rightFeeRecipientAddress,
|
|
||||||
matchedFillResults.right.takerFeePaid
|
|
||||||
);
|
|
||||||
|
|
||||||
// Left taker fee -> left fee recipient
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
leftOrderHash,
|
|
||||||
leftOrder.takerFeeAssetData,
|
|
||||||
takerAddress,
|
|
||||||
leftFeeRecipientAddress,
|
|
||||||
matchedFillResults.left.takerFeePaid
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Match complementary orders that have a profitable spread.
|
/// @dev Match complementary orders that have a profitable spread.
|
||||||
/// Each order is filled at their respective price point, and
|
/// Each order is filled at their respective price point, and
|
||||||
/// the matcher receives a profit denominated in the left maker asset.
|
/// the matcher receives a profit denominated in the left maker asset.
|
||||||
@ -606,25 +195,25 @@ contract MixinMatchOrders is
|
|||||||
{
|
{
|
||||||
// Ensure that the left and right orders have nonzero lengths.
|
// Ensure that the left and right orders have nonzero lengths.
|
||||||
if (leftOrders.length == 0) {
|
if (leftOrders.length == 0) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
|
||||||
BatchMatchOrdersErrorCodes.ZERO_LEFT_ORDERS
|
LibExchangeRichErrors.BatchMatchOrdersErrorCodes.ZERO_LEFT_ORDERS
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if (rightOrders.length == 0) {
|
if (rightOrders.length == 0) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
|
||||||
BatchMatchOrdersErrorCodes.ZERO_RIGHT_ORDERS
|
LibExchangeRichErrors.BatchMatchOrdersErrorCodes.ZERO_RIGHT_ORDERS
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the left and right arrays are compatible.
|
// Ensure that the left and right arrays are compatible.
|
||||||
if (leftOrders.length != leftSignatures.length) {
|
if (leftOrders.length != leftSignatures.length) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
|
||||||
BatchMatchOrdersErrorCodes.INVALID_LENGTH_LEFT_SIGNATURES
|
LibExchangeRichErrors.BatchMatchOrdersErrorCodes.INVALID_LENGTH_LEFT_SIGNATURES
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if (rightOrders.length != rightSignatures.length) {
|
if (rightOrders.length != rightSignatures.length) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.BatchMatchOrdersError(
|
||||||
BatchMatchOrdersErrorCodes.INVALID_LENGTH_RIGHT_SIGNATURES
|
LibExchangeRichErrors.BatchMatchOrdersErrorCodes.INVALID_LENGTH_RIGHT_SIGNATURES
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,33 +245,29 @@ contract MixinMatchOrders is
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Update the orderInfo structs with the updated takerAssetFilledAmount
|
// Update the orderInfo structs with the updated takerAssetFilledAmount
|
||||||
leftOrderInfo.orderTakerAssetFilledAmount = _safeAdd(
|
leftOrderInfo.orderTakerAssetFilledAmount = leftOrderInfo.orderTakerAssetFilledAmount.safeAdd(
|
||||||
leftOrderInfo.orderTakerAssetFilledAmount,
|
|
||||||
matchResults.left.takerAssetFilledAmount
|
matchResults.left.takerAssetFilledAmount
|
||||||
);
|
);
|
||||||
rightOrderInfo.orderTakerAssetFilledAmount = _safeAdd(
|
rightOrderInfo.orderTakerAssetFilledAmount = rightOrderInfo.orderTakerAssetFilledAmount.safeAdd(
|
||||||
rightOrderInfo.orderTakerAssetFilledAmount,
|
|
||||||
matchResults.right.takerAssetFilledAmount
|
matchResults.right.takerAssetFilledAmount
|
||||||
);
|
);
|
||||||
|
|
||||||
// Aggregate the new fill results with the previous fill results for the current orders.
|
// Aggregate the new fill results with the previous fill results for the current orders.
|
||||||
_addFillResults(
|
leftFillResults = LibFillResults.addFillResults(
|
||||||
leftFillResults,
|
leftFillResults,
|
||||||
matchResults.left
|
matchResults.left
|
||||||
);
|
);
|
||||||
_addFillResults(
|
rightFillResults = LibFillResults.addFillResults(
|
||||||
rightFillResults,
|
rightFillResults,
|
||||||
matchResults.right
|
matchResults.right
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update the profit in the left and right maker assets using the profits from
|
// Update the profit in the left and right maker assets using the profits from
|
||||||
// the match.
|
// the match.
|
||||||
batchMatchedFillResults.profitInLeftMakerAsset = _safeAdd(
|
batchMatchedFillResults.profitInLeftMakerAsset = batchMatchedFillResults.profitInLeftMakerAsset.safeAdd(
|
||||||
batchMatchedFillResults.profitInLeftMakerAsset,
|
|
||||||
matchResults.profitInLeftMakerAsset
|
matchResults.profitInLeftMakerAsset
|
||||||
);
|
);
|
||||||
batchMatchedFillResults.profitInRightMakerAsset = _safeAdd(
|
batchMatchedFillResults.profitInRightMakerAsset = batchMatchedFillResults.profitInRightMakerAsset.safeAdd(
|
||||||
batchMatchedFillResults.profitInRightMakerAsset,
|
|
||||||
matchResults.profitInRightMakerAsset
|
matchResults.profitInRightMakerAsset
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -779,10 +364,15 @@ contract MixinMatchOrders is
|
|||||||
takerAddress,
|
takerAddress,
|
||||||
rightSignature
|
rightSignature
|
||||||
);
|
);
|
||||||
_assertValidMatch(leftOrder, rightOrder);
|
_assertValidMatch(
|
||||||
|
leftOrder,
|
||||||
|
rightOrder,
|
||||||
|
leftOrderInfo,
|
||||||
|
rightOrderInfo
|
||||||
|
);
|
||||||
|
|
||||||
// Compute proportional fill amounts
|
// Compute proportional fill amounts
|
||||||
matchedFillResults = calculateMatchedFillResults(
|
matchedFillResults = LibFillResults.calculateMatchedFillResults(
|
||||||
leftOrder,
|
leftOrder,
|
||||||
rightOrder,
|
rightOrder,
|
||||||
leftOrderInfo.orderTakerAssetFilledAmount,
|
leftOrderInfo.orderTakerAssetFilledAmount,
|
||||||
@ -818,4 +408,112 @@ contract MixinMatchOrders is
|
|||||||
|
|
||||||
return matchedFillResults;
|
return matchedFillResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient.
|
||||||
|
/// @param leftOrderHash First matched order hash.
|
||||||
|
/// @param rightOrderHash Second matched order hash.
|
||||||
|
/// @param leftOrder First matched order.
|
||||||
|
/// @param rightOrder Second matched order.
|
||||||
|
/// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit.
|
||||||
|
/// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients.
|
||||||
|
function _settleMatchedOrders(
|
||||||
|
bytes32 leftOrderHash,
|
||||||
|
bytes32 rightOrderHash,
|
||||||
|
LibOrder.Order memory leftOrder,
|
||||||
|
LibOrder.Order memory rightOrder,
|
||||||
|
address takerAddress,
|
||||||
|
LibFillResults.MatchedFillResults memory matchedFillResults
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
{
|
||||||
|
address leftFeeRecipientAddress = leftOrder.feeRecipientAddress;
|
||||||
|
address rightFeeRecipientAddress = rightOrder.feeRecipientAddress;
|
||||||
|
|
||||||
|
// Right maker asset -> left maker
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
rightOrderHash,
|
||||||
|
rightOrder.makerAssetData,
|
||||||
|
rightOrder.makerAddress,
|
||||||
|
leftOrder.makerAddress,
|
||||||
|
matchedFillResults.left.takerAssetFilledAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
// Left maker asset -> right maker
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
leftOrderHash,
|
||||||
|
leftOrder.makerAssetData,
|
||||||
|
leftOrder.makerAddress,
|
||||||
|
rightOrder.makerAddress,
|
||||||
|
matchedFillResults.right.takerAssetFilledAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
// Right maker fee -> right fee recipient
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
rightOrderHash,
|
||||||
|
rightOrder.makerFeeAssetData,
|
||||||
|
rightOrder.makerAddress,
|
||||||
|
rightFeeRecipientAddress,
|
||||||
|
matchedFillResults.right.makerFeePaid
|
||||||
|
);
|
||||||
|
|
||||||
|
// Left maker fee -> left fee recipient
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
leftOrderHash,
|
||||||
|
leftOrder.makerFeeAssetData,
|
||||||
|
leftOrder.makerAddress,
|
||||||
|
leftFeeRecipientAddress,
|
||||||
|
matchedFillResults.left.makerFeePaid
|
||||||
|
);
|
||||||
|
|
||||||
|
// Settle taker profits.
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
leftOrderHash,
|
||||||
|
leftOrder.makerAssetData,
|
||||||
|
leftOrder.makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
matchedFillResults.profitInLeftMakerAsset
|
||||||
|
);
|
||||||
|
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
rightOrderHash,
|
||||||
|
rightOrder.makerAssetData,
|
||||||
|
rightOrder.makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
matchedFillResults.profitInRightMakerAsset
|
||||||
|
);
|
||||||
|
|
||||||
|
// Settle taker fees.
|
||||||
|
if (
|
||||||
|
leftFeeRecipientAddress == rightFeeRecipientAddress &&
|
||||||
|
leftOrder.takerFeeAssetData.equals(rightOrder.takerFeeAssetData)
|
||||||
|
) {
|
||||||
|
// Fee recipients and taker fee assets are identical, so we can
|
||||||
|
// transfer them in one go.
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
leftOrderHash,
|
||||||
|
leftOrder.takerFeeAssetData,
|
||||||
|
takerAddress,
|
||||||
|
leftFeeRecipientAddress,
|
||||||
|
matchedFillResults.left.takerFeePaid.safeAdd(matchedFillResults.right.takerFeePaid)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Right taker fee -> right fee recipient
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
rightOrderHash,
|
||||||
|
rightOrder.takerFeeAssetData,
|
||||||
|
takerAddress,
|
||||||
|
rightFeeRecipientAddress,
|
||||||
|
matchedFillResults.right.takerFeePaid
|
||||||
|
);
|
||||||
|
|
||||||
|
// Left taker fee -> left fee recipient
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
leftOrderHash,
|
||||||
|
leftOrder.takerFeeAssetData,
|
||||||
|
takerAddress,
|
||||||
|
leftFeeRecipientAddress,
|
||||||
|
matchedFillResults.left.takerFeePaid
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,23 +25,24 @@ import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
|||||||
import "@0x/contracts-utils/contracts/src/ReentrancyGuard.sol";
|
import "@0x/contracts-utils/contracts/src/ReentrancyGuard.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/LibZeroExTransaction.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
|
||||||
import "./interfaces/IWallet.sol";
|
import "./interfaces/IWallet.sol";
|
||||||
import "./interfaces/IEIP1271Wallet.sol";
|
import "./interfaces/IEIP1271Wallet.sol";
|
||||||
import "./interfaces/IExchangeRichErrors.sol";
|
|
||||||
import "./interfaces/ISignatureValidator.sol";
|
import "./interfaces/ISignatureValidator.sol";
|
||||||
import "./LibExchangeRichErrors.sol";
|
|
||||||
import "./MixinTransactions.sol";
|
import "./MixinTransactions.sol";
|
||||||
|
|
||||||
|
|
||||||
contract MixinSignatureValidator is
|
contract MixinSignatureValidator is
|
||||||
ReentrancyGuard,
|
ReentrancyGuard,
|
||||||
|
LibEIP712ExchangeDomain,
|
||||||
LibEIP1271,
|
LibEIP1271,
|
||||||
LibOrder,
|
|
||||||
LibZeroExTransaction,
|
|
||||||
ISignatureValidator,
|
ISignatureValidator,
|
||||||
MixinTransactions
|
MixinTransactions
|
||||||
{
|
{
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
|
using LibOrder for LibOrder.Order;
|
||||||
|
using LibZeroExTransaction for LibZeroExTransaction.ZeroExTransaction;
|
||||||
|
|
||||||
// Magic bytes to be returned by `Wallet` signature type validators.
|
// Magic bytes to be returned by `Wallet` signature type validators.
|
||||||
// bytes4(keccak256("isValidWalletSignature(bytes32,address,bytes)"))
|
// bytes4(keccak256("isValidWalletSignature(bytes32,address,bytes)"))
|
||||||
@ -109,8 +110,8 @@ contract MixinSignatureValidator is
|
|||||||
signatureType == SignatureType.Validator ||
|
signatureType == SignatureType.Validator ||
|
||||||
signatureType == SignatureType.EIP1271Wallet
|
signatureType == SignatureType.EIP1271Wallet
|
||||||
) {
|
) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
|
||||||
IExchangeRichErrors.SignatureErrorCodes.INAPPROPRIATE_SIGNATURE_TYPE,
|
LibExchangeRichErrors.SignatureErrorCodes.INAPPROPRIATE_SIGNATURE_TYPE,
|
||||||
hash,
|
hash,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
signature
|
signature
|
||||||
@ -129,14 +130,14 @@ contract MixinSignatureValidator is
|
|||||||
/// @param signature Proof that the order has been signed by signer.
|
/// @param signature Proof that the order has been signed by signer.
|
||||||
/// @return isValid `true` if the signature is valid for the given order and signer.
|
/// @return isValid `true` if the signature is valid for the given order and signer.
|
||||||
function isValidOrderSignature(
|
function isValidOrderSignature(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
view
|
view
|
||||||
returns (bool isValid)
|
returns (bool isValid)
|
||||||
{
|
{
|
||||||
bytes32 orderHash = getOrderHash(order);
|
bytes32 orderHash = order.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
|
||||||
return _isValidOrderWithHashSignature(
|
return _isValidOrderWithHashSignature(
|
||||||
order,
|
order,
|
||||||
orderHash,
|
orderHash,
|
||||||
@ -149,14 +150,14 @@ contract MixinSignatureValidator is
|
|||||||
/// @param signature Proof that the order has been signed by signer.
|
/// @param signature Proof that the order has been signed by signer.
|
||||||
/// @return isValid `true` if the signature is valid for the given transaction and signer.
|
/// @return isValid `true` if the signature is valid for the given transaction and signer.
|
||||||
function isValidTransactionSignature(
|
function isValidTransactionSignature(
|
||||||
ZeroExTransaction memory transaction,
|
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
view
|
view
|
||||||
returns (bool isValid)
|
returns (bool isValid)
|
||||||
{
|
{
|
||||||
bytes32 transactionHash = getTransactionHash(transaction);
|
bytes32 transactionHash = transaction.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
|
||||||
isValid = _isValidTransactionWithHashSignature(
|
isValid = _isValidTransactionWithHashSignature(
|
||||||
transaction,
|
transaction,
|
||||||
transactionHash,
|
transactionHash,
|
||||||
@ -198,7 +199,7 @@ contract MixinSignatureValidator is
|
|||||||
/// @param signature Proof that the hash has been signed by signer.
|
/// @param signature Proof that the hash has been signed by signer.
|
||||||
/// @return isValid True if the signature is valid for the given order and signer.
|
/// @return isValid True if the signature is valid for the given order and signer.
|
||||||
function _isValidOrderWithHashSignature(
|
function _isValidOrderWithHashSignature(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
bytes32 orderHash,
|
bytes32 orderHash,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
@ -246,7 +247,7 @@ contract MixinSignatureValidator is
|
|||||||
/// @param signature Proof that the hash has been signed by signer.
|
/// @param signature Proof that the hash has been signed by signer.
|
||||||
/// @return isValid True if the signature is valid for the given transaction and signer.
|
/// @return isValid True if the signature is valid for the given transaction and signer.
|
||||||
function _isValidTransactionWithHashSignature(
|
function _isValidTransactionWithHashSignature(
|
||||||
ZeroExTransaction memory transaction,
|
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||||
bytes32 transactionHash,
|
bytes32 transactionHash,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
@ -305,8 +306,8 @@ contract MixinSignatureValidator is
|
|||||||
// a correctly formatted but incorrect signature.
|
// a correctly formatted but incorrect signature.
|
||||||
if (signatureType == SignatureType.Invalid) {
|
if (signatureType == SignatureType.Invalid) {
|
||||||
if (signature.length != 1) {
|
if (signature.length != 1) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
|
||||||
IExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||||
hash,
|
hash,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
signature
|
signature
|
||||||
@ -317,8 +318,8 @@ contract MixinSignatureValidator is
|
|||||||
// Signature using EIP712
|
// Signature using EIP712
|
||||||
} else if (signatureType == SignatureType.EIP712) {
|
} else if (signatureType == SignatureType.EIP712) {
|
||||||
if (signature.length != 66) {
|
if (signature.length != 66) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
|
||||||
IExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||||
hash,
|
hash,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
signature
|
signature
|
||||||
@ -338,8 +339,8 @@ contract MixinSignatureValidator is
|
|||||||
// Signed using web3.eth_sign
|
// Signed using web3.eth_sign
|
||||||
} else if (signatureType == SignatureType.EthSign) {
|
} else if (signatureType == SignatureType.EthSign) {
|
||||||
if (signature.length != 66) {
|
if (signature.length != 66) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
|
||||||
IExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||||
hash,
|
hash,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
signature
|
signature
|
||||||
@ -388,8 +389,8 @@ contract MixinSignatureValidator is
|
|||||||
// Disallow address zero because it is ecrecover() returns zero on
|
// Disallow address zero because it is ecrecover() returns zero on
|
||||||
// failure.
|
// failure.
|
||||||
if (signerAddress == address(0)) {
|
if (signerAddress == address(0)) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
|
||||||
IExchangeRichErrors.SignatureErrorCodes.INVALID_SIGNER,
|
LibExchangeRichErrors.SignatureErrorCodes.INVALID_SIGNER,
|
||||||
hash,
|
hash,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
signature
|
signature
|
||||||
@ -397,8 +398,8 @@ contract MixinSignatureValidator is
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (signature.length == 0) {
|
if (signature.length == 0) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
|
||||||
IExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
LibExchangeRichErrors.SignatureErrorCodes.INVALID_LENGTH,
|
||||||
hash,
|
hash,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
signature
|
signature
|
||||||
@ -410,8 +411,8 @@ contract MixinSignatureValidator is
|
|||||||
|
|
||||||
// Ensure signature is supported
|
// Ensure signature is supported
|
||||||
if (signatureTypeRaw >= uint8(SignatureType.NSignatureTypes)) {
|
if (signatureTypeRaw >= uint8(SignatureType.NSignatureTypes)) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
|
||||||
IExchangeRichErrors.SignatureErrorCodes.UNSUPPORTED,
|
LibExchangeRichErrors.SignatureErrorCodes.UNSUPPORTED,
|
||||||
hash,
|
hash,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
signature
|
signature
|
||||||
@ -424,8 +425,8 @@ contract MixinSignatureValidator is
|
|||||||
// it an explicit option. This aids testing and analysis. It is
|
// it an explicit option. This aids testing and analysis. It is
|
||||||
// also the initialization value for the enum type.
|
// also the initialization value for the enum type.
|
||||||
if (SignatureType(signatureTypeRaw) == SignatureType.Illegal) {
|
if (SignatureType(signatureTypeRaw) == SignatureType.Illegal) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureError(
|
||||||
IExchangeRichErrors.SignatureErrorCodes.ILLEGAL,
|
LibExchangeRichErrors.SignatureErrorCodes.ILLEGAL,
|
||||||
hash,
|
hash,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
signature
|
signature
|
||||||
@ -479,7 +480,7 @@ contract MixinSignatureValidator is
|
|||||||
return returnData.readBytes4(0) == LEGACY_WALLET_MAGIC_VALUE;
|
return returnData.readBytes4(0) == LEGACY_WALLET_MAGIC_VALUE;
|
||||||
}
|
}
|
||||||
// Static call to verifier failed.
|
// Static call to verifier failed.
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureWalletError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureWalletError(
|
||||||
hash,
|
hash,
|
||||||
walletAddress,
|
walletAddress,
|
||||||
signature,
|
signature,
|
||||||
@ -533,7 +534,7 @@ contract MixinSignatureValidator is
|
|||||||
return returnData.readBytes4(0) == EIP1271_MAGIC_VALUE;
|
return returnData.readBytes4(0) == EIP1271_MAGIC_VALUE;
|
||||||
}
|
}
|
||||||
// Static call to verifier failed.
|
// Static call to verifier failed.
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureWalletError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureWalletError(
|
||||||
hash,
|
hash,
|
||||||
walletAddress,
|
walletAddress,
|
||||||
signature,
|
signature,
|
||||||
@ -569,7 +570,7 @@ contract MixinSignatureValidator is
|
|||||||
address validatorAddress = signature.readAddress(signatureLength - 21);
|
address validatorAddress = signature.readAddress(signatureLength - 21);
|
||||||
// Ensure signer has approved validator.
|
// Ensure signer has approved validator.
|
||||||
if (!allowedValidators[signerAddress][validatorAddress]) {
|
if (!allowedValidators[signerAddress][validatorAddress]) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureValidatorNotApprovedError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureValidatorNotApprovedError(
|
||||||
signerAddress,
|
signerAddress,
|
||||||
validatorAddress
|
validatorAddress
|
||||||
));
|
));
|
||||||
@ -597,7 +598,7 @@ contract MixinSignatureValidator is
|
|||||||
return returnData.readBytes4(0) == EIP1271_MAGIC_VALUE;
|
return returnData.readBytes4(0) == EIP1271_MAGIC_VALUE;
|
||||||
}
|
}
|
||||||
// Static call to verifier failed.
|
// Static call to verifier failed.
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.SignatureValidatorError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.SignatureValidatorError(
|
||||||
hash,
|
hash,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
validatorAddress,
|
validatorAddress,
|
||||||
|
@ -20,18 +20,20 @@ pragma solidity ^0.5.9;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeRichErrors.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
import "./interfaces/IExchangeRichErrors.sol";
|
|
||||||
import "./interfaces/ITransactions.sol";
|
import "./interfaces/ITransactions.sol";
|
||||||
import "./interfaces/ISignatureValidator.sol";
|
import "./interfaces/ISignatureValidator.sol";
|
||||||
import "./LibExchangeRichErrors.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract MixinTransactions is
|
contract MixinTransactions is
|
||||||
LibZeroExTransaction,
|
LibEIP712ExchangeDomain,
|
||||||
ISignatureValidator,
|
ISignatureValidator,
|
||||||
ITransactions
|
ITransactions
|
||||||
{
|
{
|
||||||
|
using LibZeroExTransaction for LibZeroExTransaction.ZeroExTransaction;
|
||||||
|
|
||||||
// Mapping of transaction hash => executed
|
// Mapping of transaction hash => executed
|
||||||
// This prevents transactions from being executed more than once.
|
// This prevents transactions from being executed more than once.
|
||||||
mapping (bytes32 => bool) public transactionsExecuted;
|
mapping (bytes32 => bool) public transactionsExecuted;
|
||||||
@ -44,7 +46,7 @@ contract MixinTransactions is
|
|||||||
/// @param signature Proof that transaction has been signed by signer.
|
/// @param signature Proof that transaction has been signed by signer.
|
||||||
/// @return ABI encoded return data of the underlying Exchange function call.
|
/// @return ABI encoded return data of the underlying Exchange function call.
|
||||||
function executeTransaction(
|
function executeTransaction(
|
||||||
ZeroExTransaction memory transaction,
|
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
@ -58,7 +60,7 @@ contract MixinTransactions is
|
|||||||
/// @param signatures Array of proofs that transactions have been signed by signer(s).
|
/// @param signatures Array of proofs that transactions have been signed by signer(s).
|
||||||
/// @return Array containing ABI encoded return data for each of the underlying Exchange function calls.
|
/// @return Array containing ABI encoded return data for each of the underlying Exchange function calls.
|
||||||
function batchExecuteTransactions(
|
function batchExecuteTransactions(
|
||||||
ZeroExTransaction[] memory transactions,
|
LibZeroExTransaction.ZeroExTransaction[] memory transactions,
|
||||||
bytes[] memory signatures
|
bytes[] memory signatures
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
@ -77,35 +79,35 @@ contract MixinTransactions is
|
|||||||
/// @param signature Proof that transaction has been signed by signer.
|
/// @param signature Proof that transaction has been signed by signer.
|
||||||
/// @return ABI encoded return data of the underlying Exchange function call.
|
/// @return ABI encoded return data of the underlying Exchange function call.
|
||||||
function _executeTransaction(
|
function _executeTransaction(
|
||||||
ZeroExTransaction memory transaction,
|
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
returns (bytes memory)
|
returns (bytes memory)
|
||||||
{
|
{
|
||||||
bytes32 transactionHash = getTransactionHash(transaction);
|
bytes32 transactionHash = transaction.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
|
||||||
|
|
||||||
// Check transaction is not expired
|
// Check transaction is not expired
|
||||||
// solhint-disable-next-line not-rely-on-time
|
// solhint-disable-next-line not-rely-on-time
|
||||||
if (block.timestamp >= transaction.expirationTimeSeconds) {
|
if (block.timestamp >= transaction.expirationTimeSeconds) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.TransactionError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionError(
|
||||||
IExchangeRichErrors.TransactionErrorCodes.EXPIRED,
|
LibExchangeRichErrors.TransactionErrorCodes.EXPIRED,
|
||||||
transactionHash
|
transactionHash
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent reentrancy
|
// Prevent reentrancy
|
||||||
if (currentContextAddress != address(0)) {
|
if (currentContextAddress != address(0)) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.TransactionError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionError(
|
||||||
IExchangeRichErrors.TransactionErrorCodes.NO_REENTRANCY,
|
LibExchangeRichErrors.TransactionErrorCodes.NO_REENTRANCY,
|
||||||
transactionHash
|
transactionHash
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate transaction has not been executed
|
// Validate transaction has not been executed
|
||||||
if (transactionsExecuted[transactionHash]) {
|
if (transactionsExecuted[transactionHash]) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.TransactionError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionError(
|
||||||
IExchangeRichErrors.TransactionErrorCodes.ALREADY_EXECUTED,
|
LibExchangeRichErrors.TransactionErrorCodes.ALREADY_EXECUTED,
|
||||||
transactionHash
|
transactionHash
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -118,7 +120,7 @@ contract MixinTransactions is
|
|||||||
transaction,
|
transaction,
|
||||||
transactionHash,
|
transactionHash,
|
||||||
signature)) {
|
signature)) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.TransactionSignatureError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionSignatureError(
|
||||||
transactionHash,
|
transactionHash,
|
||||||
signerAddress,
|
signerAddress,
|
||||||
signature
|
signature
|
||||||
@ -133,7 +135,7 @@ contract MixinTransactions is
|
|||||||
transactionsExecuted[transactionHash] = true;
|
transactionsExecuted[transactionHash] = true;
|
||||||
(bool didSucceed, bytes memory returnData) = address(this).delegatecall(transaction.data);
|
(bool didSucceed, bytes memory returnData) = address(this).delegatecall(transaction.data);
|
||||||
if (!didSucceed) {
|
if (!didSucceed) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.TransactionExecutionError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.TransactionExecutionError(
|
||||||
transactionHash,
|
transactionHash,
|
||||||
returnData
|
returnData
|
||||||
));
|
));
|
||||||
|
@ -19,15 +19,14 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/ReentrancyGuard.sol";
|
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.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/LibMath.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/LibExchangeRichErrors.sol";
|
||||||
import "./interfaces/IExchangeCore.sol";
|
import "./interfaces/IExchangeCore.sol";
|
||||||
import "./interfaces/IExchangeRichErrors.sol";
|
|
||||||
import "./interfaces/IWrapperFunctions.sol";
|
import "./interfaces/IWrapperFunctions.sol";
|
||||||
import "./LibExchangeRichErrors.sol";
|
|
||||||
import "./MixinExchangeCore.sol";
|
import "./MixinExchangeCore.sol";
|
||||||
|
|
||||||
|
|
||||||
@ -35,6 +34,8 @@ contract MixinWrapperFunctions is
|
|||||||
IWrapperFunctions,
|
IWrapperFunctions,
|
||||||
MixinExchangeCore
|
MixinExchangeCore
|
||||||
{
|
{
|
||||||
|
using LibSafeMath for uint256;
|
||||||
|
|
||||||
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
|
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
|
||||||
/// @param order Order struct containing order specifications.
|
/// @param order Order struct containing order specifications.
|
||||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||||
@ -46,7 +47,7 @@ contract MixinWrapperFunctions is
|
|||||||
)
|
)
|
||||||
public
|
public
|
||||||
nonReentrant
|
nonReentrant
|
||||||
returns (FillResults memory fillResults)
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
fillResults = _fillOrKillOrder(
|
fillResults = _fillOrKillOrder(
|
||||||
order,
|
order,
|
||||||
@ -68,11 +69,11 @@ contract MixinWrapperFunctions is
|
|||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
returns (FillResults memory fillResults)
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
// ABI encode calldata for `fillOrder`
|
// ABI encode calldata for `fillOrder`
|
||||||
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
||||||
IExchangeCore(0).fillOrder.selector,
|
IExchangeCore(address(0)).fillOrder.selector,
|
||||||
order,
|
order,
|
||||||
takerAssetFillAmount,
|
takerAssetFillAmount,
|
||||||
signature
|
signature
|
||||||
@ -81,7 +82,7 @@ contract MixinWrapperFunctions is
|
|||||||
(bool didSucceed, bytes memory returnData) = address(this).delegatecall(fillOrderCalldata);
|
(bool didSucceed, bytes memory returnData) = address(this).delegatecall(fillOrderCalldata);
|
||||||
if (didSucceed) {
|
if (didSucceed) {
|
||||||
assert(returnData.length == 128);
|
assert(returnData.length == 128);
|
||||||
fillResults = abi.decode(returnData, (FillResults));
|
fillResults = abi.decode(returnData, (LibFillResults.FillResults));
|
||||||
}
|
}
|
||||||
// fillResults values will be 0 by default if call was unsuccessful
|
// fillResults values will be 0 by default if call was unsuccessful
|
||||||
return fillResults;
|
return fillResults;
|
||||||
@ -99,10 +100,10 @@ contract MixinWrapperFunctions is
|
|||||||
)
|
)
|
||||||
public
|
public
|
||||||
nonReentrant
|
nonReentrant
|
||||||
returns (FillResults[] memory fillResults)
|
returns (LibFillResults.FillResults[] memory fillResults)
|
||||||
{
|
{
|
||||||
uint256 ordersLength = orders.length;
|
uint256 ordersLength = orders.length;
|
||||||
fillResults = new FillResults[](ordersLength);
|
fillResults = new LibFillResults.FillResults[](ordersLength);
|
||||||
for (uint256 i = 0; i != ordersLength; i++) {
|
for (uint256 i = 0; i != ordersLength; i++) {
|
||||||
fillResults[i] = _fillOrder(
|
fillResults[i] = _fillOrder(
|
||||||
orders[i],
|
orders[i],
|
||||||
@ -125,10 +126,10 @@ contract MixinWrapperFunctions is
|
|||||||
)
|
)
|
||||||
public
|
public
|
||||||
nonReentrant
|
nonReentrant
|
||||||
returns (FillResults[] memory fillResults)
|
returns (LibFillResults.FillResults[] memory fillResults)
|
||||||
{
|
{
|
||||||
uint256 ordersLength = orders.length;
|
uint256 ordersLength = orders.length;
|
||||||
fillResults = new FillResults[](ordersLength);
|
fillResults = new LibFillResults.FillResults[](ordersLength);
|
||||||
for (uint256 i = 0; i != ordersLength; i++) {
|
for (uint256 i = 0; i != ordersLength; i++) {
|
||||||
fillResults[i] = _fillOrKillOrder(
|
fillResults[i] = _fillOrKillOrder(
|
||||||
orders[i],
|
orders[i],
|
||||||
@ -150,10 +151,10 @@ contract MixinWrapperFunctions is
|
|||||||
bytes[] memory signatures
|
bytes[] memory signatures
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
returns (FillResults[] memory fillResults)
|
returns (LibFillResults.FillResults[] memory fillResults)
|
||||||
{
|
{
|
||||||
uint256 ordersLength = orders.length;
|
uint256 ordersLength = orders.length;
|
||||||
fillResults = new FillResults[](ordersLength);
|
fillResults = new LibFillResults.FillResults[](ordersLength);
|
||||||
for (uint256 i = 0; i != ordersLength; i++) {
|
for (uint256 i = 0; i != ordersLength; i++) {
|
||||||
fillResults[i] = fillOrderNoThrow(
|
fillResults[i] = fillOrderNoThrow(
|
||||||
orders[i],
|
orders[i],
|
||||||
@ -175,7 +176,7 @@ contract MixinWrapperFunctions is
|
|||||||
bytes[] memory signatures
|
bytes[] memory signatures
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
returns (FillResults memory fillResults)
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
bytes memory takerAssetData = orders[0].takerAssetData;
|
bytes memory takerAssetData = orders[0].takerAssetData;
|
||||||
|
|
||||||
@ -188,17 +189,17 @@ contract MixinWrapperFunctions is
|
|||||||
orders[i].takerAssetData = takerAssetData;
|
orders[i].takerAssetData = takerAssetData;
|
||||||
|
|
||||||
// Calculate the remaining amount of takerAsset to sell
|
// Calculate the remaining amount of takerAsset to sell
|
||||||
uint256 remainingTakerAssetFillAmount = _safeSub(takerAssetFillAmount, fillResults.takerAssetFilledAmount);
|
uint256 remainingTakerAssetFillAmount = takerAssetFillAmount.safeSub(fillResults.takerAssetFilledAmount);
|
||||||
|
|
||||||
// Attempt to sell the remaining amount of takerAsset
|
// Attempt to sell the remaining amount of takerAsset
|
||||||
FillResults memory singleFillResults = fillOrderNoThrow(
|
LibFillResults.FillResults memory singleFillResults = fillOrderNoThrow(
|
||||||
orders[i],
|
orders[i],
|
||||||
remainingTakerAssetFillAmount,
|
remainingTakerAssetFillAmount,
|
||||||
signatures[i]
|
signatures[i]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update amounts filled and fees paid by maker and taker
|
// Update amounts filled and fees paid by maker and taker
|
||||||
_addFillResults(fillResults, singleFillResults);
|
fillResults = LibFillResults.addFillResults(fillResults, singleFillResults);
|
||||||
|
|
||||||
// Stop execution if the entire amount of takerAsset has been sold
|
// Stop execution if the entire amount of takerAsset has been sold
|
||||||
if (fillResults.takerAssetFilledAmount >= takerAssetFillAmount) {
|
if (fillResults.takerAssetFilledAmount >= takerAssetFillAmount) {
|
||||||
@ -219,7 +220,7 @@ contract MixinWrapperFunctions is
|
|||||||
bytes[] memory signatures
|
bytes[] memory signatures
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
returns (FillResults memory fillResults)
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
bytes memory makerAssetData = orders[0].makerAssetData;
|
bytes memory makerAssetData = orders[0].makerAssetData;
|
||||||
|
|
||||||
@ -232,25 +233,25 @@ contract MixinWrapperFunctions is
|
|||||||
orders[i].makerAssetData = makerAssetData;
|
orders[i].makerAssetData = makerAssetData;
|
||||||
|
|
||||||
// Calculate the remaining amount of makerAsset to buy
|
// Calculate the remaining amount of makerAsset to buy
|
||||||
uint256 remainingMakerAssetFillAmount = _safeSub(makerAssetFillAmount, fillResults.makerAssetFilledAmount);
|
uint256 remainingMakerAssetFillAmount = makerAssetFillAmount.safeSub(fillResults.makerAssetFilledAmount);
|
||||||
|
|
||||||
// Convert the remaining amount of makerAsset to buy into remaining amount
|
// Convert the remaining amount of makerAsset to buy into remaining amount
|
||||||
// of takerAsset to sell, assuming entire amount can be sold in the current order
|
// of takerAsset to sell, assuming entire amount can be sold in the current order
|
||||||
uint256 remainingTakerAssetFillAmount = _getPartialAmountFloor(
|
uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountFloor(
|
||||||
orders[i].takerAssetAmount,
|
orders[i].takerAssetAmount,
|
||||||
orders[i].makerAssetAmount,
|
orders[i].makerAssetAmount,
|
||||||
remainingMakerAssetFillAmount
|
remainingMakerAssetFillAmount
|
||||||
);
|
);
|
||||||
|
|
||||||
// Attempt to sell the remaining amount of takerAsset
|
// Attempt to sell the remaining amount of takerAsset
|
||||||
FillResults memory singleFillResults = fillOrderNoThrow(
|
LibFillResults.FillResults memory singleFillResults = fillOrderNoThrow(
|
||||||
orders[i],
|
orders[i],
|
||||||
remainingTakerAssetFillAmount,
|
remainingTakerAssetFillAmount,
|
||||||
signatures[i]
|
signatures[i]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update amounts filled and fees paid by maker and taker
|
// Update amounts filled and fees paid by maker and taker
|
||||||
_addFillResults(fillResults, singleFillResults);
|
fillResults = LibFillResults.addFillResults(fillResults, singleFillResults);
|
||||||
|
|
||||||
// Stop execution if the entire amount of makerAsset has been bought
|
// Stop execution if the entire amount of makerAsset has been bought
|
||||||
if (fillResults.makerAssetFilledAmount >= makerAssetFillAmount) {
|
if (fillResults.makerAssetFilledAmount >= makerAssetFillAmount) {
|
||||||
@ -298,7 +299,7 @@ contract MixinWrapperFunctions is
|
|||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
returns (FillResults memory fillResults)
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
fillResults = _fillOrder(
|
fillResults = _fillOrder(
|
||||||
order,
|
order,
|
||||||
@ -306,7 +307,7 @@ contract MixinWrapperFunctions is
|
|||||||
signature
|
signature
|
||||||
);
|
);
|
||||||
if (fillResults.takerAssetFilledAmount != takerAssetFillAmount) {
|
if (fillResults.takerAssetFilledAmount != takerAssetFillAmount) {
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.IncompleteFillError(
|
LibRichErrors.rrevert(LibExchangeRichErrors.IncompleteFillError(
|
||||||
getOrderInfo(order).orderHash
|
getOrderInfo(order).orderHash
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ contract IExchangeCore {
|
|||||||
uint256 takerFeePaid, // Amount of takerFeeAssetData paid to feeRecipient by taker.
|
uint256 takerFeePaid, // Amount of takerFeeAssetData paid to feeRecipient by taker.
|
||||||
address takerAddress, // Address that filled the order.
|
address takerAddress, // Address that filled the order.
|
||||||
address senderAddress, // Address that called the Exchange contract (msg.sender).
|
address senderAddress, // Address that called the Exchange contract (msg.sender).
|
||||||
bytes32 indexed orderHash // EIP712 hash of order (see LibOrder.getOrderHash).
|
bytes32 indexed orderHash // EIP712 hash of order (see LibOrder.getTypedDataHash).
|
||||||
);
|
);
|
||||||
|
|
||||||
// Cancel event is emitted whenever an individual order is cancelled.
|
// Cancel event is emitted whenever an individual order is cancelled.
|
||||||
@ -47,7 +47,7 @@ contract IExchangeCore {
|
|||||||
address indexed makerAddress, // Address that created the order.
|
address indexed makerAddress, // Address that created the order.
|
||||||
address indexed feeRecipientAddress, // Address that would have recieved fees if order was filled.
|
address indexed feeRecipientAddress, // Address that would have recieved fees if order was filled.
|
||||||
address senderAddress, // Address that called the Exchange contract (msg.sender).
|
address senderAddress, // Address that called the Exchange contract (msg.sender).
|
||||||
bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash).
|
bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getTypedDataHash).
|
||||||
bytes makerAssetData, // Encoded data specific to makerAsset.
|
bytes makerAssetData, // Encoded data specific to makerAsset.
|
||||||
bytes takerAssetData // Encoded data specific to takerAsset.
|
bytes takerAssetData // Encoded data specific to takerAsset.
|
||||||
);
|
);
|
||||||
|
@ -60,26 +60,6 @@ contract IMatchOrders {
|
|||||||
public
|
public
|
||||||
returns (LibFillResults.BatchMatchedFillResults memory batchMatchedFillResults);
|
returns (LibFillResults.BatchMatchedFillResults memory batchMatchedFillResults);
|
||||||
|
|
||||||
/// @dev Calculates fill amounts for the matched orders.
|
|
||||||
/// 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 leftOrder order goes to the taker (who matched the two orders).
|
|
||||||
/// @param leftOrder First order to match.
|
|
||||||
/// @param rightOrder Second order to match.
|
|
||||||
/// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.
|
|
||||||
/// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.
|
|
||||||
/// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
|
|
||||||
function calculateMatchedFillResults(
|
|
||||||
LibOrder.Order memory leftOrder,
|
|
||||||
LibOrder.Order memory rightOrder,
|
|
||||||
uint256 leftOrderTakerAssetFilledAmount,
|
|
||||||
uint256 rightOrderTakerAssetFilledAmount,
|
|
||||||
bool shouldMaximallyFillOrders
|
|
||||||
)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (LibFillResults.MatchedFillResults memory matchedFillResults);
|
|
||||||
|
|
||||||
/// @dev Match two complementary orders that have a profitable spread.
|
/// @dev Match two complementary orders that have a profitable spread.
|
||||||
/// Each order is filled at their respective price point. However, the calculations are
|
/// 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.
|
/// carried out as though the orders are both being filled at the right order's price point.
|
||||||
|
@ -19,14 +19,12 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
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/LibExchangeRichErrors.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
import "../interfaces/IExchangeRichErrors.sol";
|
|
||||||
import "../LibExchangeRichErrors.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract LibExchangeRichErrorDecoder is
|
contract LibExchangeRichErrorDecoder {
|
||||||
IExchangeRichErrors
|
|
||||||
{
|
|
||||||
/// @dev Decompose an ABI-encoded SignatureError.
|
/// @dev Decompose an ABI-encoded SignatureError.
|
||||||
/// @param encoded ABI-encoded revert error.
|
/// @param encoded ABI-encoded revert error.
|
||||||
/// @return errorCode The error code.
|
/// @return errorCode The error code.
|
||||||
@ -36,14 +34,14 @@ contract LibExchangeRichErrorDecoder is
|
|||||||
public
|
public
|
||||||
pure
|
pure
|
||||||
returns (
|
returns (
|
||||||
SignatureErrorCodes errorCode,
|
LibExchangeRichErrors.SignatureErrorCodes errorCode,
|
||||||
bytes32 hash,
|
bytes32 hash,
|
||||||
address signerAddress,
|
address signerAddress,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.SignatureErrorSelector());
|
_assertSelectorBytes(encoded, LibExchangeRichErrors.SignatureErrorSelector());
|
||||||
errorCode = SignatureErrorCodes(_readErrorParameterAsUint256(encoded, 0));
|
errorCode = LibExchangeRichErrors.SignatureErrorCodes(_readErrorParameterAsUint256(encoded, 0));
|
||||||
hash = _readErrorParameterAsBytes32(encoded, 1);
|
hash = _readErrorParameterAsBytes32(encoded, 1);
|
||||||
signerAddress = _readErrorParameterAsAddress(encoded, 2);
|
signerAddress = _readErrorParameterAsAddress(encoded, 2);
|
||||||
signature = _readErrorParameterAsBytes(encoded, 3);
|
signature = _readErrorParameterAsBytes(encoded, 3);
|
||||||
@ -189,12 +187,12 @@ contract LibExchangeRichErrorDecoder is
|
|||||||
public
|
public
|
||||||
pure
|
pure
|
||||||
returns (
|
returns (
|
||||||
FillErrorCodes errorCode,
|
LibExchangeRichErrors.FillErrorCodes errorCode,
|
||||||
bytes32 orderHash
|
bytes32 orderHash
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.FillErrorSelector());
|
_assertSelectorBytes(encoded, LibExchangeRichErrors.FillErrorSelector());
|
||||||
errorCode = FillErrorCodes(_readErrorParameterAsUint256(encoded, 0));
|
errorCode = LibExchangeRichErrors.FillErrorCodes(_readErrorParameterAsUint256(encoded, 0));
|
||||||
orderHash = _readErrorParameterAsBytes32(encoded, 1);
|
orderHash = _readErrorParameterAsBytes32(encoded, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,13 +237,13 @@ contract LibExchangeRichErrorDecoder is
|
|||||||
public
|
public
|
||||||
pure
|
pure
|
||||||
returns (
|
returns (
|
||||||
AssetProxyDispatchErrorCodes errorCode,
|
LibExchangeRichErrors.AssetProxyDispatchErrorCodes errorCode,
|
||||||
bytes32 orderHash,
|
bytes32 orderHash,
|
||||||
bytes memory assetData
|
bytes memory assetData
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.AssetProxyDispatchErrorSelector());
|
_assertSelectorBytes(encoded, LibExchangeRichErrors.AssetProxyDispatchErrorSelector());
|
||||||
errorCode = AssetProxyDispatchErrorCodes(_readErrorParameterAsUint256(encoded, 0));
|
errorCode = LibExchangeRichErrors.AssetProxyDispatchErrorCodes(_readErrorParameterAsUint256(encoded, 0));
|
||||||
orderHash = _readErrorParameterAsBytes32(encoded, 1);
|
orderHash = _readErrorParameterAsBytes32(encoded, 1);
|
||||||
assetData = _readErrorParameterAsBytes(encoded, 2);
|
assetData = _readErrorParameterAsBytes(encoded, 2);
|
||||||
}
|
}
|
||||||
@ -295,12 +293,12 @@ contract LibExchangeRichErrorDecoder is
|
|||||||
public
|
public
|
||||||
pure
|
pure
|
||||||
returns (
|
returns (
|
||||||
TransactionErrorCodes errorCode,
|
LibExchangeRichErrors.TransactionErrorCodes errorCode,
|
||||||
bytes32 transactionHash
|
bytes32 transactionHash
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.TransactionErrorSelector());
|
_assertSelectorBytes(encoded, LibExchangeRichErrors.TransactionErrorSelector());
|
||||||
errorCode = TransactionErrorCodes(_readErrorParameterAsUint256(encoded, 0));
|
errorCode = LibExchangeRichErrors.TransactionErrorCodes(_readErrorParameterAsUint256(encoded, 0));
|
||||||
transactionHash = _readErrorParameterAsBytes32(encoded, 1);
|
transactionHash = _readErrorParameterAsBytes32(encoded, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
pragma solidity ^0.5.5;
|
pragma solidity ^0.5.5;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
import "../src/Exchange.sol";
|
import "../src/Exchange.sol";
|
||||||
|
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ contract IsolatedExchange is
|
|||||||
/// @dev Overriden to simplify signature validation.
|
/// @dev Overriden to simplify signature validation.
|
||||||
/// Unfortunately, this is `view`, so it can't log arguments.
|
/// Unfortunately, this is `view`, so it can't log arguments.
|
||||||
function _isValidOrderWithHashSignature(
|
function _isValidOrderWithHashSignature(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
bytes32 orderHash,
|
bytes32 orderHash,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
|
@ -20,6 +20,9 @@ pragma solidity ^0.5.5;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-utils/contracts/src/LibReentrancyGuardRichErrors.sol";
|
import "@0x/contracts-utils/contracts/src/LibReentrancyGuardRichErrors.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
||||||
import "../src/Exchange.sol";
|
import "../src/Exchange.sol";
|
||||||
|
|
||||||
|
|
||||||
@ -71,27 +74,27 @@ contract ReentrancyTester is
|
|||||||
|
|
||||||
/// @dev Overriden to do nothing.
|
/// @dev Overriden to do nothing.
|
||||||
function _fillOrder(
|
function _fillOrder(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
uint256 takerAssetFillAmount,
|
uint256 takerAssetFillAmount,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
returns (FillResults memory fillResults)
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/// @dev Overriden to do nothing.
|
/// @dev Overriden to do nothing.
|
||||||
function _fillOrKillOrder(
|
function _fillOrKillOrder(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
uint256 takerAssetFillAmount,
|
uint256 takerAssetFillAmount,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
returns (FillResults memory fillResults)
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/// @dev Overridden to do nothing.
|
/// @dev Overridden to do nothing.
|
||||||
function _executeTransaction(
|
function _executeTransaction(
|
||||||
ZeroExTransaction memory transaction,
|
LibZeroExTransaction.ZeroExTransaction memory transaction,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
@ -126,7 +129,7 @@ contract ReentrancyTester is
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
/// @dev Overriden to do nothing.
|
/// @dev Overriden to do nothing.
|
||||||
function _cancelOrder(Order memory order)
|
function _cancelOrder(LibOrder.Order memory order)
|
||||||
internal
|
internal
|
||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
pragma solidity ^0.5.5;
|
pragma solidity ^0.5.5;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
||||||
import "../src/Exchange.sol";
|
import "../src/Exchange.sol";
|
||||||
|
|
||||||
|
|
||||||
@ -46,69 +48,26 @@ contract TestExchangeInternals is
|
|||||||
public
|
public
|
||||||
view
|
view
|
||||||
{
|
{
|
||||||
_assertValidMatch(leftOrder, rightOrder);
|
_assertValidMatch(
|
||||||
}
|
|
||||||
|
|
||||||
function calculateFillResults(
|
|
||||||
Order memory order,
|
|
||||||
uint256 takerAssetFilledAmount
|
|
||||||
)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (FillResults memory fillResults)
|
|
||||||
{
|
|
||||||
return _calculateFillResults(order, takerAssetFilledAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateCompleteFillBoth(
|
|
||||||
uint256 leftMakerAssetAmountRemaining,
|
|
||||||
uint256 leftTakerAssetAmountRemaining,
|
|
||||||
uint256 rightMakerAssetAmountRemaining,
|
|
||||||
uint256 rightTakerAssetAmountRemaining
|
|
||||||
)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (MatchedFillResults memory fillResults)
|
|
||||||
{
|
|
||||||
_calculateCompleteFillBoth(
|
|
||||||
fillResults,
|
|
||||||
leftMakerAssetAmountRemaining,
|
|
||||||
leftTakerAssetAmountRemaining,
|
|
||||||
rightMakerAssetAmountRemaining,
|
|
||||||
rightTakerAssetAmountRemaining
|
|
||||||
);
|
|
||||||
return fillResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateCompleteRightFill(
|
|
||||||
LibOrder.Order memory leftOrder,
|
|
||||||
uint256 rightMakerAssetAmountRemaining,
|
|
||||||
uint256 rightTakerAssetAmountRemaining
|
|
||||||
)
|
|
||||||
public
|
|
||||||
pure
|
|
||||||
returns (MatchedFillResults memory fillResults)
|
|
||||||
{
|
|
||||||
_calculateCompleteRightFill(
|
|
||||||
fillResults,
|
|
||||||
leftOrder,
|
leftOrder,
|
||||||
rightMakerAssetAmountRemaining,
|
rightOrder,
|
||||||
rightTakerAssetAmountRemaining
|
getOrderInfo(leftOrder),
|
||||||
|
getOrderInfo(rightOrder)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Call `_updateFilledState()` but first set `filled[order]` to
|
/// @dev Call `_updateFilledState()` but first set `filled[order]` to
|
||||||
/// `orderTakerAssetFilledAmount`.
|
/// `orderTakerAssetFilledAmount`.
|
||||||
function testUpdateFilledState(
|
function testUpdateFilledState(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
address takerAddress,
|
address takerAddress,
|
||||||
bytes32 orderHash,
|
bytes32 orderHash,
|
||||||
uint256 orderTakerAssetFilledAmount,
|
uint256 orderTakerAssetFilledAmount,
|
||||||
FillResults memory fillResults
|
LibFillResults.FillResults memory fillResults
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
filled[getOrderHash(order)] = orderTakerAssetFilledAmount;
|
filled[LibOrder.getTypedDataHash(order, EIP712_EXCHANGE_DOMAIN_HASH)] = orderTakerAssetFilledAmount;
|
||||||
_updateFilledState(
|
_updateFilledState(
|
||||||
order,
|
order,
|
||||||
takerAddress,
|
takerAddress,
|
||||||
|
@ -20,14 +20,12 @@ pragma solidity ^0.5.5;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
|
||||||
import "../src/MixinSignatureValidator.sol";
|
import "../src/MixinSignatureValidator.sol";
|
||||||
import "../src/MixinTransactions.sol";
|
import "../src/MixinTransactions.sol";
|
||||||
|
|
||||||
|
|
||||||
contract TestSignatureValidator is
|
contract TestSignatureValidator is
|
||||||
LibEIP712ExchangeDomain,
|
LibEIP712ExchangeDomain,
|
||||||
LibOrder,
|
|
||||||
MixinSignatureValidator
|
MixinSignatureValidator
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -21,24 +21,12 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
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/LibZeroExTransaction.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
|
||||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||||
import "@0x/contracts-utils/contracts/src/LibEIP1271.sol";
|
import "@0x/contracts-utils/contracts/src/LibEIP1271.sol";
|
||||||
|
|
||||||
|
|
||||||
interface ISimplifiedExchange {
|
|
||||||
function getOrderHash(LibOrder.Order calldata order)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (bytes32 orderHash);
|
|
||||||
|
|
||||||
function getTransactionHash(LibZeroExTransaction.ZeroExTransaction calldata transaction)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (bytes32 transactionHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// solhint-disable no-unused-vars
|
// solhint-disable no-unused-vars
|
||||||
contract TestValidatorWallet is
|
contract TestValidatorWallet is
|
||||||
LibEIP1271
|
LibEIP1271
|
||||||
@ -80,8 +68,8 @@ contract TestValidatorWallet is
|
|||||||
NTypes
|
NTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev The Exchange contract.
|
/// @dev The Exchange domain hash..
|
||||||
ISimplifiedExchange internal _exchange;
|
LibEIP712ExchangeDomain internal _exchange;
|
||||||
/// @dev Internal state to modify.
|
/// @dev Internal state to modify.
|
||||||
uint256 internal _state = 1;
|
uint256 internal _state = 1;
|
||||||
/// @dev What action to execute when a hash is validated .
|
/// @dev What action to execute when a hash is validated .
|
||||||
@ -92,7 +80,7 @@ contract TestValidatorWallet is
|
|||||||
mapping (bytes32 => bytes32) internal _hashSignatureHashes;
|
mapping (bytes32 => bytes32) internal _hashSignatureHashes;
|
||||||
|
|
||||||
constructor(address exchange) public {
|
constructor(address exchange) public {
|
||||||
_exchange = ISimplifiedExchange(exchange);
|
_exchange = LibEIP712ExchangeDomain(exchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Approves an ERC20 token to spend tokens from this address.
|
/// @dev Approves an ERC20 token to spend tokens from this address.
|
||||||
@ -239,7 +227,7 @@ contract TestValidatorWallet is
|
|||||||
// Use the Exchange to calculate the hash of the order and assert
|
// Use the Exchange to calculate the hash of the order and assert
|
||||||
// that it matches the one we extracted previously.
|
// that it matches the one we extracted previously.
|
||||||
require(
|
require(
|
||||||
_exchange.getOrderHash(order) == hash,
|
LibOrder.getTypedDataHash(order, _exchange.EIP712_EXCHANGE_DOMAIN_HASH()) == hash,
|
||||||
"UNEXPECTED_ORDER_HASH"
|
"UNEXPECTED_ORDER_HASH"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -250,7 +238,7 @@ contract TestValidatorWallet is
|
|||||||
// Use the Exchange to calculate the hash of the transaction and assert
|
// Use the Exchange to calculate the hash of the transaction and assert
|
||||||
// that it matches the one we extracted previously.
|
// that it matches the one we extracted previously.
|
||||||
require(
|
require(
|
||||||
_exchange.getTransactionHash(transaction) == hash,
|
LibZeroExTransaction.getTypedDataHash(transaction, _exchange.EIP712_EXCHANGE_DOMAIN_HASH()) == hash,
|
||||||
"UNEXPECTED_TRANSACTION_HASH"
|
"UNEXPECTED_TRANSACTION_HASH"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
pragma solidity ^0.5.5;
|
pragma solidity ^0.5.5;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
|
||||||
import "../src/Exchange.sol";
|
import "../src/Exchange.sol";
|
||||||
|
|
||||||
|
|
||||||
@ -28,19 +30,19 @@ import "../src/Exchange.sol";
|
|||||||
contract TestWrapperFunctions is
|
contract TestWrapperFunctions is
|
||||||
Exchange
|
Exchange
|
||||||
{
|
{
|
||||||
uint8 internal constant MAX_ORDER_STATUS = uint8(OrderStatus.CANCELLED);
|
uint8 internal constant MAX_ORDER_STATUS = uint8(LibOrder.OrderStatus.CANCELLED);
|
||||||
uint256 internal constant ALWAYS_FAILING_SALT = uint256(-1);
|
uint256 internal constant ALWAYS_FAILING_SALT = uint256(-1);
|
||||||
string internal constant ALWAYS_FAILING_SALT_REVERT_REASON = "ALWAYS_FAILING_SALT";
|
string internal constant ALWAYS_FAILING_SALT_REVERT_REASON = "ALWAYS_FAILING_SALT";
|
||||||
|
|
||||||
// solhint-disable no-unused-vars
|
// solhint-disable no-unused-vars
|
||||||
event FillOrderCalled(
|
event FillOrderCalled(
|
||||||
Order order,
|
LibOrder.Order order,
|
||||||
uint256 takerAssetFillAmount,
|
uint256 takerAssetFillAmount,
|
||||||
bytes signature
|
bytes signature
|
||||||
);
|
);
|
||||||
|
|
||||||
event CancelOrderCalled(
|
event CancelOrderCalled(
|
||||||
Order order
|
LibOrder.Order order
|
||||||
);
|
);
|
||||||
|
|
||||||
// solhint-disable no-empty-blocks
|
// solhint-disable no-empty-blocks
|
||||||
@ -51,26 +53,26 @@ contract TestWrapperFunctions is
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
/// @dev Overridden to be deterministic and simplified.
|
/// @dev Overridden to be deterministic and simplified.
|
||||||
function getOrderInfo(Order memory order)
|
function getOrderInfo(LibOrder.Order memory order)
|
||||||
public
|
public
|
||||||
view
|
view
|
||||||
returns (OrderInfo memory orderInfo)
|
returns (LibOrder.OrderInfo memory orderInfo)
|
||||||
{
|
{
|
||||||
// Lower uint128 of `order.salt` is the `orderTakerAssetFilledAmount`.
|
// Lower uint128 of `order.salt` is the `orderTakerAssetFilledAmount`.
|
||||||
orderInfo.orderTakerAssetFilledAmount = uint128(order.salt);
|
orderInfo.orderTakerAssetFilledAmount = uint128(order.salt);
|
||||||
// High byte of `order.salt` is the `orderStatus`.
|
// High byte of `order.salt` is the `orderStatus`.
|
||||||
orderInfo.orderStatus = uint8(order.salt >> 248) % (MAX_ORDER_STATUS + 1);
|
orderInfo.orderStatus = uint8(order.salt >> 248) % (MAX_ORDER_STATUS + 1);
|
||||||
orderInfo.orderHash = _getOrderHash(order);
|
orderInfo.orderHash = _getTypedDataHash(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Overridden to log arguments, be deterministic, and revert with certain inputs.
|
/// @dev Overridden to log arguments, be deterministic, and revert with certain inputs.
|
||||||
function _fillOrder(
|
function _fillOrder(
|
||||||
Order memory order,
|
LibOrder.Order memory order,
|
||||||
uint256 takerAssetFillAmount,
|
uint256 takerAssetFillAmount,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
returns (FillResults memory fillResults)
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
emit FillOrderCalled(
|
emit FillOrderCalled(
|
||||||
order,
|
order,
|
||||||
@ -94,7 +96,7 @@ contract TestWrapperFunctions is
|
|||||||
|
|
||||||
/// @dev Overridden to only log arguments and revert with certain inputs.
|
/// @dev Overridden to only log arguments and revert with certain inputs.
|
||||||
function _cancelOrder(
|
function _cancelOrder(
|
||||||
Order memory order
|
LibOrder.Order memory order
|
||||||
)
|
)
|
||||||
internal
|
internal
|
||||||
{
|
{
|
||||||
@ -109,7 +111,7 @@ contract TestWrapperFunctions is
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Simplified order hashing.
|
/// @dev Simplified order hashing.
|
||||||
function _getOrderHash(Order memory order)
|
function _getTypedDataHash(LibOrder.Order memory order)
|
||||||
internal
|
internal
|
||||||
pure
|
pure
|
||||||
returns (bytes32 hash)
|
returns (bytes32 hash)
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
@ -34,8 +34,8 @@
|
|||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
"abis": "./generated-artifacts/@(Exchange|ExchangeWrapper|IAssetProxy|IAssetProxyDispatcher|IEIP1271Wallet|IExchange|IExchangeCore|IExchangeRichErrors|IMatchOrders|ISignatureValidator|ITransactions|ITransferSimulator|IWallet|IWrapperFunctions|IsolatedExchange|LibExchangeRichErrorDecoder|MixinAssetProxyDispatcher|MixinExchangeCore|MixinMatchOrders|MixinSignatureValidator|MixinTransactions|MixinTransferSimulator|MixinWrapperFunctions|ReentrancyTester|TestAssetProxyDispatcher|TestExchangeInternals|TestLibExchangeRichErrorDecoder|TestSignatureValidator|TestValidatorWallet|TestWrapperFunctions|Whitelist).json",
|
||||||
"abis": "./generated-artifacts/@(Exchange|ExchangeWrapper|IAssetProxyDispatcher|IEIP1271Wallet|IExchange|IExchangeCore|IMatchOrders|ISignatureValidator|ITransactions|IWallet|IWrapperFunctions|IsolatedExchange|ReentrancyTester|TestAssetProxyDispatcher|TestExchangeInternals|TestLibExchangeRichErrorDecoder|TestSignatureValidator|TestValidatorWallet|TestWrapperFunctions|Whitelist).json"
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -7,16 +7,27 @@ import { ContractArtifact } from 'ethereum-types';
|
|||||||
|
|
||||||
import * as Exchange from '../generated-artifacts/Exchange.json';
|
import * as Exchange from '../generated-artifacts/Exchange.json';
|
||||||
import * as ExchangeWrapper from '../generated-artifacts/ExchangeWrapper.json';
|
import * as ExchangeWrapper from '../generated-artifacts/ExchangeWrapper.json';
|
||||||
|
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
||||||
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
|
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
|
||||||
import * as IEIP1271Wallet from '../generated-artifacts/IEIP1271Wallet.json';
|
import * as IEIP1271Wallet from '../generated-artifacts/IEIP1271Wallet.json';
|
||||||
import * as IExchange from '../generated-artifacts/IExchange.json';
|
import * as IExchange from '../generated-artifacts/IExchange.json';
|
||||||
import * as IExchangeCore from '../generated-artifacts/IExchangeCore.json';
|
import * as IExchangeCore from '../generated-artifacts/IExchangeCore.json';
|
||||||
|
import * as IExchangeRichErrors from '../generated-artifacts/IExchangeRichErrors.json';
|
||||||
import * as IMatchOrders from '../generated-artifacts/IMatchOrders.json';
|
import * as IMatchOrders from '../generated-artifacts/IMatchOrders.json';
|
||||||
import * as ISignatureValidator from '../generated-artifacts/ISignatureValidator.json';
|
import * as ISignatureValidator from '../generated-artifacts/ISignatureValidator.json';
|
||||||
import * as ITransactions from '../generated-artifacts/ITransactions.json';
|
import * as ITransactions from '../generated-artifacts/ITransactions.json';
|
||||||
|
import * as ITransferSimulator from '../generated-artifacts/ITransferSimulator.json';
|
||||||
import * as IWallet from '../generated-artifacts/IWallet.json';
|
import * as IWallet from '../generated-artifacts/IWallet.json';
|
||||||
import * as IWrapperFunctions from '../generated-artifacts/IWrapperFunctions.json';
|
import * as IWrapperFunctions from '../generated-artifacts/IWrapperFunctions.json';
|
||||||
import * as IsolatedExchange from '../generated-artifacts/IsolatedExchange.json';
|
import * as IsolatedExchange from '../generated-artifacts/IsolatedExchange.json';
|
||||||
|
import * as LibExchangeRichErrorDecoder from '../generated-artifacts/LibExchangeRichErrorDecoder.json';
|
||||||
|
import * as MixinAssetProxyDispatcher from '../generated-artifacts/MixinAssetProxyDispatcher.json';
|
||||||
|
import * as MixinExchangeCore from '../generated-artifacts/MixinExchangeCore.json';
|
||||||
|
import * as MixinMatchOrders from '../generated-artifacts/MixinMatchOrders.json';
|
||||||
|
import * as MixinSignatureValidator from '../generated-artifacts/MixinSignatureValidator.json';
|
||||||
|
import * as MixinTransactions from '../generated-artifacts/MixinTransactions.json';
|
||||||
|
import * as MixinTransferSimulator from '../generated-artifacts/MixinTransferSimulator.json';
|
||||||
|
import * as MixinWrapperFunctions from '../generated-artifacts/MixinWrapperFunctions.json';
|
||||||
import * as ReentrancyTester from '../generated-artifacts/ReentrancyTester.json';
|
import * as ReentrancyTester from '../generated-artifacts/ReentrancyTester.json';
|
||||||
import * as TestAssetProxyDispatcher from '../generated-artifacts/TestAssetProxyDispatcher.json';
|
import * as TestAssetProxyDispatcher from '../generated-artifacts/TestAssetProxyDispatcher.json';
|
||||||
import * as TestExchangeInternals from '../generated-artifacts/TestExchangeInternals.json';
|
import * as TestExchangeInternals from '../generated-artifacts/TestExchangeInternals.json';
|
||||||
@ -29,15 +40,26 @@ export const artifacts = {
|
|||||||
ExchangeWrapper: ExchangeWrapper as ContractArtifact,
|
ExchangeWrapper: ExchangeWrapper as ContractArtifact,
|
||||||
Whitelist: Whitelist as ContractArtifact,
|
Whitelist: Whitelist as ContractArtifact,
|
||||||
Exchange: Exchange as ContractArtifact,
|
Exchange: Exchange as ContractArtifact,
|
||||||
|
MixinAssetProxyDispatcher: MixinAssetProxyDispatcher as ContractArtifact,
|
||||||
|
MixinExchangeCore: MixinExchangeCore as ContractArtifact,
|
||||||
|
MixinMatchOrders: MixinMatchOrders as ContractArtifact,
|
||||||
|
MixinSignatureValidator: MixinSignatureValidator as ContractArtifact,
|
||||||
|
MixinTransactions: MixinTransactions as ContractArtifact,
|
||||||
|
MixinTransferSimulator: MixinTransferSimulator as ContractArtifact,
|
||||||
|
MixinWrapperFunctions: MixinWrapperFunctions as ContractArtifact,
|
||||||
|
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||||
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
|
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
|
||||||
IEIP1271Wallet: IEIP1271Wallet as ContractArtifact,
|
IEIP1271Wallet: IEIP1271Wallet as ContractArtifact,
|
||||||
IExchange: IExchange as ContractArtifact,
|
IExchange: IExchange as ContractArtifact,
|
||||||
IExchangeCore: IExchangeCore as ContractArtifact,
|
IExchangeCore: IExchangeCore as ContractArtifact,
|
||||||
|
IExchangeRichErrors: IExchangeRichErrors as ContractArtifact,
|
||||||
IMatchOrders: IMatchOrders as ContractArtifact,
|
IMatchOrders: IMatchOrders as ContractArtifact,
|
||||||
ISignatureValidator: ISignatureValidator as ContractArtifact,
|
ISignatureValidator: ISignatureValidator as ContractArtifact,
|
||||||
ITransactions: ITransactions as ContractArtifact,
|
ITransactions: ITransactions as ContractArtifact,
|
||||||
|
ITransferSimulator: ITransferSimulator as ContractArtifact,
|
||||||
IWallet: IWallet as ContractArtifact,
|
IWallet: IWallet as ContractArtifact,
|
||||||
IWrapperFunctions: IWrapperFunctions as ContractArtifact,
|
IWrapperFunctions: IWrapperFunctions as ContractArtifact,
|
||||||
|
LibExchangeRichErrorDecoder: LibExchangeRichErrorDecoder as ContractArtifact,
|
||||||
IsolatedExchange: IsolatedExchange as ContractArtifact,
|
IsolatedExchange: IsolatedExchange as ContractArtifact,
|
||||||
ReentrancyTester: ReentrancyTester as ContractArtifact,
|
ReentrancyTester: ReentrancyTester as ContractArtifact,
|
||||||
TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact,
|
TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact,
|
||||||
|
@ -5,16 +5,27 @@
|
|||||||
*/
|
*/
|
||||||
export * from '../generated-wrappers/exchange';
|
export * from '../generated-wrappers/exchange';
|
||||||
export * from '../generated-wrappers/exchange_wrapper';
|
export * from '../generated-wrappers/exchange_wrapper';
|
||||||
|
export * from '../generated-wrappers/i_asset_proxy';
|
||||||
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
|
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
|
||||||
export * from '../generated-wrappers/i_e_i_p1271_wallet';
|
export * from '../generated-wrappers/i_e_i_p1271_wallet';
|
||||||
export * from '../generated-wrappers/i_exchange';
|
export * from '../generated-wrappers/i_exchange';
|
||||||
export * from '../generated-wrappers/i_exchange_core';
|
export * from '../generated-wrappers/i_exchange_core';
|
||||||
|
export * from '../generated-wrappers/i_exchange_rich_errors';
|
||||||
export * from '../generated-wrappers/i_match_orders';
|
export * from '../generated-wrappers/i_match_orders';
|
||||||
export * from '../generated-wrappers/i_signature_validator';
|
export * from '../generated-wrappers/i_signature_validator';
|
||||||
export * from '../generated-wrappers/i_transactions';
|
export * from '../generated-wrappers/i_transactions';
|
||||||
|
export * from '../generated-wrappers/i_transfer_simulator';
|
||||||
export * from '../generated-wrappers/i_wallet';
|
export * from '../generated-wrappers/i_wallet';
|
||||||
export * from '../generated-wrappers/i_wrapper_functions';
|
export * from '../generated-wrappers/i_wrapper_functions';
|
||||||
export * from '../generated-wrappers/isolated_exchange';
|
export * from '../generated-wrappers/isolated_exchange';
|
||||||
|
export * from '../generated-wrappers/lib_exchange_rich_error_decoder';
|
||||||
|
export * from '../generated-wrappers/mixin_asset_proxy_dispatcher';
|
||||||
|
export * from '../generated-wrappers/mixin_exchange_core';
|
||||||
|
export * from '../generated-wrappers/mixin_match_orders';
|
||||||
|
export * from '../generated-wrappers/mixin_signature_validator';
|
||||||
|
export * from '../generated-wrappers/mixin_transactions';
|
||||||
|
export * from '../generated-wrappers/mixin_transfer_simulator';
|
||||||
|
export * from '../generated-wrappers/mixin_wrapper_functions';
|
||||||
export * from '../generated-wrappers/reentrancy_tester';
|
export * from '../generated-wrappers/reentrancy_tester';
|
||||||
export * from '../generated-wrappers/test_asset_proxy_dispatcher';
|
export * from '../generated-wrappers/test_asset_proxy_dispatcher';
|
||||||
export * from '../generated-wrappers/test_exchange_internals';
|
export * from '../generated-wrappers/test_exchange_internals';
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -5,16 +5,27 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"generated-artifacts/Exchange.json",
|
"generated-artifacts/Exchange.json",
|
||||||
"generated-artifacts/ExchangeWrapper.json",
|
"generated-artifacts/ExchangeWrapper.json",
|
||||||
|
"generated-artifacts/IAssetProxy.json",
|
||||||
"generated-artifacts/IAssetProxyDispatcher.json",
|
"generated-artifacts/IAssetProxyDispatcher.json",
|
||||||
"generated-artifacts/IEIP1271Wallet.json",
|
"generated-artifacts/IEIP1271Wallet.json",
|
||||||
"generated-artifacts/IExchange.json",
|
"generated-artifacts/IExchange.json",
|
||||||
"generated-artifacts/IExchangeCore.json",
|
"generated-artifacts/IExchangeCore.json",
|
||||||
|
"generated-artifacts/IExchangeRichErrors.json",
|
||||||
"generated-artifacts/IMatchOrders.json",
|
"generated-artifacts/IMatchOrders.json",
|
||||||
"generated-artifacts/ISignatureValidator.json",
|
"generated-artifacts/ISignatureValidator.json",
|
||||||
"generated-artifacts/ITransactions.json",
|
"generated-artifacts/ITransactions.json",
|
||||||
|
"generated-artifacts/ITransferSimulator.json",
|
||||||
"generated-artifacts/IWallet.json",
|
"generated-artifacts/IWallet.json",
|
||||||
"generated-artifacts/IWrapperFunctions.json",
|
"generated-artifacts/IWrapperFunctions.json",
|
||||||
"generated-artifacts/IsolatedExchange.json",
|
"generated-artifacts/IsolatedExchange.json",
|
||||||
|
"generated-artifacts/LibExchangeRichErrorDecoder.json",
|
||||||
|
"generated-artifacts/MixinAssetProxyDispatcher.json",
|
||||||
|
"generated-artifacts/MixinExchangeCore.json",
|
||||||
|
"generated-artifacts/MixinMatchOrders.json",
|
||||||
|
"generated-artifacts/MixinSignatureValidator.json",
|
||||||
|
"generated-artifacts/MixinTransactions.json",
|
||||||
|
"generated-artifacts/MixinTransferSimulator.json",
|
||||||
|
"generated-artifacts/MixinWrapperFunctions.json",
|
||||||
"generated-artifacts/ReentrancyTester.json",
|
"generated-artifacts/ReentrancyTester.json",
|
||||||
"generated-artifacts/TestAssetProxyDispatcher.json",
|
"generated-artifacts/TestAssetProxyDispatcher.json",
|
||||||
"generated-artifacts/TestExchangeInternals.json",
|
"generated-artifacts/TestExchangeInternals.json",
|
||||||
|
@ -22,13 +22,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": [
|
|
||||||
"@0x/contracts-erc20/contracts/src/WETH9.sol",
|
|
||||||
"@0x/contracts-exchange/contracts/examples/ExchangeWrapper.sol",
|
|
||||||
"@0x/contracts-exchange/contracts/src/Exchange.sol",
|
|
||||||
"src/BalanceThresholdFilter/BalanceThresholdFilter.sol",
|
|
||||||
"src/DutchAuction/DutchAuction.sol",
|
|
||||||
"src/OrderMatcher/OrderMatcher.sol"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -19,16 +19,15 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
|
|
||||||
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
|
||||||
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
import "./MixinExchangeCalldata.sol";
|
import "./MixinExchangeCalldata.sol";
|
||||||
import "./interfaces/IBalanceThresholdFilterCore.sol";
|
import "./interfaces/IBalanceThresholdFilterCore.sol";
|
||||||
|
|
||||||
|
|
||||||
contract MixinBalanceThresholdFilterCore is
|
contract MixinBalanceThresholdFilterCore is
|
||||||
IBalanceThresholdFilterCore,
|
IBalanceThresholdFilterCore,
|
||||||
MixinExchangeCalldata,
|
MixinExchangeCalldata
|
||||||
LibExchangeSelectors
|
|
||||||
{
|
{
|
||||||
|
|
||||||
/// @dev Executes an Exchange transaction iff the maker and taker meet
|
/// @dev Executes an Exchange transaction iff the maker and taker meet
|
||||||
@ -100,34 +99,34 @@ contract MixinBalanceThresholdFilterCore is
|
|||||||
bytes4 exchangeFunctionSelector = bytes4(_exchangeCalldataload(0));
|
bytes4 exchangeFunctionSelector = bytes4(_exchangeCalldataload(0));
|
||||||
// solhint-disable expression-indent
|
// solhint-disable expression-indent
|
||||||
if (
|
if (
|
||||||
exchangeFunctionSelector == BATCH_FILL_ORDERS_SELECTOR ||
|
exchangeFunctionSelector == IExchange(address(0)).batchFillOrders.selector ||
|
||||||
exchangeFunctionSelector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR ||
|
exchangeFunctionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector ||
|
||||||
exchangeFunctionSelector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR ||
|
exchangeFunctionSelector == IExchange(address(0)).batchFillOrKillOrders.selector ||
|
||||||
exchangeFunctionSelector == MARKET_BUY_ORDERS_SELECTOR ||
|
exchangeFunctionSelector == IExchange(address(0)).marketBuyOrders.selector ||
|
||||||
exchangeFunctionSelector == MARKET_BUY_ORDERS_NO_THROW_SELECTOR ||
|
exchangeFunctionSelector == IExchange(address(0)).marketBuyOrdersNoThrow.selector ||
|
||||||
exchangeFunctionSelector == MARKET_SELL_ORDERS_SELECTOR ||
|
exchangeFunctionSelector == IExchange(address(0)).marketSellOrders.selector ||
|
||||||
exchangeFunctionSelector == MARKET_SELL_ORDERS_NO_THROW_SELECTOR
|
exchangeFunctionSelector == IExchange(address(0)).marketSellOrdersNoThrow.selector
|
||||||
) {
|
) {
|
||||||
addressesToValidate = _loadMakerAddressesFromOrderArray(0);
|
addressesToValidate = _loadMakerAddressesFromOrderArray(0);
|
||||||
addressesToValidate = addressesToValidate.append(signerAddress);
|
addressesToValidate = addressesToValidate.append(signerAddress);
|
||||||
} else if (
|
} else if (
|
||||||
exchangeFunctionSelector == FILL_ORDER_SELECTOR ||
|
exchangeFunctionSelector == IExchange(address(0)).fillOrder.selector ||
|
||||||
exchangeFunctionSelector == FILL_ORDER_NO_THROW_SELECTOR ||
|
exchangeFunctionSelector == IExchange(address(0)).fillOrderNoThrow.selector ||
|
||||||
exchangeFunctionSelector == FILL_OR_KILL_ORDER_SELECTOR
|
exchangeFunctionSelector == IExchange(address(0)).fillOrKillOrder.selector
|
||||||
) {
|
) {
|
||||||
address makerAddress = _loadMakerAddressFromOrder(0);
|
address makerAddress = _loadMakerAddressFromOrder(0);
|
||||||
addressesToValidate = addressesToValidate.append(makerAddress);
|
addressesToValidate = addressesToValidate.append(makerAddress);
|
||||||
addressesToValidate = addressesToValidate.append(signerAddress);
|
addressesToValidate = addressesToValidate.append(signerAddress);
|
||||||
} else if (exchangeFunctionSelector == MATCH_ORDERS_SELECTOR) {
|
} else if (exchangeFunctionSelector == IExchange(address(0)).matchOrders.selector) {
|
||||||
address leftMakerAddress = _loadMakerAddressFromOrder(0);
|
address leftMakerAddress = _loadMakerAddressFromOrder(0);
|
||||||
addressesToValidate = addressesToValidate.append(leftMakerAddress);
|
addressesToValidate = addressesToValidate.append(leftMakerAddress);
|
||||||
address rightMakerAddress = _loadMakerAddressFromOrder(1);
|
address rightMakerAddress = _loadMakerAddressFromOrder(1);
|
||||||
addressesToValidate = addressesToValidate.append(rightMakerAddress);
|
addressesToValidate = addressesToValidate.append(rightMakerAddress);
|
||||||
addressesToValidate = addressesToValidate.append(signerAddress);
|
addressesToValidate = addressesToValidate.append(signerAddress);
|
||||||
} else if (
|
} else if (
|
||||||
exchangeFunctionSelector != CANCEL_ORDER_SELECTOR &&
|
exchangeFunctionSelector != IExchange(address(0)).cancelOrder.selector &&
|
||||||
exchangeFunctionSelector != BATCH_CANCEL_ORDERS_SELECTOR &&
|
exchangeFunctionSelector != IExchange(address(0)).batchCancelOrders.selector &&
|
||||||
exchangeFunctionSelector != CANCEL_ORDERS_UP_TO_SELECTOR
|
exchangeFunctionSelector != IExchange(address(0)).cancelOrdersUpTo.selector
|
||||||
) {
|
) {
|
||||||
revert("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR");
|
revert("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR");
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
|
@ -22,12 +22,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": [
|
|
||||||
"src/AssetProxyOwner.sol",
|
|
||||||
"src/MultiSigWallet.sol",
|
|
||||||
"src/MultiSigWalletWithTimeLock.sol",
|
|
||||||
"test/TestAssetProxyOwner.sol",
|
|
||||||
"test/TestRejectEther.sol"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
|
@ -21,6 +21,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": ["src/IStaking.sol", "src/Staking.sol"]
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn pre_build && tsc -b",
|
"build": "yarn pre_build && tsc -b",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"pre_build": "run-s compile generate_contract_wrappers",
|
"pre_build": "run-s compile contracts:gen generate_contract_wrappers",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"rebuild_and_test": "run-s build test",
|
"rebuild_and_test": "run-s build test",
|
||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
|
@ -66,4 +66,6 @@ export const constants = {
|
|||||||
KECCAK256_NULL: ethUtil.addHexPrefix(ethUtil.bufferToHex(ethUtil.SHA3_NULL)),
|
KECCAK256_NULL: ethUtil.addHexPrefix(ethUtil.bufferToHex(ethUtil.SHA3_NULL)),
|
||||||
MAX_UINT256_ROOT: new BigNumber('340282366920938463463374607431768211456'),
|
MAX_UINT256_ROOT: new BigNumber('340282366920938463463374607431768211456'),
|
||||||
ONE_ETHER: new BigNumber(1e18),
|
ONE_ETHER: new BigNumber(1e18),
|
||||||
|
EIP712_DOMAIN_NAME: '0x Protocol',
|
||||||
|
EIP712_DOMAIN_VERSION: '3.0.0',
|
||||||
};
|
};
|
||||||
|
@ -41,6 +41,18 @@
|
|||||||
{
|
{
|
||||||
"note": "Throw a `SafeMathError` in `SafeMath._safeDiv()` when denominator is zero.",
|
"note": "Throw a `SafeMathError` in `SafeMath._safeDiv()` when denominator is zero.",
|
||||||
"pr": 2031
|
"pr": 2031
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Create `LibSafeMath`",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Rename `_rrevert` to `rrevert` in `LibRichErrors` contract",
|
||||||
|
"pr": 2055
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Compile and export all contracts, artifacts, and wrappers by default",
|
||||||
|
"pr": 2055
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -22,26 +22,5 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"contracts": [
|
|
||||||
"src/Authorizable.sol",
|
|
||||||
"src/LibAddress.sol",
|
|
||||||
"src/LibBytes.sol",
|
|
||||||
"src/LibEIP1271.sol",
|
|
||||||
"src/LibEIP712.sol",
|
|
||||||
"src/Ownable.sol",
|
|
||||||
"src/ReentrancyGuard.sol",
|
|
||||||
"src/SafeMath.sol",
|
|
||||||
"src/interfaces/IAuthorizable.sol",
|
|
||||||
"src/interfaces/IOwnable.sol",
|
|
||||||
"test/TestConstants.sol",
|
|
||||||
"test/TestLibAddress.sol",
|
|
||||||
"test/TestLibAddressArray.sol",
|
|
||||||
"test/TestLibBytes.sol",
|
|
||||||
"test/TestLibEIP712.sol",
|
|
||||||
"test/TestLibRichErrors.sol",
|
|
||||||
"test/TestOwnable.sol",
|
|
||||||
"test/TestReentrancyGuard.sol",
|
|
||||||
"test/TestSafeMath.sol"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ contract Authorizable is
|
|||||||
/// @dev Only authorized addresses can invoke functions with this modifier.
|
/// @dev Only authorized addresses can invoke functions with this modifier.
|
||||||
modifier onlyAuthorized {
|
modifier onlyAuthorized {
|
||||||
if (!authorized[msg.sender]) {
|
if (!authorized[msg.sender]) {
|
||||||
LibRichErrors._rrevert(LibAuthorizableRichErrors.SenderNotAuthorizedError(msg.sender));
|
LibRichErrors.rrevert(LibAuthorizableRichErrors.SenderNotAuthorizedError(msg.sender));
|
||||||
}
|
}
|
||||||
_;
|
_;
|
||||||
}
|
}
|
||||||
@ -47,12 +47,12 @@ contract Authorizable is
|
|||||||
{
|
{
|
||||||
// Ensure that the target is not the zero address.
|
// Ensure that the target is not the zero address.
|
||||||
if (target == address(0)) {
|
if (target == address(0)) {
|
||||||
LibRichErrors._rrevert(LibAuthorizableRichErrors.ZeroCantBeAuthorizedError());
|
LibRichErrors.rrevert(LibAuthorizableRichErrors.ZeroCantBeAuthorizedError());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the target is not already authorized.
|
// Ensure that the target is not already authorized.
|
||||||
if (authorized[target]) {
|
if (authorized[target]) {
|
||||||
LibRichErrors._rrevert(LibAuthorizableRichErrors.TargetAlreadyAuthorizedError(target));
|
LibRichErrors.rrevert(LibAuthorizableRichErrors.TargetAlreadyAuthorizedError(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
authorized[target] = true;
|
authorized[target] = true;
|
||||||
@ -67,7 +67,7 @@ contract Authorizable is
|
|||||||
onlyOwner
|
onlyOwner
|
||||||
{
|
{
|
||||||
if (!authorized[target]) {
|
if (!authorized[target]) {
|
||||||
LibRichErrors._rrevert(LibAuthorizableRichErrors.TargetNotAuthorizedError(target));
|
LibRichErrors.rrevert(LibAuthorizableRichErrors.TargetNotAuthorizedError(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
delete authorized[target];
|
delete authorized[target];
|
||||||
@ -92,16 +92,16 @@ contract Authorizable is
|
|||||||
onlyOwner
|
onlyOwner
|
||||||
{
|
{
|
||||||
if (!authorized[target]) {
|
if (!authorized[target]) {
|
||||||
LibRichErrors._rrevert(LibAuthorizableRichErrors.TargetNotAuthorizedError(target));
|
LibRichErrors.rrevert(LibAuthorizableRichErrors.TargetNotAuthorizedError(target));
|
||||||
}
|
}
|
||||||
if (index >= authorities.length) {
|
if (index >= authorities.length) {
|
||||||
LibRichErrors._rrevert(LibAuthorizableRichErrors.IndexOutOfBoundsError(
|
LibRichErrors.rrevert(LibAuthorizableRichErrors.IndexOutOfBoundsError(
|
||||||
index,
|
index,
|
||||||
authorities.length
|
authorities.length
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if (authorities[index] != target) {
|
if (authorities[index] != target) {
|
||||||
LibRichErrors._rrevert(LibAuthorizableRichErrors.AuthorizedAddressMismatchError(
|
LibRichErrors.rrevert(LibAuthorizableRichErrors.AuthorizedAddressMismatchError(
|
||||||
authorities[index],
|
authorities[index],
|
||||||
target
|
target
|
||||||
));
|
));
|
||||||
|
@ -54,7 +54,7 @@ library LibAddressArray {
|
|||||||
// `freeMemPtr` > `addressArrayEndPtr`: Some value occupies memory after `addressArray`
|
// `freeMemPtr` > `addressArrayEndPtr`: Some value occupies memory after `addressArray`
|
||||||
// `freeMemPtr` < `addressArrayEndPtr`: Memory has not been managed properly.
|
// `freeMemPtr` < `addressArrayEndPtr`: Memory has not been managed properly.
|
||||||
if (freeMemPtr < addressArrayEndPtr) {
|
if (freeMemPtr < addressArrayEndPtr) {
|
||||||
LibRichErrors._rrevert(LibAddressArrayRichErrors.MismanagedMemoryError(
|
LibRichErrors.rrevert(LibAddressArrayRichErrors.MismanagedMemoryError(
|
||||||
freeMemPtr,
|
freeMemPtr,
|
||||||
addressArrayEndPtr
|
addressArrayEndPtr
|
||||||
));
|
));
|
||||||
|
@ -180,14 +180,14 @@ library LibBytes {
|
|||||||
// Ensure that the from and to positions are valid positions for a slice within
|
// Ensure that the from and to positions are valid positions for a slice within
|
||||||
// the byte array that is being used.
|
// the byte array that is being used.
|
||||||
if (from > to) {
|
if (from > to) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
|
||||||
from,
|
from,
|
||||||
to
|
to
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if (to > b.length) {
|
if (to > b.length) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
|
||||||
to,
|
to,
|
||||||
b.length
|
b.length
|
||||||
@ -222,14 +222,14 @@ library LibBytes {
|
|||||||
// Ensure that the from and to positions are valid positions for a slice within
|
// Ensure that the from and to positions are valid positions for a slice within
|
||||||
// the byte array that is being used.
|
// the byte array that is being used.
|
||||||
if (from > to) {
|
if (from > to) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
|
||||||
from,
|
from,
|
||||||
to
|
to
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if (to > b.length) {
|
if (to > b.length) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
|
||||||
to,
|
to,
|
||||||
b.length
|
b.length
|
||||||
@ -253,7 +253,7 @@ library LibBytes {
|
|||||||
returns (bytes1 result)
|
returns (bytes1 result)
|
||||||
{
|
{
|
||||||
if (b.length == 0) {
|
if (b.length == 0) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired,
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired,
|
||||||
b.length,
|
b.length,
|
||||||
0
|
0
|
||||||
@ -280,7 +280,7 @@ library LibBytes {
|
|||||||
returns (address result)
|
returns (address result)
|
||||||
{
|
{
|
||||||
if (b.length < 20) {
|
if (b.length < 20) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
b.length,
|
b.length,
|
||||||
20 // 20 is length of address
|
20 // 20 is length of address
|
||||||
@ -329,7 +329,7 @@ library LibBytes {
|
|||||||
returns (address result)
|
returns (address result)
|
||||||
{
|
{
|
||||||
if (b.length < index + 20) {
|
if (b.length < index + 20) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
b.length,
|
b.length,
|
||||||
index + 20 // 20 is length of address
|
index + 20 // 20 is length of address
|
||||||
@ -364,7 +364,7 @@ library LibBytes {
|
|||||||
pure
|
pure
|
||||||
{
|
{
|
||||||
if (b.length < index + 20) {
|
if (b.length < index + 20) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
|
||||||
b.length,
|
b.length,
|
||||||
index + 20 // 20 is length of address
|
index + 20 // 20 is length of address
|
||||||
@ -413,7 +413,7 @@ library LibBytes {
|
|||||||
returns (bytes32 result)
|
returns (bytes32 result)
|
||||||
{
|
{
|
||||||
if (b.length < index + 32) {
|
if (b.length < index + 32) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
b.length,
|
b.length,
|
||||||
index + 32
|
index + 32
|
||||||
@ -443,7 +443,7 @@ library LibBytes {
|
|||||||
pure
|
pure
|
||||||
{
|
{
|
||||||
if (b.length < index + 32) {
|
if (b.length < index + 32) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
|
||||||
b.length,
|
b.length,
|
||||||
index + 32
|
index + 32
|
||||||
@ -503,7 +503,7 @@ library LibBytes {
|
|||||||
returns (bytes4 result)
|
returns (bytes4 result)
|
||||||
{
|
{
|
||||||
if (b.length < index + 4) {
|
if (b.length < index + 4) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
|
LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
|
||||||
b.length,
|
b.length,
|
||||||
index + 4
|
index + 4
|
||||||
@ -544,7 +544,7 @@ library LibBytes {
|
|||||||
// Assert length of <b> is valid, given
|
// Assert length of <b> is valid, given
|
||||||
// length of nested bytes
|
// length of nested bytes
|
||||||
if (b.length < index + nestedBytesLength) {
|
if (b.length < index + nestedBytesLength) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors
|
LibBytesRichErrors
|
||||||
.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
|
.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
|
||||||
b.length,
|
b.length,
|
||||||
@ -574,7 +574,7 @@ library LibBytes {
|
|||||||
// Assert length of <b> is valid, given
|
// Assert length of <b> is valid, given
|
||||||
// length of input
|
// length of input
|
||||||
if (b.length < index + 32 + input.length) {
|
if (b.length < index + 32 + input.length) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors
|
LibBytesRichErrors
|
||||||
.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
|
.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsNestedBytesLengthRequired,
|
||||||
b.length,
|
b.length,
|
||||||
@ -603,7 +603,7 @@ library LibBytes {
|
|||||||
uint256 sourceLen = source.length;
|
uint256 sourceLen = source.length;
|
||||||
// Dest length must be >= source length, or some bytes would not be copied.
|
// Dest length must be >= source length, or some bytes would not be copied.
|
||||||
if (dest.length < sourceLen) {
|
if (dest.length < sourceLen) {
|
||||||
LibRichErrors._rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
|
||||||
LibBytesRichErrors
|
LibBytesRichErrors
|
||||||
.InvalidByteOperationErrorCodes.DestinationLengthGreaterThanOrEqualSourceLengthRequired,
|
.InvalidByteOperationErrorCodes.DestinationLengthGreaterThanOrEqualSourceLengthRequired,
|
||||||
dest.length,
|
dest.length,
|
||||||
|
@ -19,24 +19,25 @@
|
|||||||
pragma solidity ^0.5.9;
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
|
||||||
contract LibEIP712 {
|
library LibEIP712 {
|
||||||
|
|
||||||
// Hash of the EIP712 Domain Separator Schema
|
// Hash of the EIP712 Domain Separator Schema
|
||||||
bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(
|
// keccak256(abi.encodePacked(
|
||||||
"EIP712Domain(",
|
// "EIP712Domain(",
|
||||||
"string name,",
|
// "string name,",
|
||||||
"string version,",
|
// "string version,",
|
||||||
"uint256 chainId,",
|
// "uint256 chainId,",
|
||||||
"address verifyingContractAddress",
|
// "address verifyingContractAddress",
|
||||||
")"
|
// ")"
|
||||||
));
|
// ))
|
||||||
|
bytes32 constant internal _EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = 0xb1b295f2c1ed6b459ddeb95701466e4e0b385527a6cfa3873ae72a63c08466b6;
|
||||||
|
|
||||||
/// @dev Calculates a EIP712 domain separator.
|
/// @dev Calculates a EIP712 domain separator.
|
||||||
/// @param name The EIP712 domain name.
|
/// @param name The EIP712 domain name.
|
||||||
/// @param version The EIP712 domain version.
|
/// @param version The EIP712 domain version.
|
||||||
/// @param verifyingContractAddress The EIP712 verifying contract.
|
/// @param verifyingContractAddress The EIP712 verifying contract.
|
||||||
/// @return EIP712 domain separator.
|
/// @return EIP712 domain separator.
|
||||||
function _hashEIP712Domain(
|
function hashEIP712Domain(
|
||||||
string memory name,
|
string memory name,
|
||||||
string memory version,
|
string memory version,
|
||||||
uint256 chainId,
|
uint256 chainId,
|
||||||
@ -46,13 +47,36 @@ contract LibEIP712 {
|
|||||||
pure
|
pure
|
||||||
returns (bytes32 result)
|
returns (bytes32 result)
|
||||||
{
|
{
|
||||||
return keccak256(abi.encodePacked(
|
bytes32 schemaHash = _EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH;
|
||||||
EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
|
|
||||||
keccak256(bytes(name)),
|
// Assembly for more efficient computing:
|
||||||
keccak256(bytes(version)),
|
// keccak256(abi.encodePacked(
|
||||||
chainId,
|
// _EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
|
||||||
uint256(verifyingContractAddress)
|
// keccak256(bytes(name)),
|
||||||
));
|
// keccak256(bytes(version)),
|
||||||
|
// chainId,
|
||||||
|
// uint256(verifyingContractAddress)
|
||||||
|
// ))
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
// Calculate hashes of dynamic data
|
||||||
|
let nameHash := keccak256(add(name, 32), mload(name))
|
||||||
|
let versionHash := keccak256(add(version, 32), mload(version))
|
||||||
|
|
||||||
|
// Load free memory pointer
|
||||||
|
let memPtr := mload(64)
|
||||||
|
|
||||||
|
// Store params in memory
|
||||||
|
mstore(memPtr, schemaHash)
|
||||||
|
mstore(add(memPtr, 32), nameHash)
|
||||||
|
mstore(add(memPtr, 64), versionHash)
|
||||||
|
mstore(add(memPtr, 96), chainId)
|
||||||
|
mstore(add(memPtr, 128), verifyingContractAddress)
|
||||||
|
|
||||||
|
// Compute hash
|
||||||
|
result := keccak256(memPtr, 160)
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
|
/// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
|
||||||
@ -60,7 +84,7 @@ contract LibEIP712 {
|
|||||||
/// with getDomainHash().
|
/// with getDomainHash().
|
||||||
/// @param hashStruct The EIP712 hash struct.
|
/// @param hashStruct The EIP712 hash struct.
|
||||||
/// @return EIP712 hash applied to the given EIP712 Domain.
|
/// @return EIP712 hash applied to the given EIP712 Domain.
|
||||||
function _hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
|
function hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
|
||||||
internal
|
internal
|
||||||
pure
|
pure
|
||||||
returns (bytes32 result)
|
returns (bytes32 result)
|
||||||
|
@ -47,7 +47,7 @@ library LibRichErrors {
|
|||||||
|
|
||||||
/// @dev Reverts an encoded rich revert reason `errorData`.
|
/// @dev Reverts an encoded rich revert reason `errorData`.
|
||||||
/// @param errorData ABI encoded error data.
|
/// @param errorData ABI encoded error data.
|
||||||
function _rrevert(bytes memory errorData)
|
function rrevert(bytes memory errorData)
|
||||||
internal
|
internal
|
||||||
pure
|
pure
|
||||||
{
|
{
|
||||||
|
90
contracts/utils/contracts/src/LibSafeMath.sol
Normal file
90
contracts/utils/contracts/src/LibSafeMath.sol
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
pragma solidity ^0.5.9;
|
||||||
|
|
||||||
|
import "./LibRichErrors.sol";
|
||||||
|
import "./LibSafeMathRichErrors.sol";
|
||||||
|
|
||||||
|
|
||||||
|
library LibSafeMath {
|
||||||
|
|
||||||
|
function safeMul(uint256 a, uint256 b)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
if (a == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint256 c = a * b;
|
||||||
|
if (c / a != b) {
|
||||||
|
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
|
||||||
|
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_MULTIPLICATION_OVERFLOW,
|
||||||
|
a,
|
||||||
|
b
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeDiv(uint256 a, uint256 b)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
if (b == 0) {
|
||||||
|
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
|
||||||
|
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_DIVISION_BY_ZERO,
|
||||||
|
a,
|
||||||
|
b
|
||||||
|
));
|
||||||
|
}
|
||||||
|
uint256 c = a / b;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeSub(uint256 a, uint256 b)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
if (b > a) {
|
||||||
|
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
|
||||||
|
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_SUBTRACTION_UNDERFLOW,
|
||||||
|
a,
|
||||||
|
b
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeAdd(uint256 a, uint256 b)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
uint256 c = a + b;
|
||||||
|
if (c < a) {
|
||||||
|
LibRichErrors.rrevert(LibSafeMathRichErrors.SafeMathError(
|
||||||
|
LibSafeMathRichErrors.SafeMathErrorCodes.UINT256_ADDITION_OVERFLOW,
|
||||||
|
a,
|
||||||
|
b
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
function max256(uint256 a, uint256 b)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
return a >= b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function min256(uint256 a, uint256 b)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ contract Ownable is
|
|||||||
|
|
||||||
modifier onlyOwner() {
|
modifier onlyOwner() {
|
||||||
if (msg.sender != owner) {
|
if (msg.sender != owner) {
|
||||||
LibRichErrors._rrevert(LibOwnableRichErrors.OnlyOwnerError(
|
LibRichErrors.rrevert(LibOwnableRichErrors.OnlyOwnerError(
|
||||||
msg.sender,
|
msg.sender,
|
||||||
owner
|
owner
|
||||||
));
|
));
|
||||||
@ -31,7 +31,7 @@ contract Ownable is
|
|||||||
onlyOwner
|
onlyOwner
|
||||||
{
|
{
|
||||||
if (newOwner == address(0)) {
|
if (newOwner == address(0)) {
|
||||||
LibRichErrors._rrevert(LibOwnableRichErrors.TransferOwnerToZeroError());
|
LibRichErrors.rrevert(LibOwnableRichErrors.TransferOwnerToZeroError());
|
||||||
} else {
|
} else {
|
||||||
owner = newOwner;
|
owner = newOwner;
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user