Merge pull request #1002 from 0xProject/feature/contracts/mutex
[contracts] Add mutexes and reentrancy guards
This commit is contained in:
commit
f938c989e3
@ -40,6 +40,7 @@
|
|||||||
"MultiSigWallet",
|
"MultiSigWallet",
|
||||||
"MultiSigWalletWithTimeLock",
|
"MultiSigWalletWithTimeLock",
|
||||||
"OrderValidator",
|
"OrderValidator",
|
||||||
|
"ReentrantERC20Token",
|
||||||
"TestAssetProxyOwner",
|
"TestAssetProxyOwner",
|
||||||
"TestAssetProxyDispatcher",
|
"TestAssetProxyDispatcher",
|
||||||
"TestConstants",
|
"TestConstants",
|
||||||
|
@ -20,11 +20,14 @@
|
|||||||
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
|
||||||
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
|
||||||
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
|
||||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
"run_mocha":
|
||||||
|
"mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||||
"compile": "sol-compiler --contracts-dir src",
|
"compile": "sol-compiler --contracts-dir src",
|
||||||
"clean": "shx rm -rf lib generated_contract_wrappers",
|
"clean": "shx rm -rf lib generated_contract_wrappers",
|
||||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output generated_contract_wrappers --backend ethers",
|
"generate_contract_wrappers":
|
||||||
"lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
"abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output generated_contract_wrappers --backend ethers",
|
||||||
|
"lint":
|
||||||
|
"tslint --project . --exclude **/src/generated_contract_wrappers/**/* --exclude **/lib/**/* && yarn lint-contracts",
|
||||||
"coverage:report:text": "istanbul report text",
|
"coverage:report:text": "istanbul report text",
|
||||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||||
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
"profiler:report:html": "istanbul report html && open coverage/index.html",
|
||||||
@ -34,7 +37,7 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"abis":
|
"abis":
|
||||||
"../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json"
|
"../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
pragma solidity 0.4.24;
|
pragma solidity 0.4.24;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
|
||||||
import "./libs/LibConstants.sol";
|
import "./libs/LibConstants.sol";
|
||||||
import "./libs/LibFillResults.sol";
|
import "./libs/LibFillResults.sol";
|
||||||
import "./libs/LibOrder.sol";
|
import "./libs/LibOrder.sol";
|
||||||
@ -30,6 +31,7 @@ import "./mixins/MAssetProxyDispatcher.sol";
|
|||||||
|
|
||||||
|
|
||||||
contract MixinExchangeCore is
|
contract MixinExchangeCore is
|
||||||
|
ReentrancyGuard,
|
||||||
LibConstants,
|
LibConstants,
|
||||||
LibMath,
|
LibMath,
|
||||||
LibOrder,
|
LibOrder,
|
||||||
@ -54,6 +56,7 @@ contract MixinExchangeCore is
|
|||||||
/// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.
|
/// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.
|
||||||
function cancelOrdersUpTo(uint256 targetOrderEpoch)
|
function cancelOrdersUpTo(uint256 targetOrderEpoch)
|
||||||
external
|
external
|
||||||
|
nonReentrant
|
||||||
{
|
{
|
||||||
address makerAddress = getCurrentContextAddress();
|
address makerAddress = getCurrentContextAddress();
|
||||||
// If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination.
|
// If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination.
|
||||||
@ -86,43 +89,14 @@ contract MixinExchangeCore is
|
|||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
|
nonReentrant
|
||||||
returns (FillResults memory fillResults)
|
returns (FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
// Fetch order info
|
fillResults = fillOrderInternal(
|
||||||
OrderInfo memory orderInfo = getOrderInfo(order);
|
|
||||||
|
|
||||||
// Fetch taker address
|
|
||||||
address takerAddress = getCurrentContextAddress();
|
|
||||||
|
|
||||||
// Get amount of takerAsset to fill
|
|
||||||
uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);
|
|
||||||
uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount);
|
|
||||||
|
|
||||||
// Validate context
|
|
||||||
assertValidFill(
|
|
||||||
order,
|
order,
|
||||||
orderInfo,
|
|
||||||
takerAddress,
|
|
||||||
takerAssetFillAmount,
|
takerAssetFillAmount,
|
||||||
takerAssetFilledAmount,
|
|
||||||
signature
|
signature
|
||||||
);
|
);
|
||||||
|
|
||||||
// Compute proportional fill amounts
|
|
||||||
fillResults = calculateFillResults(order, takerAssetFilledAmount);
|
|
||||||
|
|
||||||
// Update exchange internal state
|
|
||||||
updateFilledState(
|
|
||||||
order,
|
|
||||||
takerAddress,
|
|
||||||
orderInfo.orderHash,
|
|
||||||
orderInfo.orderTakerAssetFilledAmount,
|
|
||||||
fillResults
|
|
||||||
);
|
|
||||||
|
|
||||||
// Settle order
|
|
||||||
settleOrder(order, takerAddress, fillResults);
|
|
||||||
|
|
||||||
return fillResults;
|
return fillResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,6 +105,7 @@ contract MixinExchangeCore is
|
|||||||
/// @param order Order to cancel. Order must be OrderStatus.FILLABLE.
|
/// @param order Order to cancel. Order must be OrderStatus.FILLABLE.
|
||||||
function cancelOrder(Order memory order)
|
function cancelOrder(Order memory order)
|
||||||
public
|
public
|
||||||
|
nonReentrant
|
||||||
{
|
{
|
||||||
// Fetch current order status
|
// Fetch current order status
|
||||||
OrderInfo memory orderInfo = getOrderInfo(order);
|
OrderInfo memory orderInfo = getOrderInfo(order);
|
||||||
@ -203,6 +178,57 @@ contract MixinExchangeCore is
|
|||||||
return orderInfo;
|
return orderInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Fills the input order.
|
||||||
|
/// @param order Order struct containing order specifications.
|
||||||
|
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||||
|
/// @param signature Proof that order has been created by maker.
|
||||||
|
/// @return Amounts filled and fees paid by maker and taker.
|
||||||
|
function fillOrderInternal(
|
||||||
|
Order memory order,
|
||||||
|
uint256 takerAssetFillAmount,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (FillResults memory fillResults)
|
||||||
|
{
|
||||||
|
// Fetch order info
|
||||||
|
OrderInfo memory orderInfo = getOrderInfo(order);
|
||||||
|
|
||||||
|
// Fetch taker address
|
||||||
|
address takerAddress = getCurrentContextAddress();
|
||||||
|
|
||||||
|
// Get amount of takerAsset to fill
|
||||||
|
uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);
|
||||||
|
uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount);
|
||||||
|
|
||||||
|
// Validate context
|
||||||
|
assertValidFill(
|
||||||
|
order,
|
||||||
|
orderInfo,
|
||||||
|
takerAddress,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compute proportional fill amounts
|
||||||
|
fillResults = calculateFillResults(order, takerAssetFilledAmount);
|
||||||
|
|
||||||
|
// Update exchange internal state
|
||||||
|
updateFilledState(
|
||||||
|
order,
|
||||||
|
takerAddress,
|
||||||
|
orderInfo.orderHash,
|
||||||
|
orderInfo.orderTakerAssetFilledAmount,
|
||||||
|
fillResults
|
||||||
|
);
|
||||||
|
|
||||||
|
// Settle order
|
||||||
|
settleOrder(order, takerAddress, fillResults);
|
||||||
|
|
||||||
|
return fillResults;
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Updates state with results of a fill order.
|
/// @dev Updates state with results of a fill order.
|
||||||
/// @param order that was filled.
|
/// @param order that was filled.
|
||||||
/// @param takerAddress Address of taker who filled the order.
|
/// @param takerAddress Address of taker who filled the order.
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
pragma solidity 0.4.24;
|
pragma solidity 0.4.24;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
|
||||||
import "./libs/LibConstants.sol";
|
import "./libs/LibConstants.sol";
|
||||||
import "./libs/LibMath.sol";
|
import "./libs/LibMath.sol";
|
||||||
import "./libs/LibOrder.sol";
|
import "./libs/LibOrder.sol";
|
||||||
@ -25,6 +26,7 @@ import "./mixins/MAssetProxyDispatcher.sol";
|
|||||||
|
|
||||||
|
|
||||||
contract MixinMatchOrders is
|
contract MixinMatchOrders is
|
||||||
|
ReentrancyGuard,
|
||||||
LibConstants,
|
LibConstants,
|
||||||
LibMath,
|
LibMath,
|
||||||
MAssetProxyDispatcher,
|
MAssetProxyDispatcher,
|
||||||
@ -48,6 +50,7 @@ contract MixinMatchOrders is
|
|||||||
bytes memory rightSignature
|
bytes memory rightSignature
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
|
nonReentrant
|
||||||
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
|
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
|
||||||
{
|
{
|
||||||
// We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData.
|
// We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData.
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
pragma solidity 0.4.24;
|
pragma solidity 0.4.24;
|
||||||
|
|
||||||
import "../../utils/LibBytes/LibBytes.sol";
|
import "../../utils/LibBytes/LibBytes.sol";
|
||||||
|
import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
|
||||||
import "./mixins/MSignatureValidator.sol";
|
import "./mixins/MSignatureValidator.sol";
|
||||||
import "./mixins/MTransactions.sol";
|
import "./mixins/MTransactions.sol";
|
||||||
import "./interfaces/IWallet.sol";
|
import "./interfaces/IWallet.sol";
|
||||||
@ -26,6 +27,7 @@ import "./interfaces/IValidator.sol";
|
|||||||
|
|
||||||
|
|
||||||
contract MixinSignatureValidator is
|
contract MixinSignatureValidator is
|
||||||
|
ReentrancyGuard,
|
||||||
MSignatureValidator,
|
MSignatureValidator,
|
||||||
MTransactions
|
MTransactions
|
||||||
{
|
{
|
||||||
@ -69,6 +71,7 @@ contract MixinSignatureValidator is
|
|||||||
bool approval
|
bool approval
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
|
nonReentrant
|
||||||
{
|
{
|
||||||
address signerAddress = getCurrentContextAddress();
|
address signerAddress = getCurrentContextAddress();
|
||||||
allowedValidators[signerAddress][validatorAddress] = approval;
|
allowedValidators[signerAddress][validatorAddress] = approval;
|
||||||
|
@ -155,7 +155,8 @@ contract MixinTransactions is
|
|||||||
view
|
view
|
||||||
returns (address)
|
returns (address)
|
||||||
{
|
{
|
||||||
address contextAddress = currentContextAddress == address(0) ? msg.sender : currentContextAddress;
|
address currentContextAddress_ = currentContextAddress;
|
||||||
|
address contextAddress = currentContextAddress_ == address(0) ? msg.sender : currentContextAddress_;
|
||||||
return contextAddress;
|
return contextAddress;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,18 +19,22 @@
|
|||||||
pragma solidity 0.4.24;
|
pragma solidity 0.4.24;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
|
||||||
import "./libs/LibMath.sol";
|
import "./libs/LibMath.sol";
|
||||||
import "./libs/LibOrder.sol";
|
import "./libs/LibOrder.sol";
|
||||||
import "./libs/LibFillResults.sol";
|
import "./libs/LibFillResults.sol";
|
||||||
import "./libs/LibAbiEncoder.sol";
|
import "./libs/LibAbiEncoder.sol";
|
||||||
import "./mixins/MExchangeCore.sol";
|
import "./mixins/MExchangeCore.sol";
|
||||||
|
import "./mixins/MWrapperFunctions.sol";
|
||||||
|
|
||||||
|
|
||||||
contract MixinWrapperFunctions is
|
contract MixinWrapperFunctions is
|
||||||
|
ReentrancyGuard,
|
||||||
LibMath,
|
LibMath,
|
||||||
LibFillResults,
|
LibFillResults,
|
||||||
LibAbiEncoder,
|
LibAbiEncoder,
|
||||||
MExchangeCore
|
MExchangeCore,
|
||||||
|
MWrapperFunctions
|
||||||
{
|
{
|
||||||
|
|
||||||
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
|
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
|
||||||
@ -43,17 +47,14 @@ contract MixinWrapperFunctions is
|
|||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
|
nonReentrant
|
||||||
returns (FillResults memory fillResults)
|
returns (FillResults memory fillResults)
|
||||||
{
|
{
|
||||||
fillResults = fillOrder(
|
fillResults = fillOrKillOrderInternal(
|
||||||
order,
|
order,
|
||||||
takerAssetFillAmount,
|
takerAssetFillAmount,
|
||||||
signature
|
signature
|
||||||
);
|
);
|
||||||
require(
|
|
||||||
fillResults.takerAssetFilledAmount == takerAssetFillAmount,
|
|
||||||
"COMPLETE_FILL_FAILED"
|
|
||||||
);
|
|
||||||
return fillResults;
|
return fillResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,14 +89,7 @@ contract MixinWrapperFunctions is
|
|||||||
fillOrderCalldata, // write output over input
|
fillOrderCalldata, // write output over input
|
||||||
128 // output size is 128 bytes
|
128 // output size is 128 bytes
|
||||||
)
|
)
|
||||||
switch success
|
if success {
|
||||||
case 0 {
|
|
||||||
mstore(fillResults, 0)
|
|
||||||
mstore(add(fillResults, 32), 0)
|
|
||||||
mstore(add(fillResults, 64), 0)
|
|
||||||
mstore(add(fillResults, 96), 0)
|
|
||||||
}
|
|
||||||
case 1 {
|
|
||||||
mstore(fillResults, mload(fillOrderCalldata))
|
mstore(fillResults, mload(fillOrderCalldata))
|
||||||
mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))
|
mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))
|
||||||
mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))
|
mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))
|
||||||
@ -117,11 +111,12 @@ contract MixinWrapperFunctions is
|
|||||||
bytes[] memory signatures
|
bytes[] memory signatures
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
|
nonReentrant
|
||||||
returns (FillResults memory totalFillResults)
|
returns (FillResults memory totalFillResults)
|
||||||
{
|
{
|
||||||
uint256 ordersLength = orders.length;
|
uint256 ordersLength = orders.length;
|
||||||
for (uint256 i = 0; i != ordersLength; i++) {
|
for (uint256 i = 0; i != ordersLength; i++) {
|
||||||
FillResults memory singleFillResults = fillOrder(
|
FillResults memory singleFillResults = fillOrderInternal(
|
||||||
orders[i],
|
orders[i],
|
||||||
takerAssetFillAmounts[i],
|
takerAssetFillAmounts[i],
|
||||||
signatures[i]
|
signatures[i]
|
||||||
@ -143,11 +138,12 @@ contract MixinWrapperFunctions is
|
|||||||
bytes[] memory signatures
|
bytes[] memory signatures
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
|
nonReentrant
|
||||||
returns (FillResults memory totalFillResults)
|
returns (FillResults memory totalFillResults)
|
||||||
{
|
{
|
||||||
uint256 ordersLength = orders.length;
|
uint256 ordersLength = orders.length;
|
||||||
for (uint256 i = 0; i != ordersLength; i++) {
|
for (uint256 i = 0; i != ordersLength; i++) {
|
||||||
FillResults memory singleFillResults = fillOrKillOrder(
|
FillResults memory singleFillResults = fillOrKillOrderInternal(
|
||||||
orders[i],
|
orders[i],
|
||||||
takerAssetFillAmounts[i],
|
takerAssetFillAmounts[i],
|
||||||
signatures[i]
|
signatures[i]
|
||||||
@ -195,6 +191,7 @@ contract MixinWrapperFunctions is
|
|||||||
bytes[] memory signatures
|
bytes[] memory signatures
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
|
nonReentrant
|
||||||
returns (FillResults memory totalFillResults)
|
returns (FillResults memory totalFillResults)
|
||||||
{
|
{
|
||||||
bytes memory takerAssetData = orders[0].takerAssetData;
|
bytes memory takerAssetData = orders[0].takerAssetData;
|
||||||
@ -210,7 +207,7 @@ contract MixinWrapperFunctions is
|
|||||||
uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);
|
uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);
|
||||||
|
|
||||||
// Attempt to sell the remaining amount of takerAsset
|
// Attempt to sell the remaining amount of takerAsset
|
||||||
FillResults memory singleFillResults = fillOrder(
|
FillResults memory singleFillResults = fillOrderInternal(
|
||||||
orders[i],
|
orders[i],
|
||||||
remainingTakerAssetFillAmount,
|
remainingTakerAssetFillAmount,
|
||||||
signatures[i]
|
signatures[i]
|
||||||
@ -282,6 +279,7 @@ contract MixinWrapperFunctions is
|
|||||||
bytes[] memory signatures
|
bytes[] memory signatures
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
|
nonReentrant
|
||||||
returns (FillResults memory totalFillResults)
|
returns (FillResults memory totalFillResults)
|
||||||
{
|
{
|
||||||
bytes memory makerAssetData = orders[0].makerAssetData;
|
bytes memory makerAssetData = orders[0].makerAssetData;
|
||||||
@ -305,7 +303,7 @@ contract MixinWrapperFunctions is
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Attempt to sell the remaining amount of takerAsset
|
// Attempt to sell the remaining amount of takerAsset
|
||||||
FillResults memory singleFillResults = fillOrder(
|
FillResults memory singleFillResults = fillOrderInternal(
|
||||||
orders[i],
|
orders[i],
|
||||||
remainingTakerAssetFillAmount,
|
remainingTakerAssetFillAmount,
|
||||||
signatures[i]
|
signatures[i]
|
||||||
@ -400,4 +398,28 @@ contract MixinWrapperFunctions is
|
|||||||
}
|
}
|
||||||
return ordersInfo;
|
return ordersInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
|
||||||
|
/// @param order Order struct containing order specifications.
|
||||||
|
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||||
|
/// @param signature Proof that order has been created by maker.
|
||||||
|
function fillOrKillOrderInternal(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
uint256 takerAssetFillAmount,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (FillResults memory fillResults)
|
||||||
|
{
|
||||||
|
fillResults = fillOrderInternal(
|
||||||
|
order,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
require(
|
||||||
|
fillResults.takerAssetFilledAmount == takerAssetFillAmount,
|
||||||
|
"COMPLETE_FILL_FAILED"
|
||||||
|
);
|
||||||
|
return fillResults;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,19 @@ contract MExchangeCore is
|
|||||||
uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled.
|
uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled.
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// @dev Fills the input order.
|
||||||
|
/// @param order Order struct containing order specifications.
|
||||||
|
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||||
|
/// @param signature Proof that order has been created by maker.
|
||||||
|
/// @return Amounts filled and fees paid by maker and taker.
|
||||||
|
function fillOrderInternal(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
uint256 takerAssetFillAmount,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (LibFillResults.FillResults memory fillResults);
|
||||||
|
|
||||||
/// @dev Updates state with results of a fill order.
|
/// @dev Updates state with results of a fill order.
|
||||||
/// @param order that was filled.
|
/// @param order that was filled.
|
||||||
/// @param takerAddress Address of taker who filled the order.
|
/// @param takerAddress Address of taker who filled the order.
|
||||||
|
40
packages/contracts/src/2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol
vendored
Normal file
40
packages/contracts/src/2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 "../libs/LibOrder.sol";
|
||||||
|
import "../libs/LibFillResults.sol";
|
||||||
|
import "../interfaces/IWrapperFunctions.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract MWrapperFunctions {
|
||||||
|
|
||||||
|
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
|
||||||
|
/// @param order LibOrder.Order struct containing order specifications.
|
||||||
|
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||||
|
/// @param signature Proof that order has been created by maker.
|
||||||
|
function fillOrKillOrderInternal(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
uint256 takerAssetFillAmount,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (LibFillResults.FillResults memory fillResults);
|
||||||
|
}
|
182
packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol
vendored
Normal file
182
packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol
vendored
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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 "../../utils/LibBytes/LibBytes.sol";
|
||||||
|
import "../../tokens/ERC20Token/ERC20Token.sol";
|
||||||
|
import "../../protocol/Exchange/interfaces/IExchange.sol";
|
||||||
|
import "../../protocol/Exchange/libs/LibOrder.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract ReentrantERC20Token is
|
||||||
|
ERC20Token
|
||||||
|
{
|
||||||
|
|
||||||
|
using LibBytes for bytes;
|
||||||
|
|
||||||
|
// solhint-disable-next-line var-name-mixedcase
|
||||||
|
IExchange internal EXCHANGE;
|
||||||
|
|
||||||
|
bytes internal constant REENTRANCY_ILLEGAL_REVERT_REASON = abi.encodeWithSelector(
|
||||||
|
bytes4(keccak256("Error(string)")),
|
||||||
|
"REENTRANCY_ILLEGAL"
|
||||||
|
);
|
||||||
|
|
||||||
|
// All of these functions are potentially vulnerable to reentrancy
|
||||||
|
// We do not test any "noThrow" functions because `fillOrderNoThrow` makes a delegatecall to `fillOrder`
|
||||||
|
enum ExchangeFunction {
|
||||||
|
FILL_ORDER,
|
||||||
|
FILL_OR_KILL_ORDER,
|
||||||
|
BATCH_FILL_ORDERS,
|
||||||
|
BATCH_FILL_OR_KILL_ORDERS,
|
||||||
|
MARKET_BUY_ORDERS,
|
||||||
|
MARKET_SELL_ORDERS,
|
||||||
|
MATCH_ORDERS,
|
||||||
|
CANCEL_ORDER,
|
||||||
|
CANCEL_ORDERS_UP_TO,
|
||||||
|
SET_SIGNATURE_VALIDATOR_APPROVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 internal currentFunctionId = 0;
|
||||||
|
|
||||||
|
constructor (address _exchange)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
EXCHANGE = IExchange(_exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Set the current function that will be called when `transferFrom` is called.
|
||||||
|
/// @param _currentFunctionId Id that corresponds to function name.
|
||||||
|
function setCurrentFunction(uint8 _currentFunctionId)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
currentFunctionId = _currentFunctionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev A version of `transferFrom` that attempts to reenter the Exchange contract.
|
||||||
|
/// @param _from The address of the sender
|
||||||
|
/// @param _to The address of the recipient
|
||||||
|
/// @param _value The amount of token to be transferred
|
||||||
|
function transferFrom(
|
||||||
|
address _from,
|
||||||
|
address _to,
|
||||||
|
uint256 _value
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
// This order would normally be invalid, but it will be used strictly for testing reentrnacy.
|
||||||
|
// Any reentrancy checks will happen before any other checks that invalidate the order.
|
||||||
|
LibOrder.Order memory order;
|
||||||
|
|
||||||
|
// Initialize remaining null parameters
|
||||||
|
bytes memory signature;
|
||||||
|
LibOrder.Order[] memory orders;
|
||||||
|
uint256[] memory takerAssetFillAmounts;
|
||||||
|
bytes[] memory signatures;
|
||||||
|
bytes memory calldata;
|
||||||
|
|
||||||
|
// Create calldata for function that corresponds to currentFunctionId
|
||||||
|
if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER)) {
|
||||||
|
calldata = abi.encodeWithSelector(
|
||||||
|
EXCHANGE.fillOrder.selector,
|
||||||
|
order,
|
||||||
|
0,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
} else if (currentFunctionId == uint8(ExchangeFunction.FILL_OR_KILL_ORDER)) {
|
||||||
|
calldata = abi.encodeWithSelector(
|
||||||
|
EXCHANGE.fillOrKillOrder.selector,
|
||||||
|
order,
|
||||||
|
0,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
} else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS)) {
|
||||||
|
calldata = abi.encodeWithSelector(
|
||||||
|
EXCHANGE.batchFillOrders.selector,
|
||||||
|
orders,
|
||||||
|
takerAssetFillAmounts,
|
||||||
|
signatures
|
||||||
|
);
|
||||||
|
} else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_OR_KILL_ORDERS)) {
|
||||||
|
calldata = abi.encodeWithSelector(
|
||||||
|
EXCHANGE.batchFillOrKillOrders.selector,
|
||||||
|
orders,
|
||||||
|
takerAssetFillAmounts,
|
||||||
|
signatures
|
||||||
|
);
|
||||||
|
} else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS)) {
|
||||||
|
calldata = abi.encodeWithSelector(
|
||||||
|
EXCHANGE.marketBuyOrders.selector,
|
||||||
|
orders,
|
||||||
|
0,
|
||||||
|
signatures
|
||||||
|
);
|
||||||
|
} else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS)) {
|
||||||
|
calldata = abi.encodeWithSelector(
|
||||||
|
EXCHANGE.marketSellOrders.selector,
|
||||||
|
orders,
|
||||||
|
0,
|
||||||
|
signatures
|
||||||
|
);
|
||||||
|
} else if (currentFunctionId == uint8(ExchangeFunction.MATCH_ORDERS)) {
|
||||||
|
calldata = abi.encodeWithSelector(
|
||||||
|
EXCHANGE.matchOrders.selector,
|
||||||
|
order,
|
||||||
|
order,
|
||||||
|
signature,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
} else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDER)) {
|
||||||
|
calldata = abi.encodeWithSelector(
|
||||||
|
EXCHANGE.cancelOrder.selector,
|
||||||
|
order
|
||||||
|
);
|
||||||
|
} else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDERS_UP_TO)) {
|
||||||
|
calldata = abi.encodeWithSelector(
|
||||||
|
EXCHANGE.cancelOrdersUpTo.selector,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
} else if (currentFunctionId == uint8(ExchangeFunction.SET_SIGNATURE_VALIDATOR_APPROVAL)) {
|
||||||
|
calldata = abi.encodeWithSelector(
|
||||||
|
EXCHANGE.setSignatureValidatorApproval.selector,
|
||||||
|
address(0),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call Exchange function, swallow error
|
||||||
|
address(EXCHANGE).call(calldata);
|
||||||
|
|
||||||
|
// Revert reason is 100 bytes
|
||||||
|
bytes memory returnData = new bytes(100);
|
||||||
|
|
||||||
|
// Copy return data
|
||||||
|
assembly {
|
||||||
|
returndatacopy(add(returnData, 32), 0, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revert if function reverted with REENTRANCY_ILLEGAL error
|
||||||
|
require(!REENTRANCY_ILLEGAL_REVERT_REASON.equals(returnData));
|
||||||
|
|
||||||
|
// Transfer will return true if function failed for any other reason
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
44
packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol
vendored
Normal file
44
packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2018 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
pragma solidity 0.4.24;
|
||||||
|
|
||||||
|
|
||||||
|
contract ReentrancyGuard {
|
||||||
|
|
||||||
|
// Locked state of mutex
|
||||||
|
bool private locked = false;
|
||||||
|
|
||||||
|
/// @dev Functions with this modifer cannot be reentered. The mutex will be locked
|
||||||
|
/// before function execution and unlocked after.
|
||||||
|
modifier nonReentrant() {
|
||||||
|
// Ensure mutex is unlocked
|
||||||
|
require(
|
||||||
|
!locked,
|
||||||
|
"REENTRANCY_ILLEGAL"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Lock mutex before function call
|
||||||
|
locked = true;
|
||||||
|
|
||||||
|
// Perform function call
|
||||||
|
_;
|
||||||
|
|
||||||
|
// Unlock mutex after function call
|
||||||
|
locked = false;
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ import { DummyNoReturnERC20TokenContract } from '../../generated_contract_wrappe
|
|||||||
import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
|
import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
|
||||||
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
|
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
|
||||||
import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange';
|
import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange';
|
||||||
|
import { ReentrantERC20TokenContract } from '../../generated_contract_wrappers/reentrant_erc20_token';
|
||||||
import { TestStaticCallReceiverContract } from '../../generated_contract_wrappers/test_static_call_receiver';
|
import { TestStaticCallReceiverContract } from '../../generated_contract_wrappers/test_static_call_receiver';
|
||||||
import { artifacts } from '../utils/artifacts';
|
import { artifacts } from '../utils/artifacts';
|
||||||
import { expectTransactionFailedAsync } from '../utils/assertions';
|
import { expectTransactionFailedAsync } from '../utils/assertions';
|
||||||
@ -42,6 +43,7 @@ describe('Exchange core', () => {
|
|||||||
let zrxToken: DummyERC20TokenContract;
|
let zrxToken: DummyERC20TokenContract;
|
||||||
let erc721Token: DummyERC721TokenContract;
|
let erc721Token: DummyERC721TokenContract;
|
||||||
let noReturnErc20Token: DummyNoReturnERC20TokenContract;
|
let noReturnErc20Token: DummyNoReturnERC20TokenContract;
|
||||||
|
let reentrantErc20Token: ReentrantERC20TokenContract;
|
||||||
let exchange: ExchangeContract;
|
let exchange: ExchangeContract;
|
||||||
let erc20Proxy: ERC20ProxyContract;
|
let erc20Proxy: ERC20ProxyContract;
|
||||||
let erc721Proxy: ERC721ProxyContract;
|
let erc721Proxy: ERC721ProxyContract;
|
||||||
@ -117,6 +119,12 @@ describe('Exchange core', () => {
|
|||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
);
|
);
|
||||||
|
reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.ReentrantERC20Token,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
exchange.address,
|
||||||
|
);
|
||||||
|
|
||||||
defaultMakerAssetAddress = erc20TokenA.address;
|
defaultMakerAssetAddress = erc20TokenA.address;
|
||||||
defaultTakerAssetAddress = erc20TokenB.address;
|
defaultTakerAssetAddress = erc20TokenB.address;
|
||||||
@ -144,6 +152,26 @@ describe('Exchange core', () => {
|
|||||||
signedOrder = await orderFactory.newSignedOrderAsync();
|
signedOrder = await orderFactory.newSignedOrderAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const reentrancyTest = (functionNames: string[]) => {
|
||||||
|
_.forEach(functionNames, async (functionName: string, functionId: number) => {
|
||||||
|
const description = `should not allow fillOrder to reenter the Exchange contract via ${functionName}`;
|
||||||
|
it(description, async () => {
|
||||||
|
signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
|
||||||
|
});
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
exchangeWrapper.fillOrderAsync(signedOrder, takerAddress),
|
||||||
|
RevertReason.TransferFailed,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
describe('fillOrder reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
|
||||||
|
|
||||||
it('should throw if signature is invalid', async () => {
|
it('should throw if signature is invalid', async () => {
|
||||||
signedOrder = await orderFactory.newSignedOrderAsync({
|
signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
@ -502,7 +530,7 @@ describe('Exchange core', () => {
|
|||||||
// HACK(albrow): We need to hardcode the gas estimate here because
|
// HACK(albrow): We need to hardcode the gas estimate here because
|
||||||
// the Geth gas estimator doesn't work with the way we use
|
// the Geth gas estimator doesn't work with the way we use
|
||||||
// delegatecall and swallow errors.
|
// delegatecall and swallow errors.
|
||||||
gas: 490000,
|
gas: 600000,
|
||||||
});
|
});
|
||||||
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||||
|
@ -11,6 +11,7 @@ import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dumm
|
|||||||
import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
|
import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
|
||||||
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
|
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
|
||||||
import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
|
import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
|
||||||
|
import { ReentrantERC20TokenContract } from '../../generated_contract_wrappers/reentrant_erc20_token';
|
||||||
import { artifacts } from '../utils/artifacts';
|
import { artifacts } from '../utils/artifacts';
|
||||||
import { expectTransactionFailedAsync } from '../utils/assertions';
|
import { expectTransactionFailedAsync } from '../utils/assertions';
|
||||||
import { chaiSetup } from '../utils/chai_setup';
|
import { chaiSetup } from '../utils/chai_setup';
|
||||||
@ -39,6 +40,7 @@ describe('matchOrders', () => {
|
|||||||
let erc20TokenB: DummyERC20TokenContract;
|
let erc20TokenB: DummyERC20TokenContract;
|
||||||
let zrxToken: DummyERC20TokenContract;
|
let zrxToken: DummyERC20TokenContract;
|
||||||
let erc721Token: DummyERC721TokenContract;
|
let erc721Token: DummyERC721TokenContract;
|
||||||
|
let reentrantErc20Token: ReentrantERC20TokenContract;
|
||||||
let exchange: ExchangeContract;
|
let exchange: ExchangeContract;
|
||||||
let erc20Proxy: ERC20ProxyContract;
|
let erc20Proxy: ERC20ProxyContract;
|
||||||
let erc721Proxy: ERC721ProxyContract;
|
let erc721Proxy: ERC721ProxyContract;
|
||||||
@ -127,21 +129,39 @@ describe('matchOrders', () => {
|
|||||||
}),
|
}),
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.ReentrantERC20Token,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
exchange.address,
|
||||||
|
);
|
||||||
|
|
||||||
// Set default addresses
|
// Set default addresses
|
||||||
defaultERC20MakerAssetAddress = erc20TokenA.address;
|
defaultERC20MakerAssetAddress = erc20TokenA.address;
|
||||||
defaultERC20TakerAssetAddress = erc20TokenB.address;
|
defaultERC20TakerAssetAddress = erc20TokenB.address;
|
||||||
defaultERC721AssetAddress = erc721Token.address;
|
defaultERC721AssetAddress = erc721Token.address;
|
||||||
// Create default order parameters
|
// Create default order parameters
|
||||||
const defaultOrderParams = {
|
const defaultOrderParamsLeft = {
|
||||||
...constants.STATIC_ORDER_PARAMS,
|
...constants.STATIC_ORDER_PARAMS,
|
||||||
|
makerAddress: makerAddressLeft,
|
||||||
exchangeAddress: exchange.address,
|
exchangeAddress: exchange.address,
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
||||||
|
feeRecipientAddress: feeRecipientAddressLeft,
|
||||||
|
};
|
||||||
|
const defaultOrderParamsRight = {
|
||||||
|
...constants.STATIC_ORDER_PARAMS,
|
||||||
|
makerAddress: makerAddressRight,
|
||||||
|
exchangeAddress: exchange.address,
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
||||||
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
||||||
|
feeRecipientAddress: feeRecipientAddressRight,
|
||||||
};
|
};
|
||||||
const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)];
|
const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)];
|
||||||
orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParams);
|
orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParamsLeft);
|
||||||
const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)];
|
const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)];
|
||||||
orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParams);
|
orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParamsRight);
|
||||||
// Set match order tester
|
// Set match order tester
|
||||||
matchOrderTester = new MatchOrderTester(exchangeWrapper, erc20Wrapper, erc721Wrapper, zrxToken.address);
|
matchOrderTester = new MatchOrderTester(exchangeWrapper, erc20Wrapper, erc721Wrapper, zrxToken.address);
|
||||||
});
|
});
|
||||||
@ -157,22 +177,45 @@ describe('matchOrders', () => {
|
|||||||
erc721TokenIdsByOwner = await erc721Wrapper.getBalancesAsync();
|
erc721TokenIdsByOwner = await erc721Wrapper.getBalancesAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should transfer the correct amounts when orders completely fill each other', async () => {
|
const reentrancyTest = (functionNames: string[]) => {
|
||||||
// Create orders to match
|
_.forEach(functionNames, async (functionName: string, functionId: number) => {
|
||||||
|
const description = `should not allow matchOrders to reenter the Exchange contract via ${functionName}`;
|
||||||
|
it(description, async () => {
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
makerAddress: makerAddressRight,
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
takerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
feeRecipientAddress: feeRecipientAddressRight,
|
||||||
});
|
});
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress),
|
||||||
|
RevertReason.TransferFailed,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
describe('matchOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
|
||||||
|
|
||||||
|
it('should transfer the correct amounts when orders completely fill each other', async () => {
|
||||||
|
// Create orders to match
|
||||||
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
|
});
|
||||||
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
|
});
|
||||||
// Match signedOrderLeft with signedOrderRight
|
// Match signedOrderLeft with signedOrderRight
|
||||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||||
signedOrderLeft,
|
signedOrderLeft,
|
||||||
@ -192,18 +235,12 @@ describe('matchOrders', () => {
|
|||||||
it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => {
|
it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Store original taker balance
|
// Store original taker balance
|
||||||
const takerInitialBalances = _.cloneDeep(erc20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress]);
|
const takerInitialBalances = _.cloneDeep(erc20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress]);
|
||||||
@ -237,18 +274,12 @@ describe('matchOrders', () => {
|
|||||||
it('should transfer the correct amounts when left order is completely filled and right order is partially filled', async () => {
|
it('should transfer the correct amounts when left order is completely filled and right order is partially filled', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||||
@ -269,18 +300,12 @@ describe('matchOrders', () => {
|
|||||||
it('should transfer the correct amounts when right order is completely filled and left order is partially filled', async () => {
|
it('should transfer the correct amounts when right order is completely filled and left order is partially filled', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||||
@ -301,18 +326,12 @@ describe('matchOrders', () => {
|
|||||||
it('should transfer the correct amounts when consecutive calls are used to completely fill the left order', async () => {
|
it('should transfer the correct amounts when consecutive calls are used to completely fill the left order', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
let newERC20BalancesByOwner: ERC20BalancesByOwner;
|
let newERC20BalancesByOwner: ERC20BalancesByOwner;
|
||||||
@ -340,12 +359,8 @@ describe('matchOrders', () => {
|
|||||||
// However, we use 100/50 to ensure a partial fill as we want to go down the "left fill"
|
// However, we use 100/50 to ensure a partial fill as we want to go down the "left fill"
|
||||||
// branch in the contract twice for this test.
|
// branch in the contract twice for this test.
|
||||||
const signedOrderRight2 = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight2 = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match signedOrderLeft with signedOrderRight2
|
// Match signedOrderLeft with signedOrderRight2
|
||||||
const leftTakerAssetFilledAmount = signedOrderRight.makerAssetAmount;
|
const leftTakerAssetFilledAmount = signedOrderRight.makerAssetAmount;
|
||||||
@ -370,19 +385,13 @@ describe('matchOrders', () => {
|
|||||||
it('should transfer the correct amounts when consecutive calls are used to completely fill the right order', async () => {
|
it('should transfer the correct amounts when consecutive calls are used to completely fill the right order', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
let newERC20BalancesByOwner: ERC20BalancesByOwner;
|
let newERC20BalancesByOwner: ERC20BalancesByOwner;
|
||||||
@ -410,10 +419,8 @@ describe('matchOrders', () => {
|
|||||||
// However, we use 100/50 to ensure a partial fill as we want to go down the "right fill"
|
// However, we use 100/50 to ensure a partial fill as we want to go down the "right fill"
|
||||||
// branch in the contract twice for this test.
|
// branch in the contract twice for this test.
|
||||||
const signedOrderLeft2 = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft2 = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
// Match signedOrderLeft2 with signedOrderRight
|
// Match signedOrderLeft2 with signedOrderRight
|
||||||
const leftTakerAssetFilledAmount = new BigNumber(0);
|
const leftTakerAssetFilledAmount = new BigNumber(0);
|
||||||
@ -441,15 +448,11 @@ describe('matchOrders', () => {
|
|||||||
it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => {
|
it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => {
|
||||||
const feeRecipientAddress = feeRecipientAddressLeft;
|
const feeRecipientAddress = feeRecipientAddressLeft;
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress,
|
feeRecipientAddress,
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress,
|
feeRecipientAddress,
|
||||||
@ -467,18 +470,12 @@ describe('matchOrders', () => {
|
|||||||
it('should transfer the correct amounts if taker is also the left order maker', async () => {
|
it('should transfer the correct amounts if taker is also the left order maker', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
takerAddress = signedOrderLeft.makerAddress;
|
takerAddress = signedOrderLeft.makerAddress;
|
||||||
@ -494,18 +491,12 @@ describe('matchOrders', () => {
|
|||||||
it('should transfer the correct amounts if taker is also the right order maker', async () => {
|
it('should transfer the correct amounts if taker is also the right order maker', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
takerAddress = signedOrderRight.makerAddress;
|
takerAddress = signedOrderRight.makerAddress;
|
||||||
@ -521,18 +512,12 @@ describe('matchOrders', () => {
|
|||||||
it('should transfer the correct amounts if taker is also the left fee recipient', async () => {
|
it('should transfer the correct amounts if taker is also the left fee recipient', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
takerAddress = feeRecipientAddressLeft;
|
takerAddress = feeRecipientAddressLeft;
|
||||||
@ -548,18 +533,12 @@ describe('matchOrders', () => {
|
|||||||
it('should transfer the correct amounts if taker is also the right fee recipient', async () => {
|
it('should transfer the correct amounts if taker is also the right fee recipient', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
takerAddress = feeRecipientAddressRight;
|
takerAddress = feeRecipientAddressRight;
|
||||||
@ -575,18 +554,12 @@ describe('matchOrders', () => {
|
|||||||
it('should transfer the correct amounts if left maker is the left fee recipient and right maker is the right fee recipient', async () => {
|
it('should transfer the correct amounts if left maker is the left fee recipient and right maker is the right fee recipient', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: makerAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: makerAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||||
@ -601,18 +574,12 @@ describe('matchOrders', () => {
|
|||||||
it('Should throw if left order is not fillable', async () => {
|
it('Should throw if left order is not fillable', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Cancel left order
|
// Cancel left order
|
||||||
await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress);
|
await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress);
|
||||||
@ -626,18 +593,12 @@ describe('matchOrders', () => {
|
|||||||
it('Should throw if right order is not fillable', async () => {
|
it('Should throw if right order is not fillable', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Cancel right order
|
// Cancel right order
|
||||||
await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress);
|
await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress);
|
||||||
@ -651,18 +612,12 @@ describe('matchOrders', () => {
|
|||||||
it('should throw if there is not a positive spread', async () => {
|
it('should throw if there is not a positive spread', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
return expectTransactionFailedAsync(
|
return expectTransactionFailedAsync(
|
||||||
@ -674,18 +629,13 @@ describe('matchOrders', () => {
|
|||||||
it('should throw if the left maker asset is not equal to the right taker asset ', async () => {
|
it('should throw if the left maker asset is not equal to the right taker asset ', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
return expectTransactionFailedAsync(
|
return expectTransactionFailedAsync(
|
||||||
@ -701,20 +651,13 @@ describe('matchOrders', () => {
|
|||||||
it('should throw if the right maker asset is not equal to the left taker asset', async () => {
|
it('should throw if the right maker asset is not equal to the left taker asset', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
return expectTransactionFailedAsync(
|
return expectTransactionFailedAsync(
|
||||||
@ -727,20 +670,14 @@ describe('matchOrders', () => {
|
|||||||
// Create orders to match
|
// Create orders to match
|
||||||
const erc721TokenToTransfer = erc721LeftMakerAssetIds[0];
|
const erc721TokenToTransfer = erc721LeftMakerAssetIds[0];
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer),
|
makerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer),
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
makerAssetAmount: new BigNumber(1),
|
makerAssetAmount: new BigNumber(1),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer),
|
takerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer),
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: new BigNumber(1),
|
takerAssetAmount: new BigNumber(1),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||||
@ -762,20 +699,14 @@ describe('matchOrders', () => {
|
|||||||
// Create orders to match
|
// Create orders to match
|
||||||
const erc721TokenToTransfer = erc721RightMakerAssetIds[0];
|
const erc721TokenToTransfer = erc721RightMakerAssetIds[0];
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer),
|
takerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer),
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
takerAssetAmount: new BigNumber(1),
|
takerAssetAmount: new BigNumber(1),
|
||||||
feeRecipientAddress: feeRecipientAddressLeft,
|
|
||||||
});
|
});
|
||||||
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressRight,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer),
|
makerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer),
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress),
|
|
||||||
makerAssetAmount: new BigNumber(1),
|
makerAssetAmount: new BigNumber(1),
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||||
feeRecipientAddress: feeRecipientAddressRight,
|
|
||||||
});
|
});
|
||||||
// Match orders
|
// Match orders
|
||||||
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
await matchOrderTester.matchOrdersAndVerifyBalancesAsync(
|
||||||
|
@ -11,6 +11,7 @@ import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dumm
|
|||||||
import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
|
import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
|
||||||
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
|
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
|
||||||
import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
|
import { ExchangeContract } from '../../generated_contract_wrappers/exchange';
|
||||||
|
import { ReentrantERC20TokenContract } from '../../generated_contract_wrappers/reentrant_erc20_token';
|
||||||
import { artifacts } from '../utils/artifacts';
|
import { artifacts } from '../utils/artifacts';
|
||||||
import { expectTransactionFailedAsync } from '../utils/assertions';
|
import { expectTransactionFailedAsync } from '../utils/assertions';
|
||||||
import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp';
|
import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp';
|
||||||
@ -40,6 +41,7 @@ describe('Exchange wrappers', () => {
|
|||||||
let exchange: ExchangeContract;
|
let exchange: ExchangeContract;
|
||||||
let erc20Proxy: ERC20ProxyContract;
|
let erc20Proxy: ERC20ProxyContract;
|
||||||
let erc721Proxy: ERC721ProxyContract;
|
let erc721Proxy: ERC721ProxyContract;
|
||||||
|
let reentrantErc20Token: ReentrantERC20TokenContract;
|
||||||
|
|
||||||
let exchangeWrapper: ExchangeWrapper;
|
let exchangeWrapper: ExchangeWrapper;
|
||||||
let erc20Wrapper: ERC20Wrapper;
|
let erc20Wrapper: ERC20Wrapper;
|
||||||
@ -104,6 +106,13 @@ describe('Exchange wrappers', () => {
|
|||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.ReentrantERC20Token,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
exchange.address,
|
||||||
|
);
|
||||||
|
|
||||||
defaultMakerAssetAddress = erc20TokenA.address;
|
defaultMakerAssetAddress = erc20TokenA.address;
|
||||||
defaultTakerAssetAddress = erc20TokenB.address;
|
defaultTakerAssetAddress = erc20TokenB.address;
|
||||||
|
|
||||||
@ -126,6 +135,26 @@ describe('Exchange wrappers', () => {
|
|||||||
await blockchainLifecycle.revertAsync();
|
await blockchainLifecycle.revertAsync();
|
||||||
});
|
});
|
||||||
describe('fillOrKillOrder', () => {
|
describe('fillOrKillOrder', () => {
|
||||||
|
const reentrancyTest = (functionNames: string[]) => {
|
||||||
|
_.forEach(functionNames, async (functionName: string, functionId: number) => {
|
||||||
|
const description = `should not allow fillOrKillOrder to reenter the Exchange contract via ${functionName}`;
|
||||||
|
it(description, async () => {
|
||||||
|
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
|
||||||
|
});
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress),
|
||||||
|
RevertReason.TransferFailed,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
describe('fillOrKillOrder reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
|
||||||
|
|
||||||
it('should transfer the correct amounts', async () => {
|
it('should transfer the correct amounts', async () => {
|
||||||
const signedOrder = await orderFactory.newSignedOrderAsync({
|
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
||||||
@ -197,6 +226,25 @@ describe('Exchange wrappers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('fillOrderNoThrow', () => {
|
describe('fillOrderNoThrow', () => {
|
||||||
|
const reentrancyTest = (functionNames: string[]) => {
|
||||||
|
_.forEach(functionNames, async (functionName: string, functionId: number) => {
|
||||||
|
const description = `should not allow fillOrderNoThrow to reenter the Exchange contract via ${functionName}`;
|
||||||
|
it(description, async () => {
|
||||||
|
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
|
||||||
|
});
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||||
|
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||||
|
expect(erc20Balances).to.deep.equal(newBalances);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
describe('fillOrderNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
|
||||||
|
|
||||||
it('should transfer the correct amounts', async () => {
|
it('should transfer the correct amounts', async () => {
|
||||||
const signedOrder = await orderFactory.newSignedOrderAsync({
|
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
||||||
@ -397,6 +445,26 @@ describe('Exchange wrappers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('batchFillOrders', () => {
|
describe('batchFillOrders', () => {
|
||||||
|
const reentrancyTest = (functionNames: string[]) => {
|
||||||
|
_.forEach(functionNames, async (functionName: string, functionId: number) => {
|
||||||
|
const description = `should not allow batchFillOrders to reenter the Exchange contract via ${functionName}`;
|
||||||
|
it(description, async () => {
|
||||||
|
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
|
||||||
|
});
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
exchangeWrapper.batchFillOrdersAsync([signedOrder], takerAddress),
|
||||||
|
RevertReason.TransferFailed,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
describe('batchFillOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
|
||||||
|
|
||||||
it('should transfer the correct amounts', async () => {
|
it('should transfer the correct amounts', async () => {
|
||||||
const takerAssetFillAmounts: BigNumber[] = [];
|
const takerAssetFillAmounts: BigNumber[] = [];
|
||||||
const makerAssetAddress = erc20TokenA.address;
|
const makerAssetAddress = erc20TokenA.address;
|
||||||
@ -446,6 +514,26 @@ describe('Exchange wrappers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('batchFillOrKillOrders', () => {
|
describe('batchFillOrKillOrders', () => {
|
||||||
|
const reentrancyTest = (functionNames: string[]) => {
|
||||||
|
_.forEach(functionNames, async (functionName: string, functionId: number) => {
|
||||||
|
const description = `should not allow batchFillOrKillOrders to reenter the Exchange contract via ${functionName}`;
|
||||||
|
it(description, async () => {
|
||||||
|
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
|
||||||
|
});
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
exchangeWrapper.batchFillOrKillOrdersAsync([signedOrder], takerAddress),
|
||||||
|
RevertReason.TransferFailed,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
describe('batchFillOrKillOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
|
||||||
|
|
||||||
it('should transfer the correct amounts', async () => {
|
it('should transfer the correct amounts', async () => {
|
||||||
const takerAssetFillAmounts: BigNumber[] = [];
|
const takerAssetFillAmounts: BigNumber[] = [];
|
||||||
const makerAssetAddress = erc20TokenA.address;
|
const makerAssetAddress = erc20TokenA.address;
|
||||||
@ -512,6 +600,25 @@ describe('Exchange wrappers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('batchFillOrdersNoThrow', async () => {
|
describe('batchFillOrdersNoThrow', async () => {
|
||||||
|
const reentrancyTest = (functionNames: string[]) => {
|
||||||
|
_.forEach(functionNames, async (functionName: string, functionId: number) => {
|
||||||
|
const description = `should not allow batchFillOrdersNoThrow to reenter the Exchange contract via ${functionName}`;
|
||||||
|
it(description, async () => {
|
||||||
|
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
|
||||||
|
});
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await exchangeWrapper.batchFillOrdersNoThrowAsync([signedOrder], takerAddress);
|
||||||
|
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||||
|
expect(erc20Balances).to.deep.equal(newBalances);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
describe('batchFillOrdersNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
|
||||||
|
|
||||||
it('should transfer the correct amounts', async () => {
|
it('should transfer the correct amounts', async () => {
|
||||||
const takerAssetFillAmounts: BigNumber[] = [];
|
const takerAssetFillAmounts: BigNumber[] = [];
|
||||||
const makerAssetAddress = erc20TokenA.address;
|
const makerAssetAddress = erc20TokenA.address;
|
||||||
@ -625,6 +732,28 @@ describe('Exchange wrappers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('marketSellOrders', () => {
|
describe('marketSellOrders', () => {
|
||||||
|
const reentrancyTest = (functionNames: string[]) => {
|
||||||
|
_.forEach(functionNames, async (functionName: string, functionId: number) => {
|
||||||
|
const description = `should not allow marketSellOrders to reenter the Exchange contract via ${functionName}`;
|
||||||
|
it(description, async () => {
|
||||||
|
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
|
||||||
|
});
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
exchangeWrapper.marketSellOrdersAsync([signedOrder], takerAddress, {
|
||||||
|
takerAssetFillAmount: signedOrder.takerAssetAmount,
|
||||||
|
}),
|
||||||
|
RevertReason.TransferFailed,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
describe('marketSellOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
|
||||||
|
|
||||||
it('should stop when the entire takerAssetFillAmount is filled', async () => {
|
it('should stop when the entire takerAssetFillAmount is filled', async () => {
|
||||||
const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus(
|
const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus(
|
||||||
signedOrders[1].takerAssetAmount.div(2),
|
signedOrders[1].takerAssetAmount.div(2),
|
||||||
@ -717,6 +846,27 @@ describe('Exchange wrappers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('marketSellOrdersNoThrow', () => {
|
describe('marketSellOrdersNoThrow', () => {
|
||||||
|
const reentrancyTest = (functionNames: string[]) => {
|
||||||
|
_.forEach(functionNames, async (functionName: string, functionId: number) => {
|
||||||
|
const description = `should not allow marketSellOrdersNoThrow to reenter the Exchange contract via ${functionName}`;
|
||||||
|
it(description, async () => {
|
||||||
|
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
|
||||||
|
});
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await exchangeWrapper.marketSellOrdersNoThrowAsync([signedOrder], takerAddress, {
|
||||||
|
takerAssetFillAmount: signedOrder.takerAssetAmount,
|
||||||
|
});
|
||||||
|
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||||
|
expect(erc20Balances).to.deep.equal(newBalances);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
describe('marketSellOrdersNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
|
||||||
|
|
||||||
it('should stop when the entire takerAssetFillAmount is filled', async () => {
|
it('should stop when the entire takerAssetFillAmount is filled', async () => {
|
||||||
const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus(
|
const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus(
|
||||||
signedOrders[1].takerAssetAmount.div(2),
|
signedOrders[1].takerAssetAmount.div(2),
|
||||||
@ -843,6 +993,28 @@ describe('Exchange wrappers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('marketBuyOrders', () => {
|
describe('marketBuyOrders', () => {
|
||||||
|
const reentrancyTest = (functionNames: string[]) => {
|
||||||
|
_.forEach(functionNames, async (functionName: string, functionId: number) => {
|
||||||
|
const description = `should not allow marketBuyOrders to reenter the Exchange contract via ${functionName}`;
|
||||||
|
it(description, async () => {
|
||||||
|
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
|
||||||
|
});
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await expectTransactionFailedAsync(
|
||||||
|
exchangeWrapper.marketBuyOrdersAsync([signedOrder], takerAddress, {
|
||||||
|
makerAssetFillAmount: signedOrder.makerAssetAmount,
|
||||||
|
}),
|
||||||
|
RevertReason.TransferFailed,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
describe('marketBuyOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
|
||||||
|
|
||||||
it('should stop when the entire makerAssetFillAmount is filled', async () => {
|
it('should stop when the entire makerAssetFillAmount is filled', async () => {
|
||||||
const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus(
|
const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus(
|
||||||
signedOrders[1].makerAssetAmount.div(2),
|
signedOrders[1].makerAssetAmount.div(2),
|
||||||
@ -933,6 +1105,27 @@ describe('Exchange wrappers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('marketBuyOrdersNoThrow', () => {
|
describe('marketBuyOrdersNoThrow', () => {
|
||||||
|
const reentrancyTest = (functionNames: string[]) => {
|
||||||
|
_.forEach(functionNames, async (functionName: string, functionId: number) => {
|
||||||
|
const description = `should not allow marketBuyOrdersNoThrow to reenter the Exchange contract via ${functionName}`;
|
||||||
|
it(description, async () => {
|
||||||
|
const signedOrder = await orderFactory.newSignedOrderAsync({
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address),
|
||||||
|
});
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await exchangeWrapper.marketBuyOrdersNoThrowAsync([signedOrder], takerAddress, {
|
||||||
|
makerAssetFillAmount: signedOrder.makerAssetAmount,
|
||||||
|
});
|
||||||
|
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||||
|
expect(erc20Balances).to.deep.equal(newBalances);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
describe('marketBuyOrdersNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX));
|
||||||
|
|
||||||
it('should stop when the entire makerAssetFillAmount is filled', async () => {
|
it('should stop when the entire makerAssetFillAmount is filled', async () => {
|
||||||
const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus(
|
const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus(
|
||||||
signedOrders[1].makerAssetAmount.div(2),
|
signedOrders[1].makerAssetAmount.div(2),
|
||||||
|
@ -16,6 +16,7 @@ import * as MixinAuthorizable from '../../artifacts/MixinAuthorizable.json';
|
|||||||
import * as MultiSigWallet from '../../artifacts/MultiSigWallet.json';
|
import * as MultiSigWallet from '../../artifacts/MultiSigWallet.json';
|
||||||
import * as MultiSigWalletWithTimeLock from '../../artifacts/MultiSigWalletWithTimeLock.json';
|
import * as MultiSigWalletWithTimeLock from '../../artifacts/MultiSigWalletWithTimeLock.json';
|
||||||
import * as OrderValidator from '../../artifacts/OrderValidator.json';
|
import * as OrderValidator from '../../artifacts/OrderValidator.json';
|
||||||
|
import * as ReentrantERC20Token from '../../artifacts/ReentrantERC20Token.json';
|
||||||
import * as TestAssetProxyDispatcher from '../../artifacts/TestAssetProxyDispatcher.json';
|
import * as TestAssetProxyDispatcher from '../../artifacts/TestAssetProxyDispatcher.json';
|
||||||
import * as TestAssetProxyOwner from '../../artifacts/TestAssetProxyOwner.json';
|
import * as TestAssetProxyOwner from '../../artifacts/TestAssetProxyOwner.json';
|
||||||
import * as TestConstants from '../../artifacts/TestConstants.json';
|
import * as TestConstants from '../../artifacts/TestConstants.json';
|
||||||
@ -49,6 +50,7 @@ export const artifacts = {
|
|||||||
MultiSigWallet: (MultiSigWallet as any) as ContractArtifact,
|
MultiSigWallet: (MultiSigWallet as any) as ContractArtifact,
|
||||||
MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact,
|
MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact,
|
||||||
OrderValidator: (OrderValidator as any) as ContractArtifact,
|
OrderValidator: (OrderValidator as any) as ContractArtifact,
|
||||||
|
ReentrantERC20Token: (ReentrantERC20Token as any) as ContractArtifact,
|
||||||
TestAssetProxyOwner: (TestAssetProxyOwner as any) as ContractArtifact,
|
TestAssetProxyOwner: (TestAssetProxyOwner as any) as ContractArtifact,
|
||||||
TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact,
|
TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact,
|
||||||
TestConstants: (TestConstants as any) as ContractArtifact,
|
TestConstants: (TestConstants as any) as ContractArtifact,
|
||||||
|
@ -51,4 +51,16 @@ export const constants = {
|
|||||||
WORD_LENGTH: 32,
|
WORD_LENGTH: 32,
|
||||||
ZERO_AMOUNT: new BigNumber(0),
|
ZERO_AMOUNT: new BigNumber(0),
|
||||||
PERCENTAGE_DENOMINATOR: new BigNumber(10).pow(18),
|
PERCENTAGE_DENOMINATOR: new BigNumber(10).pow(18),
|
||||||
|
FUNCTIONS_WITH_MUTEX: [
|
||||||
|
'FILL_ORDER',
|
||||||
|
'FILL_OR_KILL_ORDER',
|
||||||
|
'BATCH_FILL_ORDERS',
|
||||||
|
'BATCH_FILL_OR_KILL_ORDERS',
|
||||||
|
'MARKET_BUY_ORDERS',
|
||||||
|
'MARKET_SELL_ORDERS',
|
||||||
|
'MATCH_ORDERS',
|
||||||
|
'CANCEL_ORDER',
|
||||||
|
'CANCEL_ORDERS_UP_TO',
|
||||||
|
'SET_SIGNATURE_VALIDATOR_APPROVAL',
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user