diff options
-rw-r--r-- | packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol | 214 |
1 files changed, 103 insertions, 111 deletions
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol index 8e51404a7..0b622a23a 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol @@ -32,22 +32,23 @@ contract MixinWrapperFunctions is /// @param signature Maker's signature of the order. function fillOrKillOrder( Order order, - uint takerTokenFillAmount, + uint256 takerTokenFillAmount, bytes signature) public { - require(fillOrder( - order, - takerTokenFillAmount, - signature - ) == takerTokenFillAmount); + require( + fillOrder( + order, + takerTokenFillAmount, + signature + ) == takerTokenFillAmount + ); } /// @dev Fills an order with specified parameters and ECDSA signature. Returns false if the transaction would otherwise revert. /// @param order Order struct containing order specifications. /// @param takerTokenFillAmount Desired amount of takerToken to fill. /// @param signature Maker's signature of the order. - /// @return Success if the transaction did not revert. /// @return Total amount of takerToken filled in trade. function fillOrderNoThrow( Order order, @@ -56,6 +57,21 @@ contract MixinWrapperFunctions is public returns (uint256 takerTokenFilledAmount) { + // bool success = address(this).delegatecall( + // this.fillOrder.selector, + // order, + // takerTokenFillAmount, + // signature + // ); + // require(success); + // if (success) { + // assembly { + // returndatacopy(0, 0, 32) + // return (0, 32) + // } + // } + // return 0; + // We need to call MExchangeCore.fillOrder using a delegatecall in // assembly so that we can intercept a call that throws. For this, we // need the input encoded in memory in the Ethereum ABIv2 format [1]. @@ -63,115 +79,81 @@ contract MixinWrapperFunctions is // | Offset | Length | Contents | // |--------|---------|------------------------------| // | 0 | 4 | function selector | - // | 4 | 12 * 32 | Order order | - // | 388 | 32 | uint256 takerTokenFillAmount | - // | 420 | 32 | offset to signature (448) | - // | 452 | 32 | len(signature) | - // | 486 | (1) | signature | + // | 4 | 11 * 32 | Order order | + // | 356 | 32 | uint256 takerTokenFillAmount | + // | 388 | 32 | offset to signature (416) | + // | 420 | 32 | len(signature) | + // | 452 | (1) | signature | // | (2) | (3) | padding (zero) | // | (4) | | end of input | // // (1): len(signature) - // (2): 486 + len(signature) + // (2): 452 + len(signature) // (3): 32 - (len(signature) mod 32) - // (4): 486 + len(signature) + 32 - (len(signature) mod 32) + // (4): 452 + len(signature) + 32 - (len(signature) mod 32) // // [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html - // Allocate memory for input - uint256 signatureLength = signature.length; - uint256 paddingLength = 32 - (signatureLength % 32); - uint256 inputSize = 486 + signatureLength + paddingLength; - bytes memory input = new bytes(inputSize); - - // The in memory layout of `bytes memory input` starts with - // `uint256 length`, the content of the byte array starts at - // offset 32 from the pointer. We need assembly to access the - // raw pointer value of `input`. - // TODO: I can not find an official source for this. - uint256 start; - assembly { - start := add(input, 32) - } - - // Write function signature - // We use assembly to write four bytes at a time (actually 32, - // but we are only overwriting uninitialized data). A `bytes4` - // is stored by value as 256-bit and right-padded with zeros. - bytes4 FILL_ORDER_FUNCTION_SIGNATURE = this.fillOrder.selector; - assembly { - mstore(start, FILL_ORDER_FUNCTION_SIGNATURE) - } - - // Use identity function precompile to cheaply copy Order struct over - // We need assembly to access the precompile - // Note that sizeof(Order) == 12 * 32 == 384 - assembly { - let success := delegatecall( - 51, // precompile takes 15 + 3 * numWords - 0x4, // precompile for identity function - order, // pointer to start of input - 384, // length of input - add(start, 4), // store output at offset 4 - 384 // output is 384 bytes - ) - if iszero(success) { - revert(0, 0) - } - } - - // Write takerTokenFillAmount - assembly { - mstore(add(start, 388), takerTokenFillAmount) - } + bytes4 fillOrderSelector = this.fillOrder.selector; - // Write offset to signature and len(signature) - // It is done in assembly so we can write 32 bytes at a time. In - // solidity we would have to copy one byte at a time. - assembly { - mstore(add(start, 420), 448) - } - - // Copy over signature length contents - assembly { - let size := add(signatureLength, 32) - let success := delegatecall( - gas, // precompile takes 15 + 3 * numWords - 0x4, // precompile for identity function - signature, // pointer to start of input - size, // input is (signatureLength + 32) bytes - add(start, 452), // store output at offset 4 - size // output is (signatureLength + 32) bytes - ) - if iszero(success) { - revert(0, 0) - } - } - - // Write padding - for (uint256 i = 0; i < paddingLength; i++) { - input[486 + signatureLength + i] = 0x00; - } - - // Call the function assembly { + // Load free memory pointer + let start := mload(0x40) + + // Write function signature + mstore(start, fillOrderSelector) + + // Write order struct + mstore(add(start, 4), order) // makerAddress + mstore(add(start, 36), add(order, 32)) // takerAddress + mstore(add(start, 68), add(order, 64)) // makerTokenAddress + mstore(add(start, 100), add(order, 96)) // takerTokenAddress + mstore(add(start, 132), add(order, 128)) // feeRecipientAddress + mstore(add(start, 164), add(order, 160)) // makerTokenAmount + mstore(add(start, 196), add(order, 192)) // takerTokenAmount + mstore(add(start, 228), add(order, 224)) // makerFeeAmount + mstore(add(start, 260), add(order, 256)) // takerFeeAmount + mstore(add(start, 292), add(order, 288)) // expirationTimeSeconds + mstore(add(start, 324), add(order, 320)) // salt + + // Write takerTokenFillAmount + mstore(add(start, 356), takerTokenFillAmount) + + // Write signature offset + mstore(add(start, 388), 416) + + // Write signature length + let sigLen := mload(signature) + mstore(add(start, 420), sigLen) + + // Calculate signature length with padding + let paddingLen := sub(32, mod(sigLen, 32)) + let sigLenWithPadding := add(sigLen, paddingLen) + + // Write signature + let sigStart := add(signature, 32) + for { let curr := 0 } + lt(curr, sigLenWithPadding) + { curr := add(curr, 32) } + { mstore(add(start, add(452, curr)), add(sigStart, curr)) } + + // Execute delegatecall let success := delegatecall( - gas, // If fillOrder `revert()`s, we recover unused gas - address, // call this contract - start, // pointer to start of input - inputSize, // length of input - start, // store output over input - 32 // output is 32 bytes + gas, // forward all gas + address, // call address of this contract + start, // pointer to start of input + add(452, sigLenWithPadding), // input length is 420 + signature length + padding length + start, // write output over input + 32 // output size is 32 bytes ) switch success case 0 { - // No amount filled if delegatecall is unsuccessful takerTokenFilledAmount := 0 } case 1 { - // Read output value (uint256 takerTokenFilledAmount) takerTokenFilledAmount := mload(start) } + } return takerTokenFilledAmount; } @@ -201,7 +183,7 @@ contract MixinWrapperFunctions is /// @param signatures Maker's signatures of the orders. function batchFillOrKillOrders( Order[] orders, - uint[] takerTokenFillAmounts, + uint256[] takerTokenFillAmounts, bytes[] signatures) public { @@ -220,7 +202,7 @@ contract MixinWrapperFunctions is /// @param signatures Maker's signatures of the orders. function batchFillOrdersNoThrow( Order[] orders, - uint[] takerTokenFillAmounts, + uint256[] takerTokenFillAmounts, bytes[] signatures) public { @@ -247,12 +229,17 @@ contract MixinWrapperFunctions is { for (uint256 i = 0; i < orders.length; i++) { require(orders[i].takerTokenAddress == orders[0].takerTokenAddress); - totalTakerTokenFilledAmount = safeAdd(totalTakerTokenFilledAmount, fillOrder( - orders[i], - safeSub(takerTokenFillAmount, totalTakerTokenFilledAmount), - signatures[i] - )); - if (totalTakerTokenFilledAmount == takerTokenFillAmount) break; + totalTakerTokenFilledAmount = safeAdd( + totalTakerTokenFilledAmount, + fillOrder( + orders[i], + safeSub(takerTokenFillAmount, totalTakerTokenFilledAmount), + signatures[i] + ) + ); + if (totalTakerTokenFilledAmount == takerTokenFillAmount) { + break; + } } return totalTakerTokenFilledAmount; } @@ -271,12 +258,17 @@ contract MixinWrapperFunctions is { for (uint256 i = 0; i < orders.length; i++) { require(orders[i].takerTokenAddress == orders[0].takerTokenAddress); - totalTakerTokenFilledAmount = safeAdd(totalTakerTokenFilledAmount, fillOrderNoThrow( - orders[i], - safeSub(takerTokenFillAmount, totalTakerTokenFilledAmount), - signatures[i] - )); - if (totalTakerTokenFilledAmount == takerTokenFillAmount) break; + totalTakerTokenFilledAmount = safeAdd( + totalTakerTokenFilledAmount, + fillOrderNoThrow( + orders[i], + safeSub(takerTokenFillAmount, totalTakerTokenFilledAmount), + signatures[i] + ) + ); + if (totalTakerTokenFilledAmount == takerTokenFillAmount) { + break; + } } return totalTakerTokenFilledAmount; } |