Merge branch 'v2-prototype' into bug/contracts/eip712-191-prefix

This commit is contained in:
Jacob Evans
2018-06-18 21:50:35 +10:00
committed by GitHub
475 changed files with 10357 additions and 8112 deletions

View File

@@ -63,3 +63,12 @@ yarn lint
```bash
yarn test
```
### Run Tests Against Geth
Follow the instructions in the README for the devnet package to start the
devnet.
```bash
TEST_PROVIDER=geth yarn test
```

View File

@@ -21,6 +21,7 @@
"contracts": [
"AssetProxyOwner",
"DummyERC20Token",
"DummyERC721Receiver",
"DummyERC721Token",
"ERC20Proxy",
"ERC721Proxy",
@@ -28,8 +29,10 @@
"MixinAuthorizable",
"MultiSigWallet",
"MultiSigWalletWithTimeLock",
"TestAssetDataDecoders",
"TestAssetProxyDispatcher",
"TestLibBytes",
"TestLibMem",
"TestLibs",
"TestSignatureValidator",
"TokenRegistry",

View File

@@ -11,25 +11,29 @@
"test": "test"
},
"scripts": {
"watch": "tsc -w",
"prebuild": "run-s clean compile copy_artifacts generate_contract_wrappers",
"watch_without_deps": "yarn pre_build && tsc -w",
"build": "yarn pre_build && tsc",
"pre_build": "run-s compile copy_artifacts generate_contract_wrappers",
"copy_artifacts": "copyfiles -u 4 '../migrations/artifacts/2.0.0/**/*' ./lib/src/artifacts;",
"build": "tsc",
"test": "run-s build run_mocha",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
"run_mocha": "mocha 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
"run_mocha": "mocha --require source-map-support/register 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler",
"clean": "shx rm -rf lib src/contract_wrappers/generated",
"clean": "shx rm -rf lib src/generated_contract_wrappers",
"generate_contract_wrappers":
"abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers && prettier --write 'src/contract_wrappers/generated/**.ts'",
"lint": "tslint --project .",
"abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/generated_contract_wrappers --backend ethers",
"lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/* --exclude **/lib/**/*",
"coverage:report:text": "istanbul report text",
"coverage:report:html": "istanbul report html && open coverage/index.html",
"profiler:report:html": "istanbul report html && open coverage/index.html",
"coverage:report:lcov": "istanbul report lcov",
"test:circleci": "yarn test"
},
"config": {
"abis": "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|TestAssetProxyDispatcher|TestLibBytes|TestLibs|TestSignatureValidator|TokenRegistry|Whitelist|WETH9|ZRXToken).json"
"abis": "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|TestAssetDataDecoders|TestAssetProxyDispatcher|TestLibBytes|TestLibMem|TestLibs|TestSignatureValidator|TokenRegistry|Whitelist|WETH9|ZRXToken).json"
},
"repository": {
"type": "git",
@@ -60,7 +64,6 @@
"make-promises-safe": "^1.1.0",
"mocha": "^4.0.1",
"npm-run-all": "^4.1.2",
"prettier": "^1.11.1",
"shx": "^0.2.2",
"solc": "^0.4.24",
"tslint": "5.8.0",

View File

@@ -34,36 +34,51 @@ contract ERC20Proxy is
uint8 constant PROXY_ID = 1;
/// @dev Internal version of `transferFrom`.
/// @param assetMetadata Encoded byte array.
/// @param assetData Encoded byte array.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFromInternal(
bytes memory assetMetadata,
bytes memory assetData,
address from,
address to,
uint256 amount
)
internal
{
// Data must be intended for this proxy.
uint256 length = assetMetadata.length;
require(
length == 21,
LENGTH_21_REQUIRED
);
// TODO: Is this too inflexible in the future?
require(
uint8(assetMetadata[length - 1]) == PROXY_ID,
ASSET_PROXY_ID_MISMATCH
);
// Decode metadata.
address token = readAddress(assetMetadata, 0);
// Decode asset data.
address token = readAddress(assetData, 0);
// Transfer tokens.
bool success = IERC20Token(token).transferFrom(from, to, amount);
// We do a raw call so we can check the success separate
// from the return data.
bool success = token.call(abi.encodeWithSelector(
IERC20Token(token).transferFrom.selector,
from,
to,
amount
));
require(
success,
TRANSFER_FAILED
);
// Check return data.
// If there is no return data, we assume the token incorrectly
// does not return a bool. In this case we expect it to revert
// on failure, which was handled above.
// If the token does return data, we require that it is a single
// value that evaluates to true.
assembly {
if returndatasize {
success := 0
if eq(returndatasize, 32) {
// First 64 bytes of memory are reserved scratch space
returndatacopy(0, 0, 32)
success := mload(0)
}
}
}
require(
success,
TRANSFER_FAILED

View File

@@ -34,47 +34,38 @@ contract ERC721Proxy is
uint8 constant PROXY_ID = 2;
/// @dev Internal version of `transferFrom`.
/// @param assetMetadata Encoded byte array.
/// @param assetData Encoded byte array.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFromInternal(
bytes memory assetMetadata,
bytes memory assetData,
address from,
address to,
uint256 amount
)
internal
{
// Data must be intended for this proxy.
uint256 length = assetMetadata.length;
require(
length == 53,
LENGTH_53_REQUIRED
);
// TODO: Is this too inflexible in the future?
require(
uint8(assetMetadata[length - 1]) == PROXY_ID,
ASSET_PROXY_ID_MISMATCH
);
// There exists only 1 of each token.
require(
amount == 1,
INVALID_AMOUNT
);
// Decode asset data.
(
address token,
uint256 tokenId,
bytes memory receiverData
) = decodeERC721AssetData(assetData);
// Decode metadata
address token = readAddress(assetMetadata, 0);
uint256 tokenId = readUint256(assetMetadata, 20);
// Transfer token.
// Either succeeds or throws.
// @TODO: Call safeTransferFrom if there is additional
// data stored in `assetMetadata`.
ERC721Token(token).transferFrom(from, to, tokenId);
// Transfer token. Saves gas by calling safeTransferFrom only
// when there is receiverData present. Either succeeds or throws.
if (receiverData.length > 0) {
ERC721Token(token).safeTransferFrom(from, to, tokenId, receiverData);
} else {
ERC721Token(token).transferFrom(from, to, tokenId);
}
}
/// @dev Gets the proxy id associated with the proxy address.
@@ -86,4 +77,34 @@ contract ERC721Proxy is
{
return PROXY_ID;
}
/// @dev Decodes ERC721 Asset data.
/// @param assetData Encoded byte array.
/// @return proxyId Intended ERC721 proxy id.
/// @return token ERC721 token address.
/// @return tokenId ERC721 token id.
/// @return receiverData Additional data with no specific format, which
/// is passed to the receiving contract's onERC721Received.
function decodeERC721AssetData(bytes memory assetData)
internal
pure
returns (
address token,
uint256 tokenId,
bytes memory receiverData
)
{
// Decode asset data.
token = readAddress(assetData, 0);
tokenId = readUint256(assetData, 20);
if (assetData.length > 52) {
receiverData = readBytes(assetData, 52);
}
return (
token,
tokenId,
receiverData
);
}
}

View File

@@ -28,12 +28,12 @@ contract MixinAssetProxy is
{
/// @dev Transfers assets. Either succeeds or throws.
/// @param assetMetadata Encoded byte array.
/// @param assetData Encoded byte array.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFrom(
bytes assetMetadata,
bytes assetData,
address from,
address to,
uint256 amount
@@ -42,7 +42,7 @@ contract MixinAssetProxy is
onlyAuthorized
{
transferFromInternal(
assetMetadata,
assetData,
from,
to,
amount
@@ -50,12 +50,12 @@ contract MixinAssetProxy is
}
/// @dev Makes multiple transfers of assets. Either succeeds or throws.
/// @param assetMetadata Array of byte arrays encoded for the respective asset proxy.
/// @param assetData Array of byte arrays encoded for the respective asset proxy.
/// @param from Array of addresses to transfer assets from.
/// @param to Array of addresses to transfer assets to.
/// @param amounts Array of amounts of assets to transfer.
function batchTransferFrom(
bytes[] memory assetMetadata,
bytes[] memory assetData,
address[] memory from,
address[] memory to,
uint256[] memory amounts
@@ -63,9 +63,9 @@ contract MixinAssetProxy is
public
onlyAuthorized
{
for (uint256 i = 0; i < assetMetadata.length; i++) {
for (uint256 i = 0; i < assetData.length; i++) {
transferFromInternal(
assetMetadata[i],
assetData[i],
from[i],
to[i],
amounts[i]

View File

@@ -26,12 +26,12 @@ contract IAssetProxy is
{
/// @dev Transfers assets. Either succeeds or throws.
/// @param assetMetadata Byte array encoded for the respective asset proxy.
/// @param assetData Byte array encoded for the respective asset proxy.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFrom(
bytes assetMetadata,
bytes assetData,
address from,
address to,
uint256 amount
@@ -39,12 +39,12 @@ contract IAssetProxy is
external;
/// @dev Makes multiple transfers of assets. Either succeeds or throws.
/// @param assetMetadata Array of byte arrays encoded for the respective asset proxy.
/// @param assetData Array of byte arrays encoded for the respective asset proxy.
/// @param from Array of addresses to transfer assets from.
/// @param to Array of addresses to transfer assets to.
/// @param amounts Array of amounts of assets to transfer.
function batchTransferFrom(
bytes[] memory assetMetadata,
bytes[] memory assetData,
address[] memory from,
address[] memory to,
uint256[] memory amounts

View File

@@ -27,11 +27,6 @@ contract LibAssetProxyErrors {
string constant AUTHORIZED_ADDRESS_MISMATCH = "AUTHORIZED_ADDRESS_MISMATCH"; // Address at index does not match given target address.
/// AssetProxy errors ///
string constant ASSET_PROXY_ID_MISMATCH = "ASSET_PROXY_ID_MISMATCH"; // Proxy id in metadata does not match this proxy id.
string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Transfer amount must equal 1.
string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Transfer failed.
/// Length validation errors ///
string constant LENGTH_21_REQUIRED = "LENGTH_21_REQUIRED"; // Byte array must have a length of 21.
string constant LENGTH_53_REQUIRED = "LENGTH_53_REQUIRED"; // Byte array must have a length of 53.
string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Transfer failed.
}

View File

@@ -26,12 +26,12 @@ contract MAssetProxy is
{
/// @dev Internal version of `transferFrom`.
/// @param assetMetadata Encoded byte array.
/// @param assetData Encoded byte array.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
function transferFromInternal(
bytes memory assetMetadata,
bytes memory assetData,
address from,
address to,
uint256 amount

View File

@@ -40,11 +40,11 @@ contract Exchange is
string constant public VERSION = "2.0.1-alpha";
// Mixins are instantiated in the order they are inherited
constructor (bytes memory _zrxProxyData)
constructor (bytes memory _zrxAssetData)
public
MixinExchangeCore()
MixinMatchOrders()
MixinSettlement(_zrxProxyData)
MixinSettlement(_zrxAssetData)
MixinSignatureValidator()
MixinTransactions()
MixinAssetProxyDispatcher()

View File

@@ -19,12 +19,14 @@
pragma solidity ^0.4.24;
import "../../utils/Ownable/Ownable.sol";
import "../../utils/LibBytes/LibBytes.sol";
import "./libs/LibExchangeErrors.sol";
import "./mixins/MAssetProxyDispatcher.sol";
import "../AssetProxy/interfaces/IAssetProxy.sol";
contract MixinAssetProxyDispatcher is
Ownable,
LibBytes,
LibExchangeErrors,
MAssetProxyDispatcher
{
@@ -80,12 +82,14 @@ contract MixinAssetProxyDispatcher is
}
/// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.
/// @param assetMetadata Byte array encoded for the respective asset proxy.
/// @param assetData Byte array encoded for the respective asset proxy.
/// @param assetProxyId Id of assetProxy to dispach to.
/// @param from Address to transfer token from.
/// @param to Address to transfer token to.
/// @param amount Amount of token to transfer.
function dispatchTransferFrom(
bytes memory assetMetadata,
bytes memory assetData,
uint8 assetProxyId,
address from,
address to,
uint256 amount
@@ -94,18 +98,10 @@ contract MixinAssetProxyDispatcher is
{
// Do nothing if no amount should be transferred.
if (amount > 0) {
// Lookup asset proxy
uint256 length = assetMetadata.length;
require(
length > 0,
LENGTH_GREATER_THAN_0_REQUIRED
);
uint8 assetProxyId = uint8(assetMetadata[length - 1]);
// Lookup assetProxy
IAssetProxy assetProxy = assetProxies[assetProxyId];
// transferFrom will either succeed or throw.
assetProxy.transferFrom(assetMetadata, from, to, amount);
assetProxy.transferFrom(assetData, from, to, amount);
}
}
}

View File

@@ -50,7 +50,7 @@ contract MixinExchangeCore is
////// Core exchange functions //////
/// @dev Cancels all orders reated by sender with a salt less than or equal to the specified salt value.
/// @dev Cancels all orders created by sender with a salt less than or equal to the specified salt value.
/// @param salt Orders created with a salt less or equal to this value will be cancelled.
function cancelOrdersUpTo(uint256 salt)
external
@@ -108,9 +108,6 @@ contract MixinExchangeCore is
// Compute proportional fill amounts
fillResults = calculateFillResults(order, takerAssetFilledAmount);
// Settle order
settleOrder(order, takerAddress, fillResults);
// Update exchange internal state
updateFilledState(
order,
@@ -119,6 +116,10 @@ contract MixinExchangeCore is
orderInfo.orderTakerAssetFilledAmount,
fillResults
);
// Settle order
settleOrder(order, takerAddress, fillResults);
return fillResults;
}

View File

@@ -14,7 +14,6 @@
pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../utils/LibBytes/LibBytes.sol";
import "./libs/LibMath.sol";
import "./libs/LibOrder.sol";
import "./libs/LibFillResults.sol";
@@ -25,7 +24,6 @@ import "./mixins/MSettlement.sol";
import "./mixins/MTransactions.sol";
contract MixinMatchOrders is
LibBytes,
LibMath,
LibExchangeErrors,
MExchangeCore,
@@ -94,14 +92,6 @@ contract MixinMatchOrders is
rightSignature
);
// Settle matched orders. Succeeds or throws.
settleMatchedOrders(
leftOrder,
rightOrder,
takerAddress,
matchedFillResults
);
// Update exchange state
updateFilledState(
leftOrder,
@@ -117,6 +107,14 @@ contract MixinMatchOrders is
rightOrderInfo.orderTakerAssetFilledAmount,
matchedFillResults.right
);
// Settle matched orders. Succeeds or throws.
settleMatchedOrders(
leftOrder,
rightOrder,
takerAddress,
matchedFillResults
);
return matchedFillResults;
}

View File

@@ -19,6 +19,7 @@
pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../utils/LibBytes/LibBytes.sol";
import "./libs/LibMath.sol";
import "./libs/LibFillResults.sol";
import "./libs/LibOrder.sol";
@@ -28,33 +29,26 @@ import "./mixins/MSettlement.sol";
import "./mixins/MAssetProxyDispatcher.sol";
contract MixinSettlement is
LibBytes,
LibMath,
LibExchangeErrors,
MMatchOrders,
MSettlement,
MAssetProxyDispatcher
{
// ZRX metadata used for fee transfers.
// ZRX address encoded as a byte array.
// This will be constant throughout the life of the Exchange contract,
// since ZRX will always be transferred via the ERC20 AssetProxy.
bytes internal ZRX_PROXY_DATA;
bytes internal ZRX_ASSET_DATA;
uint8 constant ZRX_PROXY_ID = 1;
/// @dev Gets the ZRX metadata used for fee transfers.
function zrxProxyData()
external
view
returns (bytes memory)
{
return ZRX_PROXY_DATA;
}
/// TODO: _zrxProxyData should be a constant in production.
/// TODO: _zrxAssetData should be a constant in production.
/// @dev Constructor sets the metadata that will be used for paying ZRX fees.
/// @param _zrxProxyData Byte array containing ERC20 proxy id concatenated with address of ZRX.
constructor (bytes memory _zrxProxyData)
/// @param _zrxAssetData Byte array containing ERC20 proxy id concatenated with address of ZRX.
constructor (bytes memory _zrxAssetData)
public
{
ZRX_PROXY_DATA = _zrxProxyData;
ZRX_ASSET_DATA = _zrxAssetData;
}
/// @dev Settles an order by transferring assets between counterparties.
@@ -68,26 +62,33 @@ contract MixinSettlement is
)
internal
{
uint8 makerAssetProxyId = uint8(popLastByte(order.makerAssetData));
uint8 takerAssetProxyId = uint8(popLastByte(order.takerAssetData));
bytes memory zrxAssetData = ZRX_ASSET_DATA;
dispatchTransferFrom(
order.makerAssetData,
makerAssetProxyId,
order.makerAddress,
takerAddress,
fillResults.makerAssetFilledAmount
);
dispatchTransferFrom(
order.takerAssetData,
takerAssetProxyId,
takerAddress,
order.makerAddress,
fillResults.takerAssetFilledAmount
);
dispatchTransferFrom(
ZRX_PROXY_DATA,
zrxAssetData,
ZRX_PROXY_ID,
order.makerAddress,
order.feeRecipientAddress,
fillResults.makerFeePaid
);
dispatchTransferFrom(
ZRX_PROXY_DATA,
zrxAssetData,
ZRX_PROXY_ID,
takerAddress,
order.feeRecipientAddress,
fillResults.takerFeePaid
@@ -107,21 +108,27 @@ contract MixinSettlement is
)
internal
{
uint8 leftMakerAssetProxyId = uint8(popLastByte(leftOrder.makerAssetData));
uint8 rightMakerAssetProxyId = uint8(popLastByte(rightOrder.makerAssetData));
bytes memory zrxAssetData = ZRX_ASSET_DATA;
// Order makers and taker
dispatchTransferFrom(
leftOrder.makerAssetData,
leftMakerAssetProxyId,
leftOrder.makerAddress,
rightOrder.makerAddress,
matchedFillResults.right.takerAssetFilledAmount
);
dispatchTransferFrom(
rightOrder.makerAssetData,
rightMakerAssetProxyId,
rightOrder.makerAddress,
leftOrder.makerAddress,
matchedFillResults.left.takerAssetFilledAmount
);
dispatchTransferFrom(
leftOrder.makerAssetData,
leftMakerAssetProxyId,
leftOrder.makerAddress,
takerAddress,
matchedFillResults.leftMakerAssetSpreadAmount
@@ -129,13 +136,15 @@ contract MixinSettlement is
// Maker fees
dispatchTransferFrom(
ZRX_PROXY_DATA,
zrxAssetData,
ZRX_PROXY_ID,
leftOrder.makerAddress,
leftOrder.feeRecipientAddress,
matchedFillResults.left.makerFeePaid
);
dispatchTransferFrom(
ZRX_PROXY_DATA,
zrxAssetData,
ZRX_PROXY_ID,
rightOrder.makerAddress,
rightOrder.feeRecipientAddress,
matchedFillResults.right.makerFeePaid
@@ -144,7 +153,8 @@ contract MixinSettlement is
// Taker fees
if (leftOrder.feeRecipientAddress == rightOrder.feeRecipientAddress) {
dispatchTransferFrom(
ZRX_PROXY_DATA,
zrxAssetData,
ZRX_PROXY_ID,
takerAddress,
leftOrder.feeRecipientAddress,
safeAdd(
@@ -154,13 +164,15 @@ contract MixinSettlement is
);
} else {
dispatchTransferFrom(
ZRX_PROXY_DATA,
zrxAssetData,
ZRX_PROXY_ID,
takerAddress,
leftOrder.feeRecipientAddress,
matchedFillResults.left.takerFeePaid
);
dispatchTransferFrom(
ZRX_PROXY_DATA,
zrxAssetData,
ZRX_PROXY_ID,
takerAddress,
rightOrder.feeRecipientAddress,
matchedFillResults.right.takerFeePaid

View File

@@ -93,7 +93,7 @@ contract MixinSignatureValidator is
);
// Pop last byte off of signature byte array.
SignatureType signatureType = SignatureType(uint8(popByte(signature)));
SignatureType signatureType = SignatureType(uint8(popLastByte(signature)));
// Variables are not scoped in Solidity.
uint8 v;
@@ -183,7 +183,7 @@ contract MixinSignatureValidator is
// | 0x14 + x | 1 | Signature type is always "\x06" |
} else if (signatureType == SignatureType.Validator) {
// Pop last 20 bytes off of signature byte array.
address validator = popAddress(signature);
address validator = popLast20Bytes(signature);
// Ensure signer has approved validator.
if (!allowedValidators[signer][validator]) {
return false;

View File

@@ -19,7 +19,6 @@
pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../utils/LibBytes/LibBytes.sol";
import "./libs/LibMath.sol";
import "./libs/LibOrder.sol";
import "./libs/LibFillResults.sol";
@@ -27,7 +26,6 @@ import "./libs/LibExchangeErrors.sol";
import "./mixins/MExchangeCore.sol";
contract MixinWrapperFunctions is
LibBytes,
LibMath,
LibFillResults,
LibExchangeErrors,
@@ -40,7 +38,8 @@ contract MixinWrapperFunctions is
function fillOrKillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature)
bytes memory signature
)
public
returns (FillResults memory fillResults)
{
@@ -65,7 +64,8 @@ contract MixinWrapperFunctions is
function fillOrderNoThrow(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature)
bytes memory signature
)
public
returns (FillResults memory fillResults)
{
@@ -91,12 +91,12 @@ contract MixinWrapperFunctions is
// | | 0x0E0 | | 8. takerFeeAmount |
// | | 0x100 | | 9. expirationTimeSeconds |
// | | 0x120 | | 10. salt |
// | | 0x140 | | 11. Offset to makerAssetProxyMetadata (*) |
// | | 0x160 | | 12. Offset to takerAssetProxyMetadata (*) |
// | | 0x180 | 32 | makerAssetProxyMetadata Length |
// | | 0x1A0 | ** | makerAssetProxyMetadata Contents |
// | | 0x1C0 | 32 | takerAssetProxyMetadata Length |
// | | 0x1E0 | ** | takerAssetProxyMetadata Contents |
// | | 0x140 | | 11. Offset to makerAssetData (*) |
// | | 0x160 | | 12. Offset to takerAssetData (*) |
// | | 0x180 | 32 | makerAssetData Length |
// | | 0x1A0 | ** | makerAssetData Contents |
// | | 0x1C0 | 32 | takerAssetData Length |
// | | 0x1E0 | ** | takerAssetData Contents |
// | | 0x200 | 32 | signature Length |
// | | 0x220 | ** | signature Contents |
@@ -163,43 +163,45 @@ contract MixinWrapperFunctions is
mstore(add(dataAreaEnd, 0xE0), mload(add(sourceOffset, 0xE0))) // takerFeeAmount
mstore(add(dataAreaEnd, 0x100), mload(add(sourceOffset, 0x100))) // expirationTimeSeconds
mstore(add(dataAreaEnd, 0x120), mload(add(sourceOffset, 0x120))) // salt
mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140))) // Offset to makerAssetProxyMetadata
mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160))) // Offset to takerAssetProxyMetadata
mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140))) // Offset to makerAssetData
mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160))) // Offset to takerAssetData
dataAreaEnd := add(dataAreaEnd, 0x180)
sourceOffset := add(sourceOffset, 0x180)
// Write offset to <order.makerAssetProxyMetadata>
// Write offset to <order.makerAssetData>
mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart))
// Calculate length of <order.makerAssetProxyMetadata>
// Calculate length of <order.makerAssetData>
sourceOffset := mload(add(order, 0x140)) // makerAssetData
arrayLenBytes := mload(sourceOffset)
sourceOffset := add(sourceOffset, 0x20)
arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
// Write length of <order.makerAssetProxyMetadata>
// Write length of <order.makerAssetData>
mstore(dataAreaEnd, arrayLenBytes)
dataAreaEnd := add(dataAreaEnd, 0x20)
// Write contents of <order.makerAssetProxyMetadata>
// Write contents of <order.makerAssetData>
for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
mstore(dataAreaEnd, mload(sourceOffset))
dataAreaEnd := add(dataAreaEnd, 0x20)
sourceOffset := add(sourceOffset, 0x20)
}
// Write offset to <order.takerAssetProxyMetadata>
// Write offset to <order.takerAssetData>
mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))
// Calculate length of <order.takerAssetProxyMetadata>
// Calculate length of <order.takerAssetData>
sourceOffset := mload(add(order, 0x160)) // takerAssetData
arrayLenBytes := mload(sourceOffset)
sourceOffset := add(sourceOffset, 0x20)
arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
// Write length of <order.takerAssetProxyMetadata>
// Write length of <order.takerAssetData>
mstore(dataAreaEnd, arrayLenBytes)
dataAreaEnd := add(dataAreaEnd, 0x20)
// Write contents of <order.takerAssetProxyMetadata>
// Write contents of <order.takerAssetData>
for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
mstore(dataAreaEnd, mload(sourceOffset))
dataAreaEnd := add(dataAreaEnd, 0x20)
@@ -264,7 +266,8 @@ contract MixinWrapperFunctions is
function batchFillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures)
bytes[] memory signatures
)
public
{
for (uint256 i = 0; i < orders.length; i++) {
@@ -283,7 +286,8 @@ contract MixinWrapperFunctions is
function batchFillOrKillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures)
bytes[] memory signatures
)
public
{
for (uint256 i = 0; i < orders.length; i++) {
@@ -303,7 +307,8 @@ contract MixinWrapperFunctions is
function batchFillOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures)
bytes[] memory signatures
)
public
{
for (uint256 i = 0; i < orders.length; i++) {
@@ -323,18 +328,18 @@ contract MixinWrapperFunctions is
function marketSellOrders(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
bytes[] memory signatures)
bytes[] memory signatures
)
public
returns (FillResults memory totalFillResults)
{
bytes memory takerAssetData = orders[0].takerAssetData;
for (uint256 i = 0; i < orders.length; i++) {
// Token being sold by taker must be the same for each order
// TODO: optimize by only using takerAssetData for first order.
require(
areBytesEqual(orders[i].takerAssetData, orders[0].takerAssetData),
ASSET_DATA_MISMATCH
);
// We assume that asset being sold by taker is the same for each order.
// Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.
orders[i].takerAssetData = takerAssetData;
// Calculate the remaining amount of takerAsset to sell
uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);
@@ -346,6 +351,14 @@ contract MixinWrapperFunctions is
signatures[i]
);
// HACK: the proxyId is "popped" from the byte array before a fill is settled
// by subtracting from the length of the array. Since the popped byte is
// still in memory, we can "unpop" it by incrementing the length of the byte array.
assembly {
let len := mload(takerAssetData)
mstore(takerAssetData, add(len, 1))
}
// Update amounts filled and fees paid by maker and taker
addFillResults(totalFillResults, singleFillResults);
@@ -366,18 +379,18 @@ contract MixinWrapperFunctions is
function marketSellOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
bytes[] memory signatures)
bytes[] memory signatures
)
public
returns (FillResults memory totalFillResults)
{
bytes memory takerAssetData = orders[0].takerAssetData;
for (uint256 i = 0; i < orders.length; i++) {
// Token being sold by taker must be the same for each order
// TODO: optimize by only using takerAssetData for first order.
require(
areBytesEqual(orders[i].takerAssetData, orders[0].takerAssetData),
ASSET_DATA_MISMATCH
);
// We assume that asset being sold by taker is the same for each order.
// Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.
orders[i].takerAssetData = takerAssetData;
// Calculate the remaining amount of takerAsset to sell
uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);
@@ -408,18 +421,18 @@ contract MixinWrapperFunctions is
function marketBuyOrders(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
bytes[] memory signatures)
bytes[] memory signatures
)
public
returns (FillResults memory totalFillResults)
{
bytes memory makerAssetData = orders[0].makerAssetData;
for (uint256 i = 0; i < orders.length; i++) {
// Token being bought by taker must be the same for each order
// TODO: optimize by only using makerAssetData for first order.
require(
areBytesEqual(orders[i].makerAssetData, orders[0].makerAssetData),
ASSET_DATA_MISMATCH
);
// We assume that asset being bought by taker is the same for each order.
// Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.
orders[i].makerAssetData = makerAssetData;
// Calculate the remaining amount of makerAsset to buy
uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);
@@ -439,6 +452,14 @@ contract MixinWrapperFunctions is
signatures[i]
);
// HACK: the proxyId is "popped" from the byte array before a fill is settled
// by subtracting from the length of the array. Since the popped byte is
// still in memory, we can "unpop" it by incrementing the length of the byte array.
assembly {
let len := mload(makerAssetData)
mstore(makerAssetData, add(len, 1))
}
// Update amounts filled and fees paid by maker and taker
addFillResults(totalFillResults, singleFillResults);
@@ -459,18 +480,18 @@ contract MixinWrapperFunctions is
function marketBuyOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
bytes[] memory signatures)
bytes[] memory signatures
)
public
returns (FillResults memory totalFillResults)
{
bytes memory makerAssetData = orders[0].makerAssetData;
for (uint256 i = 0; i < orders.length; i++) {
// Token being bought by taker must be the same for each order
// TODO: optimize by only using makerAssetData for first order.
require(
areBytesEqual(orders[i].makerAssetData, orders[0].makerAssetData),
ASSET_DATA_MISMATCH
);
// We assume that asset being bought by taker is the same for each order.
// Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.
orders[i].makerAssetData = makerAssetData;
// Calculate the remaining amount of makerAsset to buy
uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);

View File

@@ -28,7 +28,8 @@ contract IAssetProxyDispatcher {
function registerAssetProxy(
uint8 assetProxyId,
address newAssetProxy,
address oldAssetProxy)
address oldAssetProxy
)
external;
/// @dev Gets an asset proxy.

View File

@@ -28,6 +28,7 @@ contract ITransactions {
uint256 salt,
address signer,
bytes data,
bytes signature)
bytes signature
)
external;
}

