Fixed the bug and added tests that fail without the patch
This commit is contained in:
parent
47c3ed9705
commit
2949db5f49
@ -31,8 +31,6 @@ import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
|||||||
contract OrderTransferSimulationUtils is
|
contract OrderTransferSimulationUtils is
|
||||||
LibExchangeRichErrorDecoder
|
LibExchangeRichErrorDecoder
|
||||||
{
|
{
|
||||||
bool second;
|
|
||||||
|
|
||||||
using LibBytes for bytes;
|
using LibBytes for bytes;
|
||||||
|
|
||||||
enum OrderTransferResults {
|
enum OrderTransferResults {
|
||||||
@ -69,12 +67,11 @@ contract OrderTransferSimulationUtils is
|
|||||||
public
|
public
|
||||||
returns (OrderTransferResults orderTransferResults)
|
returns (OrderTransferResults orderTransferResults)
|
||||||
{
|
{
|
||||||
LibFillResults.FillResults memory fillResults = LibFillResults.calculateFillResultsTrace(
|
LibFillResults.FillResults memory fillResults = LibFillResults.calculateFillResults(
|
||||||
order,
|
order,
|
||||||
takerAssetFillAmount,
|
takerAssetFillAmount,
|
||||||
_EXCHANGE.protocolFeeMultiplier(),
|
_EXCHANGE.protocolFeeMultiplier(),
|
||||||
tx.gasprice,
|
tx.gasprice
|
||||||
second
|
|
||||||
);
|
);
|
||||||
|
|
||||||
bytes[] memory assetData = new bytes[](2);
|
bytes[] memory assetData = new bytes[](2);
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.5.5;
|
pragma solidity ^0.5.9;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||||
@ -127,6 +127,11 @@ contract OrderValidationUtils is
|
|||||||
fillableTakerAssetAmount
|
fillableTakerAssetAmount
|
||||||
) == OrderTransferResults.TransfersSuccessful ? fillableTakerAssetAmount : 0;
|
) == OrderTransferResults.TransfersSuccessful ? fillableTakerAssetAmount : 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Perform taker asset validation
|
||||||
|
fillableTakerAssetAmount = _isTakerAssetDataValid(order) ? fillableTakerAssetAmount : 0;
|
||||||
|
*/
|
||||||
|
|
||||||
return (orderInfo, fillableTakerAssetAmount, isValidSignature);
|
return (orderInfo, fillableTakerAssetAmount, isValidSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,10 +159,6 @@ contract OrderValidationUtils is
|
|||||||
isValidSignature = new bool[](length);
|
isValidSignature = new bool[](length);
|
||||||
|
|
||||||
for (uint256 i = 0; i != length; i++) {
|
for (uint256 i = 0; i != length; i++) {
|
||||||
if (i == 1) {
|
|
||||||
second = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
(ordersInfo[i], fillableTakerAssetAmounts[i], isValidSignature[i]) = getOrderRelevantState(
|
(ordersInfo[i], fillableTakerAssetAmounts[i], isValidSignature[i]) = getOrderRelevantState(
|
||||||
orders[i],
|
orders[i],
|
||||||
signatures[i]
|
signatures[i]
|
||||||
@ -184,4 +185,49 @@ contract OrderValidationUtils is
|
|||||||
transferableAssetAmount = LibSafeMath.min256(balance, allowance);
|
transferableAssetAmount = LibSafeMath.min256(balance, allowance);
|
||||||
return transferableAssetAmount;
|
return transferableAssetAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev This function handles the edge cases around taker validation. This function
|
||||||
|
/// currently attempts to find duplicate ERC721 token's in the taker
|
||||||
|
/// multiAssetData.
|
||||||
|
/// @param order The order that should be validated.
|
||||||
|
/// @return Whether or not the order should be considered valid.
|
||||||
|
function _isTakerAssetDataValid(LibOrder.Order memory order)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
bytes memory takerAssetData = order.takerAssetData;
|
||||||
|
|
||||||
|
// Only process the taker asset data if it is multiAssetData.
|
||||||
|
bytes4 assetProxyId = takerAssetData.readBytes4(0);
|
||||||
|
if (assetProxyId != IAssetData(address(0)).MultiAsset.selector) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get array of values and array of assetDatas
|
||||||
|
(, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(takerAssetData);
|
||||||
|
|
||||||
|
uint256 length = nestedAssetData.length;
|
||||||
|
for (uint256 i = 0; i != length; i++) {
|
||||||
|
// NOTE(jalextowle): As an optimization, we will break out of this function
|
||||||
|
// as soon as it is determined that there are no possible ERC721 duplicates.
|
||||||
|
bool hasSeenERC721 = false;
|
||||||
|
|
||||||
|
bytes4 nestedAssetProxyId = nestedAssetData[i].readBytes4(0);
|
||||||
|
if (nestedAssetProxyId == IAssetData(address(0)).ERC721Token.selector) {
|
||||||
|
hasSeenERC721 = true;
|
||||||
|
for (uint256 j = i; j != length; j++) {
|
||||||
|
if (nestedAssetData[i].equals(nestedAssetData[j])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasSeenERC721) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,51 +49,6 @@ library LibFillResults {
|
|||||||
uint256 profitInRightMakerAsset; // Profit taken from the right maker
|
uint256 profitInRightMakerAsset; // Profit taken from the right maker
|
||||||
}
|
}
|
||||||
|
|
||||||
event TestEvent(string file_name, string value_name, uint256 value);
|
|
||||||
|
|
||||||
/// FIXME: Remove this function after investigation
|
|
||||||
/// @dev Calculates amounts filled and fees paid by maker and taker.
|
|
||||||
/// @param order to be filled.
|
|
||||||
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
|
|
||||||
/// @param protocolFeeMultiplier The current protocol fee of the exchange contract.
|
|
||||||
/// @param gasPrice The gasprice of the transaction. This is provided so that the function call can continue
|
|
||||||
/// to be pure rather than view.
|
|
||||||
/// @return fillResults Amounts filled and fees paid by maker and taker.
|
|
||||||
function calculateFillResultsTrace(
|
|
||||||
LibOrder.Order memory order,
|
|
||||||
uint256 takerAssetFilledAmount,
|
|
||||||
uint256 protocolFeeMultiplier,
|
|
||||||
uint256 gasPrice,
|
|
||||||
bool shouldTrace
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
returns (FillResults memory fillResults)
|
|
||||||
{
|
|
||||||
// Compute proportional transfer amounts
|
|
||||||
fillResults.takerAssetFilledAmount = takerAssetFilledAmount;
|
|
||||||
fillResults.makerAssetFilledAmount = LibMath.safeGetPartialAmountFloor(
|
|
||||||
takerAssetFilledAmount,
|
|
||||||
order.takerAssetAmount,
|
|
||||||
order.makerAssetAmount
|
|
||||||
);
|
|
||||||
fillResults.makerFeePaid = LibMath.safeGetPartialAmountFloor(
|
|
||||||
takerAssetFilledAmount,
|
|
||||||
order.takerAssetAmount,
|
|
||||||
order.makerFee
|
|
||||||
);
|
|
||||||
fillResults.takerFeePaid = LibMath.safeGetPartialAmountFloor(
|
|
||||||
takerAssetFilledAmount,
|
|
||||||
order.takerAssetAmount,
|
|
||||||
order.takerFee
|
|
||||||
);
|
|
||||||
|
|
||||||
// Compute the protocol fee that should be paid for a single fill.
|
|
||||||
fillResults.protocolFeePaid = gasPrice.safeMul(protocolFeeMultiplier);
|
|
||||||
|
|
||||||
return fillResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Calculates amounts filled and fees paid by maker and taker.
|
/// @dev Calculates amounts filled and fees paid by maker and taker.
|
||||||
/// @param order to be filled.
|
/// @param order to be filled.
|
||||||
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
|
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
|
||||||
|
@ -22,6 +22,7 @@ blockchainTests.resets.only('OrderValidationUtils/OrderTransferSimulatorUtils',
|
|||||||
let feeErc20Token: DummyERC20TokenContract;
|
let feeErc20Token: DummyERC20TokenContract;
|
||||||
let erc20Proxy: ERC20ProxyContract;
|
let erc20Proxy: ERC20ProxyContract;
|
||||||
let exchange: ExchangeContract;
|
let exchange: ExchangeContract;
|
||||||
|
let deployment: DeploymentManager;
|
||||||
|
|
||||||
let erc20AssetData: string;
|
let erc20AssetData: string;
|
||||||
let erc20AssetData2: string;
|
let erc20AssetData2: string;
|
||||||
@ -33,7 +34,7 @@ blockchainTests.resets.only('OrderValidationUtils/OrderTransferSimulatorUtils',
|
|||||||
before(async () => {
|
before(async () => {
|
||||||
[takerAddress, owner] = await env.getAccountAddressesAsync();
|
[takerAddress, owner] = await env.getAccountAddressesAsync();
|
||||||
|
|
||||||
const deployment = await DeploymentManager.deployAsync(env, {
|
deployment = await DeploymentManager.deployAsync(env, {
|
||||||
numErc20TokensToDeploy: 3,
|
numErc20TokensToDeploy: 3,
|
||||||
numErc721TokensToDeploy: 1,
|
numErc721TokensToDeploy: 1,
|
||||||
owner,
|
owner,
|
||||||
@ -60,9 +61,6 @@ blockchainTests.resets.only('OrderValidationUtils/OrderTransferSimulatorUtils',
|
|||||||
feeRecipientAddress: constants.NULL_ADDRESS,
|
feeRecipientAddress: constants.NULL_ADDRESS,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
/*
|
|
||||||
await Promise.all(deployment.tokens.erc20.map(async token => maker.configureERC20TokenAsync(token)));
|
|
||||||
*/
|
|
||||||
const [tokenID] = await maker.configureERC721TokenAsync(deployment.tokens.erc721[0]);
|
const [tokenID] = await maker.configureERC721TokenAsync(deployment.tokens.erc721[0]);
|
||||||
erc721AssetData = assetDataUtils.encodeERC721AssetData(deployment.tokens.erc721[0].address, tokenID);
|
erc721AssetData = assetDataUtils.encodeERC721AssetData(deployment.tokens.erc721[0].address, tokenID);
|
||||||
});
|
});
|
||||||
@ -165,11 +163,35 @@ blockchainTests.resets.only('OrderValidationUtils/OrderTransferSimulatorUtils',
|
|||||||
.callAsync();
|
.callAsync();
|
||||||
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||||
});
|
});
|
||||||
it('should return a fillableTakerAssetAmount of 0 when an erc721 asset is duplicated in a multi-asset proxy order', async () => {
|
it('should return a fillableTakerAssetAmount of 0 when an erc721 asset is duplicated in the maker side of a multi-asset proxy order', async () => {
|
||||||
const multiAssetData = await devUtils
|
const multiAssetData = await devUtils
|
||||||
.encodeMultiAssetData([new BigNumber(1), new BigNumber(1)], [erc721AssetData, erc721AssetData])
|
.encodeMultiAssetData([new BigNumber(1), new BigNumber(1)], [erc721AssetData, erc721AssetData])
|
||||||
.callAsync();
|
.callAsync();
|
||||||
signedOrder = await maker.signOrderAsync({ makerAssetData: multiAssetData });
|
signedOrder = await maker.signOrderAsync({
|
||||||
|
makerAssetData: multiAssetData,
|
||||||
|
makerAssetAmount: new BigNumber(1),
|
||||||
|
takerAssetData: erc721AssetData,
|
||||||
|
takerAssetAmount: new BigNumber(1),
|
||||||
|
makerFee: constants.ZERO_AMOUNT,
|
||||||
|
takerFee: constants.ZERO_AMOUNT,
|
||||||
|
});
|
||||||
|
const [, fillableTakerAssetAmount] = await devUtils
|
||||||
|
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||||
|
.callAsync();
|
||||||
|
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
|
||||||
|
});
|
||||||
|
it('should return a fillableTakerAssetAmount of 0 when an erc721 asset is duplicated in the taker side of a multi-asset proxy order', async () => {
|
||||||
|
const multiAssetData = await devUtils
|
||||||
|
.encodeMultiAssetData([new BigNumber(1), new BigNumber(1)], [erc721AssetData, erc721AssetData])
|
||||||
|
.callAsync();
|
||||||
|
signedOrder = await maker.signOrderAsync({
|
||||||
|
makerAssetData: erc721AssetData,
|
||||||
|
makerAssetAmount: new BigNumber(1),
|
||||||
|
takerAssetData: multiAssetData,
|
||||||
|
takerAssetAmount: new BigNumber(1),
|
||||||
|
makerFee: constants.ZERO_AMOUNT,
|
||||||
|
takerFee: constants.ZERO_AMOUNT,
|
||||||
|
});
|
||||||
const [, fillableTakerAssetAmount] = await devUtils
|
const [, fillableTakerAssetAmount] = await devUtils
|
||||||
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
.getOrderRelevantState(signedOrder, signedOrder.signature)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user