Reorder Fill event args to get around stack limit

This commit is contained in:
Amir Bandeali
2019-08-25 16:34:20 -07:00
committed by Alex Towle
parent 2c970a0466
commit 5ee7c2f9dc
2 changed files with 21 additions and 129 deletions

View File

@@ -278,128 +278,23 @@ contract MixinExchangeCore is
// Update state
filled[orderHash] = orderTakerAssetFilledAmount.safeAdd(fillResults.takerAssetFilledAmount);
// Emit a Fill() event THE HARD WAY to avoid a stack overflow.
// All this logic is equivalent to:
// emit Fill(
// order.makerAddress,
// order.feeRecipientAddress,
// orderHash,
// takerAddress,
// msg.sender,
// fillResults.makerAssetFilledAmount,
// fillResults.takerAssetFilledAmount,
// fillResults.makerFeePaid,
// fillResults.takerFeePaid,
// fillResults.protocolFeePaid,
// msg.value >= fillResults.protocolFeePaid,
// order.makerAssetData,
// order.takerAssetData,
// order.makerFeeAssetData,
// order.takerFeeAssetData
// );
// There are 15 total fields in the `Fill()` event. Out of all of these fields,
// 3 are indexed fields. Due to the evm semantics of event logging, the 12 non-indexed
// fields will be abi-encoded into a bytes structure and then put into the `data` slot
// of the event logs.
//
// Since there are 12 fields, we will need 12 * 32 = 384 bytes to store the fields. Due
// to the fact that 4 of the fields are of type `bytes`, there is added complexity in that
// these fields in the log data are actually byte offsets to the length-prefaced `bytes`
// of the field. This means that we also need to account for 4 length slots -- 4 * 32 = 128
// more bytes in the log data -- and for the length of the bytes themselves -- total_length =
// order.makerAssetData.length + order.takerAssetData.length + order.makerFeeAssetData.length
// + order.takerFeeAssetData.length.
//
// Altogether, the log data will be 384 + 128 + total_length = 512 + total_length bytes long,
// which explains the memory allocation of the bytes below:
bytes memory logData = new bytes(
512
+ order.makerAssetData.length
+ order.takerAssetData.length
+ order.makerFeeAssetData.length
+ order.takerFeeAssetData.length
emit Fill(
order.makerAddress,
order.feeRecipientAddress,
order.makerAssetData,
order.takerAssetData,
order.makerFeeAssetData,
order.takerFeeAssetData,
orderHash,
takerAddress,
msg.sender,
fillResults.makerAssetFilledAmount,
fillResults.takerAssetFilledAmount,
fillResults.makerFeePaid,
fillResults.takerFeePaid,
fillResults.protocolFeePaid,
msg.value >= fillResults.protocolFeePaid
);
uint256 argOffset = 0;
// Push takerAddress to logData
logData.writeAddress(argOffset, takerAddress);
argOffset += 32;
// Push senderAddress to logData
logData.writeAddress(argOffset, msg.sender);
argOffset += 32;
// Push makerAssetFilledAmount to logData
logData.writeUint256(argOffset, fillResults.makerAssetFilledAmount);
argOffset += 32;
// Push takerAssetFilledAmount to logData
logData.writeUint256(argOffset, fillResults.takerAssetFilledAmount);
argOffset += 32;
// Push makerFeePaid to logData
logData.writeUint256(argOffset, fillResults.makerFeePaid);
argOffset += 32;
// Push takerFeePaid to logData
logData.writeUint256(argOffset, fillResults.takerFeePaid);
argOffset += 32;
// Push protocolFeePaid to logData
logData.writeUint256(argOffset, fillResults.protocolFeePaid);
argOffset += 32;
// Push isProtocolFeePaidInEth to logData
logData.writeUint256(argOffset, msg.value >= fillResults.protocolFeePaid ? 1 : 0);
argOffset += 32;
// The next four fields are `bytes` fields. Constructing the logData for these fields
// entails storing a byte offset in their respective memory slots and then storing their
// length and contents in the location pointed to by the byte offset.
//
// With this in mind, the `dataOffset` will initially be set to the thirteenth memory slot
// in logData and will be incremented by the length of every assetData `bytes` that is placed
// and 32 bytes that reflects the slot that stores the length of the array.
uint256 dataOffset = argOffset + 128;
// Push makerAssetData to logData
logData.writeUint256(argOffset, dataOffset);
logData.writeBytesWithLength(dataOffset, order.makerAssetData);
argOffset += 32;
dataOffset += order.makerAssetData.length;
// Push takerAssetData to logData
logData.writeUint256(argOffset, dataOffset);
logData.writeBytesWithLength(dataOffset, order.takerAssetData);
argOffset += 32;
dataOffset += order.takerAssetData.length;
// Push makerFeeAssetData to logData
logData.writeUint256(argOffset, dataOffset);
logData.writeBytesWithLength(dataOffset, order.makerFeeAssetData);
argOffset += 32;
dataOffset += order.makerFeeAssetData.length;
// Push takerFeeAssetData to logData
logData.writeUint256(argOffset, dataOffset);
logData.writeBytesWithLength(dataOffset, order.takerFeeAssetData);
argOffset += 32;
dataOffset += order.takerFeeAssetData.length;
// Add the event topics and log the event.
bytes32 fillTopic = FILL_EVENT_TOPIC;
assembly {
log4(
add(logData, 0x20), // A pointer to the log data
mload(logData), // The size of the log data
fillTopic, // The `Fill()` event's name topic
mload(order), // address indexed makerAddress
mload(add(order, 0x40)), // address indexed feeRecipientAddress
orderHash // bytes32 indexed orderHash
)
}
}
/// @dev Updates state with results of cancelling an order.

View File

@@ -25,13 +25,14 @@ import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
contract IExchangeCore {
// keccak256("Fill(address,address,bytes32,address,address,uint256,uint256,uint256,uint256,uint256,bool,bytes,bytes,bytes,bytes)")
bytes32 internal constant FILL_EVENT_TOPIC = 0x266de417a663e51231ccdf89b2794cea06fde5e2c433d76473160b32d31fd867;
// Fill event is emitted whenever an order is filled.
event Fill(
address indexed makerAddress, // Address that created the order.
address indexed feeRecipientAddress, // Address that received fees.
bytes makerAssetData, // Encoded data specific to makerAsset.
bytes takerAssetData, // Encoded data specific to takerAsset.
bytes makerFeeAssetData, // Encoded data specific to makerFeeAsset.
bytes takerFeeAssetData, // Encoded data specific to takerFeeAsset.
bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getTypedDataHash).
address takerAddress, // Address that filled the order.
address senderAddress, // Address that called the Exchange contract (msg.sender).
@@ -40,11 +41,7 @@ contract IExchangeCore {
uint256 makerFeePaid, // Amount of makerFeeAssetData paid to feeRecipient by maker.
uint256 takerFeePaid, // Amount of takerFeeAssetData paid to feeRecipient by taker.
uint256 protocolFeePaid, // Amount of eth or weth paid to the staking contract.
bool isProtocolFeePaidInEth, // Indicates whether the protocol fee is paid in ETH or WETH.
bytes makerAssetData, // Encoded data specific to makerAsset.
bytes takerAssetData, // Encoded data specific to takerAsset.
bytes makerFeeAssetData, // Encoded data specific to makerFeeAsset.
bytes takerFeeAssetData // Encoded data specific to takerFeeAsset.
bool isProtocolFeePaidInWei // Indicates whether the protocol fee is paid in Wei (ETH) or WETH.
);
// Cancel event is emitted whenever an individual order is cancelled.