View File

@@ -33,7 +33,8 @@ contract IWrapperFunctions is
function fillOrKillOrder(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature)
bytes memory signature
)
public
returns (LibFillResults.FillResults memory fillResults);
@@ -46,7 +47,8 @@ contract IWrapperFunctions is
function fillOrderNoThrow(
LibOrder.Order memory order,
uint256 takerAssetFillAmount,
bytes memory signature)
bytes memory signature
)
public
returns (LibFillResults.FillResults memory fillResults);
@@ -57,7 +59,8 @@ contract IWrapperFunctions is
function batchFillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures)
bytes[] memory signatures
)
public;
/// @dev Synchronously executes multiple calls of fillOrKill.
@@ -67,7 +70,8 @@ contract IWrapperFunctions is
function batchFillOrKillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures)
bytes[] memory signatures
)
public;
/// @dev Fills an order with specified parameters and ECDSA signature.
@@ -78,7 +82,8 @@ contract IWrapperFunctions is
function batchFillOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures)
bytes[] memory signatures
)
public;
/// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.
@@ -89,7 +94,8 @@ contract IWrapperFunctions is
function marketSellOrders(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
bytes[] memory signatures)
bytes[] memory signatures
)
public
returns (LibFillResults.FillResults memory totalFillResults);
@@ -102,7 +108,8 @@ contract IWrapperFunctions is
function marketSellOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 takerAssetFillAmount,
bytes[] memory signatures)
bytes[] memory signatures
)
public
returns (LibFillResults.FillResults memory totalFillResults);
@@ -114,7 +121,8 @@ contract IWrapperFunctions is
function marketBuyOrders(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
bytes[] memory signatures)
bytes[] memory signatures
)
public
returns (LibFillResults.FillResults memory totalFillResults);
@@ -127,7 +135,8 @@ contract IWrapperFunctions is
function marketBuyOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256 makerAssetFillAmount,
bytes[] memory signatures)
bytes[] memory signatures
)
public
returns (LibFillResults.FillResults memory totalFillResults);

View File

@@ -25,7 +25,6 @@ contract LibExchangeErrors {
string constant INVALID_TAKER = "INVALID_TAKER"; // Invalid takerAddress.
string constant INVALID_SENDER = "INVALID_SENDER"; // Invalid `msg.sender`.
string constant INVALID_ORDER_SIGNATURE = "INVALID_ORDER_SIGNATURE"; // Signature validation failed.
string constant ASSET_DATA_MISMATCH = "ASSET_DATA_MISMATCH"; // Asset data must be the same for each order.
/// fillOrder validation errors ///
string constant INVALID_TAKER_AMOUNT = "INVALID_TAKER_AMOUNT"; // takerAssetFillAmount cannot equal 0.

View File

@@ -33,12 +33,14 @@ contract MAssetProxyDispatcher is
);
/// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.
/// @param assetMetadata Byte array encoded for the respective asset proxy.
/// @param assetData Byte array encoded for the respective asset proxy.
/// @param assetProxyId Id of assetProxy to dispach to.
/// @param from Address to transfer token from.
/// @param to Address to transfer token to.
/// @param amount Amount of token to transfer.
function dispatchTransferFrom(
bytes memory assetMetadata,
bytes memory assetData,
uint8 assetProxyId,
address from,
address to,
uint256 amount

View File

@@ -31,7 +31,8 @@ contract DummyERC20Token is Mintable, Ownable {
string _name,
string _symbol,
uint256 _decimals,
uint256 _totalSupply)
uint256 _totalSupply
)
public
{
name = _name;

View File

@@ -0,0 +1,63 @@
/*
The MIT License (MIT)
Copyright (c) 2016 Smart Contract Solutions, Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
pragma solidity ^0.4.24;
import "../../tokens/ERC721Token/IERC721Receiver.sol";
contract DummyERC721Receiver is
IERC721Receiver
{
event TokenReceived(
address from,
uint256 tokenId,
bytes data
);
/**
* @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient
* after a `safetransfer`. This function MAY throw to revert and reject the
* transfer. This function MUST use 50,000 gas or less. Return of other
* than the magic value MUST result in the transaction being reverted.
* Note: the contract address is always the message sender.
* @param _from The sending address
* @param _tokenId The NFT identifier which is being transfered
* @param _data Additional data with no specified format
* @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
*/
function onERC721Received(
address _from,
uint256 _tokenId,
bytes _data
)
public
returns (bytes4)
{
emit TokenReceived(_from, _tokenId, _data);
return ERC721_RECEIVED;
}
}

View File

@@ -34,7 +34,8 @@ contract DummyERC721Token is
*/
constructor (
string name,
string symbol)
string symbol
)
public
ERC721Token(name, symbol)
{}

View File

@@ -0,0 +1,56 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../protocol/AssetProxy/ERC20Proxy.sol";
import "../../protocol/AssetProxy/ERC721Proxy.sol";
contract TestAssetDataDecoders is
ERC721Proxy
{
/// @dev Decodes ERC721 Asset data.
/// @param assetData Encoded byte array.
/// @return proxyId Intended ERC721 proxy id.
/// @return token ERC721 token address.
/// @return tokenId ERC721 token id.
/// @return receiverData Additional data with no specific format, which
/// is passed to the receiving contract's onERC721Received.
function publicDecodeERC721Data(bytes memory assetData)
public
pure
returns (
address token,
uint256 tokenId,
bytes memory receiverData
)
{
(
token,
tokenId,
receiverData
) = decodeERC721AssetData(assetData);
return (
token,
tokenId,
receiverData
);
}
}

View File

@@ -23,12 +23,13 @@ import "../../protocol/Exchange/MixinAssetProxyDispatcher.sol";
contract TestAssetProxyDispatcher is MixinAssetProxyDispatcher {
function publicDispatchTransferFrom(
bytes memory assetMetadata,
bytes memory assetData,
uint8 assetProxyId,
address from,
address to,
uint256 amount)
public
{
dispatchTransferFrom(assetMetadata, from, to, amount);
dispatchTransferFrom(assetData, assetProxyId, from, to, amount);
}
}

View File

@@ -28,24 +28,24 @@ contract TestLibBytes is
/// @dev Pops the last byte off of a byte array by modifying its length.
/// @param b Byte array that will be modified.
/// @return The byte that was popped off.
function publicPopByte(bytes memory b)
function publicPopLastByte(bytes memory b)
public
pure
returns (bytes memory, bytes1 result)
{
result = popByte(b);
result = popLastByte(b);
return (b, result);
}
/// @dev Pops the last 20 bytes off of a byte array by modifying its length.
/// @param b Byte array that will be modified.
/// @return The 20 byte address that was popped off.
function publicPopAddress(bytes memory b)
function publicPopLast20Bytes(bytes memory b)
public
pure
returns (bytes memory, address result)
{
result = popAddress(b);
result = popLast20Bytes(b);
return (b, result);
}
@@ -62,13 +62,29 @@ contract TestLibBytes is
return equal;
}
/// @dev Performs a deep copy of a byte array onto another byte array of greater than or equal length.
/// @param dest Byte array that will be overwritten with source bytes.
/// @param source Byte array to copy onto dest bytes.
function publicDeepCopyBytes(
bytes memory dest,
bytes memory source
)
public
pure
returns (bytes memory)
{
deepCopyBytes(dest, source);
return dest;
}
/// @dev Reads an address from a position in a byte array.
/// @param b Byte array containing an address.
/// @param index Index in byte array of address.
/// @return address from byte array.
function publicReadAddress(
bytes memory b,
uint256 index)
uint256 index
)
public
pure
returns (address result)
@@ -84,7 +100,8 @@ contract TestLibBytes is
function publicWriteAddress(
bytes memory b,
uint256 index,
address input)
address input
)
public
pure
returns (bytes memory)
@@ -99,7 +116,8 @@ contract TestLibBytes is
/// @return bytes32 value from byte array.
function publicReadBytes32(
bytes memory b,
uint256 index)
uint256 index
)
public
pure
returns (bytes32 result)
@@ -115,7 +133,8 @@ contract TestLibBytes is
function publicWriteBytes32(
bytes memory b,
uint256 index,
bytes32 input)
bytes32 input
)
public
pure
returns (bytes memory)
@@ -130,7 +149,8 @@ contract TestLibBytes is
/// @return uint256 value from byte array.
function publicReadUint256(
bytes memory b,
uint256 index)
uint256 index
)
public
pure
returns (uint256 result)
@@ -146,7 +166,8 @@ contract TestLibBytes is
function publicWriteUint256(
bytes memory b,
uint256 index,
uint256 input)
uint256 input
)
public
pure
returns (bytes memory)
@@ -166,4 +187,38 @@ contract TestLibBytes is
result = readFirst4(b);
return result;
}
/// @dev Reads nested bytes from a specific position.
/// @param b Byte array containing nested bytes.
/// @param index Index of nested bytes.
/// @return result Nested bytes.
function publicReadBytes(
bytes memory b,
uint256 index
)
public
pure
returns (bytes memory result)
{
result = readBytes(b, index);
return result;
}
/// @dev Inserts bytes at a specific position in a byte array.
/// @param b Byte array to insert <input> into.
/// @param index Index in byte array of <input>.
/// @param input bytes to insert.
/// @return b Updated input byte array
function publicWriteBytes(
bytes memory b,
uint256 index,
bytes memory input
)
public
pure
returns (bytes memory)
{
writeBytes(b, index, input);
return b;
}
}

View File

@@ -0,0 +1,56 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.4.24;
import "../../utils/LibMem/LibMem.sol";
contract TestLibMem is
LibMem
{
/// @dev Copies a block of memory from one location to another.
/// @param mem Memory contents we want to apply memCopy to
/// @param dest Destination offset into <mem>.
/// @param source Source offset into <mem>.
/// @param length Length of bytes to copy from <source> to <dest>
/// @return mem Memory contents after calling memCopy.
function testMemcpy(
bytes mem,
uint256 dest,
uint256 source,
uint256 length
)
public // not external, we need input in memory
pure
returns (bytes)
{
// Sanity check. Overflows are not checked.
require(source + length <= mem.length);
require(dest + length <= mem.length);
// Get pointer to memory contents
uint256 offset = getMemAddress(mem) + 32;
// Execute memCopy adjusted for memory array location
memCopy(offset + dest, offset + source, length);
// Return modified memory contents
return mem;
}
}

View File

