diff --git a/contracts/utils/CHANGELOG.json b/contracts/utils/CHANGELOG.json index f1e76f9fdf..261da9242a 100644 --- a/contracts/utils/CHANGELOG.json +++ b/contracts/utils/CHANGELOG.json @@ -9,6 +9,10 @@ { "note": "Set GST Collector address in `DeploymentConstants`", "pr": 2530 + }, + { + "note": "Add solidity 0.6 contracts", + "pr": 2540 } ] }, diff --git a/contracts/utils/contracts/src/v06/LibBytesV06.sol b/contracts/utils/contracts/src/v06/LibBytesV06.sol new file mode 100644 index 0000000000..391ab69148 --- /dev/null +++ b/contracts/utils/contracts/src/v06/LibBytesV06.sol @@ -0,0 +1,513 @@ +/* + + Copyright 2020 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.6.5; + +import "./errors/LibBytesRichErrorsV06.sol"; +import "./errors/LibRichErrorsV06.sol"; + + +library LibBytesV06 { + + using LibBytesV06 for bytes; + + /// @dev Gets the memory address for a byte array. + /// @param input Byte array to lookup. + /// @return memoryAddress Memory address of byte array. This + /// points to the header of the byte array which contains + /// the length. + function rawAddress(bytes memory input) + internal + pure + returns (uint256 memoryAddress) + { + assembly { + memoryAddress := input + } + return memoryAddress; + } + + /// @dev Gets the memory address for the contents of a byte array. + /// @param input Byte array to lookup. + /// @return memoryAddress Memory address of the contents of the byte array. + function contentAddress(bytes memory input) + internal + pure + returns (uint256 memoryAddress) + { + assembly { + memoryAddress := add(input, 32) + } + return memoryAddress; + } + + /// @dev Copies `length` bytes from memory location `source` to `dest`. + /// @param dest memory address to copy bytes to. + /// @param source memory address to copy bytes from. + /// @param length number of bytes to copy. + function memCopy( + uint256 dest, + uint256 source, + uint256 length + ) + internal + pure + { + if (length < 32) { + // Handle a partial word by reading destination and masking + // off the bits we are interested in. + // This correctly handles overlap, zero lengths and source == dest + assembly { + let mask := sub(exp(256, sub(32, length)), 1) + let s := and(mload(source), not(mask)) + let d := and(mload(dest), mask) + mstore(dest, or(s, d)) + } + } else { + // Skip the O(length) loop when source == dest. + if (source == dest) { + return; + } + + // For large copies we copy whole words at a time. The final + // word is aligned to the end of the range (instead of after the + // previous) to handle partial words. So a copy will look like this: + // + // #### + // #### + // #### + // #### + // + // We handle overlap in the source and destination range by + // changing the copying direction. This prevents us from + // overwriting parts of source that we still need to copy. + // + // This correctly handles source == dest + // + if (source > dest) { + assembly { + // We subtract 32 from `sEnd` and `dEnd` because it + // is easier to compare with in the loop, and these + // are also the addresses we need for copying the + // last bytes. + length := sub(length, 32) + let sEnd := add(source, length) + let dEnd := add(dest, length) + + // Remember the last 32 bytes of source + // This needs to be done here and not after the loop + // because we may have overwritten the last bytes in + // source already due to overlap. + let last := mload(sEnd) + + // Copy whole words front to back + // Note: the first check is always true, + // this could have been a do-while loop. + // solhint-disable-next-line no-empty-blocks + for {} lt(source, sEnd) {} { + mstore(dest, mload(source)) + source := add(source, 32) + dest := add(dest, 32) + } + + // Write the last 32 bytes + mstore(dEnd, last) + } + } else { + assembly { + // We subtract 32 from `sEnd` and `dEnd` because those + // are the starting points when copying a word at the end. + length := sub(length, 32) + let sEnd := add(source, length) + let dEnd := add(dest, length) + + // Remember the first 32 bytes of source + // This needs to be done here and not after the loop + // because we may have overwritten the first bytes in + // source already due to overlap. + let first := mload(source) + + // Copy whole words back to front + // We use a signed comparisson here to allow dEnd to become + // negative (happens when source and dest < 32). Valid + // addresses in local memory will never be larger than + // 2**255, so they can be safely re-interpreted as signed. + // Note: the first check is always true, + // this could have been a do-while loop. + // solhint-disable-next-line no-empty-blocks + for {} slt(dest, dEnd) {} { + mstore(dEnd, mload(sEnd)) + sEnd := sub(sEnd, 32) + dEnd := sub(dEnd, 32) + } + + // Write the first 32 bytes + mstore(dest, first) + } + } + } + } + + /// @dev Returns a slices from a byte array. + /// @param b The byte array to take a slice from. + /// @param from The starting index for the slice (inclusive). + /// @param to The final index for the slice (exclusive). + /// @return result The slice containing bytes at indices [from, to) + function slice( + bytes memory b, + uint256 from, + uint256 to + ) + internal + pure + returns (bytes memory result) + { + // Ensure that the from and to positions are valid positions for a slice within + // the byte array that is being used. + if (from > to) { + LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError( + LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired, + from, + to + )); + } + if (to > b.length) { + LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError( + LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired, + to, + b.length + )); + } + + // Create a new bytes structure and copy contents + result = new bytes(to - from); + memCopy( + result.contentAddress(), + b.contentAddress() + from, + result.length + ); + return result; + } + + /// @dev Returns a slice from a byte array without preserving the input. + /// When `from == 0`, the original array will match the slice. + /// In other cases its state will be corrupted. + /// @param b The byte array to take a slice from. Will be destroyed in the process. + /// @param from The starting index for the slice (inclusive). + /// @param to The final index for the slice (exclusive). + /// @return result The slice containing bytes at indices [from, to) + function sliceDestructive( + bytes memory b, + uint256 from, + uint256 to + ) + internal + pure + returns (bytes memory result) + { + // Ensure that the from and to positions are valid positions for a slice within + // the byte array that is being used. + if (from > to) { + LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError( + LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired, + from, + to + )); + } + if (to > b.length) { + LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError( + LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired, + to, + b.length + )); + } + + // Create a new bytes structure around [from, to) in-place. + assembly { + result := add(b, from) + mstore(result, sub(to, from)) + } + return result; + } + + /// @dev Pops the last byte off of a byte array by modifying its length. + /// @param b Byte array that will be modified. + /// @return result The byte that was popped off. + function popLastByte(bytes memory b) + internal + pure + returns (bytes1 result) + { + if (b.length == 0) { + LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError( + LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired, + b.length, + 0 + )); + } + + // Store last byte. + result = b[b.length - 1]; + + assembly { + // Decrement length of byte array. + let newLen := sub(mload(b), 1) + mstore(b, newLen) + } + return result; + } + + /// @dev Tests equality of two byte arrays. + /// @param lhs First byte array to compare. + /// @param rhs Second byte array to compare. + /// @return equal True if arrays are the same. False otherwise. + function equals( + bytes memory lhs, + bytes memory rhs + ) + internal + pure + returns (bool equal) + { + // Keccak gas cost is 30 + numWords * 6. This is a cheap way to compare. + // We early exit on unequal lengths, but keccak would also correctly + // handle this. + return lhs.length == rhs.length && keccak256(lhs) == keccak256(rhs); + } + + /// @dev Reads an address from a position in a byte array. + /// @param b Byte array containing an address. + /// @param index Index in byte array of address. + /// @return result address from byte array. + function readAddress( + bytes memory b, + uint256 index + ) + internal + pure + returns (address result) + { + if (b.length < index + 20) { + LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError( + LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired, + b.length, + index + 20 // 20 is length of address + )); + } + + // Add offset to index: + // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index) + // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index) + index += 20; + + // Read address from array memory + assembly { + // 1. Add index to address of bytes array + // 2. Load 32-byte word from memory + // 3. Apply 20-byte mask to obtain address + result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff) + } + return result; + } + + /// @dev Writes an address into a specific position in a byte array. + /// @param b Byte array to insert address into. + /// @param index Index in byte array of address. + /// @param input Address to put into byte array. + function writeAddress( + bytes memory b, + uint256 index, + address input + ) + internal + pure + { + if (b.length < index + 20) { + LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError( + LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired, + b.length, + index + 20 // 20 is length of address + )); + } + + // Add offset to index: + // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index) + // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index) + index += 20; + + // Store address into array memory + assembly { + // The address occupies 20 bytes and mstore stores 32 bytes. + // First fetch the 32-byte word where we'll be storing the address, then + // apply a mask so we have only the bytes in the word that the address will not occupy. + // Then combine these bytes with the address and store the 32 bytes back to memory with mstore. + + // 1. Add index to address of bytes array + // 2. Load 32-byte word from memory + // 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address + let neighbors := and( + mload(add(b, index)), + 0xffffffffffffffffffffffff0000000000000000000000000000000000000000 + ) + + // Make sure input address is clean. + // (Solidity does not guarantee this) + input := and(input, 0xffffffffffffffffffffffffffffffffffffffff) + + // Store the neighbors and address into memory + mstore(add(b, index), xor(input, neighbors)) + } + } + + /// @dev Reads a bytes32 value from a position in a byte array. + /// @param b Byte array containing a bytes32 value. + /// @param index Index in byte array of bytes32 value. + /// @return result bytes32 value from byte array. + function readBytes32( + bytes memory b, + uint256 index + ) + internal + pure + returns (bytes32 result) + { + if (b.length < index + 32) { + LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError( + LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired, + b.length, + index + 32 + )); + } + + // Arrays are prefixed by a 256 bit length parameter + index += 32; + + // Read the bytes32 from array memory + assembly { + result := mload(add(b, index)) + } + return result; + } + + /// @dev Writes a bytes32 into a specific position in a byte array. + /// @param b Byte array to insert into. + /// @param index Index in byte array of . + /// @param input bytes32 to put into byte array. + function writeBytes32( + bytes memory b, + uint256 index, + bytes32 input + ) + internal + pure + { + if (b.length < index + 32) { + LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError( + LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired, + b.length, + index + 32 + )); + } + + // Arrays are prefixed by a 256 bit length parameter + index += 32; + + // Read the bytes32 from array memory + assembly { + mstore(add(b, index), input) + } + } + + /// @dev Reads a uint256 value from a position in a byte array. + /// @param b Byte array containing a uint256 value. + /// @param index Index in byte array of uint256 value. + /// @return result uint256 value from byte array. + function readUint256( + bytes memory b, + uint256 index + ) + internal + pure + returns (uint256 result) + { + result = uint256(readBytes32(b, index)); + return result; + } + + /// @dev Writes a uint256 into a specific position in a byte array. + /// @param b Byte array to insert into. + /// @param index Index in byte array of . + /// @param input uint256 to put into byte array. + function writeUint256( + bytes memory b, + uint256 index, + uint256 input + ) + internal + pure + { + writeBytes32(b, index, bytes32(input)); + } + + /// @dev Reads an unpadded bytes4 value from a position in a byte array. + /// @param b Byte array containing a bytes4 value. + /// @param index Index in byte array of bytes4 value. + /// @return result bytes4 value from byte array. + function readBytes4( + bytes memory b, + uint256 index + ) + internal + pure + returns (bytes4 result) + { + if (b.length < index + 4) { + LibRichErrorsV06.rrevert(LibBytesRichErrorsV06.InvalidByteOperationError( + LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired, + b.length, + index + 4 + )); + } + + // Arrays are prefixed by a 32 byte length field + index += 32; + + // Read the bytes4 from array memory + assembly { + result := mload(add(b, index)) + // Solidity does not require us to clean the trailing bytes. + // We do it anyway + result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000) + } + return result; + } + + /// @dev Writes a new length to a byte array. + /// Decreasing length will lead to removing the corresponding lower order bytes from the byte array. + /// Increasing length may lead to appending adjacent in-memory bytes to the end of the byte array. + /// @param b Bytes array to write new length to. + /// @param length New length of byte array. + function writeLength(bytes memory b, uint256 length) + internal + pure + { + assembly { + mstore(b, length) + } + } +} diff --git a/contracts/utils/contracts/src/v06/errors/LibBytesRichErrorsV06.sol b/contracts/utils/contracts/src/v06/errors/LibBytesRichErrorsV06.sol new file mode 100644 index 0000000000..6863a14a46 --- /dev/null +++ b/contracts/utils/contracts/src/v06/errors/LibBytesRichErrorsV06.sol @@ -0,0 +1,56 @@ +/* + + Copyright 2020 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.6.5; + + +library LibBytesRichErrorsV06 { + + enum InvalidByteOperationErrorCodes { + FromLessThanOrEqualsToRequired, + ToLessThanOrEqualsLengthRequired, + LengthGreaterThanZeroRequired, + LengthGreaterThanOrEqualsFourRequired, + LengthGreaterThanOrEqualsTwentyRequired, + LengthGreaterThanOrEqualsThirtyTwoRequired, + LengthGreaterThanOrEqualsNestedBytesLengthRequired, + DestinationLengthGreaterThanOrEqualSourceLengthRequired + } + + // bytes4(keccak256("InvalidByteOperationError(uint8,uint256,uint256)")) + bytes4 internal constant INVALID_BYTE_OPERATION_ERROR_SELECTOR = + 0x28006595; + + // solhint-disable func-name-mixedcase + function InvalidByteOperationError( + InvalidByteOperationErrorCodes errorCode, + uint256 offset, + uint256 required + ) + internal + pure + returns (bytes memory) + { + return abi.encodeWithSelector( + INVALID_BYTE_OPERATION_ERROR_SELECTOR, + errorCode, + offset, + required + ); + } +} diff --git a/contracts/utils/contracts/src/v06/errors/LibRichErrorsV06.sol b/contracts/utils/contracts/src/v06/errors/LibRichErrorsV06.sol new file mode 100644 index 0000000000..caa6c22371 --- /dev/null +++ b/contracts/utils/contracts/src/v06/errors/LibRichErrorsV06.sol @@ -0,0 +1,55 @@ +/* + + Copyright 2020 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.6.5; + + +library LibRichErrorsV06 { + + // bytes4(keccak256("Error(string)")) + bytes4 internal constant STANDARD_ERROR_SELECTOR = 0x08c379a0; + + // solhint-disable func-name-mixedcase + /// @dev ABI encode a standard, string revert error payload. + /// This is the same payload that would be included by a `revert(string)` + /// solidity statement. It has the function signature `Error(string)`. + /// @param message The error string. + /// @return The ABI encoded error. + function StandardError(string memory message) + internal + pure + returns (bytes memory) + { + return abi.encodeWithSelector( + STANDARD_ERROR_SELECTOR, + bytes(message) + ); + } + // solhint-enable func-name-mixedcase + + /// @dev Reverts an encoded rich revert reason `errorData`. + /// @param errorData ABI encoded error data. + function rrevert(bytes memory errorData) + internal + pure + { + assembly { + revert(add(errorData, 0x20), mload(errorData)) + } + } +} diff --git a/contracts/utils/contracts/src/v06/interfaces/IOwnableV06.sol b/contracts/utils/contracts/src/v06/interfaces/IOwnableV06.sol new file mode 100644 index 0000000000..0beeb4d665 --- /dev/null +++ b/contracts/utils/contracts/src/v06/interfaces/IOwnableV06.sol @@ -0,0 +1,36 @@ +/* + + Copyright 2020 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.6.5; + + +interface IOwnableV06 { + + /// @dev Emitted by Ownable when ownership is transferred. + /// @param previousOwner The previous owner of the contract. + /// @param newOwner The new owner of the contract. + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /// @dev Transfers ownership of the contract to a new address. + /// @param newOwner The address that will become the owner. + function transferOwnership(address newOwner) external; + + /// @dev Get the owner of this contract. + /// @return owner_ The owner of this contract. + function getOwner() external view returns (address owner_); +}