Merge pull request #2043 from jalextowle/feature/contracts/3.0/order-matching-unit-tests
MatchOrders Unit Tests
This commit is contained in:
commit
434d027133
@ -473,6 +473,116 @@ contract MixinMatchOrders is
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient.
|
||||||
|
/// @param leftOrderHash First matched order hash.
|
||||||
|
/// @param rightOrderHash Second matched order hash.
|
||||||
|
/// @param leftOrder First matched order.
|
||||||
|
/// @param rightOrder Second matched order.
|
||||||
|
/// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit.
|
||||||
|
/// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients.
|
||||||
|
function _settleMatchedOrders(
|
||||||
|
bytes32 leftOrderHash,
|
||||||
|
bytes32 rightOrderHash,
|
||||||
|
LibOrder.Order memory leftOrder,
|
||||||
|
LibOrder.Order memory rightOrder,
|
||||||
|
address takerAddress,
|
||||||
|
LibFillResults.MatchedFillResults memory matchedFillResults
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
{
|
||||||
|
address leftFeeRecipientAddress = leftOrder.feeRecipientAddress;
|
||||||
|
address rightFeeRecipientAddress = rightOrder.feeRecipientAddress;
|
||||||
|
|
||||||
|
// Right maker asset -> left maker
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
rightOrderHash,
|
||||||
|
rightOrder.makerAssetData,
|
||||||
|
rightOrder.makerAddress,
|
||||||
|
leftOrder.makerAddress,
|
||||||
|
matchedFillResults.left.takerAssetFilledAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
// Left maker asset -> right maker
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
leftOrderHash,
|
||||||
|
leftOrder.makerAssetData,
|
||||||
|
leftOrder.makerAddress,
|
||||||
|
rightOrder.makerAddress,
|
||||||
|
matchedFillResults.right.takerAssetFilledAmount
|
||||||
|
);
|
||||||
|
|
||||||
|
// Right maker fee -> right fee recipient
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
rightOrderHash,
|
||||||
|
rightOrder.makerFeeAssetData,
|
||||||
|
rightOrder.makerAddress,
|
||||||
|
rightFeeRecipientAddress,
|
||||||
|
matchedFillResults.right.makerFeePaid
|
||||||
|
);
|
||||||
|
|
||||||
|
// Left maker fee -> left fee recipient
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
leftOrderHash,
|
||||||
|
leftOrder.makerFeeAssetData,
|
||||||
|
leftOrder.makerAddress,
|
||||||
|
leftFeeRecipientAddress,
|
||||||
|
matchedFillResults.left.makerFeePaid
|
||||||
|
);
|
||||||
|
|
||||||
|
// Settle taker profits.
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
leftOrderHash,
|
||||||
|
leftOrder.makerAssetData,
|
||||||
|
leftOrder.makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
matchedFillResults.profitInLeftMakerAsset
|
||||||
|
);
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
rightOrderHash,
|
||||||
|
rightOrder.makerAssetData,
|
||||||
|
rightOrder.makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
matchedFillResults.profitInRightMakerAsset
|
||||||
|
);
|
||||||
|
|
||||||
|
// Settle taker fees.
|
||||||
|
if (
|
||||||
|
leftFeeRecipientAddress == rightFeeRecipientAddress &&
|
||||||
|
leftOrder.takerFeeAssetData.equals(rightOrder.takerFeeAssetData)
|
||||||
|
) {
|
||||||
|
// Fee recipients and taker fee assets are identical, so we can
|
||||||
|
// transfer them in one go.
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
leftOrderHash,
|
||||||
|
leftOrder.takerFeeAssetData,
|
||||||
|
takerAddress,
|
||||||
|
leftFeeRecipientAddress,
|
||||||
|
_safeAdd(
|
||||||
|
matchedFillResults.left.takerFeePaid,
|
||||||
|
matchedFillResults.right.takerFeePaid
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Right taker fee -> right fee recipient
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
rightOrderHash,
|
||||||
|
rightOrder.takerFeeAssetData,
|
||||||
|
takerAddress,
|
||||||
|
rightFeeRecipientAddress,
|
||||||
|
matchedFillResults.right.takerFeePaid
|
||||||
|
);
|
||||||
|
|
||||||
|
// Left taker fee -> left fee recipient
|
||||||
|
_dispatchTransferFrom(
|
||||||
|
leftOrderHash,
|
||||||
|
leftOrder.takerFeeAssetData,
|
||||||
|
takerAddress,
|
||||||
|
leftFeeRecipientAddress,
|
||||||
|
matchedFillResults.left.takerFeePaid
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Match complementary orders that have a profitable spread.
|
/// @dev Match complementary orders that have a profitable spread.
|
||||||
/// Each order is filled at their respective price point, and
|
/// Each order is filled at their respective price point, and
|
||||||
/// the matcher receives a profit denominated in the left maker asset.
|
/// the matcher receives a profit denominated in the left maker asset.
|
||||||
@ -708,115 +818,4 @@ contract MixinMatchOrders is
|
|||||||
|
|
||||||
return matchedFillResults;
|
return matchedFillResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient.
|
|
||||||
/// @param leftOrderHash First matched order hash.
|
|
||||||
/// @param rightOrderHash Second matched order hash.
|
|
||||||
/// @param leftOrder First matched order.
|
|
||||||
/// @param rightOrder Second matched order.
|
|
||||||
/// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit.
|
|
||||||
/// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients.
|
|
||||||
function _settleMatchedOrders(
|
|
||||||
bytes32 leftOrderHash,
|
|
||||||
bytes32 rightOrderHash,
|
|
||||||
LibOrder.Order memory leftOrder,
|
|
||||||
LibOrder.Order memory rightOrder,
|
|
||||||
address takerAddress,
|
|
||||||
LibFillResults.MatchedFillResults memory matchedFillResults
|
|
||||||
)
|
|
||||||
private
|
|
||||||
{
|
|
||||||
address leftFeeRecipientAddress = leftOrder.feeRecipientAddress;
|
|
||||||
address rightFeeRecipientAddress = rightOrder.feeRecipientAddress;
|
|
||||||
|
|
||||||
// Right maker asset -> left maker
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
rightOrderHash,
|
|
||||||
rightOrder.makerAssetData,
|
|
||||||
rightOrder.makerAddress,
|
|
||||||
leftOrder.makerAddress,
|
|
||||||
matchedFillResults.left.takerAssetFilledAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
// Left maker asset -> right maker
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
leftOrderHash,
|
|
||||||
leftOrder.makerAssetData,
|
|
||||||
leftOrder.makerAddress,
|
|
||||||
rightOrder.makerAddress,
|
|
||||||
matchedFillResults.right.takerAssetFilledAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
// Right maker fee -> right fee recipient
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
rightOrderHash,
|
|
||||||
rightOrder.makerFeeAssetData,
|
|
||||||
rightOrder.makerAddress,
|
|
||||||
rightFeeRecipientAddress,
|
|
||||||
matchedFillResults.right.makerFeePaid
|
|
||||||
);
|
|
||||||
|
|
||||||
// Left maker fee -> left fee recipient
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
leftOrderHash,
|
|
||||||
leftOrder.makerFeeAssetData,
|
|
||||||
leftOrder.makerAddress,
|
|
||||||
leftFeeRecipientAddress,
|
|
||||||
matchedFillResults.left.makerFeePaid
|
|
||||||
);
|
|
||||||
|
|
||||||
// Settle taker profits.
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
leftOrderHash,
|
|
||||||
leftOrder.makerAssetData,
|
|
||||||
leftOrder.makerAddress,
|
|
||||||
takerAddress,
|
|
||||||
matchedFillResults.profitInLeftMakerAsset
|
|
||||||
);
|
|
||||||
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
rightOrderHash,
|
|
||||||
rightOrder.makerAssetData,
|
|
||||||
rightOrder.makerAddress,
|
|
||||||
takerAddress,
|
|
||||||
matchedFillResults.profitInRightMakerAsset
|
|
||||||
);
|
|
||||||
|
|
||||||
// Settle taker fees.
|
|
||||||
if (
|
|
||||||
leftFeeRecipientAddress == rightFeeRecipientAddress &&
|
|
||||||
leftOrder.takerFeeAssetData.equals(rightOrder.takerFeeAssetData)
|
|
||||||
) {
|
|
||||||
// Fee recipients and taker fee assets are identical, so we can
|
|
||||||
// transfer them in one go.
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
leftOrderHash,
|
|
||||||
leftOrder.takerFeeAssetData,
|
|
||||||
takerAddress,
|
|
||||||
leftFeeRecipientAddress,
|
|
||||||
_safeAdd(
|
|
||||||
matchedFillResults.left.takerFeePaid,
|
|
||||||
matchedFillResults.right.takerFeePaid
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Right taker fee -> right fee recipient
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
rightOrderHash,
|
|
||||||
rightOrder.takerFeeAssetData,
|
|
||||||
takerAddress,
|
|
||||||
rightFeeRecipientAddress,
|
|
||||||
matchedFillResults.right.takerFeePaid
|
|
||||||
);
|
|
||||||
|
|
||||||
// Left taker fee -> left fee recipient
|
|
||||||
_dispatchTransferFrom(
|
|
||||||
leftOrderHash,
|
|
||||||
leftOrder.takerFeeAssetData,
|
|
||||||
takerAddress,
|
|
||||||
leftFeeRecipientAddress,
|
|
||||||
matchedFillResults.left.takerFeePaid
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,16 @@ contract TestExchangeInternals is
|
|||||||
Exchange(chainId)
|
Exchange(chainId)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
function assertValidMatch(
|
||||||
|
LibOrder.Order memory leftOrder,
|
||||||
|
LibOrder.Order memory rightOrder
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
{
|
||||||
|
_assertValidMatch(leftOrder, rightOrder);
|
||||||
|
}
|
||||||
|
|
||||||
function calculateFillResults(
|
function calculateFillResults(
|
||||||
Order memory order,
|
Order memory order,
|
||||||
uint256 takerAssetFilledAmount
|
uint256 takerAssetFilledAmount
|
||||||
@ -50,6 +60,43 @@ contract TestExchangeInternals is
|
|||||||
return _calculateFillResults(order, takerAssetFilledAmount);
|
return _calculateFillResults(order, takerAssetFilledAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calculateCompleteFillBoth(
|
||||||
|
uint256 leftMakerAssetAmountRemaining,
|
||||||
|
uint256 leftTakerAssetAmountRemaining,
|
||||||
|
uint256 rightMakerAssetAmountRemaining,
|
||||||
|
uint256 rightTakerAssetAmountRemaining
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (MatchedFillResults memory fillResults)
|
||||||
|
{
|
||||||
|
_calculateCompleteFillBoth(
|
||||||
|
fillResults,
|
||||||
|
leftMakerAssetAmountRemaining,
|
||||||
|
leftTakerAssetAmountRemaining,
|
||||||
|
rightMakerAssetAmountRemaining,
|
||||||
|
rightTakerAssetAmountRemaining
|
||||||
|
);
|
||||||
|
return fillResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateCompleteRightFill(
|
||||||
|
LibOrder.Order memory leftOrder,
|
||||||
|
uint256 rightMakerAssetAmountRemaining,
|
||||||
|
uint256 rightTakerAssetAmountRemaining
|
||||||
|
)
|
||||||
|
public
|
||||||
|
pure
|
||||||
|
returns (MatchedFillResults memory fillResults)
|
||||||
|
{
|
||||||
|
_calculateCompleteRightFill(
|
||||||
|
fillResults,
|
||||||
|
leftOrder,
|
||||||
|
rightMakerAssetAmountRemaining,
|
||||||
|
rightTakerAssetAmountRemaining
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Call `_updateFilledState()` but first set `filled[order]` to
|
/// @dev Call `_updateFilledState()` but first set `filled[order]` to
|
||||||
/// `orderTakerAssetFilledAmount`.
|
/// `orderTakerAssetFilledAmount`.
|
||||||
function testUpdateFilledState(
|
function testUpdateFilledState(
|
||||||
@ -82,6 +129,26 @@ contract TestExchangeInternals is
|
|||||||
_settleOrder(orderHash, order, takerAddress, fillResults);
|
_settleOrder(orderHash, order, takerAddress, fillResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function settleMatchOrders(
|
||||||
|
bytes32 leftOrderHash,
|
||||||
|
bytes32 rightOrderHash,
|
||||||
|
LibOrder.Order memory leftOrder,
|
||||||
|
LibOrder.Order memory rightOrder,
|
||||||
|
address takerAddress,
|
||||||
|
LibFillResults.MatchedFillResults memory matchedFillResults
|
||||||
|
)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
_settleMatchedOrders(
|
||||||
|
leftOrderHash,
|
||||||
|
rightOrderHash,
|
||||||
|
leftOrder,
|
||||||
|
rightOrder,
|
||||||
|
takerAddress,
|
||||||
|
matchedFillResults
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Overidden to only log arguments so we can test `_settleOrder()`.
|
/// @dev Overidden to only log arguments so we can test `_settleOrder()`.
|
||||||
function _dispatchTransferFrom(
|
function _dispatchTransferFrom(
|
||||||
bytes32 orderHash,
|
bytes32 orderHash,
|
||||||
|
@ -34,8 +34,8 @@
|
|||||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"abis": "./generated-artifacts/@(Exchange|ExchangeWrapper|IAssetProxyDispatcher|IEIP1271Wallet|IExchange|IExchangeCore|IMatchOrders|ISignatureValidator|ITransactions|IWallet|IWrapperFunctions|IsolatedExchange|ReentrancyTester|TestAssetProxyDispatcher|TestExchangeInternals|TestLibExchangeRichErrorDecoder|TestSignatureValidator|TestValidatorWallet|TestWrapperFunctions|Whitelist).json",
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
"abis": "./generated-artifacts/@(Exchange|ExchangeWrapper|IAssetProxyDispatcher|IEIP1271Wallet|IExchange|IExchangeCore|IMatchOrders|ISignatureValidator|ITransactions|IWallet|IWrapperFunctions|IsolatedExchange|ReentrancyTester|TestAssetProxyDispatcher|TestExchangeInternals|TestLibExchangeRichErrorDecoder|TestSignatureValidator|TestValidatorWallet|TestWrapperFunctions|Whitelist).json"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -271,7 +271,7 @@ describe('AssetProxyDispatcher', () => {
|
|||||||
return expect(tx).to.revertWith(expectedError);
|
return expect(tx).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should should revert with the correct error when assetData length < 4 bytes', async () => {
|
it('should revert with the correct error when assetData length < 4 bytes', async () => {
|
||||||
await assetProxyDispatcher.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address, {
|
await assetProxyDispatcher.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address, {
|
||||||
from: owner,
|
from: owner,
|
||||||
});
|
});
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1336,7 +1336,7 @@ describe('matchOrders', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('matchOrdersWithMaximalFill', () => {
|
describe('matchOrdersWithMaximalFill', () => {
|
||||||
it('Should transfer correct amounts when right order is fully filled and values pass isRoundingErrorCeil but fail isRoundingErrorFloor', async () => {
|
it('should transfer correct amounts when right order is fully filled and values pass isRoundingErrorCeil but fail isRoundingErrorFloor', async () => {
|
||||||
// Create orders to match
|
// Create orders to match
|
||||||
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({
|
||||||
makerAddress: makerAddressLeft,
|
makerAddress: makerAddressLeft,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user