@@ -18,31 +18,36 @@
pragma solidity ^0.4.24;
contract LibBytes {
import "../LibMem/LibMem.sol";
contract LibBytes is
LibMem
{
// Revert reasons
string constant GT_ZERO_LENGTH_REQUIRED = "Length must be greater than 0.";
string constant GTE_4_LENGTH_REQUIRED = "Length must be greater than or equal to 4.";
string constant GTE_20_LENGTH_REQUIRED = "Length must be greater than or equal to 20.";
string constant GTE_32_LENGTH_REQUIRED = "Length must be greater than or equal to 32.";
string constant INDEX_OUT_OF_BOUNDS = "Specified array index is out of bounds.";
string constant GREATER_THAN_ZERO_LENGTH_REQUIRED = "GREATER_THAN_ZERO_LENGTH_REQUIRED";
string constant GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED";
string constant GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED";
string constant GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED";
string constant GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED";
string constant GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED";
/// @dev Pops the last byte off of a byte array by modifying its length.
/// @param b Byte array that will be modified.
/// @return The byte that was popped off.
function popByte(bytes memory b)
function popLastByte(bytes memory b)
internal
pure
returns (bytes1 result)
{
require(
b.length > 0,
GT_ZERO_LENGTH_REQUIRED
GREATER_THAN_ZERO_LENGTH_REQUIRED
);
// Store last byte.
result = b[b.length - 1];
assembly {
// Decrement length of byte array.
let newLen := sub(mload(b), 1)
@@ -54,14 +59,14 @@ contract LibBytes {
/// @dev Pops the last 20 bytes off of a byte array by modifying its length.
/// @param b Byte array that will be modified.
/// @return The 20 byte address that was popped off.
function popAddress(bytes memory b)
function popLast20Bytes(bytes memory b)
internal
pure
returns (address result)
{
require(
b.length >= 20,
GTE_20_LENGTH_REQUIRED
GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED
);
// Store last 20 bytes.
@@ -75,41 +80,6 @@ contract LibBytes {
return result;
}
/// @dev Tests equality of two byte arrays.
/// @param lhs First byte array to compare.
/// @param rhs Second byte array to compare.
/// @return True if arrays are the same. False otherwise.
function areBytesEqual(
bytes memory lhs,
bytes memory rhs
)
internal
pure
returns (bool equal)
{
assembly {
// Get the number of words occupied by <lhs>
let lenFullWords := div(add(mload(lhs), 0x1F), 0x20)
// Add 1 to the number of words, to account for the length field
lenFullWords := add(lenFullWords, 0x1)
// Test equality word-by-word.
// Terminates early if there is a mismatch.
for {let i := 0} lt(i, lenFullWords) {i := add(i, 1)} {
let lhsWord := mload(add(lhs, mul(i, 0x20)))
let rhsWord := mload(add(rhs, mul(i, 0x20)))
equal := eq(lhsWord, rhsWord)
if eq(equal, 0) {
// Break
i := lenFullWords
}
}
}
return equal;
}
/// @dev Reads an address from a position in a byte array.
/// @param b Byte array containing an address.
/// @param index Index in byte array of address.
@@ -124,8 +94,8 @@ contract LibBytes {
{
require(
b.length >= index + 20, // 20 is length of address
GTE_20_LENGTH_REQUIRED
);
GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED
);
// Add offset to index:
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
@@ -156,8 +126,8 @@ contract LibBytes {
{
require(
b.length >= index + 20, // 20 is length of address
GTE_20_LENGTH_REQUIRED
);
GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED
);
// Add offset to index:
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
@@ -195,7 +165,7 @@ contract LibBytes {
{
require(
b.length >= index + 32,
GTE_32_LENGTH_REQUIRED
GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED
);
// Arrays are prefixed by a 256 bit length parameter
@@ -222,7 +192,7 @@ contract LibBytes {
{
require(
b.length >= index + 32,
GTE_32_LENGTH_REQUIRED
GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED
);
// Arrays are prefixed by a 256 bit length parameter
@@ -274,11 +244,130 @@ contract LibBytes {
{
require(
b.length >= 4,
GTE_4_LENGTH_REQUIRED
GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED
);
assembly {
result := mload(add(b, 32))
}
return result;
}
/// @dev Reads nested bytes from a specific position.
/// @param b Byte array containing nested bytes.
/// @param index Index of nested bytes.
/// @return result Nested bytes.
function readBytes(
bytes memory b,
uint256 index
)
internal
pure
returns (bytes memory result)
{
// Read length of nested bytes
uint256 nestedBytesLength = readUint256(b, index);
index += 32;
// Assert length of <b> is valid, given
// length of nested bytes
require(
b.length >= index + nestedBytesLength,
GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED
);
// Allocate memory and copy value to result
result = new bytes(nestedBytesLength);
memCopy(
getMemAddress(result) + 32, // +32 skips array length
getMemAddress(b) + index + 32,
nestedBytesLength
);
return result;
}
/// @dev Inserts bytes at a specific position in a byte array.
/// @param b Byte array to insert <input> into.
/// @param index Index in byte array of <input>.
/// @param input bytes to insert.
function writeBytes(
bytes memory b,
uint256 index,
bytes memory input
)
internal
pure
{
// Assert length of <b> is valid, given
// length of input
require(
b.length >= index + 32 /* 32 bytes to store length */ + input.length,
GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED
);
// Copy <input> into <b>
memCopy(
getMemAddress(b) + 32 + index, // +32 to skip length of <b>
getMemAddress(input), // includes length of <input>
input.length + 32 // +32 bytes to store <input> length
);
}
/// @dev Tests equality of two byte arrays.
/// @param lhs First byte array to compare.
/// @param rhs Second byte array to compare.
/// @return True if arrays are the same. False otherwise.
function areBytesEqual(
bytes memory lhs,
bytes memory rhs
)
internal
pure
returns (bool equal)
{
assembly {
// Get the number of words occupied by <lhs>
let lenFullWords := div(add(mload(lhs), 0x1F), 0x20)
// Add 1 to the number of words, to account for the length field
lenFullWords := add(lenFullWords, 0x1)
// Test equality word-by-word.
// Terminates early if there is a mismatch.
for {let i := 0} lt(i, lenFullWords) {i := add(i, 1)} {
let lhsWord := mload(add(lhs, mul(i, 0x20)))
let rhsWord := mload(add(rhs, mul(i, 0x20)))
equal := eq(lhsWord, rhsWord)
if eq(equal, 0) {
// Break
i := lenFullWords
}
}
}
return equal;
}
/// @dev Performs a deep copy of a byte array onto another byte array of greater than or equal length.
/// @param dest Byte array that will be overwritten with source bytes.
/// @param source Byte array to copy onto dest bytes.
function deepCopyBytes(
bytes memory dest,
bytes memory source
)
internal
pure
{
uint256 sourceLen = source.length;
// Dest length must be >= source length, or some bytes would not be copied.
require(
dest.length >= sourceLen,
GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED
);
memCopy(
getMemAddress(dest) + 32, // +32 to skip length of <dest>
getMemAddress(source) + 32, // +32 to skip length of <source>
sourceLen
);
}
}

View File

@@ -0,0 +1,140 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.4.24;
contract LibMem
{
/// @dev Gets the memory address for a byte array.
/// @param input Byte array to lookup.
/// @return memoryAddress Memory address of byte array.
function getMemAddress(bytes memory input)
internal
pure
returns (uint256 memoryAddress)
{
assembly {
memoryAddress := input
}
return memoryAddress;
}
/// @dev Copies `length` bytes from memory location `source` to `dest`.
/// @param dest memory address to copy bytes to.
/// @param source memory address to copy bytes from.
/// @param length number of bytes to copy.
function memCopy(
uint256 dest,
uint256 source,
uint256 length
)
internal
pure
{
if (length < 32) {
// Handle a partial word by reading destination and masking
// off the bits we are interested in.
// This correctly handles overlap, zero lengths and source == dest
assembly {
let mask := sub(exp(256, sub(32, length)), 1)
let s := and(mload(source), not(mask))
let d := and(mload(dest), mask)
mstore(dest, or(s, d))
}
} else {
// Skip the O(length) loop when source == dest.
if (source == dest) {
return;
}
// For large copies we copy whole words at a time. The final
// word is aligned to the end of the range (instead of after the
// previous) to handle partial words. So a copy will look like this:
//
// ####
// ####
// ####
// ####
//
// We handle overlap in the source and destination range by
// changing the copying direction. This prevents us from
// overwriting parts of source that we still need to copy.
//
// This correctly handles source == dest
//
if (source > dest) {
assembly {
// Record the total number of full words to copy
let nWords := div(length, 32)
// We subtract 32 from `sEnd` and `dEnd` because it
// is easier to compare with in the loop, and these
// are also the addresses we need for copying the
// last bytes.
length := sub(length, 32)
let sEnd := add(source, length)
let dEnd := add(dest, length)
// Remember the last 32 bytes of source
// This needs to be done here and not after the loop
// because we may have overwritten the last bytes in
// source already due to overlap.
let last := mload(sEnd)
// Copy whole words front to back
for {let i := 0} lt(i, nWords) {i := add(i, 1)} {
mstore(dest, mload(source))
source := add(source, 32)
dest := add(dest, 32)
}
// Write the last 32 bytes
mstore(dEnd, last)
}
} else {
assembly {
// Record the total number of full words to copy
let nWords := div(length, 32)
// We subtract 32 from `sEnd` and `dEnd` because those
// are the starting points when copying a word at the end.
length := sub(length, 32)
let sEnd := add(source, length)
let dEnd := add(dest, length)
// Remember the first 32 bytes of source
// This needs to be done here and not after the loop
// because we may have overwritten the first bytes in
// source already due to overlap.
let first := mload(source)
// Copy whole words back to front
for {let i := 0} lt(i, nWords) {i := add(i, 1)} {
mstore(dEnd, mload(sEnd))
sEnd := sub(sEnd, 32)
dEnd := sub(dEnd, 32)
}
// Write the first 32 bytes
mstore(dest, first)
}
}
}
}
}

View File

@@ -2,6 +2,7 @@ import { ContractArtifact } from '@0xproject/sol-compiler';
import * as AssetProxyOwner from '../artifacts/AssetProxyOwner.json';
import * as DummyERC20Token from '../artifacts/DummyERC20Token.json';
import * as DummyERC721Receiver from '../artifacts/DummyERC721Receiver.json';
import * as DummyERC721Token from '../artifacts/DummyERC721Token.json';
import * as ERC20Proxy from '../artifacts/ERC20Proxy.json';
import * as ERC721Proxy from '../artifacts/ERC721Proxy.json';
@@ -9,8 +10,10 @@ import * as Exchange from '../artifacts/Exchange.json';
import * as MixinAuthorizable from '../artifacts/MixinAuthorizable.json';
import * as MultiSigWallet from '../artifacts/MultiSigWallet.json';
import * as MultiSigWalletWithTimeLock from '../artifacts/MultiSigWalletWithTimeLock.json';
import * as TestAssetDataDecoders from '../artifacts/TestAssetDataDecoders.json';
import * as TestAssetProxyDispatcher from '../artifacts/TestAssetProxyDispatcher.json';
import * as TestLibBytes from '../artifacts/TestLibBytes.json';
import * as TestLibMem from '../artifacts/TestLibMem.json';
import * as TestLibs from '../artifacts/TestLibs.json';
import * as TestSignatureValidator from '../artifacts/TestSignatureValidator.json';
import * as TokenRegistry from '../artifacts/TokenRegistry.json';
@@ -21,6 +24,7 @@ import * as ZRX from '../artifacts/ZRXToken.json';
export const artifacts = {
AssetProxyOwner: (AssetProxyOwner as any) as ContractArtifact,
DummyERC20Token: (DummyERC20Token as any) as ContractArtifact,
DummyERC721Receiver: (DummyERC721Receiver as any) as ContractArtifact,
DummyERC721Token: (DummyERC721Token as any) as ContractArtifact,
ERC20Proxy: (ERC20Proxy as any) as ContractArtifact,
ERC721Proxy: (ERC721Proxy as any) as ContractArtifact,
@@ -30,7 +34,9 @@ export const artifacts = {
MultiSigWallet: (MultiSigWallet as any) as ContractArtifact,
MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact,
TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact,
TestAssetDataDecoders: (TestAssetDataDecoders as any) as ContractArtifact,
TestLibBytes: (TestLibBytes as any) as ContractArtifact,
TestLibMem: (TestLibMem as any) as ContractArtifact,
TestLibs: (TestLibs as any) as ContractArtifact,
TestSignatureValidator: (TestSignatureValidator as any) as ContractArtifact,
TokenRegistry: (TokenRegistry as any) as ContractArtifact,

View File

@@ -0,0 +1,63 @@
import * as chai from 'chai';
import * as _ from 'lodash';
import { constants } from './constants';
const expect = chai.expect;
function _expectEitherErrorAsync<T>(p: Promise<T>, error1: string, error2: string): PromiseLike<void> {
return expect(p)
.to.be.rejected()
.then(e => {
expect(e).to.satisfy(
(err: Error) => _.includes(err.message, error1) || _.includes(err.message, error2),
`expected promise to reject with error message that includes "${error1}" or "${error2}", but got: ` +
`"${e.message}"\n`,
);
});
}
/**
* Rejects if the given Promise does not reject with an error indicating
* insufficient funds.
* @param p the Promise which is expected to reject
* @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value.
*/
export function expectInsufficientFundsAsync<T>(p: Promise<T>): PromiseLike<void> {
return _expectEitherErrorAsync(p, 'insufficient funds', "sender doesn't have enough funds");
}
/**
* Rejects if the given Promise does not reject with a "revert" error or the
* given otherError.
* @param p the Promise which is expected to reject
* @param otherError the other error which is accepted as a valid reject error.
* @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value.
*/
export function expectRevertOrOtherErrorAsync<T>(p: Promise<T>, otherError: string): PromiseLike<void> {
return _expectEitherErrorAsync(p, constants.REVERT, otherError);
}
/**
* Rejects if the given Promise does not reject with a "revert" or "always
* failing transaction" error.
* @param p the Promise which is expected to reject
* @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value.
*/
export function expectRevertOrAlwaysFailingTransactionAsync<T>(p: Promise<T>): PromiseLike<void> {
return expectRevertOrOtherErrorAsync(p, 'always failing transaction');
}
/**
* Rejects if the given Promise does not reject with a "revert" or "Contract
* call failed" error.
* @param p the Promise which is expected to reject
* @returns a new Promise which will reject if the conditions are not met and
* otherwise resolve with no value.
*/
export function expectRevertOrContractCallFailedAsync<T>(p: Promise<T>): PromiseLike<void> {
return expectRevertOrOtherErrorAsync<T>(p, 'Contract call failed');
}

View File

@@ -19,8 +19,19 @@ const TESTRPC_PRIVATE_KEYS_STRINGS = [
export const constants = {
INVALID_OPCODE: 'invalid opcode',
REVERT: 'revert',
LIB_BYTES_GREATER_THAN_ZERO_LENGTH_REQUIRED: 'GREATER_THAN_ZERO_LENGTH_REQUIRED',
LIB_BYTES_GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED: 'GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED',
LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED: 'GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED',
LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED: 'GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED',
LIB_BYTES_GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED: 'GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED',
LIB_BYTES_GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED: 'GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED',
ERC20_INSUFFICIENT_BALANCE: 'Insufficient balance to complete transfer.',
ERC20_INSUFFICIENT_ALLOWANCE: 'Insufficient allowance to complete transfer.',
TESTRPC_NETWORK_ID: 50,
AWAIT_TRANSACTION_MINED_MS: 100,
// Note(albrow): In practice V8 and most other engines limit the minimum
// interval for setInterval to 10ms. We still set it to 0 here in order to
// ensure we always use the minimum interval.
AWAIT_TRANSACTION_MINED_MS: 0,
MAX_ETHERTOKEN_WITHDRAW_GAS: 43000,
MAX_TOKEN_TRANSFERFROM_GAS: 80000,
MAX_TOKEN_APPROVE_GAS: 60000,

View File

@@ -1,6 +1,5 @@
import { devConstants } from '@0xproject/dev-utils';
import { CoverageSubprovider, SolCompilerArtifactAdapter } from '@0xproject/sol-cov';
import * as fs from 'fs';
import * as _ from 'lodash';
let coverageSubprovider: CoverageSubprovider;
@@ -15,7 +14,8 @@ export const coverage = {
_getCoverageSubprovider(): CoverageSubprovider {
const defaultFromAddress = devConstants.TESTRPC_FIRST_ADDRESS;
const solCompilerArtifactAdapter = new SolCompilerArtifactAdapter();
const subprovider = new CoverageSubprovider(solCompilerArtifactAdapter, defaultFromAddress);
const isVerbose = true;
const subprovider = new CoverageSubprovider(solCompilerArtifactAdapter, defaultFromAddress, isVerbose);
return subprovider;
},
};

View File

@@ -3,8 +3,8 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';
import { DummyERC20TokenContract } from '../contract_wrappers/generated/dummy_e_r_c20_token';
import { ERC20ProxyContract } from '../contract_wrappers/generated/e_r_c20_proxy';
import { DummyERC20TokenContract } from '../generated_contract_wrappers/dummy_e_r_c20_token';
import { ERC20ProxyContract } from '../generated_contract_wrappers/e_r_c20_proxy';
import { artifacts } from './artifacts';
import { constants } from './constants';

View File

@@ -4,8 +4,8 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';
import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token';
import { ERC721ProxyContract } from '../contract_wrappers/generated/e_r_c721_proxy';
import { DummyERC721TokenContract } from '../generated_contract_wrappers/dummy_e_r_c721_token';
import { ERC721ProxyContract } from '../generated_contract_wrappers/e_r_c721_proxy';
import { artifacts } from './artifacts';
import { constants } from './constants';

View File

@@ -1,10 +1,10 @@
import { AssetProxyId, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { LogEntry, Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { ExchangeContract } from '../contract_wrappers/generated/exchange';
import { ExchangeContract } from '../generated_contract_wrappers/exchange';
import { constants } from './constants';
import { formatters } from './formatters';
@@ -60,14 +60,14 @@ export class ExchangeWrapper {
public async fillOrderNoThrowAsync(
signedOrder: SignedOrder,
from: string,
opts: { takerAssetFillAmount?: BigNumber } = {},
opts: { takerAssetFillAmount?: BigNumber; gas?: number } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
const txHash = await this._exchange.fillOrderNoThrow.sendTransactionAsync(
params.order,
params.takerAssetFillAmount,
params.signature,
{ from },
{ from, gas: opts.gas },
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
@@ -105,14 +105,14 @@ export class ExchangeWrapper {
public async batchFillOrdersNoThrowAsync(
orders: SignedOrder[],
from: string,
opts: { takerAssetFillAmounts?: BigNumber[] } = {},
opts: { takerAssetFillAmounts?: BigNumber[]; gas?: number } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts);
const txHash = await this._exchange.batchFillOrdersNoThrow.sendTransactionAsync(
params.orders,
params.takerAssetFillAmounts,
params.signatures,
{ from },
{ from, gas: opts.gas },
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
@@ -135,14 +135,14 @@ export class ExchangeWrapper {
public async marketSellOrdersNoThrowAsync(
orders: SignedOrder[],
from: string,
opts: { takerAssetFillAmount: BigNumber },
opts: { takerAssetFillAmount: BigNumber; gas?: number },
): Promise<TransactionReceiptWithDecodedLogs> {
const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount);
const txHash = await this._exchange.marketSellOrdersNoThrow.sendTransactionAsync(
params.orders,
params.takerAssetFillAmount,
params.signatures,
{ from },
{ from, gas: opts.gas },
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
@@ -165,14 +165,14 @@ export class ExchangeWrapper {
public async marketBuyOrdersNoThrowAsync(
orders: SignedOrder[],
from: string,
opts: { makerAssetFillAmount: BigNumber },
opts: { makerAssetFillAmount: BigNumber; gas?: number },
): Promise<TransactionReceiptWithDecodedLogs> {
const params = formatters.createMarketBuyOrders(orders, opts.makerAssetFillAmount);
const txHash = await this._exchange.marketBuyOrdersNoThrow.sendTransactionAsync(
params.orders,
params.makerAssetFillAmount,
params.signatures,
{ from },
{ from, gas: opts.gas },
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;

View File

@@ -2,6 +2,7 @@ import { SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
import { constants } from './constants';
import { orderUtils } from './order_utils';
import { BatchCancelOrders, BatchFillOrders, MarketBuyOrders, MarketSellOrders } from './types';
@@ -28,8 +29,11 @@ export const formatters = {
signatures: [],
takerAssetFillAmount,
};
_.forEach(signedOrders, signedOrder => {
_.forEach(signedOrders, (signedOrder, i) => {
const orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder);
if (i !== 0) {
orderWithoutExchangeAddress.takerAssetData = constants.NULL_BYTES;
}
marketSellOrders.orders.push(orderWithoutExchangeAddress);
marketSellOrders.signatures.push(signedOrder.signature);
});
@@ -41,8 +45,11 @@ export const formatters = {
signatures: [],
makerAssetFillAmount,
};
_.forEach(signedOrders, signedOrder => {
_.forEach(signedOrders, (signedOrder, i) => {
const orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder);
if (i !== 0) {
orderWithoutExchangeAddress.makerAssetData = constants.NULL_BYTES;
}
marketBuyOrders.orders.push(orderWithoutExchangeAddress);
marketBuyOrders.signatures.push(signedOrder.signature);
});

View File

@@ -0,0 +1,31 @@
import * as _ from 'lodash';
import { constants } from './constants';
import { web3Wrapper } from './web3_wrapper';
let firstAccount: string | undefined;
/**
* Increases time by the given number of seconds and then mines a block so that
* the current block timestamp has the offset applied.
* @param seconds the number of seconds by which to incrase the time offset.
* @returns a new Promise which will resolve with the new total time offset or
* reject if the time could not be increased.
*/
export async function increaseTimeAndMineBlockAsync(seconds: number): Promise<number> {
if (_.isUndefined(firstAccount)) {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
firstAccount = accounts[0];
}
const offset = await web3Wrapper.increaseTimeAsync(seconds);
// Note: we need to send a transaction after increasing time so
// that a block is actually mined. The contract looks at the
// last mined block for the timestamp.
await web3Wrapper.awaitTransactionSuccessAsync(
await web3Wrapper.sendTransactionAsync({ from: firstAccount, to: firstAccount, value: 0 }),
constants.AWAIT_TRANSACTION_MINED_MS,
);
return offset;
}

View File

@@ -39,6 +39,7 @@ export class LogDecoder {
}
public decodeLogOrThrow<ArgsType extends DecodedLogArgs>(log: LogEntry): LogWithDecodedArgs<ArgsType> | RawLog {
const logWithDecodedArgsOrLog = this._abiDecoder.tryToDecodeLogOrNoop(log);
// tslint:disable-next-line:no-unnecessary-type-assertion
if (_.isUndefined((logWithDecodedArgsOrLog as LogWithDecodedArgs<ArgsType>).args)) {
throw new Error(`Unable to decode log: ${JSON.stringify(log)}`);
}

View File

@@ -1,38 +1,21 @@
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { assetProxyUtils, crypto, orderHashUtils } from '@0xproject/order-utils';
import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils';
import { AssetProxyId, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
import { DummyERC20TokenContract } from '../contract_wrappers/generated/dummy_e_r_c20_token';
import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token';
import { ERC20ProxyContract } from '../contract_wrappers/generated/e_r_c20_proxy';
import { ERC721ProxyContract } from '../contract_wrappers/generated/e_r_c721_proxy';
import {
CancelContractEventArgs,
ExchangeContract,
FillContractEventArgs,
} from '../contract_wrappers/generated/exchange';
import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper';
import { ERC721Wrapper } from '../utils/erc721_wrapper';
import { ExchangeWrapper } from '../utils/exchange_wrapper';
import { OrderFactory } from '../utils/order_factory';
import {
ContractName,
ERC20BalancesByOwner,
ERC721TokenIdsByOwner,
TransferAmountsByMatchOrders as TransferAmounts,
} from '../utils/types';
import { provider, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
export class MatchOrderTester {
private _exchangeWrapper: ExchangeWrapper;
@@ -112,11 +95,6 @@ export class MatchOrderTester {
initialTakerAssetFilledAmountLeft?: BigNumber,
initialTakerAssetFilledAmountRight?: BigNumber,
): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> {
// Test setup & verify preconditions
const makerAddressLeft = signedOrderLeft.makerAddress;
const makerAddressRight = signedOrderRight.makerAddress;
const feeRecipientAddressLeft = signedOrderLeft.feeRecipientAddress;
const feeRecipientAddressRight = signedOrderRight.feeRecipientAddress;
// Verify Left order preconditions
const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync(
orderHashUtils.getOrderHashHex(signedOrderLeft),
@@ -259,11 +237,11 @@ export class MatchOrderTester {
const expectedNewERC20BalancesByOwner = _.cloneDeep(erc20BalancesByOwner);
const expectedNewERC721TokenIdsByOwner = _.cloneDeep(erc721TokenIdsByOwner);
// Left Maker Asset (Right Taker Asset)
const makerAssetProxyIdLeft = assetProxyUtils.decodeProxyDataId(signedOrderLeft.makerAssetData);
const makerAssetProxyIdLeft = assetProxyUtils.decodeAssetDataId(signedOrderLeft.makerAssetData);
if (makerAssetProxyIdLeft === AssetProxyId.ERC20) {
// Decode asset data
const erc20ProxyData = assetProxyUtils.decodeERC20ProxyData(signedOrderLeft.makerAssetData);
const makerAssetAddressLeft = erc20ProxyData.tokenAddress;
const erc20AssetData = assetProxyUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData);
const makerAssetAddressLeft = erc20AssetData.tokenAddress;
const takerAssetAddressRight = makerAssetAddressLeft;
// Left Maker
expectedNewERC20BalancesByOwner[makerAddressLeft][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
@@ -281,9 +259,9 @@ export class MatchOrderTester {
][makerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByTaker);
} else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) {
// Decode asset data
const erc721ProxyData = assetProxyUtils.decodeERC721ProxyData(signedOrderLeft.makerAssetData);
const makerAssetAddressLeft = erc721ProxyData.tokenAddress;
const makerAssetIdLeft = erc721ProxyData.tokenId;
const erc721AssetData = assetProxyUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData);
const makerAssetAddressLeft = erc721AssetData.tokenAddress;
const makerAssetIdLeft = erc721AssetData.tokenId;
const takerAssetAddressRight = makerAssetAddressLeft;
const takerAssetIdRight = makerAssetIdLeft;
// Left Maker
@@ -294,11 +272,11 @@ export class MatchOrderTester {
}
// Left Taker Asset (Right Maker Asset)
// Note: This exchange is only between the order makers: the Taker does not receive any of the left taker asset.
const takerAssetProxyIdLeft = assetProxyUtils.decodeProxyDataId(signedOrderLeft.takerAssetData);
const takerAssetProxyIdLeft = assetProxyUtils.decodeAssetDataId(signedOrderLeft.takerAssetData);
if (takerAssetProxyIdLeft === AssetProxyId.ERC20) {
// Decode asset data
const erc20ProxyData = assetProxyUtils.decodeERC20ProxyData(signedOrderLeft.takerAssetData);
const takerAssetAddressLeft = erc20ProxyData.tokenAddress;
const erc20AssetData = assetProxyUtils.decodeERC20AssetData(signedOrderLeft.takerAssetData);
const takerAssetAddressLeft = erc20AssetData.tokenAddress;
const makerAssetAddressRight = takerAssetAddressLeft;
// Left Maker
expectedNewERC20BalancesByOwner[makerAddressLeft][takerAssetAddressLeft] = expectedNewERC20BalancesByOwner[
@@ -312,9 +290,9 @@ export class MatchOrderTester {
);
} else if (takerAssetProxyIdLeft === AssetProxyId.ERC721) {
// Decode asset data
const erc721ProxyData = assetProxyUtils.decodeERC721ProxyData(signedOrderRight.makerAssetData);
const makerAssetAddressRight = erc721ProxyData.tokenAddress;
const makerAssetIdRight = erc721ProxyData.tokenId;
const erc721AssetData = assetProxyUtils.decodeERC721AssetData(signedOrderRight.makerAssetData);
const makerAssetAddressRight = erc721AssetData.tokenAddress;
const makerAssetIdRight = erc721AssetData.tokenId;
const takerAssetAddressLeft = makerAssetAddressRight;
const takerAssetIdLeft = makerAssetIdRight;
// Right Maker

View File

@@ -3,10 +3,9 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { AssetProxyOwnerContract } from '../contract_wrappers/generated/asset_proxy_owner';
import { MultiSigWalletContract } from '../contract_wrappers/generated/multi_sig_wallet';
import { AssetProxyOwnerContract } from '../generated_contract_wrappers/asset_proxy_owner';
import { MultiSigWalletContract } from '../generated_contract_wrappers/multi_sig_wallet';
import { constants } from './constants';
import { LogDecoder } from './log_decoder';
export class MultiSigWrapper {
@@ -45,6 +44,7 @@ export class MultiSigWrapper {
txId: BigNumber,
from: string,
): Promise<TransactionReceiptWithDecodedLogs> {
// tslint:disable-next-line:no-unnecessary-type-assertion
const txHash = await (this
._multiSig as AssetProxyOwnerContract).executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from });
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);

View File

@@ -1,7 +1,6 @@
import { generatePseudoRandomSalt, orderHashUtils } from '@0xproject/order-utils';
import { Order, SignatureType, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
import { constants } from './constants';
import { signingUtils } from './signing_utils';

View File

@@ -1,7 +1,7 @@
import { OrderWithoutExchangeAddress, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import ethUtil = require('ethereumjs-util');
import { constants } from './constants';
import { CancelOrder, MatchOrder } from './types';
export const orderUtils = {
@@ -44,6 +44,8 @@ export const orderUtils = {
leftSignature: signedOrderLeft.signature,
rightSignature: signedOrderRight.signature,
};
fill.right.makerAssetData = constants.NULL_BYTES;
fill.right.takerAssetData = constants.NULL_BYTES;
return fill;
},
};

View File

@@ -0,0 +1,27 @@
import { devConstants } from '@0xproject/dev-utils';
import { ProfilerSubprovider, SolCompilerArtifactAdapter } from '@0xproject/sol-cov';
import * as _ from 'lodash';
let profilerSubprovider: ProfilerSubprovider;
export const profiler = {
start(): void {
profiler.getProfilerSubproviderSingleton().start();
},
stop(): void {
profiler.getProfilerSubproviderSingleton().stop();
},
getProfilerSubproviderSingleton(): ProfilerSubprovider {
if (_.isUndefined(profilerSubprovider)) {
profilerSubprovider = profiler._getProfilerSubprovider();
}
return profilerSubprovider;
},
_getProfilerSubprovider(): ProfilerSubprovider {
const defaultFromAddress = devConstants.TESTRPC_FIRST_ADDRESS;
const solCompilerArtifactAdapter = new SolCompilerArtifactAdapter();
const isVerbose = true;
const subprovider = new ProfilerSubprovider(solCompilerArtifactAdapter, defaultFromAddress, isVerbose);
return subprovider;
},
};

View File

@@ -0,0 +1,21 @@
import { devConstants } from '@0xproject/dev-utils';
import { RevertTraceSubprovider, SolCompilerArtifactAdapter } from '@0xproject/sol-cov';
import * as _ from 'lodash';
let revertTraceSubprovider: RevertTraceSubprovider;
export const revertTrace = {
getRevertTraceSubproviderSingleton(): RevertTraceSubprovider {
if (_.isUndefined(revertTraceSubprovider)) {
revertTraceSubprovider = revertTrace._getRevertTraceSubprovider();
}
return revertTraceSubprovider;
},
_getRevertTraceSubprovider(): RevertTraceSubprovider {
const defaultFromAddress = devConstants.TESTRPC_FIRST_ADDRESS;
const solCompilerArtifactAdapter = new SolCompilerArtifactAdapter();
const isVerbose = true;
const subprovider = new RevertTraceSubprovider(solCompilerArtifactAdapter, defaultFromAddress, isVerbose);
return subprovider;
},
};

View File

@@ -1,7 +1,7 @@
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import { TokenRegistryContract } from '../contract_wrappers/generated/token_registry';
import { TokenRegistryContract } from '../generated_contract_wrappers/token_registry';
import { Token } from './types';

View File

@@ -1,6 +1,6 @@
import { Order, OrderWithoutExchangeAddress } from '@0xproject/types';
import { OrderWithoutExchangeAddress } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import { AbiDefinition, ContractAbi } from 'ethereum-types';
import { AbiDefinition } from 'ethereum-types';
export interface ERC20BalancesByOwner {
[ownerAddress: string]: {
@@ -90,11 +90,14 @@ export enum ContractName {
AccountLevels = 'AccountLevels',
EtherDelta = 'EtherDelta',
Arbitrage = 'Arbitrage',
TestAssetDataDecoders = 'TestAssetDataDecoders',
TestAssetProxyDispatcher = 'TestAssetProxyDispatcher',
TestLibMem = 'TestLibMem',
TestLibs = 'TestLibs',
TestSignatureValidator = 'TestSignatureValidator',
ERC20Proxy = 'ERC20Proxy',
ERC721Proxy = 'ERC721Proxy',
DummyERC721Receiver = 'DummyERC721Receiver',
DummyERC721Token = 'DummyERC721Token',
TestLibBytes = 'TestLibBytes',
Authorizable = 'Authorizable',

View File

@@ -1,19 +1,82 @@
import { devConstants, env, EnvVars, web3Factory } from '@0xproject/dev-utils';
import { prependSubprovider } from '@0xproject/subproviders';
import { logUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { Provider } from 'ethereum-types';
import * as _ from 'lodash';
import { coverage } from './coverage';
import { profiler } from './profiler';
import { revertTrace } from './revert_trace';
export const txDefaults = {
enum ProviderType {
Ganache = 'ganache',
Geth = 'geth',
}
let testProvider: ProviderType;
switch (process.env.TEST_PROVIDER) {
case undefined:
testProvider = ProviderType.Ganache;
break;
case 'ganache':
testProvider = ProviderType.Ganache;
break;
case 'geth':
testProvider = ProviderType.Geth;
break;
default:
throw new Error(`Unknown TEST_PROVIDER: ${process.env.TEST_PROVIDER}`);
}
const ganacheTxDefaults = {
from: devConstants.TESTRPC_FIRST_ADDRESS,
gas: devConstants.GAS_LIMIT,
};
const providerConfigs = { shouldUseInProcessGanache: true };
const gethTxDefaults = {
from: devConstants.TESTRPC_FIRST_ADDRESS,
};
export const txDefaults = testProvider === ProviderType.Ganache ? ganacheTxDefaults : gethTxDefaults;
const gethConfigs = {
shouldUseInProcessGanache: false,
rpcUrl: 'http://localhost:8501',
shouldUseFakeGasEstimate: false,
};
const ganacheConfigs = {
shouldUseInProcessGanache: true,
};
const providerConfigs = testProvider === ProviderType.Ganache ? ganacheConfigs : gethConfigs;
export const provider = web3Factory.getRpcProvider(providerConfigs);
const isCoverageEnabled = env.parseBoolean(EnvVars.SolidityCoverage);
const isProfilerEnabled = env.parseBoolean(EnvVars.SolidityProfiler);
const isRevertTraceEnabled = env.parseBoolean(EnvVars.SolidityRevertTrace);
const enabledSubproviderCount = _.filter([isCoverageEnabled, isProfilerEnabled, isRevertTraceEnabled], _.identity)
.length;
if (enabledSubproviderCount > 1) {
throw new Error(`Only one of coverage, profiler, or revert trace subproviders can be enabled at a time`);
}
if (isCoverageEnabled) {
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
prependSubprovider(provider, coverageSubprovider);
}
if (isProfilerEnabled) {
if (testProvider === ProviderType.Ganache) {
logUtils.warn(
"Gas costs in Ganache traces are incorrect and we don't recommend using it for profiling. Please switch to Geth",
);
process.exit(1);
}
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
logUtils.log(
"By default profilerSubprovider is stopped so that you don't get noise from setup code. Don't forget to start it before the code you want to profile and stop it afterwards",
);
profilerSubprovider.stop();
prependSubprovider(provider, profilerSubprovider);
}
if (isRevertTraceEnabled) {
const revertTraceSubprovider = revertTrace.getRevertTraceSubproviderSingleton();
prependSubprovider(provider, revertTraceSubprovider);
}
export const web3Wrapper = new Web3Wrapper(provider);

View File

@@ -1,11 +1,10 @@
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import * as chai from 'chai';
import 'make-promises-safe';
import * as Web3 from 'web3';
import { MixinAuthorizableContract } from '../../src/contract_wrappers/generated/mixin_authorizable';
import { MixinAuthorizableContract } from '../../src/generated_contract_wrappers/mixin_authorizable';
import { artifacts } from '../../src/utils/artifacts';
import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions';
import { chaiSetup } from '../../src/utils/chai_setup';
import { constants } from '../../src/utils/constants';
import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper';
@@ -44,9 +43,9 @@ describe('Authorizable', () => {
});
describe('addAuthorizedAddress', () => {
it('should throw if not called by owner', async () => {
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should allow owner to add an authorized address', async () => {
await web3Wrapper.awaitTransactionSuccessAsync(
@@ -61,9 +60,9 @@ describe('Authorizable', () => {
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS,
);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
).to.be.rejectedWith(constants.REVERT);
);
});
});
@@ -73,11 +72,11 @@ describe('Authorizable', () => {
await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS,
);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
from: notOwner,
}),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should allow owner to remove an authorized address', async () => {
@@ -96,11 +95,11 @@ describe('Authorizable', () => {
});
it('should throw if owner attempts to remove an address that is not authorized', async () => {
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
authorizable.removeAuthorizedAddress.sendTransactionAsync(address, {
from: owner,
}),
).to.be.rejectedWith(constants.REVERT);
);
});
});

View File

@@ -0,0 +1,86 @@
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import ethUtil = require('ethereumjs-util');
import { TestAssetDataDecodersContract } from '../../src/generated_contract_wrappers/test_asset_data_decoders';
import { artifacts } from '../../src/utils/artifacts';
import { chaiSetup } from '../../src/utils/chai_setup';
import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('TestAssetDataDecoders', () => {
let testAssetProxyDecoder: TestAssetDataDecodersContract;
let testAddress: string;
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
// Setup accounts & addresses
const accounts = await web3Wrapper.getAvailableAddressesAsync();
testAddress = accounts[0];
// Deploy TestLibMem
testAssetProxyDecoder = await TestAssetDataDecodersContract.deployFrom0xArtifactAsync(
artifacts.TestAssetDataDecoders,
provider,
txDefaults,
);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('Asset Data Decoders', () => {
it('should correctly decode ERC721 asset data', async () => {
const tokenId = generatePseudoRandomSalt();
const encodedAssetData = assetProxyUtils.encodeERC721AssetData(testAddress, tokenId);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
const expectedDecodedAssetData = assetProxyUtils.decodeERC721AssetData(encodedAssetData);
let decodedTokenAddress: string;
let decodedTokenId: BigNumber;
let decodedData: string;
[
decodedTokenAddress,
decodedTokenId,
decodedData,
] = await testAssetProxyDecoder.publicDecodeERC721Data.callAsync(encodedAssetDataWithoutProxyId);
expect(decodedTokenAddress).to.be.equal(expectedDecodedAssetData.tokenAddress);
expect(decodedTokenId).to.be.bignumber.equal(expectedDecodedAssetData.tokenId);
expect(decodedData).to.be.equal(expectedDecodedAssetData.receiverData);
});
it('should correctly decode ERC721 asset data with receiver data', async () => {
const tokenId = generatePseudoRandomSalt();
const receiverDataFirst32Bytes = ethUtil.bufferToHex(
assetProxyUtils.encodeUint256(generatePseudoRandomSalt()),
);
const receiverDataExtraBytes = 'FFFF';
// We add extra bytes to generate a value that doesn't fit perfectly into one word
const receiverData = receiverDataFirst32Bytes + receiverDataExtraBytes;
const encodedAssetData = assetProxyUtils.encodeERC721AssetData(testAddress, tokenId, receiverData);
const expectedDecodedAssetData = assetProxyUtils.decodeERC721AssetData(encodedAssetData);
let decodedTokenAddress: string;
let decodedTokenId: BigNumber;
let decodedReceiverData: string;
[
decodedTokenAddress,
decodedTokenId,
decodedReceiverData,
] = await testAssetProxyDecoder.publicDecodeERC721Data.callAsync(encodedAssetData);
expect(decodedTokenAddress).to.be.equal(expectedDecodedAssetData.tokenAddress);
expect(decodedTokenId).to.be.bignumber.equal(expectedDecodedAssetData.tokenId);
expect(decodedReceiverData).to.be.equal(expectedDecodedAssetData.receiverData);
});
});
});

View File

@@ -1,25 +1,33 @@
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { assetProxyUtils } from '@0xproject/order-utils';
import { AssetProxyId } from '@0xproject/types';
import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
import * as Web3 from 'web3';
import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token';
import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token';
import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy';
import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy';
import { DummyERC20TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c20_token';
import {
DummyERC721ReceiverContract,
TokenReceivedContractEventArgs,
} from '../../src/generated_contract_wrappers/dummy_e_r_c721_receiver';
import { DummyERC721TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c721_token';
import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c20_proxy';
import { ERC721ProxyContract } from '../../src/generated_contract_wrappers/e_r_c721_proxy';
import { artifacts } from '../../src/utils/artifacts';
import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions';
import { chaiSetup } from '../../src/utils/chai_setup';
import { constants } from '../../src/utils/constants';
import { ERC20Wrapper } from '../../src/utils/erc20_wrapper';
import { ERC721Wrapper } from '../../src/utils/erc721_wrapper';
import { provider, web3Wrapper } from '../../src/utils/web3_wrapper';
import { LogDecoder } from '../../src/utils/log_decoder';
import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('Asset Transfer Proxies', () => {
let owner: string;
let notAuthorized: string;
@@ -29,6 +37,7 @@ describe('Asset Transfer Proxies', () => {
let zrxToken: DummyERC20TokenContract;
let erc721Token: DummyERC721TokenContract;
let erc721Receiver: DummyERC721ReceiverContract;
let erc20Proxy: ERC20ProxyContract;
let erc721Proxy: ERC721ProxyContract;
@@ -70,6 +79,11 @@ describe('Asset Transfer Proxies', () => {
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync(
artifacts.DummyERC721Receiver,
provider,
txDefaults,
);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
@@ -80,14 +94,15 @@ describe('Asset Transfer Proxies', () => {
describe('Transfer Proxy - ERC20', () => {
describe('transferFrom', () => {
it('should successfully transfer tokens', async () => {
// Construct metadata for ERC20 proxy
const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address);
// Construct ERC20 asset data
const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Perform a transfer from makerAddress to takerAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(10);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Proxy.transferFrom.sendTransactionAsync(
encodedProxyMetadata,
encodedAssetDataWithoutProxyId,
makerAddress,
takerAddress,
amount,
@@ -106,14 +121,15 @@ describe('Asset Transfer Proxies', () => {
});
it('should do nothing if transferring 0 amount of a token', async () => {
// Construct metadata for ERC20 proxy
const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address);
// Construct ERC20 asset data
const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Perform a transfer from makerAddress to takerAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(0);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Proxy.transferFrom.sendTransactionAsync(
encodedProxyMetadata,
encodedAssetDataWithoutProxyId,
makerAddress,
takerAddress,
amount,
@@ -132,8 +148,8 @@ describe('Asset Transfer Proxies', () => {
});
it('should throw if allowances are too low', async () => {
// Construct metadata for ERC20 proxy
const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address);
// Construct ERC20 asset data
const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address);
// Create allowance less than transfer amount. Set allowance on proxy.
const allowance = new BigNumber(0);
const transferAmount = new BigNumber(10);
@@ -144,25 +160,26 @@ describe('Asset Transfer Proxies', () => {
constants.AWAIT_TRANSACTION_MINED_MS,
);
// Perform a transfer; expect this to fail.
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
erc20Proxy.transferFrom.sendTransactionAsync(
encodedProxyMetadata,
encodedAssetData,
makerAddress,
takerAddress,
transferAmount,
{ from: notAuthorized },
),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if requesting address is not authorized', async () => {
// Construct metadata for ERC20 proxy
const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address);
// Construct ERC20 asset data
const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Perform a transfer from makerAddress to takerAddress
const amount = new BigNumber(10);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
erc20Proxy.transferFrom.sendTransactionAsync(
encodedProxyMetadata,
encodedAssetDataWithoutProxyId,
makerAddress,
takerAddress,
amount,
@@ -170,7 +187,7 @@ describe('Asset Transfer Proxies', () => {
from: notAuthorized,
},
),
).to.be.rejectedWith(constants.REVERT);
);
});
});
@@ -178,16 +195,17 @@ describe('Asset Transfer Proxies', () => {
it('should succesfully make multiple token transfers', async () => {
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address);
const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
const amount = new BigNumber(10);
const numTransfers = 2;
const assetMetadata = _.times(numTransfers, () => encodedProxyMetadata);
const assetData = _.times(numTransfers, () => encodedAssetDataWithoutProxyId);
const fromAddresses = _.times(numTransfers, () => makerAddress);
const toAddresses = _.times(numTransfers, () => takerAddress);
const amounts = _.times(numTransfers, () => amount);
const txHash = await erc20Proxy.batchTransferFrom.sendTransactionAsync(
assetMetadata,
assetData,
fromAddresses,
toAddresses,
amounts,
@@ -209,23 +227,20 @@ describe('Asset Transfer Proxies', () => {
});
it('should throw if not called by an authorized address', async () => {
const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address);
const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
const amount = new BigNumber(10);
const numTransfers = 2;
const assetMetadata = _.times(numTransfers, () => encodedProxyMetadata);
const assetData = _.times(numTransfers, () => encodedAssetDataWithoutProxyId);
const fromAddresses = _.times(numTransfers, () => makerAddress);
const toAddresses = _.times(numTransfers, () => takerAddress);
const amounts = _.times(numTransfers, () => amount);
return expect(
erc20Proxy.batchTransferFrom.sendTransactionAsync(
assetMetadata,
fromAddresses,
toAddresses,
amounts,
{ from: notAuthorized },
),
).to.be.rejectedWith(constants.REVERT);
return expectRevertOrAlwaysFailingTransactionAsync(
erc20Proxy.batchTransferFrom.sendTransactionAsync(assetData, fromAddresses, toAddresses, amounts, {
from: notAuthorized,
}),
);
});
});
@@ -238,20 +253,17 @@ describe('Asset Transfer Proxies', () => {
describe('Transfer Proxy - ERC721', () => {
describe('transferFrom', () => {
it('should successfully transfer tokens', async () => {
// Construct metadata for ERC721 proxy
const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(
erc721Token.address,
erc721MakerTokenId,
);
// Construct ERC721 asset data
const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Verify pre-condition
const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress);
// Perform a transfer from makerAddress to takerAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(1);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Proxy.transferFrom.sendTransactionAsync(
encodedProxyMetadata,
encodedAssetDataWithoutProxyId,
makerAddress,
takerAddress,
amount,
@@ -264,58 +276,138 @@ describe('Asset Transfer Proxies', () => {
expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress);
});
it('should throw if transferring 0 amount of a token', async () => {
// Construct metadata for ERC721 proxy
const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(
erc721Token.address,
erc721MakerTokenId,
);
it('should not call onERC721Received when transferring to a smart contract without receiver data', async () => {
// Construct ERC721 asset data
const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Verify pre-condition
const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress);
// Perform a transfer from makerAddress to takerAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(0);
return expect(
const amount = new BigNumber(1);
const txHash = await erc721Proxy.transferFrom.sendTransactionAsync(
encodedAssetDataWithoutProxyId,
makerAddress,
erc721Receiver.address,
amount,
{ from: exchangeAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
// Parse transaction logs
const logDecoder = new LogDecoder(web3Wrapper, erc721Receiver.address);
const tx = await logDecoder.getTxWithDecodedLogsAsync(txHash);
// Verify that no log was emitted by erc721 receiver
expect(tx.logs.length).to.be.equal(0);
// Verify transfer was successful
const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
expect(newOwnerMakerAsset).to.be.bignumber.equal(erc721Receiver.address);
});
it('should call onERC721Received when transferring to a smart contract with receiver data', async () => {
// Construct ERC721 asset data
const receiverData = ethUtil.bufferToHex(assetProxyUtils.encodeUint256(generatePseudoRandomSalt()));
const encodedAssetData = assetProxyUtils.encodeERC721AssetData(
erc721Token.address,
erc721MakerTokenId,
receiverData,
);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Verify pre-condition
const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress);
// Perform a transfer from makerAddress to takerAddress
const amount = new BigNumber(1);
const txHash = await erc721Proxy.transferFrom.sendTransactionAsync(
encodedAssetDataWithoutProxyId,
makerAddress,
erc721Receiver.address,
amount,
{ from: exchangeAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
// Parse transaction logs
const logDecoder = new LogDecoder(web3Wrapper, erc721Receiver.address);
const tx = await logDecoder.getTxWithDecodedLogsAsync(txHash);
// Validate log emitted by erc721 receiver
expect(tx.logs.length).to.be.equal(1);
const tokenReceivedLog = tx.logs[0] as LogWithDecodedArgs<TokenReceivedContractEventArgs>;
expect(tokenReceivedLog.args.from).to.be.equal(makerAddress);
expect(tokenReceivedLog.args.tokenId).to.be.bignumber.equal(erc721MakerTokenId);
expect(tokenReceivedLog.args.data).to.be.equal(receiverData);
// Verify transfer was successful
const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
expect(newOwnerMakerAsset).to.be.bignumber.equal(erc721Receiver.address);
});
it('should throw if there is receiver data but contract does not have onERC721Received', async () => {
// Construct ERC721 asset data
const receiverData = ethUtil.bufferToHex(assetProxyUtils.encodeUint256(generatePseudoRandomSalt()));
const encodedAssetData = assetProxyUtils.encodeERC721AssetData(
erc721Token.address,
erc721MakerTokenId,
receiverData,
);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Verify pre-condition
const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress);
// Perform a transfer from makerAddress to takerAddress
const amount = new BigNumber(1);
return expectRevertOrAlwaysFailingTransactionAsync(
erc721Proxy.transferFrom.sendTransactionAsync(
encodedProxyMetadata,
encodedAssetDataWithoutProxyId,
makerAddress,
erc20Proxy.address, // the ERC20 proxy does not have an ERC721 receiver
amount,
{ from: exchangeAddress },
),
);
});
it('should throw if transferring 0 amount of a token', async () => {
// Construct ERC721 asset data
const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Verify pre-condition
const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress);
// Perform a transfer from makerAddress to takerAddress
const amount = new BigNumber(0);
return expectRevertOrAlwaysFailingTransactionAsync(
erc721Proxy.transferFrom.sendTransactionAsync(
encodedAssetDataWithoutProxyId,
makerAddress,
takerAddress,
amount,
{ from: exchangeAddress },
),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if transferring > 1 amount of a token', async () => {
// Construct metadata for ERC721 proxy
const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(
erc721Token.address,
erc721MakerTokenId,
);
// Construct ERC721 asset data
const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Verify pre-condition
const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress);
// Perform a transfer from makerAddress to takerAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(500);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
erc721Proxy.transferFrom.sendTransactionAsync(
encodedProxyMetadata,
encodedAssetDataWithoutProxyId,
makerAddress,
takerAddress,
amount,
{ from: exchangeAddress },
),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if allowances are too low', async () => {
// Construct metadata for ERC721 proxy
const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(
erc721Token.address,
erc721MakerTokenId,
);
// Construct ERC721 asset data
const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Remove transfer approval for makerAddress.
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, false, {
@@ -325,9 +417,9 @@ describe('Asset Transfer Proxies', () => {
);
// Perform a transfer; expect this to fail.
const amount = new BigNumber(1);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
erc20Proxy.transferFrom.sendTransactionAsync(
encodedProxyMetadata,
encodedAssetDataWithoutProxyId,
makerAddress,
takerAddress,
amount,
@@ -335,26 +427,24 @@ describe('Asset Transfer Proxies', () => {
from: notAuthorized,
},
),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if requesting address is not authorized', async () => {
// Construct metadata for ERC721 proxy
const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(
erc721Token.address,
erc721MakerTokenId,
);
// Construct ERC721 asset data
const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Perform a transfer from makerAddress to takerAddress
const amount = new BigNumber(1);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
erc721Proxy.transferFrom.sendTransactionAsync(
encodedProxyMetadata,
encodedAssetDataWithoutProxyId,
makerAddress,
takerAddress,
amount,
{ from: notAuthorized },
),
).to.be.rejectedWith(constants.REVERT);
);
});
});
@@ -364,16 +454,16 @@ describe('Asset Transfer Proxies', () => {
const [makerTokenIdA, makerTokenIdB] = erc721TokensById[makerAddress][erc721Token.address];
const numTransfers = 2;
const assetMetadata = [
assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerTokenIdA),
assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerTokenIdB),
const assetData = [
assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerTokenIdA).slice(0, -2),
assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerTokenIdB).slice(0, -2),
];
const fromAddresses = _.times(numTransfers, () => makerAddress);
const toAddresses = _.times(numTransfers, () => takerAddress);
const amounts = _.times(numTransfers, () => new BigNumber(1));
const txHash = await erc721Proxy.batchTransferFrom.sendTransactionAsync(
assetMetadata,
assetData,
fromAddresses,
toAddresses,
amounts,
@@ -396,23 +486,19 @@ describe('Asset Transfer Proxies', () => {
const [makerTokenIdA, makerTokenIdB] = erc721TokensById[makerAddress][erc721Token.address];
const numTransfers = 2;
const assetMetadata = [
assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerTokenIdA),
assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerTokenIdB),
const assetData = [
assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerTokenIdA).slice(0, -2),
assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerTokenIdB).slice(0, -2),
];
const fromAddresses = _.times(numTransfers, () => makerAddress);
const toAddresses = _.times(numTransfers, () => takerAddress);
const amounts = _.times(numTransfers, () => new BigNumber(1));
return expect(
erc721Proxy.batchTransferFrom.sendTransactionAsync(
assetMetadata,
fromAddresses,
toAddresses,
amounts,
{ from: notAuthorized },
),
).to.be.rejectedWith(constants.REVERT);
return expectRevertOrAlwaysFailingTransactionAsync(
erc721Proxy.batchTransferFrom.sendTransactionAsync(assetData, fromAddresses, toAddresses, amounts, {
from: notAuthorized,
}),
);
});
});
@@ -422,3 +508,5 @@ describe('Asset Transfer Proxies', () => {
});
});
});
// tslint:enable:no-unnecessary-type-assertion
// tslint:disable:max-file-line-count

View File

@@ -2,9 +2,7 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
import 'make-promises-safe';
import * as Web3 from 'web3';
import {
AssetProxyOwnerContract,
@@ -12,18 +10,23 @@ import {
ExecutionContractEventArgs,
ExecutionFailureContractEventArgs,
SubmissionContractEventArgs,
} from '../src/contract_wrappers/generated/asset_proxy_owner';
import { MixinAuthorizableContract } from '../src/contract_wrappers/generated/mixin_authorizable';
} from '../src/generated_contract_wrappers/asset_proxy_owner';
import { MixinAuthorizableContract } from '../src/generated_contract_wrappers/mixin_authorizable';
import { artifacts } from '../src/utils/artifacts';
import {
expectRevertOrAlwaysFailingTransactionAsync,
expectRevertOrContractCallFailedAsync,
} from '../src/utils/assertions';
import { chaiSetup } from '../src/utils/chai_setup';
import { constants } from '../src/utils/constants';
import { increaseTimeAndMineBlockAsync } from '../src/utils/increase_time';
import { MultiSigWrapper } from '../src/utils/multi_sig_wrapper';
import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('AssetProxyOwner', () => {
let owners: string[];
let authorized: string;
@@ -101,7 +104,7 @@ describe('AssetProxyOwner', () => {
});
it('should throw if a null address is included in assetProxyContracts', async () => {
const assetProxyContractAddresses = [erc20Proxy.address, constants.NULL_ADDRESS];
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
AssetProxyOwnerContract.deployFrom0xArtifactAsync(
artifacts.AssetProxyOwner,
provider,
@@ -111,7 +114,7 @@ describe('AssetProxyOwner', () => {
REQUIRED_APPROVALS,
SECONDS_TIME_LOCKED,
),
).to.be.rejectedWith(constants.REVERT);
);
});
});
@@ -120,9 +123,9 @@ describe('AssetProxyOwner', () => {
const notRemoveAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData(
owners[0],
);
return expect(
return expectRevertOrContractCallFailedAsync(
multiSig.isFunctionRemoveAuthorizedAddress.callAsync(notRemoveAuthorizedAddressData),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should return true if data is for removeAuthorizedAddress', async () => {
@@ -139,9 +142,9 @@ describe('AssetProxyOwner', () => {
describe('registerAssetProxy', () => {
it('should throw if not called by multisig', async () => {
const isRegistered = true;
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
multiSig.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, isRegistered, { from: owners[0] }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should register an address if called by multisig after timelock', async () => {
@@ -156,11 +159,12 @@ describe('AssetProxyOwner', () => {
registerAssetProxyData,
owners[0],
);
const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
const txId = log.args.transactionId;
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
await web3Wrapper.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber());
await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber());
const executeTxRes = await multiSigWrapper.executeTransactionAsync(txId, owners[0]);
const registerLog = executeTxRes.logs[0] as LogWithDecodedArgs<AssetProxyRegistrationContractEventArgs>;
@@ -187,7 +191,7 @@ describe('AssetProxyOwner', () => {
const txId = log.args.transactionId;
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
await web3Wrapper.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber());
await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber());
const executeTxRes = await multiSigWrapper.executeTransactionAsync(txId, owners[0]);
const failureLog = executeTxRes.logs[0] as LogWithDecodedArgs<ExecutionFailureContractEventArgs>;
@@ -239,7 +243,7 @@ describe('AssetProxyOwner', () => {
await multiSigWrapper.confirmTransactionAsync(erc20AddAuthorizedAddressTxId, owners[1]);
await multiSigWrapper.confirmTransactionAsync(erc721AddAuthorizedAddressTxId, owners[1]);
await web3Wrapper.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber());
await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber());
await multiSigWrapper.executeTransactionAsync(registerAssetProxyTxId, owners[0]);
await multiSigWrapper.executeTransactionAsync(erc20AddAuthorizedAddressTxId, owners[0]);
await multiSigWrapper.executeTransactionAsync(erc721AddAuthorizedAddressTxId, owners[0]);
@@ -257,9 +261,9 @@ describe('AssetProxyOwner', () => {
const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
const txId = log.args.transactionId;
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if tx destination is not registered', async () => {
@@ -276,9 +280,9 @@ describe('AssetProxyOwner', () => {
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if tx data is not for removeAuthorizedAddress', async () => {
@@ -296,9 +300,9 @@ describe('AssetProxyOwner', () => {
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should execute removeAuthorizedAddress for registered address if fully confirmed', async () => {
@@ -349,9 +353,10 @@ describe('AssetProxyOwner', () => {
const isExecuted = tx[3];
expect(isExecuted).to.equal(true);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
).to.be.rejectedWith(constants.REVERT);
);
});
});
});
// tslint:enable:no-unnecessary-type-assertion

View File

@@ -1,11 +1,12 @@
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { BigNumber, promisify } from '@0xproject/utils';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
import 'make-promises-safe';
import { WETH9Contract } from '../src/contract_wrappers/generated/weth9';
import { WETH9Contract } from '../src/generated_contract_wrappers/weth9';
import { artifacts } from '../src/utils/artifacts';
import { expectInsufficientFundsAsync, expectRevertOrAlwaysFailingTransactionAsync } from '../src/utils/assertions';
import { chaiSetup } from '../src/utils/chai_setup';
import { constants } from '../src/utils/constants';
import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper';
@@ -45,9 +46,7 @@ describe('EtherToken', () => {
const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account);
const ethToDeposit = initEthBalance.plus(1);
return expect(etherToken.deposit.sendTransactionAsync({ value: ethToDeposit })).to.be.rejectedWith(
"ender doesn't have enough funds to send tx.",
);
return expectInsufficientFundsAsync(etherToken.deposit.sendTransactionAsync({ value: ethToDeposit }));
});
it('should convert deposited Ether to wrapped Ether tokens', async () => {
@@ -76,8 +75,8 @@ describe('EtherToken', () => {
const initEthTokenBalance = await etherToken.balanceOf.callAsync(account);
const ethTokensToWithdraw = initEthTokenBalance.plus(1);
return expect(etherToken.withdraw.sendTransactionAsync(ethTokensToWithdraw)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
etherToken.withdraw.sendTransactionAsync(ethTokensToWithdraw),
);
});

View File

@@ -8,16 +8,17 @@ import { LogWithDecodedArgs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
import 'make-promises-safe';
import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token';
import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token';
import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy';
import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy';
import { DummyERC20TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c20_token';
import { DummyERC721TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c721_token';
import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c20_proxy';
import { ERC721ProxyContract } from '../../src/generated_contract_wrappers/e_r_c721_proxy';
import {
CancelContractEventArgs,
ExchangeContract,
FillContractEventArgs,
} from '../../src/contract_wrappers/generated/exchange';
} from '../../src/generated_contract_wrappers/exchange';
import { artifacts } from '../../src/utils/artifacts';
import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions';
import { chaiSetup } from '../../src/utils/chai_setup';
import { constants } from '../../src/utils/constants';
import { ERC20Wrapper } from '../../src/utils/erc20_wrapper';
@@ -30,7 +31,7 @@ import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('Exchange core', () => {
let makerAddress: string;
let owner: string;
@@ -86,7 +87,7 @@ describe('Exchange core', () => {
artifacts.Exchange,
provider,
txDefaults,
assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
zrxToken.address,
);
exchangeWrapper = new ExchangeWrapper(exchange, provider);
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner);
@@ -113,8 +114,8 @@ describe('Exchange core', () => {
exchangeAddress: exchange.address,
makerAddress,
feeRecipientAddress,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultMakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerAssetAddress),
};
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
@@ -414,8 +415,8 @@ describe('Exchange core', () => {
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
});
return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
);
});
@@ -431,8 +432,8 @@ describe('Exchange core', () => {
const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]);
const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`;
signedOrder.signature = invalidSigHex;
return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
);
});
@@ -441,8 +442,8 @@ describe('Exchange core', () => {
makerAssetAmount: new BigNumber(0),
});
return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
);
});
@@ -451,19 +452,19 @@ describe('Exchange core', () => {
takerAssetAmount: new BigNumber(0),
});
return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
);
});
it('should throw if takerAssetFillAmount is 0', async () => {
signedOrder = orderFactory.newSignedOrder();
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, {
takerAssetFillAmount: new BigNumber(0),
}),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if maker erc20Balances are too low to fill order', async () => {
@@ -471,8 +472,8 @@ describe('Exchange core', () => {
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18),
});
return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
);
});
@@ -480,9 +481,8 @@ describe('Exchange core', () => {
signedOrder = orderFactory.newSignedOrder({
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18),
});
return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
);
});
@@ -493,11 +493,8 @@ describe('Exchange core', () => {
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// HACK: `rejectWith` returns a "promise-like" type, but not an actual "Promise", so TSLint
// complains, even though we do need to `await` it. So we disable the TSLint error below.
// tslint:disable-next-line:await-promise
await expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
);
});
@@ -508,11 +505,8 @@ describe('Exchange core', () => {
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
// HACK: `rejectWith` returns a "promise-like" type, but not an actual "Promise", so TSLint
// complains, even though we do need to `await` it. So we disable the TSLint error below.
// tslint:disable-next-line:await-promise
await expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
);
});
@@ -520,16 +514,16 @@ describe('Exchange core', () => {
signedOrder = orderFactory.newSignedOrder({
expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
});
return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
);
});
it('should throw if no value is filled', async () => {
signedOrder = orderFactory.newSignedOrder();
await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
);
});
});
@@ -541,8 +535,8 @@ describe('Exchange core', () => {
});
it('should throw if not sent by maker', async () => {
return expect(exchangeWrapper.cancelOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.cancelOrderAsync(signedOrder, takerAddress),
);
});
@@ -551,8 +545,8 @@ describe('Exchange core', () => {
makerAssetAmount: new BigNumber(0),
});
return expect(exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress),
);
});
@@ -561,22 +555,21 @@ describe('Exchange core', () => {
takerAssetAmount: new BigNumber(0),
});
return expect(exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress),
);
});
it('should be able to cancel a full order', async () => {
await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, {
takerAssetFillAmount: signedOrder.takerAssetAmount.div(2),
}),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should log 1 event with correct arguments', async () => {
const divisor = 2;
const res = await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress);
expect(res.logs).to.have.length(1);
@@ -592,8 +585,8 @@ describe('Exchange core', () => {
it('should throw if already cancelled', async () => {
await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress);
return expect(exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress),
);
});
@@ -601,8 +594,8 @@ describe('Exchange core', () => {
signedOrder = orderFactory.newSignedOrder({
expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
});
return expect(exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress),
);
});
@@ -618,11 +611,11 @@ describe('Exchange core', () => {
});
const fillTakerAssetAmount2 = new BigNumber(1);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, {
takerAssetFillAmount: fillTakerAssetAmount2,
}),
).to.be.rejectedWith(constants.REVERT);
);
});
});
@@ -631,16 +624,16 @@ describe('Exchange core', () => {
const makerEpoch = new BigNumber(1);
await exchangeWrapper.cancelOrdersUpToAsync(makerEpoch, makerAddress);
const lesserMakerEpoch = new BigNumber(0);
return expect(exchangeWrapper.cancelOrdersUpToAsync(lesserMakerEpoch, makerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.cancelOrdersUpToAsync(lesserMakerEpoch, makerAddress),
);
});
it('should fail to set makerEpoch equal to existing makerEpoch', async () => {
const makerEpoch = new BigNumber(1);
await exchangeWrapper.cancelOrdersUpToAsync(makerEpoch, makerAddress);
return expect(exchangeWrapper.cancelOrdersUpToAsync(makerEpoch, makerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.cancelOrdersUpToAsync(makerEpoch, makerAddress),
);
});
@@ -674,7 +667,12 @@ describe('Exchange core', () => {
salt: new BigNumber(3),
}),
];
await exchangeWrapper.batchFillOrdersNoThrowAsync(signedOrders, takerAddress);
await exchangeWrapper.batchFillOrdersNoThrowAsync(signedOrders, takerAddress, {
// HACK(albrow): We need to hardcode the gas estimate here because
// the Geth gas estimator doesn't work with the way we use
// delegatecall and swallow errors.
gas: 490000,
});
const newBalances = await erc20Wrapper.getBalancesAsync();
const fillMakerAssetAmount = signedOrders[2].makerAssetAmount.add(signedOrders[3].makerAssetAmount);
@@ -713,8 +711,8 @@ describe('Exchange core', () => {
signedOrder = orderFactory.newSignedOrder({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(1),
makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId),
makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId),
});
// Verify pre-conditions
const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
@@ -723,6 +721,7 @@ describe('Exchange core', () => {
expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
// Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount;
// tslint:disable-next-line:no-unused-variable
const res = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount });
// Verify post-conditions
const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
@@ -738,8 +737,8 @@ describe('Exchange core', () => {
signedOrder = orderFactory.newSignedOrder({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(1),
makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId),
makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId),
});
// Verify pre-conditions
const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
@@ -748,9 +747,9 @@ describe('Exchange core', () => {
expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
// Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount;
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw when taker does not own the token with id takerAssetId', async () => {
@@ -760,8 +759,8 @@ describe('Exchange core', () => {
signedOrder = orderFactory.newSignedOrder({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(1),
makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId),
makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId),
});
// Verify pre-conditions
const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
@@ -770,9 +769,9 @@ describe('Exchange core', () => {
expect(initialOwnerTakerAsset).to.be.bignumber.not.equal(takerAddress);
// Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount;
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw when makerAssetAmount is greater than 1', async () => {
@@ -782,8 +781,8 @@ describe('Exchange core', () => {
signedOrder = orderFactory.newSignedOrder({
makerAssetAmount: new BigNumber(2),
takerAssetAmount: new BigNumber(1),
makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId),
makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId),
});
// Verify pre-conditions
const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
@@ -792,9 +791,9 @@ describe('Exchange core', () => {
expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
// Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount;
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw when takerAssetAmount is greater than 1', async () => {
@@ -804,8 +803,8 @@ describe('Exchange core', () => {
signedOrder = orderFactory.newSignedOrder({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(500),
makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId),
makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId),
});
// Verify pre-conditions
const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
@@ -814,9 +813,9 @@ describe('Exchange core', () => {
expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
// Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount;
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw on partial fill', async () => {
@@ -826,8 +825,8 @@ describe('Exchange core', () => {
signedOrder = orderFactory.newSignedOrder({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(0),
makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId),
makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId),
});
// Verify pre-conditions
const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
@@ -836,9 +835,9 @@ describe('Exchange core', () => {
expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
// Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount;
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should successfully fill order when makerAsset is ERC721 and takerAsset is ERC20', async () => {
@@ -847,8 +846,8 @@ describe('Exchange core', () => {
signedOrder = orderFactory.newSignedOrder({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerAssetAddress),
});
// Verify pre-conditions
const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
@@ -887,8 +886,8 @@ describe('Exchange core', () => {
signedOrder = orderFactory.newSignedOrder({
takerAssetAmount: new BigNumber(1),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId),
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultMakerAssetAddress),
});
// Verify pre-conditions
const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId);
@@ -921,4 +920,6 @@ describe('Exchange core', () => {
);
});
});
}); // tslint:disable-line:max-file-line-count
});
// tslint:disable:max-file-line-count
// tslint:enable:no-unnecessary-type-assertion

View File

@@ -3,13 +3,13 @@ import { assetProxyUtils } from '@0xproject/order-utils';
import { AssetProxyId } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import * as Web3 from 'web3';
import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token';
import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy';
import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy';
import { TestAssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/test_asset_proxy_dispatcher';
import { DummyERC20TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c20_token';
import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c20_proxy';
import { ERC721ProxyContract } from '../../src/generated_contract_wrappers/e_r_c721_proxy';
import { TestAssetProxyDispatcherContract } from '../../src/generated_contract_wrappers/test_asset_proxy_dispatcher';
import { artifacts } from '../../src/utils/artifacts';
import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions';
import { chaiSetup } from '../../src/utils/chai_setup';
import { constants } from '../../src/utils/constants';
import { ERC20Wrapper } from '../../src/utils/erc20_wrapper';
@@ -23,7 +23,6 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('AssetProxyDispatcher', () => {
let owner: string;
let notOwner: string;
let notAuthorized: string;
let makerAddress: string;
let takerAddress: string;
@@ -45,7 +44,6 @@ describe('AssetProxyDispatcher', () => {
// Setup accounts & addresses
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, notOwner, makerAddress, takerAddress] = accounts);
notAuthorized = notOwner;
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
@@ -177,14 +175,14 @@ describe('AssetProxyDispatcher', () => {
const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
expect(proxyAddress).to.be.equal(erc20Proxy.address);
// The following transaction will throw because the currentAddress is no longer constants.NULL_ADDRESS
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
AssetProxyId.ERC20,
erc20Proxy.address,
constants.NULL_ADDRESS,
{ from: owner },
),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should be able to reset proxy address to NULL', async () => {
@@ -218,26 +216,26 @@ describe('AssetProxyDispatcher', () => {
it('should throw if requesting address is not owner', async () => {
const prevProxyAddress = constants.NULL_ADDRESS;
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
AssetProxyId.ERC20,
erc20Proxy.address,
prevProxyAddress,
{ from: notOwner },
),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if attempting to register a proxy to the incorrect id', async () => {
const prevProxyAddress = constants.NULL_ADDRESS;
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(
AssetProxyId.ERC721,
erc20Proxy.address,
prevProxyAddress,
{ from: owner },
),
).to.be.rejectedWith(constants.REVERT);
);
});
});
@@ -277,13 +275,15 @@ describe('AssetProxyDispatcher', () => {
constants.AWAIT_TRANSACTION_MINED_MS,
);
// Construct metadata for ERC20 proxy
const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address);
const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Perform a transfer from makerAddress to takerAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(10);
await web3Wrapper.awaitTransactionSuccessAsync(
await assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync(
encodedProxyMetadata,
encodedAssetDataWithoutProxyId,
AssetProxyId.ERC20,
makerAddress,
takerAddress,
amount,
@@ -303,19 +303,20 @@ describe('AssetProxyDispatcher', () => {
it('should throw if dispatching to unregistered proxy', async () => {
// Construct metadata for ERC20 proxy
const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address);
const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
// Perform a transfer from makerAddress to takerAddress
const erc20Balances = await erc20Wrapper.getBalancesAsync();
const amount = new BigNumber(10);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync(
encodedProxyMetadata,
encodedAssetDataWithoutProxyId,
AssetProxyId.ERC20,
makerAddress,
takerAddress,
amount,
{ from: owner },
),
).to.be.rejectedWith(constants.REVERT);
);
});
});
});

View File

@@ -3,9 +3,8 @@ import { assetProxyUtils, EIP712Utils, orderHashUtils } from '@0xproject/order-u
import { SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import ethUtil = require('ethereumjs-util');
import { TestLibsContract } from '../../src/contract_wrappers/generated/test_libs';
import { TestLibsContract } from '../../src/generated_contract_wrappers/test_libs';
import { addressUtils } from '../../src/utils/address_utils';
import { artifacts } from '../../src/utils/artifacts';
import { chaiSetup } from '../../src/utils/chai_setup';
@@ -39,8 +38,8 @@ describe('Exchange libs', () => {
exchangeAddress: libs.address,
makerAddress,
feeRecipientAddress: addressUtils.generatePseudoRandomAddress(),
makerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()),
makerAssetData: assetProxyUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
takerAssetData: assetProxyUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
};
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);

View File

@@ -1,23 +1,18 @@
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { assetProxyUtils, crypto } from '@0xproject/order-utils';
import { AssetProxyId, SignedOrder } from '@0xproject/types';
import { assetProxyUtils } from '@0xproject/order-utils';
import { AssetProxyId } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token';
import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token';
import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy';
import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy';
import {
CancelContractEventArgs,
ExchangeContract,
FillContractEventArgs,
} from '../../src/contract_wrappers/generated/exchange';
import { DummyERC20TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c20_token';
import { DummyERC721TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c721_token';
import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c20_proxy';
import { ERC721ProxyContract } from '../../src/generated_contract_wrappers/e_r_c721_proxy';
import { ExchangeContract } from '../../src/generated_contract_wrappers/exchange';
import { artifacts } from '../../src/utils/artifacts';
import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions';
import { chaiSetup } from '../../src/utils/chai_setup';
import { constants } from '../../src/utils/constants';
import { ERC20Wrapper } from '../../src/utils/erc20_wrapper';
@@ -25,13 +20,7 @@ import { ERC721Wrapper } from '../../src/utils/erc721_wrapper';
import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
import { MatchOrderTester } from '../../src/utils/match_order_tester';
import { OrderFactory } from '../../src/utils/order_factory';
import {
ContractName,
ERC20BalancesByOwner,
ERC721TokenIdsByOwner,
OrderInfo,
OrderStatus,
} from '../../src/utils/types';
import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, OrderInfo, OrderStatus } from '../../src/utils/types';
import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper';
chaiSetup.configure();
@@ -64,7 +53,6 @@ describe('matchOrders', () => {
let erc721LeftMakerAssetIds: BigNumber[];
let erc721RightMakerAssetIds: BigNumber[];
let erc721TakerAssetIds: BigNumber[];
let defaultERC20MakerAssetAddress: string;
let defaultERC20TakerAssetAddress: string;
@@ -103,13 +91,12 @@ describe('matchOrders', () => {
const erc721Balances = await erc721Wrapper.getBalancesAsync();
erc721LeftMakerAssetIds = erc721Balances[makerAddressLeft][erc721Token.address];
erc721RightMakerAssetIds = erc721Balances[makerAddressRight][erc721Token.address];
erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address];
// Depoy exchange
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
artifacts.Exchange,
provider,
txDefaults,
assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
zrxToken.address,
);
exchangeWrapper = new ExchangeWrapper(exchange, provider);
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner);
@@ -135,8 +122,8 @@ describe('matchOrders', () => {
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
exchangeAddress: exchange.address,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
};
const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)];
orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParams);
@@ -161,16 +148,14 @@ describe('matchOrders', () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -195,16 +180,14 @@ describe('matchOrders', () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -240,16 +223,14 @@ describe('matchOrders', () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -274,16 +255,14 @@ describe('matchOrders', () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -308,16 +287,14 @@ describe('matchOrders', () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -347,8 +324,8 @@ describe('matchOrders', () => {
// branch in the contract twice for this test.
const signedOrderRight2 = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -377,8 +354,6 @@ describe('matchOrders', () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: feeRecipientAddressLeft,
@@ -386,8 +361,8 @@ describe('matchOrders', () => {
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -417,8 +392,6 @@ describe('matchOrders', () => {
// branch in the contract twice for this test.
const signedOrderLeft2 = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
feeRecipientAddress: feeRecipientAddressLeft,
@@ -450,16 +423,14 @@ describe('matchOrders', () => {
const feeRecipientAddress = feeRecipientAddressLeft;
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress,
@@ -478,16 +449,14 @@ describe('matchOrders', () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -507,16 +476,14 @@ describe('matchOrders', () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -536,16 +503,14 @@ describe('matchOrders', () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -565,16 +530,14 @@ describe('matchOrders', () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -594,16 +557,14 @@ describe('matchOrders', () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: makerAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: makerAddressRight,
@@ -622,16 +583,14 @@ describe('matchOrders', () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -639,25 +598,23 @@ describe('matchOrders', () => {
// Cancel left order
await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress);
// Match orders
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress),
).to.be.rejectedWith(constants.REVERT);
);
});
it('Should throw if right order is not fillable', async () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: feeRecipientAddressRight,
@@ -665,31 +622,29 @@ describe('matchOrders', () => {
// Cancel right order
await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress);
// Match orders
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if there is not a positive spread', async () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
feeRecipientAddress: feeRecipientAddressRight,
});
// Match orders
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
matchOrderTester.matchOrdersAndVerifyBalancesAsync(
signedOrderLeft,
signedOrderRight,
@@ -697,29 +652,27 @@ describe('matchOrders', () => {
erc20BalancesByOwner,
erc721TokenIdsByOwner,
),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if the left maker asset is not equal to the right taker asset ', async () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: feeRecipientAddressRight,
});
// Match orders
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
matchOrderTester.matchOrdersAndVerifyBalancesAsync(
signedOrderLeft,
signedOrderRight,
@@ -727,29 +680,29 @@ describe('matchOrders', () => {
erc20BalancesByOwner,
erc721TokenIdsByOwner,
),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if the right maker asset is not equal to the left taker asset', async () => {
// Create orders to match
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
feeRecipientAddress: feeRecipientAddressRight,
});
// Match orders
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
matchOrderTester.matchOrdersAndVerifyBalancesAsync(
signedOrderLeft,
signedOrderRight,
@@ -757,7 +710,7 @@ describe('matchOrders', () => {
erc20BalancesByOwner,
erc721TokenIdsByOwner,
),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should transfer correct amounts when left order maker asset is an ERC721 token', async () => {
@@ -765,16 +718,16 @@ describe('matchOrders', () => {
const erc721TokenToTransfer = erc721LeftMakerAssetIds[0];
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
makerAssetAmount: new BigNumber(1),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: new BigNumber(1),
feeRecipientAddress: feeRecipientAddressRight,
@@ -800,16 +753,16 @@ describe('matchOrders', () => {
const erc721TokenToTransfer = erc721RightMakerAssetIds[0];
const signedOrderLeft = orderFactoryLeft.newSignedOrder({
makerAddress: makerAddressLeft,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer),
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
takerAssetAmount: new BigNumber(1),
feeRecipientAddress: feeRecipientAddressLeft,
});
const signedOrderRight = orderFactoryRight.newSignedOrder({
makerAddress: makerAddressRight,
makerAssetData: assetProxyUtils.encodeERC721ProxyData(defaultERC721AssetAddress, erc721TokenToTransfer),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultERC20MakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
makerAssetAmount: new BigNumber(1),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
feeRecipientAddress: feeRecipientAddressRight,

View File

@@ -1,11 +1,10 @@
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils';
import { SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import ethUtil = require('ethereumjs-util');
import { TestSignatureValidatorContract } from '../../src/contract_wrappers/generated/test_signature_validator';
import { TestSignatureValidatorContract } from '../../src/generated_contract_wrappers/test_signature_validator';
import { addressUtils } from '../../src/utils/address_utils';
import { artifacts } from '../../src/utils/artifacts';
import { chaiSetup } from '../../src/utils/chai_setup';
@@ -43,8 +42,8 @@ describe('MixinSignatureValidator', () => {
exchangeAddress: signatureValidator.address,
makerAddress,
feeRecipientAddress: addressUtils.generatePseudoRandomAddress(),
makerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()),
makerAssetData: assetProxyUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
takerAssetData: assetProxyUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
};
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);

View File

@@ -1,16 +1,15 @@
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils';
import { AssetProxyId, Order, OrderWithoutExchangeAddress, SignedOrder } from '@0xproject/types';
import { AssetProxyId, OrderWithoutExchangeAddress, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util';
import * as Web3 from 'web3';
import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token';
import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy';
import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange';
import { WhitelistContract } from '../../src/contract_wrappers/generated/whitelist';
import { DummyERC20TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c20_token';
import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c20_proxy';
import { ExchangeContract } from '../../src/generated_contract_wrappers/exchange';
import { WhitelistContract } from '../../src/generated_contract_wrappers/whitelist';
import { artifacts } from '../../src/utils/artifacts';
import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions';
import { chaiSetup } from '../../src/utils/chai_setup';
import { constants } from '../../src/utils/constants';
import { ERC20Wrapper } from '../../src/utils/erc20_wrapper';
@@ -18,7 +17,7 @@ import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
import { OrderFactory } from '../../src/utils/order_factory';
import { orderUtils } from '../../src/utils/order_utils';
import { TransactionFactory } from '../../src/utils/transaction_factory';
import { ERC20BalancesByOwner, OrderStatus, SignedTransaction } from '../../src/utils/types';
import { ERC20BalancesByOwner, SignedTransaction } from '../../src/utils/types';
import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper';
chaiSetup.configure();
@@ -73,7 +72,7 @@ describe('Exchange transactions', () => {
artifacts.Exchange,
provider,
txDefaults,
assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
zrxToken.address,
);
exchangeWrapper = new ExchangeWrapper(exchange, provider);
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner);
@@ -92,8 +91,8 @@ describe('Exchange transactions', () => {
exchangeAddress: exchange.address,
makerAddress,
feeRecipientAddress,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerTokenAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerTokenAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultMakerTokenAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerTokenAddress),
};
makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
@@ -126,8 +125,8 @@ describe('Exchange transactions', () => {
});
it('should throw if not called by specified sender', async () => {
return expect(exchangeWrapper.executeTransactionAsync(signedTx, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.executeTransactionAsync(signedTx, takerAddress),
);
});
@@ -168,8 +167,8 @@ describe('Exchange transactions', () => {
it('should throw if the a 0x transaction with the same transactionHash has already been executed', async () => {
await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress);
return expect(exchangeWrapper.executeTransactionAsync(signedTx, senderAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.executeTransactionAsync(signedTx, senderAddress),
);
});
@@ -187,15 +186,15 @@ describe('Exchange transactions', () => {
});
it('should throw if not called by specified sender', async () => {
return expect(exchangeWrapper.executeTransactionAsync(signedTx, makerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.executeTransactionAsync(signedTx, makerAddress),
);
});
it('should cancel the order when signed by maker and called by sender', async () => {
await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress);
return expect(exchangeWrapper.fillOrderAsync(signedOrder, senderAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrderAsync(signedOrder, senderAddress),
);
});
});
@@ -217,6 +216,7 @@ describe('Exchange transactions', () => {
await exchange.setSignatureValidatorApproval.sendTransactionAsync(whitelist.address, isApproved, {
from: takerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
@@ -224,8 +224,8 @@ describe('Exchange transactions', () => {
exchangeAddress: exchange.address,
makerAddress,
feeRecipientAddress,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerTokenAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerTokenAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultMakerTokenAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerTokenAddress),
};
whitelistOrderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams);
});
@@ -239,12 +239,13 @@ describe('Exchange transactions', () => {
const isApproved = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await whitelist.updateWhitelistStatus.sendTransactionAsync(takerAddress, isApproved, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS,
);
orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder);
const takerAssetFillAmount = signedOrder.takerAssetAmount;
const salt = generatePseudoRandomSalt();
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
whitelist.fillOrderIfWhitelisted.sendTransactionAsync(
orderWithoutExchangeAddress,
takerAssetFillAmount,
@@ -252,19 +253,20 @@ describe('Exchange transactions', () => {
signedOrder.signature,
{ from: takerAddress },
),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should revert if taker has not been whitelisted', async () => {
const isApproved = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await whitelist.updateWhitelistStatus.sendTransactionAsync(makerAddress, isApproved, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS,
);
orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder);
const takerAssetFillAmount = signedOrder.takerAssetAmount;
const salt = generatePseudoRandomSalt();
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
whitelist.fillOrderIfWhitelisted.sendTransactionAsync(
orderWithoutExchangeAddress,
takerAssetFillAmount,
@@ -272,17 +274,19 @@ describe('Exchange transactions', () => {
signedOrder.signature,
{ from: takerAddress },
),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should fill the order if maker and taker have been whitelisted', async () => {
const isApproved = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await whitelist.updateWhitelistStatus.sendTransactionAsync(makerAddress, isApproved, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await whitelist.updateWhitelistStatus.sendTransactionAsync(takerAddress, isApproved, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS,
);
orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder);
@@ -296,6 +300,7 @@ describe('Exchange transactions', () => {
signedOrder.signature,
{ from: takerAddress },
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newBalances = await erc20Wrapper.getBalancesAsync();

View File

@@ -1,4 +1,4 @@
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { assetProxyUtils } from '@0xproject/order-utils';
import { AssetProxyId, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
@@ -6,15 +6,14 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
import * as _ from 'lodash';
import 'make-promises-safe';
import * as Web3 from 'web3';
import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token';
import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token';
import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy';
import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy';
import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange';
import { TokenRegistryContract } from '../../src/contract_wrappers/generated/token_registry';
import { DummyERC20TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c20_token';
import { DummyERC721TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c721_token';
import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c20_proxy';
import { ERC721ProxyContract } from '../../src/generated_contract_wrappers/e_r_c721_proxy';
import { ExchangeContract } from '../../src/generated_contract_wrappers/exchange';
import { artifacts } from '../../src/utils/artifacts';
import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions';
import { chaiSetup } from '../../src/utils/chai_setup';
import { constants } from '../../src/utils/constants';
import { ERC20Wrapper } from '../../src/utils/erc20_wrapper';
@@ -82,7 +81,7 @@ describe('Exchange wrappers', () => {
artifacts.Exchange,
provider,
txDefaults,
assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
zrxToken.address,
);
exchangeWrapper = new ExchangeWrapper(exchange, provider);
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner);
@@ -109,8 +108,8 @@ describe('Exchange wrappers', () => {
exchangeAddress: exchange.address,
makerAddress,
feeRecipientAddress,
makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerAssetAddress),
makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultMakerAssetAddress),
takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerAssetAddress),
};
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
@@ -172,8 +171,8 @@ describe('Exchange wrappers', () => {
expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)),
});
return expect(exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress),
);
});
@@ -184,8 +183,8 @@ describe('Exchange wrappers', () => {
takerAssetFillAmount: signedOrder.takerAssetAmount.div(2),
});
return expect(exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress),
);
});
});
@@ -197,12 +196,16 @@ describe('Exchange wrappers', () => {
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
});
const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2);
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress, {
takerAssetFillAmount,
// HACK(albrow): We need to hardcode the gas estimate here because
// the Geth gas estimator doesn't work with the way we use
// delegatecall and swallow errors.
gas: 250000,
});
const newBalances = await erc20Wrapper.getBalancesAsync();
const makerAssetFilledAmount = takerAssetFillAmount
.times(signedOrder.makerAssetAmount)
.dividedToIntegerBy(signedOrder.takerAssetAmount);
@@ -212,6 +215,7 @@ describe('Exchange wrappers', () => {
const takerFee = signedOrder.takerFee
.times(makerAssetFilledAmount)
.dividedToIntegerBy(signedOrder.makerAssetAmount);
expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount),
);
@@ -300,7 +304,7 @@ describe('Exchange wrappers', () => {
const signedOrder = orderFactory.newSignedOrder({
makerAssetAmount: makerZRXBalance,
makerFee: new BigNumber(1),
makerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
});
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
const newBalances = await erc20Wrapper.getBalancesAsync();
@@ -312,7 +316,7 @@ describe('Exchange wrappers', () => {
const signedOrder = orderFactory.newSignedOrder({
makerAssetAmount: new BigNumber(makerZRXAllowance),
makerFee: new BigNumber(1),
makerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
});
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
const newBalances = await erc20Wrapper.getBalancesAsync();
@@ -324,7 +328,7 @@ describe('Exchange wrappers', () => {
const signedOrder = orderFactory.newSignedOrder({
takerAssetAmount: takerZRXBalance,
takerFee: new BigNumber(1),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
takerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
});
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
const newBalances = await erc20Wrapper.getBalancesAsync();
@@ -336,7 +340,7 @@ describe('Exchange wrappers', () => {
const signedOrder = orderFactory.newSignedOrder({
takerAssetAmount: new BigNumber(takerZRXAllowance),
takerFee: new BigNumber(1),
takerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
takerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
});
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
const newBalances = await erc20Wrapper.getBalancesAsync();
@@ -350,8 +354,8 @@ describe('Exchange wrappers', () => {
const signedOrder = orderFactory.newSignedOrder({
makerAssetAmount: new BigNumber(1),
takerAssetAmount: new BigNumber(1),
makerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721ProxyData(erc721Token.address, takerAssetId),
makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId),
});
// Verify pre-conditions
const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
@@ -360,7 +364,13 @@ describe('Exchange wrappers', () => {
expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
// Call Exchange
const takerAssetFillAmount = signedOrder.takerAssetAmount;
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress, { takerAssetFillAmount });
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress, {
takerAssetFillAmount,
// HACK(albrow): We need to hardcode the gas estimate here because
// the Geth gas estimator doesn't work with the way we use
// delegatecall and swallow errors.
gas: 270000,
});
// Verify post-conditions
const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress);
@@ -485,11 +495,11 @@ describe('Exchange wrappers', () => {
await exchangeWrapper.fillOrKillOrderAsync(signedOrders[0], takerAddress);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.batchFillOrKillOrdersAsync(signedOrders, takerAddress, {
takerAssetFillAmounts,
}),
).to.be.rejectedWith(constants.REVERT);
);
});
});
@@ -535,6 +545,10 @@ describe('Exchange wrappers', () => {
await exchangeWrapper.batchFillOrdersNoThrowAsync(signedOrders, takerAddress, {
takerAssetFillAmounts,
// HACK(albrow): We need to hardcode the gas estimate here because
// the Geth gas estimator doesn't work with the way we use
// delegatecall and swallow errors.
gas: 600000,
});
const newBalances = await erc20Wrapper.getBalancesAsync();
@@ -591,6 +605,10 @@ describe('Exchange wrappers', () => {
const newOrders = [invalidOrder, ...validOrders];
await exchangeWrapper.batchFillOrdersNoThrowAsync(newOrders, takerAddress, {
takerAssetFillAmounts,
// HACK(albrow): We need to hardcode the gas estimate here because
// the Geth gas estimator doesn't work with the way we use
// delegatecall and swallow errors.
gas: 450000,
});
const newBalances = await erc20Wrapper.getBalancesAsync();
@@ -674,16 +692,16 @@ describe('Exchange wrappers', () => {
signedOrders = [
orderFactory.newSignedOrder(),
orderFactory.newSignedOrder({
takerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
takerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
}),
orderFactory.newSignedOrder(),
];
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, {
takerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18),
}),
).to.be.rejectedWith(constants.REVERT);
);
});
});
@@ -694,6 +712,10 @@ describe('Exchange wrappers', () => {
);
await exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, {
takerAssetFillAmount,
// HACK(albrow): We need to hardcode the gas estimate here because
// the Geth gas estimator doesn't work with the way we use
// delegatecall and swallow errors.
gas: 6000000,
});
const newBalances = await erc20Wrapper.getBalancesAsync();
@@ -753,26 +775,59 @@ describe('Exchange wrappers', () => {
});
await exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, {
takerAssetFillAmount,
// HACK(albrow): We need to hardcode the gas estimate here because
// the Geth gas estimator doesn't work with the way we use
// delegatecall and swallow errors.
gas: 600000,
});
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances).to.be.deep.equal(erc20Balances);
});
it('should throw when a signedOrder does not use the same takerAssetAddress', async () => {
it('should not fill a signedOrder that does not use the same takerAssetAddress', async () => {
signedOrders = [
orderFactory.newSignedOrder(),
orderFactory.newSignedOrder({
takerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
}),
orderFactory.newSignedOrder(),
];
return expect(
exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, {
takerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18),
orderFactory.newSignedOrder({
takerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
}),
).to.be.rejectedWith(constants.REVERT);
];
const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18);
const filledSignedOrders = signedOrders.slice(0, -1);
_.forEach(filledSignedOrders, signedOrder => {
erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][
defaultMakerAssetAddress
].minus(signedOrder.makerAssetAmount);
erc20Balances[makerAddress][defaultTakerAssetAddress] = erc20Balances[makerAddress][
defaultTakerAssetAddress
].add(signedOrder.takerAssetAmount);
erc20Balances[makerAddress][zrxToken.address] = erc20Balances[makerAddress][zrxToken.address].minus(
signedOrder.makerFee,
);
erc20Balances[takerAddress][defaultMakerAssetAddress] = erc20Balances[takerAddress][
defaultMakerAssetAddress
].add(signedOrder.makerAssetAmount);
erc20Balances[takerAddress][defaultTakerAssetAddress] = erc20Balances[takerAddress][
defaultTakerAssetAddress
].minus(signedOrder.takerAssetAmount);
erc20Balances[takerAddress][zrxToken.address] = erc20Balances[takerAddress][zrxToken.address].minus(
signedOrder.takerFee,
);
erc20Balances[feeRecipientAddress][zrxToken.address] = erc20Balances[feeRecipientAddress][
zrxToken.address
].add(signedOrder.makerFee.add(signedOrder.takerFee));
});
await exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, {
takerAssetFillAmount,
// HACK(albrow): We need to hardcode the gas estimate here because
// the Geth gas estimator doesn't work with the way we use
// delegatecall and swallow errors.
gas: 600000,
});
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances).to.be.deep.equal(erc20Balances);
});
});
@@ -852,16 +907,16 @@ describe('Exchange wrappers', () => {
signedOrders = [
orderFactory.newSignedOrder(),
orderFactory.newSignedOrder({
makerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
}),
orderFactory.newSignedOrder(),
];
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, {
makerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18),
}),
).to.be.rejectedWith(constants.REVERT);
);
});
});
@@ -872,6 +927,10 @@ describe('Exchange wrappers', () => {
);
await exchangeWrapper.marketBuyOrdersNoThrowAsync(signedOrders, takerAddress, {
makerAssetFillAmount,
// HACK(albrow): We need to hardcode the gas estimate here because
// the Geth gas estimator doesn't work with the way we use
// delegatecall and swallow errors.
gas: 600000,
});
const newBalances = await erc20Wrapper.getBalancesAsync();
@@ -904,8 +963,8 @@ describe('Exchange wrappers', () => {
);
});
it('should fill all signedOrders if cannot fill entire takerAssetFillAmount', async () => {
const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18);
it('should fill all signedOrders if cannot fill entire makerAssetFillAmount', async () => {
const makerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18);
_.forEach(signedOrders, signedOrder => {
erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][
defaultMakerAssetAddress
@@ -929,28 +988,62 @@ describe('Exchange wrappers', () => {
zrxToken.address
].add(signedOrder.makerFee.add(signedOrder.takerFee));
});
await exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, {
takerAssetFillAmount,
await exchangeWrapper.marketBuyOrdersNoThrowAsync(signedOrders, takerAddress, {
makerAssetFillAmount,
// HACK(albrow): We need to hardcode the gas estimate here because
// the Geth gas estimator doesn't work with the way we use
// delegatecall and swallow errors.
gas: 600000,
});
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances).to.be.deep.equal(erc20Balances);
});
it('should throw when a signedOrder does not use the same makerAssetAddress', async () => {
it('should not fill a signedOrder that does not use the same makerAssetAddress', async () => {
signedOrders = [
orderFactory.newSignedOrder(),
orderFactory.newSignedOrder({
makerAssetData: assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
}),
orderFactory.newSignedOrder(),
orderFactory.newSignedOrder({
makerAssetData: assetProxyUtils.encodeERC20AssetData(zrxToken.address),
}),
];
return expect(
exchangeWrapper.marketBuyOrdersNoThrowAsync(signedOrders, takerAddress, {
makerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18),
}),
).to.be.rejectedWith(constants.REVERT);
const makerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18);
const filledSignedOrders = signedOrders.slice(0, -1);
_.forEach(filledSignedOrders, signedOrder => {
erc20Balances[makerAddress][defaultMakerAssetAddress] = erc20Balances[makerAddress][
defaultMakerAssetAddress
].minus(signedOrder.makerAssetAmount);
erc20Balances[makerAddress][defaultTakerAssetAddress] = erc20Balances[makerAddress][
defaultTakerAssetAddress
].add(signedOrder.takerAssetAmount);
erc20Balances[makerAddress][zrxToken.address] = erc20Balances[makerAddress][zrxToken.address].minus(
signedOrder.makerFee,
);
erc20Balances[takerAddress][defaultMakerAssetAddress] = erc20Balances[takerAddress][
defaultMakerAssetAddress
].add(signedOrder.makerAssetAmount);
erc20Balances[takerAddress][defaultTakerAssetAddress] = erc20Balances[takerAddress][
defaultTakerAssetAddress
].minus(signedOrder.takerAssetAmount);
erc20Balances[takerAddress][zrxToken.address] = erc20Balances[takerAddress][zrxToken.address].minus(
signedOrder.takerFee,
);
erc20Balances[feeRecipientAddress][zrxToken.address] = erc20Balances[feeRecipientAddress][
zrxToken.address
].add(signedOrder.makerFee.add(signedOrder.takerFee));
});
await exchangeWrapper.marketBuyOrdersNoThrowAsync(signedOrders, takerAddress, {
makerAssetFillAmount,
// HACK(albrow): We need to hardcode the gas estimate here because
// the Geth gas estimator doesn't work with the way we use
// delegatecall and swallow errors.
gas: 600000,
});
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(newBalances).to.be.deep.equal(erc20Balances);
});
});

View File

@@ -1,10 +1,15 @@
import { env, EnvVars } from '@0xproject/dev-utils';
import { coverage } from '../src/utils/coverage';
import { profiler } from '../src/utils/profiler';
after('generate coverage report', async () => {
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
await coverageSubprovider.writeCoverageAsync();
}
if (env.parseBoolean(EnvVars.SolidityProfiler)) {
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
await profilerSubprovider.writeProfilerOutputAsync();
}
});

View File

@@ -1,15 +1,14 @@
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { AssetProxyId } from '@0xproject/types';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import BN = require('bn.js');
import * as chai from 'chai';
import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
import * as Web3 from 'web3';
import * as _ from 'lodash';
import { TestLibBytesContract } from '../../src/contract_wrappers/generated/test_lib_bytes';
import { TestLibBytesContract } from '../../src/generated_contract_wrappers/test_lib_bytes';
import { artifacts } from '../../src/utils/artifacts';
import { expectRevertOrOtherErrorAsync } from '../../src/utils/assertions';
import { chaiSetup } from '../../src/utils/chai_setup';
import { constants } from '../../src/utils/constants';
import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper';
@@ -19,7 +18,6 @@ const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('LibBytes', () => {
let owner: string;
let libBytes: TestLibBytesContract;
const byteArrayShorterThan32Bytes = '0x012345';
const byteArrayShorterThan20Bytes = byteArrayShorterThan32Bytes;
@@ -30,8 +28,20 @@ describe('LibBytes', () => {
const byteArrayLongerThan32BytesLastBytesSwapped =
'0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abefcd';
let testAddress: string;
let testAddressB: string;
const testBytes32 = '0x102030405060708090a0b0c0d0e0f0102030405060708090a0b0c0d0e0f01020';
const testBytes32B = '0x534877abd8443578526845cdfef020047528759477fedef87346527659aced32';
const testUint256 = new BigNumber(testBytes32, 16);
const testUint256B = new BigNumber(testBytes32B, 16);
let shortData: string;
let shortTestBytes: string;
let shortTestBytesAsBuffer: Buffer;
let wordOfData: string;
let wordOfTestBytes: string;
let wordOfTestBytesAsBuffer: Buffer;
let longData: string;
let longTestBytes: string;
let longTestBytesAsBuffer: Buffer;
before(async () => {
await blockchainLifecycle.startAsync();
@@ -42,8 +52,8 @@ describe('LibBytes', () => {
before(async () => {
// Setup accounts & addresses
const accounts = await web3Wrapper.getAvailableAddressesAsync();
owner = accounts[0];
testAddress = accounts[1];
testAddressB = accounts[2];
// Deploy LibBytes
libBytes = await TestLibBytesContract.deployFrom0xArtifactAsync(artifacts.TestLibBytes, provider, txDefaults);
// Verify lengths of test data
@@ -53,6 +63,26 @@ describe('LibBytes', () => {
expect(byteArrayLongerThan32BytesLength).to.be.greaterThan(32);
const testBytes32Length = ethUtil.toBuffer(testBytes32).byteLength;
expect(testBytes32Length).to.be.equal(32);
// Create short test bytes
shortData = '0xffffaa';
const encodedShortData = ethUtil.toBuffer(shortData);
const shortDataLength = new BigNumber(encodedShortData.byteLength);
const encodedShortDataLength = assetProxyUtils.encodeUint256(shortDataLength);
shortTestBytesAsBuffer = Buffer.concat([encodedShortDataLength, encodedShortData]);
shortTestBytes = ethUtil.bufferToHex(shortTestBytesAsBuffer);
// Create test bytes one word in length
wordOfData = ethUtil.bufferToHex(assetProxyUtils.encodeUint256(generatePseudoRandomSalt()));
const encodedWordOfData = ethUtil.toBuffer(wordOfData);
const wordOfDataLength = new BigNumber(encodedWordOfData.byteLength);
const encodedWordOfDataLength = assetProxyUtils.encodeUint256(wordOfDataLength);
wordOfTestBytesAsBuffer = Buffer.concat([encodedWordOfDataLength, encodedWordOfData]);
wordOfTestBytes = ethUtil.bufferToHex(wordOfTestBytesAsBuffer);
// Create long test bytes (combines short test bytes with word of test bytes)
longData = ethUtil.bufferToHex(Buffer.concat([encodedShortData, encodedWordOfData]));
const longDataLength = new BigNumber(encodedShortData.byteLength + encodedWordOfData.byteLength);
const encodedLongDataLength = assetProxyUtils.encodeUint256(longDataLength);
longTestBytesAsBuffer = Buffer.concat([encodedLongDataLength, encodedShortData, encodedWordOfData]);
longTestBytes = ethUtil.bufferToHex(longTestBytesAsBuffer);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
@@ -61,13 +91,15 @@ describe('LibBytes', () => {
await blockchainLifecycle.revertAsync();
});
describe('popByte', () => {
describe('popLastByte', () => {
it('should revert if length is 0', async () => {
return expect(libBytes.publicPopByte.callAsync(constants.NULL_BYTES)).to.be.rejectedWith(constants.REVERT);
return expectRevertOrOtherErrorAsync(
libBytes.publicPopLastByte.callAsync(constants.NULL_BYTES),
constants.LIB_BYTES_GREATER_THAN_ZERO_LENGTH_REQUIRED,
);
});
it('should pop the last byte from the input and return it', async () => {
const [newBytes, poppedByte] = await libBytes.publicPopByte.callAsync(byteArrayLongerThan32Bytes);
const [newBytes, poppedByte] = await libBytes.publicPopLastByte.callAsync(byteArrayLongerThan32Bytes);
const expectedNewBytes = byteArrayLongerThan32Bytes.slice(0, -2);
const expectedPoppedByte = `0x${byteArrayLongerThan32Bytes.slice(-2)}`;
expect(newBytes).to.equal(expectedNewBytes);
@@ -75,15 +107,15 @@ describe('LibBytes', () => {
});
});
describe('popAddress', () => {
describe('popLast20Bytes', () => {
it('should revert if length is less than 20', async () => {
return expect(libBytes.publicPopAddress.callAsync(byteArrayShorterThan20Bytes)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrOtherErrorAsync(
libBytes.publicPopLast20Bytes.callAsync(byteArrayShorterThan20Bytes),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED,
);
});
it('should pop the last 20 bytes from the input and return it', async () => {
const [newBytes, poppedAddress] = await libBytes.publicPopAddress.callAsync(byteArrayLongerThan32Bytes);
const [newBytes, poppedAddress] = await libBytes.publicPopLast20Bytes.callAsync(byteArrayLongerThan32Bytes);
const expectedNewBytes = byteArrayLongerThan32Bytes.slice(0, -40);
const expectedPoppedAddress = `0x${byteArrayLongerThan32Bytes.slice(-40)}`;
expect(newBytes).to.equal(expectedNewBytes);
@@ -99,7 +131,6 @@ describe('LibBytes', () => {
);
return expect(areBytesEqual).to.be.true();
});
it('should return true if byte arrays are equal (both arrays > 32 bytes)', async () => {
const areBytesEqual = await libBytes.publicAreBytesEqual.callAsync(
byteArrayLongerThan32Bytes,
@@ -107,7 +138,6 @@ describe('LibBytes', () => {
);
return expect(areBytesEqual).to.be.true();
});
it('should return false if byte arrays are not equal (first array < 32 bytes, second array > 32 bytes)', async () => {
const areBytesEqual = await libBytes.publicAreBytesEqual.callAsync(
byteArrayShorterThan32Bytes,
@@ -115,7 +145,6 @@ describe('LibBytes', () => {
);
return expect(areBytesEqual).to.be.false();
});
it('should return false if byte arrays are not equal (first array > 32 bytes, second array < 32 bytes)', async () => {
const areBytesEqual = await libBytes.publicAreBytesEqual.callAsync(
byteArrayLongerThan32Bytes,
@@ -123,7 +152,6 @@ describe('LibBytes', () => {
);
return expect(areBytesEqual).to.be.false();
});
it('should return false if byte arrays are not equal (same length, but a byte in first word differs)', async () => {
const areBytesEqual = await libBytes.publicAreBytesEqual.callAsync(
byteArrayLongerThan32BytesFirstBytesSwapped,
@@ -131,7 +159,6 @@ describe('LibBytes', () => {
);
return expect(areBytesEqual).to.be.false();
});
it('should return false if byte arrays are not equal (same length, but a byte in last word differs)', async () => {
const areBytesEqual = await libBytes.publicAreBytesEqual.callAsync(
byteArrayLongerThan32BytesLastBytesSwapped,
@@ -141,15 +168,50 @@ describe('LibBytes', () => {
});
});
describe('deepCopyBytes', () => {
it('should revert if dest is shorter than source', async () => {
return expectRevertOrOtherErrorAsync(
libBytes.publicDeepCopyBytes.callAsync(byteArrayShorterThan32Bytes, byteArrayLongerThan32Bytes),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED,
);
});
it('should overwrite dest with source if source and dest have equal length', async () => {
const zeroedByteArrayLongerThan32Bytes = `0x${_.repeat('0', byteArrayLongerThan32Bytes.length - 2)}`;
const zeroedBytesAfterCopy = await libBytes.publicDeepCopyBytes.callAsync(
zeroedByteArrayLongerThan32Bytes,
byteArrayLongerThan32Bytes,
);
return expect(zeroedBytesAfterCopy).to.be.equal(byteArrayLongerThan32Bytes);
});
it('should overwrite the leftmost len(source) bytes of dest if dest is larger than source', async () => {
const zeroedByteArrayLongerThan32Bytes = `0x${_.repeat('0', byteArrayLongerThan32Bytes.length * 2)}`;
const zeroedBytesAfterCopy = await libBytes.publicDeepCopyBytes.callAsync(
zeroedByteArrayLongerThan32Bytes,
byteArrayLongerThan32Bytes,
);
const copiedBytes = zeroedBytesAfterCopy.slice(0, byteArrayLongerThan32Bytes.length);
return expect(copiedBytes).to.be.equal(byteArrayLongerThan32Bytes);
});
it('should not overwrite the rightmost bytes of dest if dest is larger than source', async () => {
const zeroedByteArrayLongerThan32Bytes = `0x${_.repeat('0', byteArrayLongerThan32Bytes.length * 2)}`;
const zeroedBytesAfterCopy = await libBytes.publicDeepCopyBytes.callAsync(
zeroedByteArrayLongerThan32Bytes,
byteArrayLongerThan32Bytes,
);
const expectedNotCopiedBytes = zeroedByteArrayLongerThan32Bytes.slice(byteArrayLongerThan32Bytes.length);
const notCopiedBytes = zeroedBytesAfterCopy.slice(byteArrayLongerThan32Bytes.length);
return expect(notCopiedBytes).to.be.equal(expectedNotCopiedBytes);
});
});
describe('readAddress', () => {
it('should successfully read address when the address takes up the whole array)', async () => {
it('should successfully read address when the address takes up the whole array', async () => {
const byteArray = ethUtil.addHexPrefix(testAddress);
const testAddressOffset = new BigNumber(0);
const address = await libBytes.publicReadAddress.callAsync(byteArray, testAddressOffset);
return expect(address).to.be.equal(testAddress);
});
it('should successfully read address when it is offset in the array)', async () => {
it('should successfully read address when it is offset in the array', async () => {
const addressByteArrayBuffer = ethUtil.toBuffer(testAddress);
const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef');
const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, addressByteArrayBuffer]);
@@ -158,80 +220,145 @@ describe('LibBytes', () => {
const address = await libBytes.publicReadAddress.callAsync(combinedByteArray, testAddressOffset);
return expect(address).to.be.equal(testAddress);
});
it('should fail if the byte array is too short to hold an address)', async () => {
it('should fail if the byte array is too short to hold an address', async () => {
const shortByteArray = '0xabcdef';
const offset = new BigNumber(0);
return expect(libBytes.publicReadAddress.callAsync(shortByteArray, offset)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrOtherErrorAsync(
libBytes.publicReadAddress.callAsync(shortByteArray, offset),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED,
);
});
it('should fail if the length between the offset and end of the byte array is too short to hold an address)', async () => {
const byteArray = ethUtil.addHexPrefix(testAddress);
it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => {
const byteArray = testAddress;
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
return expect(libBytes.publicReadAddress.callAsync(byteArray, badOffset)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrOtherErrorAsync(
libBytes.publicReadAddress.callAsync(byteArray, badOffset),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED,
);
});
});
/// @TODO Implement test cases for writeAddress. Test template below.
/// Currently, the generated contract wrappers do not support this library's write methods.
/*
describe('writeAddress', () => {
it('should successfully write address when the address takes up the whole array)', async () => {});
it('should successfully write address when it is offset in the array)', async () => {});
it('should fail if the byte array is too short to hold an address)', async () => {});
it('should fail if the length between the offset and end of the byte array is too short to hold an address)', async () => {});
it('should successfully write address when the address takes up the whole array', async () => {
const byteArray = testAddress;
const testAddressOffset = new BigNumber(0);
const newByteArray = await libBytes.publicWriteAddress.callAsync(
byteArray,
testAddressOffset,
testAddressB,
);
return expect(newByteArray).to.be.equal(testAddressB);
});
it('should successfully write address when it is offset in the array', async () => {
const addressByteArrayBuffer = ethUtil.toBuffer(testAddress);
const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef');
const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, addressByteArrayBuffer]);
const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer);
const testAddressOffset = new BigNumber(prefixByteArrayBuffer.byteLength);
const newByteArray = await libBytes.publicWriteAddress.callAsync(
combinedByteArray,
testAddressOffset,
testAddressB,
);
const newByteArrayBuffer = ethUtil.toBuffer(newByteArray);
const addressFromOffsetBuffer = newByteArrayBuffer.slice(prefixByteArrayBuffer.byteLength);
const addressFromOffset = ethUtil.addHexPrefix(ethUtil.bufferToHex(addressFromOffsetBuffer));
return expect(addressFromOffset).to.be.equal(testAddressB);
});
it('should fail if the byte array is too short to hold an address', async () => {
const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync(
libBytes.publicWriteAddress.callAsync(byteArrayShorterThan20Bytes, offset, testAddress),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED,
);
});
it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => {
const byteArray = byteArrayLongerThan32Bytes;
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
return expectRevertOrOtherErrorAsync(
libBytes.publicWriteAddress.callAsync(byteArray, badOffset, testAddress),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED,
);
});
});
*/
describe('readBytes32', () => {
it('should successfully read bytes32 when the bytes32 takes up the whole array)', async () => {
it('should successfully read bytes32 when the bytes32 takes up the whole array', async () => {
const testBytes32Offset = new BigNumber(0);
const bytes32 = await libBytes.publicReadBytes32.callAsync(testBytes32, testBytes32Offset);
return expect(bytes32).to.be.equal(testBytes32);
});
it('should successfully read bytes32 when it is offset in the array)', async () => {
const bytes32ByteArrayBuffer = ethUtil.toBuffer(testBytes32);
const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef');
const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, bytes32ByteArrayBuffer]);
const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer);
const testAddressOffset = new BigNumber(prefixByteArrayBuffer.byteLength);
const bytes32 = await libBytes.publicReadBytes32.callAsync(combinedByteArray, testAddressOffset);
const testBytes32Offset = new BigNumber(prefixByteArrayBuffer.byteLength);
const bytes32 = await libBytes.publicReadBytes32.callAsync(combinedByteArray, testBytes32Offset);
return expect(bytes32).to.be.equal(testBytes32);
});
it('should fail if the byte array is too short to hold a bytes32)', async () => {
it('should fail if the byte array is too short to hold a bytes32', async () => {
const offset = new BigNumber(0);
return expect(libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrOtherErrorAsync(
libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED,
);
});
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32)', async () => {
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => {
const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength);
return expect(libBytes.publicReadBytes32.callAsync(testBytes32, badOffset)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrOtherErrorAsync(
libBytes.publicReadBytes32.callAsync(testBytes32, badOffset),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED,
);
});
});
/// @TODO Implement test cases for writeBytes32. Test template below.
/// Currently, the generated contract wrappers do not support this library's write methods.
/*
describe('writeBytes32', () => {
it('should successfully write bytes32 when the address takes up the whole array)', async () => {});
it('should successfully write bytes32 when it is offset in the array)', async () => {});
it('should fail if the byte array is too short to hold a bytes32)', async () => {});
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32)', async () => {});
it('should successfully write bytes32 when the address takes up the whole array', async () => {
const byteArray = testBytes32;
const testBytes32Offset = new BigNumber(0);
const newByteArray = await libBytes.publicWriteBytes32.callAsync(
byteArray,
testBytes32Offset,
testBytes32B,
);
return expect(newByteArray).to.be.equal(testBytes32B);
});
it('should successfully write bytes32 when it is offset in the array', async () => {
const bytes32ByteArrayBuffer = ethUtil.toBuffer(testBytes32);
const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef');
const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, bytes32ByteArrayBuffer]);
const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer);
const testBytes32Offset = new BigNumber(prefixByteArrayBuffer.byteLength);
const newByteArray = await libBytes.publicWriteBytes32.callAsync(
combinedByteArray,
testBytes32Offset,
testBytes32B,
);
const newByteArrayBuffer = ethUtil.toBuffer(newByteArray);
const bytes32FromOffsetBuffer = newByteArrayBuffer.slice(prefixByteArrayBuffer.byteLength);
const bytes32FromOffset = ethUtil.addHexPrefix(ethUtil.bufferToHex(bytes32FromOffsetBuffer));
return expect(bytes32FromOffset).to.be.equal(testBytes32B);
});
it('should fail if the byte array is too short to hold a bytes32', async () => {
const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync(
libBytes.publicWriteBytes32.callAsync(byteArrayShorterThan32Bytes, offset, testBytes32),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED,
);
});
it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => {
const byteArray = byteArrayLongerThan32Bytes;
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
return expectRevertOrOtherErrorAsync(
libBytes.publicWriteBytes32.callAsync(byteArray, badOffset, testBytes32),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED,
);
});
});
*/
describe('readUint256', () => {
it('should successfully read uint256 when the uint256 takes up the whole array)', async () => {
it('should successfully read uint256 when the uint256 takes up the whole array', async () => {
const formattedTestUint256 = new BN(testUint256.toString(10));
const testUint256AsBuffer = ethUtil.toBuffer(formattedTestUint256);
const byteArray = ethUtil.bufferToHex(testUint256AsBuffer);
@@ -239,8 +366,7 @@ describe('LibBytes', () => {
const uint256 = await libBytes.publicReadUint256.callAsync(byteArray, testUint256Offset);
return expect(uint256).to.bignumber.equal(testUint256);
});
it('should successfully read uint256 when it is offset in the array)', async () => {
it('should successfully read uint256 when it is offset in the array', async () => {
const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef');
const formattedTestUint256 = new BN(testUint256.toString(10));
const testUint256AsBuffer = ethUtil.toBuffer(formattedTestUint256);
@@ -250,41 +376,80 @@ describe('LibBytes', () => {
const uint256 = await libBytes.publicReadUint256.callAsync(combinedByteArray, testUint256Offset);
return expect(uint256).to.bignumber.equal(testUint256);
});
it('should fail if the byte array is too short to hold a uint256)', async () => {
it('should fail if the byte array is too short to hold a uint256', async () => {
const offset = new BigNumber(0);
return expect(libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrOtherErrorAsync(
libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED,
);
});
it('should fail if the length between the offset and end of the byte array is too short to hold a uint256)', async () => {
it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => {
const formattedTestUint256 = new BN(testUint256.toString(10));
const testUint256AsBuffer = ethUtil.toBuffer(formattedTestUint256);
const byteArray = ethUtil.bufferToHex(testUint256AsBuffer);
const badOffset = new BigNumber(testUint256AsBuffer.byteLength);
return expect(libBytes.publicReadUint256.callAsync(byteArray, badOffset)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrOtherErrorAsync(
libBytes.publicReadUint256.callAsync(byteArray, badOffset),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED,
);
});
});
/// @TODO Implement test cases for writeUint256. Test template below.
/// Currently, the generated contract wrappers do not support this library's write methods.
/*
describe('writeUint256', () => {
it('should successfully write uint256 when the address takes up the whole array)', async () => {});
it('should successfully write uint256 when it is offset in the array)', async () => {});
it('should fail if the byte array is too short to hold a uint256)', async () => {});
it('should fail if the length between the offset and end of the byte array is too short to hold a uint256)', async () => {});
it('should successfully write uint256 when the address takes up the whole array', async () => {
const byteArray = testBytes32;
const testUint256Offset = new BigNumber(0);
const newByteArray = await libBytes.publicWriteUint256.callAsync(
byteArray,
testUint256Offset,
testUint256B,
);
const newByteArrayAsUint256 = new BigNumber(newByteArray, 16);
return expect(newByteArrayAsUint256).to.be.bignumber.equal(testUint256B);
});
it('should successfully write uint256 when it is offset in the array', async () => {
const bytes32ByteArrayBuffer = ethUtil.toBuffer(testBytes32);
const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef');
const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, bytes32ByteArrayBuffer]);
const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer);
const testUint256Offset = new BigNumber(prefixByteArrayBuffer.byteLength);
const newByteArray = await libBytes.publicWriteUint256.callAsync(
combinedByteArray,
testUint256Offset,
testUint256B,
);
const newByteArrayBuffer = ethUtil.toBuffer(newByteArray);
const uint256FromOffsetBuffer = newByteArrayBuffer.slice(prefixByteArrayBuffer.byteLength);
const uint256FromOffset = new BigNumber(
ethUtil.addHexPrefix(ethUtil.bufferToHex(uint256FromOffsetBuffer)),
16,
);
return expect(uint256FromOffset).to.be.bignumber.equal(testUint256B);
});
it('should fail if the byte array is too short to hold a uint256', async () => {
const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync(
libBytes.publicWriteUint256.callAsync(byteArrayShorterThan32Bytes, offset, testUint256),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED,
);
});
it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => {
const byteArray = byteArrayLongerThan32Bytes;
const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength);
return expectRevertOrOtherErrorAsync(
libBytes.publicWriteUint256.callAsync(byteArray, badOffset, testUint256),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED,
);
});
});
*/
describe('readFirst4', () => {
// AssertionError: expected promise to be rejected with an error including 'revert' but it was fulfilled with '0x08c379a0'
it('should revert if byte array has a length < 4', async () => {
const byteArrayLessThan4Bytes = '0x010101';
return expect(libBytes.publicReadFirst4.callAsync(byteArrayLessThan4Bytes)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrOtherErrorAsync(
libBytes.publicReadFirst4.callAsync(byteArrayLessThan4Bytes),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED,
);
});
it('should return the first 4 bytes of a byte array of arbitrary length', async () => {
@@ -293,4 +458,164 @@ describe('LibBytes', () => {
expect(first4Bytes).to.equal(expectedFirst4Bytes);
});
});
describe('readBytes', () => {
it('should successfully read short, nested array of bytes when it takes up the whole array', async () => {
const testBytesOffset = new BigNumber(0);
const bytes = await libBytes.publicReadBytes.callAsync(shortTestBytes, testBytesOffset);
return expect(bytes).to.be.equal(shortData);
});
it('should successfully read short, nested array of bytes when it is offset in the array', async () => {
const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef');
const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, shortTestBytesAsBuffer]);
const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer);
const testUint256Offset = new BigNumber(prefixByteArrayBuffer.byteLength);
const bytes = await libBytes.publicReadBytes.callAsync(combinedByteArray, testUint256Offset);
return expect(bytes).to.be.equal(shortData);
});
it('should successfully read a nested array of bytes - one word in length - when it takes up the whole array', async () => {
const testBytesOffset = new BigNumber(0);
const bytes = await libBytes.publicReadBytes.callAsync(wordOfTestBytes, testBytesOffset);
return expect(bytes).to.be.equal(wordOfData);
});
it('should successfully read a nested array of bytes - one word in length - when it is offset in the array', async () => {
const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef');
const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, wordOfTestBytesAsBuffer]);
const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer);
const testUint256Offset = new BigNumber(prefixByteArrayBuffer.byteLength);
const bytes = await libBytes.publicReadBytes.callAsync(combinedByteArray, testUint256Offset);
return expect(bytes).to.be.equal(wordOfData);
});
it('should successfully read long, nested array of bytes when it takes up the whole array', async () => {
const testBytesOffset = new BigNumber(0);
const bytes = await libBytes.publicReadBytes.callAsync(longTestBytes, testBytesOffset);
return expect(bytes).to.be.equal(longData);
});
it('should successfully read long, nested array of bytes when it is offset in the array', async () => {
const prefixByteArrayBuffer = ethUtil.toBuffer('0xabcdef');
const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, longTestBytesAsBuffer]);
const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer);
const testUint256Offset = new BigNumber(prefixByteArrayBuffer.byteLength);
const bytes = await libBytes.publicReadBytes.callAsync(combinedByteArray, testUint256Offset);
return expect(bytes).to.be.equal(longData);
});
it('should fail if the byte array is too short to hold the length of a nested byte array', async () => {
// The length of the nested array is 32 bytes. By storing less than 32 bytes, a length cannot be read.
const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync(
libBytes.publicReadBytes.callAsync(byteArrayShorterThan32Bytes, offset),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED,
);
});
it('should fail if we store a nested byte array length, without a nested byte array', async () => {
const offset = new BigNumber(0);
return expectRevertOrOtherErrorAsync(
libBytes.publicReadBytes.callAsync(testBytes32, offset),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED,
);
});
it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => {
const badOffset = new BigNumber(ethUtil.toBuffer(byteArrayShorterThan32Bytes).byteLength);
return expectRevertOrOtherErrorAsync(
libBytes.publicReadBytes.callAsync(byteArrayShorterThan32Bytes, badOffset),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED,
);
});
it('should fail if the length between the offset and end of the byte array is too short to hold the nested byte array', async () => {
const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength);
return expectRevertOrOtherErrorAsync(
libBytes.publicReadBytes.callAsync(testBytes32, badOffset),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED,
);
});
});
describe('writeBytes', () => {
it('should successfully write short, nested array of bytes when it takes up the whole array)', async () => {
const testBytesOffset = new BigNumber(0);
const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength));
const bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, testBytesOffset, shortData);
const bytesRead = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset);
return expect(bytesRead).to.be.equal(shortData);
});
it('should successfully write short, nested array of bytes when it is offset in the array', async () => {
// Write a prefix to the array
const prefixData = '0xabcdef';
const prefixDataAsBuffer = ethUtil.toBuffer(prefixData);
const prefixOffset = new BigNumber(0);
const emptyByteArray = ethUtil.bufferToHex(
new Buffer(prefixDataAsBuffer.byteLength + shortTestBytesAsBuffer.byteLength),
);
let bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, prefixOffset, prefixData);
// Write data after prefix
const testBytesOffset = new BigNumber(prefixDataAsBuffer.byteLength);
bytesWritten = await libBytes.publicWriteBytes.callAsync(bytesWritten, testBytesOffset, shortData);
// Read data after prefix and validate
const bytes = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset);
return expect(bytes).to.be.equal(shortData);
});
it('should successfully write a nested array of bytes - one word in length - when it takes up the whole array', async () => {
const testBytesOffset = new BigNumber(0);
const emptyByteArray = ethUtil.bufferToHex(new Buffer(wordOfTestBytesAsBuffer.byteLength));
const bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, testBytesOffset, wordOfData);
const bytesRead = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset);
return expect(bytesRead).to.be.equal(wordOfData);
});
it('should successfully write a nested array of bytes - one word in length - when it is offset in the array', async () => {
// Write a prefix to the array
const prefixData = '0xabcdef';
const prefixDataAsBuffer = ethUtil.toBuffer(prefixData);
const prefixOffset = new BigNumber(0);
const emptyByteArray = ethUtil.bufferToHex(
new Buffer(prefixDataAsBuffer.byteLength + wordOfTestBytesAsBuffer.byteLength),
);
let bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, prefixOffset, prefixData);
// Write data after prefix
const testBytesOffset = new BigNumber(prefixDataAsBuffer.byteLength);
bytesWritten = await libBytes.publicWriteBytes.callAsync(bytesWritten, testBytesOffset, wordOfData);
// Read data after prefix and validate
const bytes = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset);
return expect(bytes).to.be.equal(wordOfData);
});
it('should successfully write a long, nested bytes when it takes up the whole array', async () => {
const testBytesOffset = new BigNumber(0);
const emptyByteArray = ethUtil.bufferToHex(new Buffer(longTestBytesAsBuffer.byteLength));
const bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, testBytesOffset, longData);
const bytesRead = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset);
return expect(bytesRead).to.be.equal(longData);
});
it('should successfully write long, nested array of bytes when it is offset in the array', async () => {
// Write a prefix to the array
const prefixData = '0xabcdef';
const prefixDataAsBuffer = ethUtil.toBuffer(prefixData);
const prefixOffset = new BigNumber(0);
const emptyByteArray = ethUtil.bufferToHex(
new Buffer(prefixDataAsBuffer.byteLength + longTestBytesAsBuffer.byteLength),
);
let bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, prefixOffset, prefixData);
// Write data after prefix
const testBytesOffset = new BigNumber(prefixDataAsBuffer.byteLength);
bytesWritten = await libBytes.publicWriteBytes.callAsync(bytesWritten, testBytesOffset, longData);
// Read data after prefix and validate
const bytes = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset);
return expect(bytes).to.be.equal(longData);
});
it('should fail if the byte array is too short to hold the length of a nested byte array', async () => {
const offset = new BigNumber(0);
const emptyByteArray = ethUtil.bufferToHex(new Buffer(1));
return expectRevertOrOtherErrorAsync(
libBytes.publicWriteBytes.callAsync(emptyByteArray, offset, longData),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED,
);
});
it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array)', async () => {
const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength));
const badOffset = new BigNumber(ethUtil.toBuffer(shortTestBytesAsBuffer).byteLength);
return expectRevertOrOtherErrorAsync(
libBytes.publicWriteBytes.callAsync(emptyByteArray, badOffset, shortData),
constants.LIB_BYTES_GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED,
);
});
});
});
// tslint:disable:max-file-line-count

View File

@@ -0,0 +1,190 @@
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import { TestLibMemContract } from '../../src/generated_contract_wrappers/test_lib_mem';
import { artifacts } from '../../src/utils/artifacts';
import { chaiSetup } from '../../src/utils/chai_setup';
import { provider, txDefaults } from '../../src/utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
// BUG: Ideally we would use Buffer.from(memory).toString('hex')
// https://github.com/Microsoft/TypeScript/issues/23155
const toHex = (buf: Uint8Array): string => buf.reduce((a, v) => a + ('00' + v.toString(16)).slice(-2), '0x');
const fromHex = (str: string): Uint8Array => Uint8Array.from(Buffer.from(str.slice(2), 'hex'));
describe('LibMem', () => {
let testLibMem: TestLibMemContract;
before(async () => {
// Deploy TestLibMem
testLibMem = await TestLibMemContract.deployFrom0xArtifactAsync(artifacts.TestLibMem, provider, txDefaults);
});
describe('memCopy', () => {
// Create memory 0x000102...FF
const memSize = 256;
const memory = new Uint8Array(memSize).map((_, i) => i);
const memHex = toHex(memory);
// Reference implementation to test against
const refMemcpy = (_mem: Uint8Array, dest: number, source: number, length: number): Uint8Array =>
Uint8Array.from(memory).copyWithin(dest, source, source + length);
// Test vectors: destination, source, length, job description
type Tests = Array<[number, number, number, string]>;
const test = (tests: Tests) =>
tests.forEach(([dest, source, length, job]) =>
it(job, async () => {
const expected = refMemcpy(memory, dest, source, length);
const resultStr = await testLibMem.testMemcpy.callAsync(
memHex,
new BigNumber(dest),
new BigNumber(source),
new BigNumber(length),
);
const result = fromHex(resultStr);
expect(result).to.deep.equal(expected);
}),
);
test([[0, 0, 0, 'copies zero bytes with overlap']]);
describe('copies forward', () =>
test([
[128, 0, 0, 'zero bytes'],
[128, 0, 1, 'one byte'],
[128, 0, 11, 'eleven bytes'],
[128, 0, 31, 'thirty-one bytes'],
[128, 0, 32, 'one word'],
[128, 0, 64, 'two words'],
[128, 0, 96, 'three words'],
[128, 0, 33, 'one word and one byte'],
[128, 0, 72, 'two words and eight bytes'],
[128, 0, 100, 'three words and four bytes'],
]));
describe('copies forward within one word', () =>
test([
[16, 0, 0, 'zero bytes'],
[16, 0, 1, 'one byte'],
[16, 0, 11, 'eleven bytes'],
[16, 0, 16, 'sixteen bytes'],
]));
describe('copies forward with one byte overlap', () =>
test([
[0, 0, 1, 'one byte'],
[10, 0, 11, 'eleven bytes'],
[30, 0, 31, 'thirty-one bytes'],
[31, 0, 32, 'one word'],
[32, 0, 33, 'one word and one byte'],
[71, 0, 72, 'two words and eight bytes'],
[99, 0, 100, 'three words and four bytes'],
]));
describe('copies forward with thirty-one bytes overlap', () =>
test([
[0, 0, 31, 'thirty-one bytes'],
[1, 0, 32, 'one word'],
[2, 0, 33, 'one word and one byte'],
[41, 0, 72, 'two words and eight bytes'],
[69, 0, 100, 'three words and four bytes'],
]));
describe('copies forward with one word overlap', () =>
test([
[0, 0, 32, 'one word'],
[1, 0, 33, 'one word and one byte'],
[41, 0, 72, 'two words and eight bytes'],
[69, 0, 100, 'three words and four bytes'],
]));
describe('copies forward with one word and one byte overlap', () =>
test([
[0, 0, 33, 'one word and one byte'],
[40, 0, 72, 'two words and eight bytes'],
[68, 0, 100, 'three words and four bytes'],
]));
describe('copies forward with two words overlap', () =>
test([
[0, 0, 64, 'two words'],
[8, 0, 72, 'two words and eight bytes'],
[36, 0, 100, 'three words and four bytes'],
]));
describe('copies forward within one word and one byte overlap', () =>
test([[0, 0, 1, 'one byte'], [10, 0, 11, 'eleven bytes'], [15, 0, 16, 'sixteen bytes']]));
describe('copies backward', () =>
test([
[0, 128, 0, 'zero bytes'],
[0, 128, 1, 'one byte'],
[0, 128, 11, 'eleven bytes'],
[0, 128, 31, 'thirty-one bytes'],
[0, 128, 32, 'one word'],
[0, 128, 64, 'two words'],
[0, 128, 96, 'three words'],
[0, 128, 33, 'one word and one byte'],
[0, 128, 72, 'two words and eight bytes'],
[0, 128, 100, 'three words and four bytes'],
]));
describe('copies backward within one word', () =>
test([
[0, 16, 0, 'zero bytes'],
[0, 16, 1, 'one byte'],
[0, 16, 11, 'eleven bytes'],
[0, 16, 16, 'sixteen bytes'],
]));
describe('copies backward with one byte overlap', () =>
test([
[0, 0, 1, 'one byte'],
[0, 10, 11, 'eleven bytes'],
[0, 30, 31, 'thirty-one bytes'],
[0, 31, 32, 'one word'],
[0, 32, 33, 'one word and one byte'],
[0, 71, 72, 'two words and eight bytes'],
[0, 99, 100, 'three words and four bytes'],
]));
describe('copies backward with thirty-one bytes overlap', () =>
test([
[0, 0, 31, 'thirty-one bytes'],
[0, 1, 32, 'one word'],
[0, 2, 33, 'one word and one byte'],
[0, 41, 72, 'two words and eight bytes'],
[0, 69, 100, 'three words and four bytes'],
]));
describe('copies backward with one word overlap', () =>
test([
[0, 0, 32, 'one word'],
[0, 1, 33, 'one word and one byte'],
[0, 41, 72, 'two words and eight bytes'],
[0, 69, 100, 'three words and four bytes'],
]));
describe('copies backward with one word and one byte overlap', () =>
test([
[0, 0, 33, 'one word and one byte'],
[0, 40, 72, 'two words and eight bytes'],
[0, 68, 100, 'three words and four bytes'],
]));
describe('copies backward with two words overlap', () =>
test([
[0, 0, 64, 'two words'],
[0, 8, 72, 'two words and eight bytes'],
[0, 36, 100, 'three words and four bytes'],
]));
describe('copies forward within one word and one byte overlap', () =>
test([[0, 0, 1, 'one byte'], [0, 10, 11, 'eleven bytes'], [0, 15, 16, 'sixteen bytes']]));
});
});

View File

@@ -1,26 +1,25 @@
import { BlockchainLifecycle, web3Factory } from '@0xproject/dev-utils';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
import 'make-promises-safe';
import * as Web3 from 'web3';
import {
MultiSigWalletWithTimeLockContract,
SubmissionContractEventArgs,
} from '../src/contract_wrappers/generated/multi_sig_wallet_with_time_lock';
} from '../src/generated_contract_wrappers/multi_sig_wallet_with_time_lock';
import { artifacts } from '../src/utils/artifacts';
import { expectRevertOrAlwaysFailingTransactionAsync } from '../src/utils/assertions';
import { chaiSetup } from '../src/utils/chai_setup';
import { constants } from '../src/utils/constants';
import { increaseTimeAndMineBlockAsync } from '../src/utils/increase_time';
import { MultiSigWrapper } from '../src/utils/multi_sig_wrapper';
import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('MultiSigWalletWithTimeLock', () => {
let owners: string[];
const REQUIRED_APPROVALS = new BigNumber(2);
@@ -69,9 +68,9 @@ describe('MultiSigWalletWithTimeLock', () => {
});
it('should throw when not called by wallet', async () => {
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
multiSig.changeTimeLock.sendTransactionAsync(SECONDS_TIME_LOCKED, { from: owners[0] }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw without enough confirmations', async () => {
@@ -80,10 +79,9 @@ describe('MultiSigWalletWithTimeLock', () => {
const res = await multiSigWrapper.submitTransactionAsync(destination, changeTimeLockData, owners[0]);
const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
const txId = log.args.transactionId;
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should set confirmation time with enough confirmations', async () => {
@@ -148,14 +146,15 @@ describe('MultiSigWalletWithTimeLock', () => {
txId = log.args.transactionId;
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
});
it('should throw if it has enough confirmations but is not past the time lock', async () => {
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should execute if it has enough confirmations and is past the time lock', async () => {
await web3Wrapper.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber());
await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber());
await web3Wrapper.awaitTransactionSuccessAsync(
await multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }),
constants.AWAIT_TRANSACTION_MINED_MS,
@@ -167,3 +166,4 @@ describe('MultiSigWalletWithTimeLock', () => {
});
});
});
// tslint:enable:no-unnecessary-type-assertion

View File

@@ -1,14 +1,13 @@
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { BigNumber, NULL_BYTES } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
import 'make-promises-safe';
import * as Web3 from 'web3';
import { TokenRegistryContract } from '../src/contract_wrappers/generated/token_registry';
import { TokenRegistryContract } from '../src/generated_contract_wrappers/token_registry';
import { artifacts } from '../src/utils/artifacts';
import { expectRevertOrAlwaysFailingTransactionAsync } from '../src/utils/assertions';
import { chaiSetup } from '../src/utils/chai_setup';
import { constants } from '../src/utils/constants';
import { TokenRegWrapper } from '../src/utils/token_registry_wrapper';
@@ -76,7 +75,7 @@ describe('TokenRegistry', () => {
describe('addToken', () => {
it('should throw when not called by owner', async () => {
return expect(tokenRegWrapper.addTokenAsync(token1, notOwner)).to.be.rejectedWith(constants.REVERT);
return expectRevertOrAlwaysFailingTransactionAsync(tokenRegWrapper.addTokenAsync(token1, notOwner));
});
it('should add token metadata when called by owner', async () => {
@@ -88,19 +87,19 @@ describe('TokenRegistry', () => {
it('should throw if token already exists', async () => {
await tokenRegWrapper.addTokenAsync(token1, owner);
return expect(tokenRegWrapper.addTokenAsync(token1, owner)).to.be.rejectedWith(constants.REVERT);
return expectRevertOrAlwaysFailingTransactionAsync(tokenRegWrapper.addTokenAsync(token1, owner));
});
it('should throw if token address is null', async () => {
return expect(tokenRegWrapper.addTokenAsync(nullToken, owner)).to.be.rejectedWith(constants.REVERT);
return expectRevertOrAlwaysFailingTransactionAsync(tokenRegWrapper.addTokenAsync(nullToken, owner));
});
it('should throw if name already exists', async () => {
await tokenRegWrapper.addTokenAsync(token1, owner);
const duplicateNameToken = _.assign({}, token2, { name: token1.name });
return expect(tokenRegWrapper.addTokenAsync(duplicateNameToken, owner)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
tokenRegWrapper.addTokenAsync(duplicateNameToken, owner),
);
});
@@ -110,8 +109,8 @@ describe('TokenRegistry', () => {
symbol: token1.symbol,
});
return expect(tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner)).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrAlwaysFailingTransactionAsync(
tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner),
);
});
});
@@ -137,9 +136,9 @@ describe('TokenRegistry', () => {
describe('setTokenName', () => {
it('should throw when not called by owner', async () => {
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
tokenReg.setTokenName.sendTransactionAsync(token1.address, token2.name, { from: notOwner }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should change the token name when called by owner', async () => {
@@ -163,25 +162,25 @@ describe('TokenRegistry', () => {
it('should throw if the name already exists', async () => {
await tokenRegWrapper.addTokenAsync(token2, owner);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
tokenReg.setTokenName.sendTransactionAsync(token1.address, token2.name, { from: owner }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if token does not exist', async () => {
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
tokenReg.setTokenName.sendTransactionAsync(nullToken.address, token2.name, { from: owner }),
).to.be.rejectedWith(constants.REVERT);
);
});
});
describe('setTokenSymbol', () => {
it('should throw when not called by owner', async () => {
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
tokenReg.setTokenSymbol.sendTransactionAsync(token1.address, token2.symbol, {
from: notOwner,
}),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should change the token symbol when called by owner', async () => {
@@ -203,28 +202,28 @@ describe('TokenRegistry', () => {
it('should throw if the symbol already exists', async () => {
await tokenRegWrapper.addTokenAsync(token2, owner);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
tokenReg.setTokenSymbol.sendTransactionAsync(token1.address, token2.symbol, {
from: owner,
}),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if token does not exist', async () => {
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
tokenReg.setTokenSymbol.sendTransactionAsync(nullToken.address, token2.symbol, {
from: owner,
}),
).to.be.rejectedWith(constants.REVERT);
);
});
});
describe('removeToken', () => {
it('should throw if not called by owner', async () => {
const index = new BigNumber(0);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
tokenReg.removeToken.sendTransactionAsync(token1.address, index, { from: notOwner }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should remove token metadata when called by owner', async () => {
@@ -241,17 +240,17 @@ describe('TokenRegistry', () => {
it('should throw if token does not exist', async () => {
const index = new BigNumber(0);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
tokenReg.removeToken.sendTransactionAsync(nullToken.address, index, { from: owner }),
).to.be.rejectedWith(constants.REVERT);
);
});
it('should throw if token at given index does not match address', async () => {
await tokenRegWrapper.addTokenAsync(token2, owner);
const incorrectIndex = new BigNumber(0);
return expect(
return expectRevertOrAlwaysFailingTransactionAsync(
tokenReg.removeToken.sendTransactionAsync(token2.address, incorrectIndex, { from: owner }),
).to.be.rejectedWith(constants.REVERT);
);
});
});
});

View File

@@ -8,12 +8,12 @@
// import ethUtil = require('ethereumjs-util');
// import * as Web3 from 'web3';
// import { AccountLevelsContract } from '../../src/contract_wrappers/generated/account_levels';
// import { ArbitrageContract } from '../../src/contract_wrappers/generated/arbitrage';
// import { DummyTokenContract } from '../../src/contract_wrappers/generated/dummy_token';
// import { EtherDeltaContract } from '../../src/contract_wrappers/generated/ether_delta';
// import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange';
// import { TokenTransferProxyContract } from '../../src/contract_wrappers/generated/token_transfer_proxy';
// import { AccountLevelsContract } from '../../src/generated_contract_wrappers/account_levels';
// import { ArbitrageContract } from '../../src/generated_contract_wrappers/arbitrage';
// import { DummyTokenContract } from '../../src/generated_contract_wrappers/dummy_token';
// import { EtherDeltaContract } from '../../src/generated_contract_wrappers/ether_delta';
// import { ExchangeContract } from '../../src/generated_contract_wrappers/exchange';
// import { TokenTransferProxyContract } from '../../src/generated_contract_wrappers/token_transfer_proxy';
// import { artifacts } from '../../util/artifacts';
// import { Balances } from '../../util/balances';
// import { constants } from '../../util/constants';

View File

@@ -1,12 +1,11 @@
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
import 'make-promises-safe';
import * as Web3 from 'web3';
import { DummyERC20TokenContract } from '../src/contract_wrappers/generated/dummy_e_r_c20_token';
import { DummyERC20TokenContract } from '../src/generated_contract_wrappers/dummy_e_r_c20_token';
import { artifacts } from '../src/utils/artifacts';
import { expectRevertOrOtherErrorAsync } from '../src/utils/assertions';
import { chaiSetup } from '../src/utils/chai_setup';
import { constants } from '../src/utils/constants';
import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper';
@@ -55,8 +54,9 @@ describe('UnlimitedAllowanceToken', () => {
it('should throw if owner has insufficient balance', async () => {
const ownerBalance = await token.balanceOf.callAsync(owner);
const amountToTransfer = ownerBalance.plus(1);
return expect(token.transfer.callAsync(spender, amountToTransfer, { from: owner })).to.be.rejectedWith(
constants.REVERT,
return expectRevertOrOtherErrorAsync(
token.transfer.callAsync(spender, amountToTransfer, { from: owner }),
constants.ERC20_INSUFFICIENT_BALANCE,
);
});
@@ -93,11 +93,12 @@ describe('UnlimitedAllowanceToken', () => {
await token.approve.sendTransactionAsync(spender, amountToTransfer, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS,
);
return expect(
return expectRevertOrOtherErrorAsync(
token.transferFrom.callAsync(owner, spender, amountToTransfer, {
from: spender,
}),
).to.be.rejectedWith(constants.REVERT);
constants.ERC20_INSUFFICIENT_BALANCE,
);
});
it('should throw if spender has insufficient allowance', async () => {
@@ -108,11 +109,12 @@ describe('UnlimitedAllowanceToken', () => {
const isSpenderAllowanceInsufficient = spenderAllowance.cmp(amountToTransfer) < 0;
expect(isSpenderAllowanceInsufficient).to.be.true();
return expect(
return expectRevertOrOtherErrorAsync(
token.transferFrom.callAsync(owner, spender, amountToTransfer, {
from: spender,
}),
).to.be.rejectedWith(constants.REVERT);
constants.ERC20_INSUFFICIENT_ALLOWANCE,
);
});
it('should return true on a 0 value transfer', async () => {

View File

@@ -1,11 +1,10 @@
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as chai from 'chai';
import 'make-promises-safe';
import * as Web3 from 'web3';
import { ZRXTokenContract } from '../src/contract_wrappers/generated/zrx_token';
import { ZRXTokenContract } from '../src/generated_contract_wrappers/zrx_token';
import { artifacts } from '../src/utils/artifacts';
import { chaiSetup } from '../src/utils/chai_setup';
import { constants } from '../src/utils/constants';
@@ -79,7 +78,10 @@ describe('ZRXToken', () => {
const receiver = spender;
const initOwnerBalance = await zrxToken.balanceOf.callAsync(owner);
const amountToTransfer = new BigNumber(1);
await zrxToken.transfer.sendTransactionAsync(receiver, amountToTransfer, { from: owner });
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.transfer.sendTransactionAsync(receiver, amountToTransfer, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const finalOwnerBalance = await zrxToken.balanceOf.callAsync(owner);
const finalReceiverBalance = await zrxToken.balanceOf.callAsync(receiver);
@@ -101,10 +103,13 @@ describe('ZRXToken', () => {
it('should return false if owner has insufficient balance', async () => {
const ownerBalance = await zrxToken.balanceOf.callAsync(owner);
const amountToTransfer = ownerBalance.plus(1);
await zrxToken.approve.sendTransactionAsync(spender, amountToTransfer, {
from: owner,
gas: constants.MAX_TOKEN_APPROVE_GAS,
});
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(spender, amountToTransfer, {
from: owner,
gas: constants.MAX_TOKEN_APPROVE_GAS,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const didReturnTrue = await zrxToken.transferFrom.callAsync(owner, spender, amountToTransfer, {
from: spender,
});
@@ -137,14 +142,20 @@ describe('ZRXToken', () => {
const initOwnerBalance = await zrxToken.balanceOf.callAsync(owner);
const amountToTransfer = initOwnerBalance;
const initSpenderAllowance = MAX_UINT;
await zrxToken.approve.sendTransactionAsync(spender, initSpenderAllowance, {
from: owner,
gas: constants.MAX_TOKEN_APPROVE_GAS,
});
await zrxToken.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, {
from: spender,
gas: constants.MAX_TOKEN_TRANSFERFROM_GAS,
});
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(spender, initSpenderAllowance, {
from: owner,
gas: constants.MAX_TOKEN_APPROVE_GAS,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, {
from: spender,
gas: constants.MAX_TOKEN_TRANSFERFROM_GAS,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newSpenderAllowance = await zrxToken.allowance.callAsync(owner, spender);
expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance);
@@ -155,11 +166,17 @@ describe('ZRXToken', () => {
const initSpenderBalance = await zrxToken.balanceOf.callAsync(spender);
const amountToTransfer = initOwnerBalance;
const initSpenderAllowance = initOwnerBalance;
await zrxToken.approve.sendTransactionAsync(spender, initSpenderAllowance);
await zrxToken.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, {
from: spender,
gas: constants.MAX_TOKEN_TRANSFERFROM_GAS,
});
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(spender, initSpenderAllowance),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, {
from: spender,
gas: constants.MAX_TOKEN_TRANSFERFROM_GAS,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newOwnerBalance = await zrxToken.balanceOf.callAsync(owner);
const newSpenderBalance = await zrxToken.balanceOf.callAsync(spender);
@@ -171,11 +188,17 @@ describe('ZRXToken', () => {
it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => {
const initOwnerBalance = await zrxToken.balanceOf.callAsync(owner);
const amountToTransfer = initOwnerBalance;
await zrxToken.approve.sendTransactionAsync(spender, amountToTransfer);
await zrxToken.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, {
from: spender,
gas: constants.MAX_TOKEN_TRANSFERFROM_GAS,
});
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(spender, amountToTransfer),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.transferFrom.sendTransactionAsync(owner, spender, amountToTransfer, {
from: spender,
gas: constants.MAX_TOKEN_TRANSFERFROM_GAS,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const newSpenderAllowance = await zrxToken.allowance.callAsync(owner, spender);
expect(newSpenderAllowance).to.be.bignumber.equal(0);