From 97099c07f3a1fb510cebd77c7a1b2f3b13df8b60 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 7 Mar 2022 15:56:43 +0100 Subject: [PATCH 01/21] automatic SafeCast generation --- .gitignore | 3 + contracts/utils/math/SafeCast.sol | 241 ------------------- package.json | 3 +- scripts/generate/run.js | 52 ++++ scripts/generate/templates/index.js | 6 + scripts/generate/templates/toInt.js | 14 ++ scripts/generate/templates/toIntDownCast.js | 19 ++ scripts/generate/templates/toUint.js | 13 + scripts/generate/templates/toUintDownCast.js | 16 ++ 9 files changed, 125 insertions(+), 242 deletions(-) delete mode 100644 contracts/utils/math/SafeCast.sol create mode 100755 scripts/generate/run.js create mode 100644 scripts/generate/templates/index.js create mode 100644 scripts/generate/templates/toInt.js create mode 100644 scripts/generate/templates/toIntDownCast.js create mode 100644 scripts/generate/templates/toUint.js create mode 100644 scripts/generate/templates/toUintDownCast.js diff --git a/.gitignore b/.gitignore index c60c5d945c0..7476ab09d76 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,6 @@ artifacts .certora* .last_confs certora_* + +# code generation +contracts/utils/math/SafeCast.sol diff --git a/contracts/utils/math/SafeCast.sol b/contracts/utils/math/SafeCast.sol deleted file mode 100644 index 3cd64735789..00000000000 --- a/contracts/utils/math/SafeCast.sol +++ /dev/null @@ -1,241 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow - * checks. - * - * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can - * easily result in undesired exploitation or bugs, since developers usually - * assume that overflows raise errors. `SafeCast` restores this intuition by - * reverting the transaction when such an operation overflows. - * - * Using this library instead of the unchecked operations eliminates an entire - * class of bugs, so it's recommended to use it always. - * - * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing - * all math on `uint256` and `int256` and then downcasting. - */ -library SafeCast { - /** - * @dev Returns the downcasted uint224 from uint256, reverting on - * overflow (when the input is greater than largest uint224). - * - * Counterpart to Solidity's `uint224` operator. - * - * Requirements: - * - * - input must fit into 224 bits - */ - function toUint224(uint256 value) internal pure returns (uint224) { - require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); - return uint224(value); - } - - /** - * @dev Returns the downcasted uint128 from uint256, reverting on - * overflow (when the input is greater than largest uint128). - * - * Counterpart to Solidity's `uint128` operator. - * - * Requirements: - * - * - input must fit into 128 bits - */ - function toUint128(uint256 value) internal pure returns (uint128) { - require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); - return uint128(value); - } - - /** - * @dev Returns the downcasted uint96 from uint256, reverting on - * overflow (when the input is greater than largest uint96). - * - * Counterpart to Solidity's `uint96` operator. - * - * Requirements: - * - * - input must fit into 96 bits - */ - function toUint96(uint256 value) internal pure returns (uint96) { - require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); - return uint96(value); - } - - /** - * @dev Returns the downcasted uint64 from uint256, reverting on - * overflow (when the input is greater than largest uint64). - * - * Counterpart to Solidity's `uint64` operator. - * - * Requirements: - * - * - input must fit into 64 bits - */ - function toUint64(uint256 value) internal pure returns (uint64) { - require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); - return uint64(value); - } - - /** - * @dev Returns the downcasted uint32 from uint256, reverting on - * overflow (when the input is greater than largest uint32). - * - * Counterpart to Solidity's `uint32` operator. - * - * Requirements: - * - * - input must fit into 32 bits - */ - function toUint32(uint256 value) internal pure returns (uint32) { - require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); - return uint32(value); - } - - /** - * @dev Returns the downcasted uint16 from uint256, reverting on - * overflow (when the input is greater than largest uint16). - * - * Counterpart to Solidity's `uint16` operator. - * - * Requirements: - * - * - input must fit into 16 bits - */ - function toUint16(uint256 value) internal pure returns (uint16) { - require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); - return uint16(value); - } - - /** - * @dev Returns the downcasted uint8 from uint256, reverting on - * overflow (when the input is greater than largest uint8). - * - * Counterpart to Solidity's `uint8` operator. - * - * Requirements: - * - * - input must fit into 8 bits. - */ - function toUint8(uint256 value) internal pure returns (uint8) { - require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); - return uint8(value); - } - - /** - * @dev Converts a signed int256 into an unsigned uint256. - * - * Requirements: - * - * - input must be greater than or equal to 0. - */ - function toUint256(int256 value) internal pure returns (uint256) { - require(value >= 0, "SafeCast: value must be positive"); - return uint256(value); - } - - /** - * @dev Returns the downcasted int128 from int256, reverting on - * overflow (when the input is less than smallest int128 or - * greater than largest int128). - * - * Counterpart to Solidity's `int128` operator. - * - * Requirements: - * - * - input must fit into 128 bits - * - * _Available since v3.1._ - */ - function toInt128(int256 value) internal pure returns (int128) { - require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits"); - return int128(value); - } - - /** - * @dev Returns the downcasted int64 from int256, reverting on - * overflow (when the input is less than smallest int64 or - * greater than largest int64). - * - * Counterpart to Solidity's `int64` operator. - * - * Requirements: - * - * - input must fit into 64 bits - * - * _Available since v3.1._ - */ - function toInt64(int256 value) internal pure returns (int64) { - require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits"); - return int64(value); - } - - /** - * @dev Returns the downcasted int32 from int256, reverting on - * overflow (when the input is less than smallest int32 or - * greater than largest int32). - * - * Counterpart to Solidity's `int32` operator. - * - * Requirements: - * - * - input must fit into 32 bits - * - * _Available since v3.1._ - */ - function toInt32(int256 value) internal pure returns (int32) { - require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits"); - return int32(value); - } - - /** - * @dev Returns the downcasted int16 from int256, reverting on - * overflow (when the input is less than smallest int16 or - * greater than largest int16). - * - * Counterpart to Solidity's `int16` operator. - * - * Requirements: - * - * - input must fit into 16 bits - * - * _Available since v3.1._ - */ - function toInt16(int256 value) internal pure returns (int16) { - require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits"); - return int16(value); - } - - /** - * @dev Returns the downcasted int8 from int256, reverting on - * overflow (when the input is less than smallest int8 or - * greater than largest int8). - * - * Counterpart to Solidity's `int8` operator. - * - * Requirements: - * - * - input must fit into 8 bits. - * - * _Available since v3.1._ - */ - function toInt8(int256 value) internal pure returns (int8) { - require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits"); - return int8(value); - } - - /** - * @dev Converts an unsigned uint256 into a signed int256. - * - * Requirements: - * - * - input must be less than or equal to maxInt256. - */ - function toInt256(uint256 value) internal pure returns (int256) { - // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive - require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); - return int256(value); - } -} diff --git a/package.json b/package.json index 13df7274d98..f42ca47326f 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,9 @@ "lint:sol": "solhint 'contracts/**/*.sol' && prettier -c 'contracts/**/*.sol'", "lint:sol:fix": "prettier --write \"contracts/**/*.sol\"", "clean": "hardhat clean && rimraf build contracts/build", - "prepare": "npm run clean && env COMPILE_MODE=production npm run compile", + "prepare": "npm run clean && npm run generate && env COMPILE_MODE=production npm run compile", "prepack": "scripts/prepack.sh", + "generate": "scripts/generate/run.js", "release": "scripts/release/release.sh", "version": "scripts/release/version.sh", "test": "hardhat test", diff --git a/scripts/generate/run.js b/scripts/generate/run.js new file mode 100755 index 00000000000..61c3427297c --- /dev/null +++ b/scripts/generate/run.js @@ -0,0 +1,52 @@ +#!/usr/bin/env node + +const fs = require('fs'); + +const { + toInt, + toUint, + toIntDownCast, + toUintDownCast, +} = require('./templates'); + +function genSafeCast(path) { + content = []; + + // Header + content.push([ + `// SPDX-License-Identifier: MIT`, + `pragma solidity ^0.8.0;`, + ``, + `/**`, + ` * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow`, + ` * checks.`, + ` *`, + ` * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can`, + ` * easily result in undesired exploitation or bugs, since developers usually`, + ` * assume that overflows raise errors. \`SafeCast\` restores this intuition by`, + ` * reverting the transaction when such an operation overflows.`, + ` *`, + ` * Using this library instead of the unchecked operations eliminates an entire`, + ` * class of bugs, so it's recommended to use it always.`, + ` *`, + ` * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing`, + ` * all math on \`uint256\` and \`int256\` and then downcasting.`, + ` */`, + ].join('\n')); + + // Library + content.push(`library SafeCast {`); + for (size = 224; size > 0; size -= 8) { + content.push(toUintDownCast(size)); + } + content.push(toUint(256)); + for (size = 224; size > 0; size -= 8) { + content.push(toIntDownCast(size)); + } + content.push(toInt(256)); + content.push(`}`); + + fs.writeFileSync(path, content.join('\n')); +} + +genSafeCast('./contracts/utils/math/SafeCast.sol') \ No newline at end of file diff --git a/scripts/generate/templates/index.js b/scripts/generate/templates/index.js new file mode 100644 index 00000000000..23bf20070a4 --- /dev/null +++ b/scripts/generate/templates/index.js @@ -0,0 +1,6 @@ +module.exports = { + toInt: require('./toInt'), + toUint: require('./toUint'), + toIntDownCast: require('./toIntDownCast'), + toUintDownCast: require('./toUintDownCast'), +} \ No newline at end of file diff --git a/scripts/generate/templates/toInt.js b/scripts/generate/templates/toInt.js new file mode 100644 index 00000000000..c021282f9e2 --- /dev/null +++ b/scripts/generate/templates/toInt.js @@ -0,0 +1,14 @@ +module.exports = length => [ + ` /**`, + ` * @dev Converts an unsigned uint${length} into a signed int${length}.`, + ` *`, + ` * Requirements:`, + ` *`, + ` * - input must be less than or equal to maxInt${length}.`, + ` */`, + ` function toInt${length}(uint${length} value) internal pure returns (int${length}) {`, + ` // Note: Unsafe cast below is okay because \`type(int${length}).max\` is guaranteed to be positive`, + ` require(value <= uint${length}(type(int${length}).max), "SafeCast: value doesn't fit in an int${length}");`, + ` return int${length}(value);`, + ` }`, +].join('\n'); \ No newline at end of file diff --git a/scripts/generate/templates/toIntDownCast.js b/scripts/generate/templates/toIntDownCast.js new file mode 100644 index 00000000000..6ff50b9639f --- /dev/null +++ b/scripts/generate/templates/toIntDownCast.js @@ -0,0 +1,19 @@ +module.exports = length => [ + ` /**`, + ` * @dev Returns the downcasted int${length} from int256, reverting on`, + ` * overflow (when the input is less than smallest int${length} or`, + ` * greater than largest int${length}).`, + ` *`, + ` * Counterpart to Solidity's \`int${length}\` operator.`, + ` *`, + ` * Requirements:`, + ` *`, + ` * - input must fit into ${length} bits`, + ` *`, + ` * _Available since v3.1._`, + ` */`, + ` function toInt${length}(int256 value) internal pure returns (int${length}) {`, + ` require(value >= type(int${length}).min && value <= type(int${length}).max, "SafeCast: value doesn't fit in ${length} bits");`, + ` return int${length}(value);`, + ` }`, +].join('\n'); \ No newline at end of file diff --git a/scripts/generate/templates/toUint.js b/scripts/generate/templates/toUint.js new file mode 100644 index 00000000000..2a8c1e880dc --- /dev/null +++ b/scripts/generate/templates/toUint.js @@ -0,0 +1,13 @@ +module.exports = length => [ + ` /**`, + ` * @dev Converts a signed int${length} into an unsigned uint${length}.`, + ` *`, + ` * Requirements:`, + ` *`, + ` * - input must be greater than or equal to 0.`, + ` */`, + ` function toUint${length}(int${length} value) internal pure returns (uint${length}) {`, + ` require(value >= 0, "SafeCast: value must be positive");`, + ` return uint${length}(value);`, + ` }`, +].join('\n'); \ No newline at end of file diff --git a/scripts/generate/templates/toUintDownCast.js b/scripts/generate/templates/toUintDownCast.js new file mode 100644 index 00000000000..b9370ebbd37 --- /dev/null +++ b/scripts/generate/templates/toUintDownCast.js @@ -0,0 +1,16 @@ +module.exports = length => [ + ` /**`, + ` * @dev Returns the downcasted uint${length} from uint256, reverting on`, + ` * overflow (when the input is greater than largest uint${length}).`, + ` *`, + ` * Counterpart to Solidity's \`uint${length}\` operator.`, + ` *`, + ` * Requirements:`, + ` *`, + ` * - input must fit into ${length} bits`, + ` */`, + ` function toUint${length}(uint256 value) internal pure returns (uint${length}) {`, + ` require(value <= type(uint${length}).max, "SafeCast: value doesn't fit in ${length} bits");`, + ` return uint${length}(value);`, + ` }`, +].join('\n'); \ No newline at end of file From 921f5158f3cc50efb8725aa91eb0eeb630b615ee Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 7 Mar 2022 16:07:32 +0100 Subject: [PATCH 02/21] lint --- scripts/generate/templates/index.js | 2 +- scripts/generate/templates/toInt.js | 2 +- scripts/generate/templates/toIntDownCast.js | 2 +- scripts/generate/templates/toUint.js | 2 +- scripts/generate/templates/toUintDownCast.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/generate/templates/index.js b/scripts/generate/templates/index.js index 23bf20070a4..d395dedf122 100644 --- a/scripts/generate/templates/index.js +++ b/scripts/generate/templates/index.js @@ -3,4 +3,4 @@ module.exports = { toUint: require('./toUint'), toIntDownCast: require('./toIntDownCast'), toUintDownCast: require('./toUintDownCast'), -} \ No newline at end of file +} diff --git a/scripts/generate/templates/toInt.js b/scripts/generate/templates/toInt.js index c021282f9e2..644395df077 100644 --- a/scripts/generate/templates/toInt.js +++ b/scripts/generate/templates/toInt.js @@ -11,4 +11,4 @@ module.exports = length => [ ` require(value <= uint${length}(type(int${length}).max), "SafeCast: value doesn't fit in an int${length}");`, ` return int${length}(value);`, ` }`, -].join('\n'); \ No newline at end of file +].join('\n'); diff --git a/scripts/generate/templates/toIntDownCast.js b/scripts/generate/templates/toIntDownCast.js index 6ff50b9639f..9c352065028 100644 --- a/scripts/generate/templates/toIntDownCast.js +++ b/scripts/generate/templates/toIntDownCast.js @@ -16,4 +16,4 @@ module.exports = length => [ ` require(value >= type(int${length}).min && value <= type(int${length}).max, "SafeCast: value doesn't fit in ${length} bits");`, ` return int${length}(value);`, ` }`, -].join('\n'); \ No newline at end of file +].join('\n'); diff --git a/scripts/generate/templates/toUint.js b/scripts/generate/templates/toUint.js index 2a8c1e880dc..286d358f09e 100644 --- a/scripts/generate/templates/toUint.js +++ b/scripts/generate/templates/toUint.js @@ -10,4 +10,4 @@ module.exports = length => [ ` require(value >= 0, "SafeCast: value must be positive");`, ` return uint${length}(value);`, ` }`, -].join('\n'); \ No newline at end of file +].join('\n'); diff --git a/scripts/generate/templates/toUintDownCast.js b/scripts/generate/templates/toUintDownCast.js index b9370ebbd37..ec4af7f1307 100644 --- a/scripts/generate/templates/toUintDownCast.js +++ b/scripts/generate/templates/toUintDownCast.js @@ -13,4 +13,4 @@ module.exports = length => [ ` require(value <= type(uint${length}).max, "SafeCast: value doesn't fit in ${length} bits");`, ` return uint${length}(value);`, ` }`, -].join('\n'); \ No newline at end of file +].join('\n'); From f19f913c4d1804194c7b9e2e9ab4bcf513f71120 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 7 Mar 2022 16:50:38 +0100 Subject: [PATCH 03/21] fix lint --- scripts/generate/run.js | 83 ++++++++++---------- scripts/generate/templates/index.js | 10 +-- scripts/generate/templates/toInt.js | 24 +++--- scripts/generate/templates/toIntDownCast.js | 35 +++++---- scripts/generate/templates/toUint.js | 22 +++--- scripts/generate/templates/toUintDownCast.js | 28 +++---- 6 files changed, 103 insertions(+), 99 deletions(-) diff --git a/scripts/generate/run.js b/scripts/generate/run.js index 61c3427297c..5497921b34f 100755 --- a/scripts/generate/run.js +++ b/scripts/generate/run.js @@ -3,50 +3,53 @@ const fs = require('fs'); const { - toInt, - toUint, - toIntDownCast, - toUintDownCast, + toInt, + toUint, + toIntDownCast, + toUintDownCast, } = require('./templates'); -function genSafeCast(path) { - content = []; +function genSafeCast (path) { + const content = []; - // Header - content.push([ - `// SPDX-License-Identifier: MIT`, - `pragma solidity ^0.8.0;`, - ``, - `/**`, - ` * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow`, - ` * checks.`, - ` *`, - ` * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can`, - ` * easily result in undesired exploitation or bugs, since developers usually`, - ` * assume that overflows raise errors. \`SafeCast\` restores this intuition by`, - ` * reverting the transaction when such an operation overflows.`, - ` *`, - ` * Using this library instead of the unchecked operations eliminates an entire`, - ` * class of bugs, so it's recommended to use it always.`, - ` *`, - ` * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing`, - ` * all math on \`uint256\` and \`int256\` and then downcasting.`, - ` */`, - ].join('\n')); + // Header + content.push([ + '// SPDX-License-Identifier: MIT', + 'pragma solidity ^0.8.0;', + '', + '/**', + ' * @dev Wrappers over Solidity\'s uintXX/intXX casting operators with added overflow', + ' * checks.', + ' *', + ' * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can', + ' * easily result in undesired exploitation or bugs, since developers usually', + ' * assume that overflows raise errors. `SafeCast` restores this intuition by', + ' * reverting the transaction when such an operation overflows.', + ' *', + ' * Using this library instead of the unchecked operations eliminates an entire', + ' * class of bugs, so it\'s recommended to use it always.', + ' *', + ' * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing', + ' * all math on `uint256` and `int256` and then downcasting.', + ' */', + ].join('\n')); - // Library - content.push(`library SafeCast {`); - for (size = 224; size > 0; size -= 8) { - content.push(toUintDownCast(size)); - } - content.push(toUint(256)); - for (size = 224; size > 0; size -= 8) { - content.push(toIntDownCast(size)); - } - content.push(toInt(256)); - content.push(`}`); + // Library + content.push('library SafeCast {'); + for (let size = 224; size > 0; size -= 8) { + content.push(toUintDownCast(size)); + content.push(''); + } + content.push(toUint(256)); + content.push(''); + for (let size = 224; size > 0; size -= 8) { + content.push(toIntDownCast(size)); + content.push(''); + } + content.push(toInt(256)); + content.push('}'); - fs.writeFileSync(path, content.join('\n')); + fs.writeFileSync(path, content.join('\n')); } -genSafeCast('./contracts/utils/math/SafeCast.sol') \ No newline at end of file +genSafeCast('./contracts/utils/math/SafeCast.sol'); diff --git a/scripts/generate/templates/index.js b/scripts/generate/templates/index.js index d395dedf122..e69b70d4f9b 100644 --- a/scripts/generate/templates/index.js +++ b/scripts/generate/templates/index.js @@ -1,6 +1,6 @@ module.exports = { - toInt: require('./toInt'), - toUint: require('./toUint'), - toIntDownCast: require('./toIntDownCast'), - toUintDownCast: require('./toUintDownCast'), -} + toInt: require('./toInt'), + toUint: require('./toUint'), + toIntDownCast: require('./toIntDownCast'), + toUintDownCast: require('./toUintDownCast'), +}; diff --git a/scripts/generate/templates/toInt.js b/scripts/generate/templates/toInt.js index 644395df077..6125a4ba2fd 100644 --- a/scripts/generate/templates/toInt.js +++ b/scripts/generate/templates/toInt.js @@ -1,14 +1,14 @@ module.exports = length => [ - ` /**`, - ` * @dev Converts an unsigned uint${length} into a signed int${length}.`, - ` *`, - ` * Requirements:`, - ` *`, - ` * - input must be less than or equal to maxInt${length}.`, - ` */`, - ` function toInt${length}(uint${length} value) internal pure returns (int${length}) {`, - ` // Note: Unsafe cast below is okay because \`type(int${length}).max\` is guaranteed to be positive`, - ` require(value <= uint${length}(type(int${length}).max), "SafeCast: value doesn't fit in an int${length}");`, - ` return int${length}(value);`, - ` }`, + ' /**', + ` * @dev Converts an unsigned uint${length} into a signed int${length}.`, + ' *', + ' * Requirements:', + ' *', + ` * - input must be less than or equal to maxInt${length}.`, + ' */', + ` function toInt${length}(uint${length} value) internal pure returns (int${length}) {`, + ` // Note: Unsafe cast below is okay because \`type(int${length}).max\` is guaranteed to be positive`, + ` require(value <= uint${length}(type(int${length}).max), "SafeCast: value doesn't fit in an int${length}");`, + ` return int${length}(value);`, + ' }', ].join('\n'); diff --git a/scripts/generate/templates/toIntDownCast.js b/scripts/generate/templates/toIntDownCast.js index 9c352065028..c6204859ae2 100644 --- a/scripts/generate/templates/toIntDownCast.js +++ b/scripts/generate/templates/toIntDownCast.js @@ -1,19 +1,20 @@ module.exports = length => [ - ` /**`, - ` * @dev Returns the downcasted int${length} from int256, reverting on`, - ` * overflow (when the input is less than smallest int${length} or`, - ` * greater than largest int${length}).`, - ` *`, - ` * Counterpart to Solidity's \`int${length}\` operator.`, - ` *`, - ` * Requirements:`, - ` *`, - ` * - input must fit into ${length} bits`, - ` *`, - ` * _Available since v3.1._`, - ` */`, - ` function toInt${length}(int256 value) internal pure returns (int${length}) {`, - ` require(value >= type(int${length}).min && value <= type(int${length}).max, "SafeCast: value doesn't fit in ${length} bits");`, - ` return int${length}(value);`, - ` }`, + ' /**', + ` * @dev Returns the downcasted int${length} from int256, reverting on`, + ` * overflow (when the input is less than smallest int${length} or`, + ` * greater than largest int${length}).`, + ' *', + ` * Counterpart to Solidity's \`int${length}\` operator.`, + ' *', + ' * Requirements:', + ' *', + ` * - input must fit into ${length} bits`, + ' *', + ' * _Available since v3.1._', + ' */', + ` function toInt${length}(int256 value) internal pure returns (int${length}) {`, + // eslint-disable-next-line max-len + ` require(value >= type(int${length}).min && value <= type(int${length}).max, "SafeCast: value doesn't fit in ${length} bits");`, + ` return int${length}(value);`, + ' }', ].join('\n'); diff --git a/scripts/generate/templates/toUint.js b/scripts/generate/templates/toUint.js index 286d358f09e..7e26543e94f 100644 --- a/scripts/generate/templates/toUint.js +++ b/scripts/generate/templates/toUint.js @@ -1,13 +1,13 @@ module.exports = length => [ - ` /**`, - ` * @dev Converts a signed int${length} into an unsigned uint${length}.`, - ` *`, - ` * Requirements:`, - ` *`, - ` * - input must be greater than or equal to 0.`, - ` */`, - ` function toUint${length}(int${length} value) internal pure returns (uint${length}) {`, - ` require(value >= 0, "SafeCast: value must be positive");`, - ` return uint${length}(value);`, - ` }`, + ' /**', + ` * @dev Converts a signed int${length} into an unsigned uint${length}.`, + ' *', + ' * Requirements:', + ' *', + ' * - input must be greater than or equal to 0.', + ' */', + ` function toUint${length}(int${length} value) internal pure returns (uint${length}) {`, + ' require(value >= 0, "SafeCast: value must be positive");', + ` return uint${length}(value);`, + ' }', ].join('\n'); diff --git a/scripts/generate/templates/toUintDownCast.js b/scripts/generate/templates/toUintDownCast.js index ec4af7f1307..00de90030cd 100644 --- a/scripts/generate/templates/toUintDownCast.js +++ b/scripts/generate/templates/toUintDownCast.js @@ -1,16 +1,16 @@ module.exports = length => [ - ` /**`, - ` * @dev Returns the downcasted uint${length} from uint256, reverting on`, - ` * overflow (when the input is greater than largest uint${length}).`, - ` *`, - ` * Counterpart to Solidity's \`uint${length}\` operator.`, - ` *`, - ` * Requirements:`, - ` *`, - ` * - input must fit into ${length} bits`, - ` */`, - ` function toUint${length}(uint256 value) internal pure returns (uint${length}) {`, - ` require(value <= type(uint${length}).max, "SafeCast: value doesn't fit in ${length} bits");`, - ` return uint${length}(value);`, - ` }`, + ' /**', + ` * @dev Returns the downcasted uint${length} from uint256, reverting on`, + ` * overflow (when the input is greater than largest uint${length}).`, + ' *', + ` * Counterpart to Solidity's \`uint${length}\` operator.`, + ' *', + ' * Requirements:', + ' *', + ` * - input must fit into ${length} bits`, + ' */', + ` function toUint${length}(uint256 value) internal pure returns (uint${length}) {`, + ` require(value <= type(uint${length}).max, "SafeCast: value doesn't fit in ${length} bits");`, + ` return uint${length}(value);`, + ' }', ].join('\n'); From 33032e603911e99d204e5e416e6da43887205f12 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 7 Mar 2022 16:55:59 +0100 Subject: [PATCH 04/21] update workflows --- .github/workflows/test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d119119c0c7..6040c87288c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,6 +24,7 @@ jobs: restore-keys: npm-v2- - run: npm ci if: steps.cache.outputs.cache-hit != 'true' + - run: npm run prepare - run: npm run lint - run: npm run test env: @@ -50,6 +51,7 @@ jobs: restore-keys: npm-v2- - run: npm ci if: steps.cache.outputs.cache-hit != 'true' + - run: npm run prepare - run: npm run coverage env: NODE_OPTIONS: --max_old_space_size=4096 @@ -70,9 +72,9 @@ jobs: restore-keys: npm-v2- - run: npm ci if: steps.cache.outputs.cache-hit != 'true' + - run: npm run prepare - name: Set up Python uses: actions/setup-python@v2 - - name: Install dependencies run: pip3 install slither-analyzer - name: Summary of static analysis From 1261fdfa2f5a2f2a9372ff995f0ce3e9deaaf1c2 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 7 Mar 2022 18:37:13 +0100 Subject: [PATCH 05/21] re-track SafeCast.sol --- .gitignore | 3 - contracts/utils/math/SafeCast.sol | 968 ++++++++++++++++++++++++++++++ package.json | 2 +- 3 files changed, 969 insertions(+), 4 deletions(-) create mode 100644 contracts/utils/math/SafeCast.sol diff --git a/.gitignore b/.gitignore index 7476ab09d76..c60c5d945c0 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,3 @@ artifacts .certora* .last_confs certora_* - -# code generation -contracts/utils/math/SafeCast.sol diff --git a/contracts/utils/math/SafeCast.sol b/contracts/utils/math/SafeCast.sol new file mode 100644 index 00000000000..99812c66a5f --- /dev/null +++ b/contracts/utils/math/SafeCast.sol @@ -0,0 +1,968 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow + * checks. + * + * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can + * easily result in undesired exploitation or bugs, since developers usually + * assume that overflows raise errors. `SafeCast` restores this intuition by + * reverting the transaction when such an operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + * + * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing + * all math on `uint256` and `int256` and then downcasting. + */ +library SafeCast { + /** + * @dev Returns the downcasted uint224 from uint256, reverting on + * overflow (when the input is greater than largest uint224). + * + * Counterpart to Solidity's `uint224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + */ + function toUint224(uint256 value) internal pure returns (uint224) { + require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); + return uint224(value); + } + + /** + * @dev Returns the downcasted uint216 from uint256, reverting on + * overflow (when the input is greater than largest uint216). + * + * Counterpart to Solidity's `uint216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + */ + function toUint216(uint256 value) internal pure returns (uint216) { + require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); + return uint216(value); + } + + /** + * @dev Returns the downcasted uint208 from uint256, reverting on + * overflow (when the input is greater than largest uint208). + * + * Counterpart to Solidity's `uint208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + */ + function toUint208(uint256 value) internal pure returns (uint208) { + require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); + return uint208(value); + } + + /** + * @dev Returns the downcasted uint200 from uint256, reverting on + * overflow (when the input is greater than largest uint200). + * + * Counterpart to Solidity's `uint200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + */ + function toUint200(uint256 value) internal pure returns (uint200) { + require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); + return uint200(value); + } + + /** + * @dev Returns the downcasted uint192 from uint256, reverting on + * overflow (when the input is greater than largest uint192). + * + * Counterpart to Solidity's `uint192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + */ + function toUint192(uint256 value) internal pure returns (uint192) { + require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); + return uint192(value); + } + + /** + * @dev Returns the downcasted uint184 from uint256, reverting on + * overflow (when the input is greater than largest uint184). + * + * Counterpart to Solidity's `uint184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + */ + function toUint184(uint256 value) internal pure returns (uint184) { + require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); + return uint184(value); + } + + /** + * @dev Returns the downcasted uint176 from uint256, reverting on + * overflow (when the input is greater than largest uint176). + * + * Counterpart to Solidity's `uint176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + */ + function toUint176(uint256 value) internal pure returns (uint176) { + require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); + return uint176(value); + } + + /** + * @dev Returns the downcasted uint168 from uint256, reverting on + * overflow (when the input is greater than largest uint168). + * + * Counterpart to Solidity's `uint168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + */ + function toUint168(uint256 value) internal pure returns (uint168) { + require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); + return uint168(value); + } + + /** + * @dev Returns the downcasted uint160 from uint256, reverting on + * overflow (when the input is greater than largest uint160). + * + * Counterpart to Solidity's `uint160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + */ + function toUint160(uint256 value) internal pure returns (uint160) { + require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); + return uint160(value); + } + + /** + * @dev Returns the downcasted uint152 from uint256, reverting on + * overflow (when the input is greater than largest uint152). + * + * Counterpart to Solidity's `uint152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + */ + function toUint152(uint256 value) internal pure returns (uint152) { + require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); + return uint152(value); + } + + /** + * @dev Returns the downcasted uint144 from uint256, reverting on + * overflow (when the input is greater than largest uint144). + * + * Counterpart to Solidity's `uint144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + */ + function toUint144(uint256 value) internal pure returns (uint144) { + require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); + return uint144(value); + } + + /** + * @dev Returns the downcasted uint136 from uint256, reverting on + * overflow (when the input is greater than largest uint136). + * + * Counterpart to Solidity's `uint136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + */ + function toUint136(uint256 value) internal pure returns (uint136) { + require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); + return uint136(value); + } + + /** + * @dev Returns the downcasted uint128 from uint256, reverting on + * overflow (when the input is greater than largest uint128). + * + * Counterpart to Solidity's `uint128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + */ + function toUint128(uint256 value) internal pure returns (uint128) { + require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); + return uint128(value); + } + + /** + * @dev Returns the downcasted uint120 from uint256, reverting on + * overflow (when the input is greater than largest uint120). + * + * Counterpart to Solidity's `uint120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + */ + function toUint120(uint256 value) internal pure returns (uint120) { + require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); + return uint120(value); + } + + /** + * @dev Returns the downcasted uint112 from uint256, reverting on + * overflow (when the input is greater than largest uint112). + * + * Counterpart to Solidity's `uint112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + */ + function toUint112(uint256 value) internal pure returns (uint112) { + require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); + return uint112(value); + } + + /** + * @dev Returns the downcasted uint104 from uint256, reverting on + * overflow (when the input is greater than largest uint104). + * + * Counterpart to Solidity's `uint104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + */ + function toUint104(uint256 value) internal pure returns (uint104) { + require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); + return uint104(value); + } + + /** + * @dev Returns the downcasted uint96 from uint256, reverting on + * overflow (when the input is greater than largest uint96). + * + * Counterpart to Solidity's `uint96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + */ + function toUint96(uint256 value) internal pure returns (uint96) { + require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); + return uint96(value); + } + + /** + * @dev Returns the downcasted uint88 from uint256, reverting on + * overflow (when the input is greater than largest uint88). + * + * Counterpart to Solidity's `uint88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + */ + function toUint88(uint256 value) internal pure returns (uint88) { + require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); + return uint88(value); + } + + /** + * @dev Returns the downcasted uint80 from uint256, reverting on + * overflow (when the input is greater than largest uint80). + * + * Counterpart to Solidity's `uint80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + */ + function toUint80(uint256 value) internal pure returns (uint80) { + require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); + return uint80(value); + } + + /** + * @dev Returns the downcasted uint72 from uint256, reverting on + * overflow (when the input is greater than largest uint72). + * + * Counterpart to Solidity's `uint72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + */ + function toUint72(uint256 value) internal pure returns (uint72) { + require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); + return uint72(value); + } + + /** + * @dev Returns the downcasted uint64 from uint256, reverting on + * overflow (when the input is greater than largest uint64). + * + * Counterpart to Solidity's `uint64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + */ + function toUint64(uint256 value) internal pure returns (uint64) { + require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); + return uint64(value); + } + + /** + * @dev Returns the downcasted uint56 from uint256, reverting on + * overflow (when the input is greater than largest uint56). + * + * Counterpart to Solidity's `uint56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + */ + function toUint56(uint256 value) internal pure returns (uint56) { + require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); + return uint56(value); + } + + /** + * @dev Returns the downcasted uint48 from uint256, reverting on + * overflow (when the input is greater than largest uint48). + * + * Counterpart to Solidity's `uint48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + */ + function toUint48(uint256 value) internal pure returns (uint48) { + require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); + return uint48(value); + } + + /** + * @dev Returns the downcasted uint40 from uint256, reverting on + * overflow (when the input is greater than largest uint40). + * + * Counterpart to Solidity's `uint40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + */ + function toUint40(uint256 value) internal pure returns (uint40) { + require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); + return uint40(value); + } + + /** + * @dev Returns the downcasted uint32 from uint256, reverting on + * overflow (when the input is greater than largest uint32). + * + * Counterpart to Solidity's `uint32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + */ + function toUint32(uint256 value) internal pure returns (uint32) { + require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); + return uint32(value); + } + + /** + * @dev Returns the downcasted uint24 from uint256, reverting on + * overflow (when the input is greater than largest uint24). + * + * Counterpart to Solidity's `uint24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + */ + function toUint24(uint256 value) internal pure returns (uint24) { + require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); + return uint24(value); + } + + /** + * @dev Returns the downcasted uint16 from uint256, reverting on + * overflow (when the input is greater than largest uint16). + * + * Counterpart to Solidity's `uint16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + */ + function toUint16(uint256 value) internal pure returns (uint16) { + require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); + return uint16(value); + } + + /** + * @dev Returns the downcasted uint8 from uint256, reverting on + * overflow (when the input is greater than largest uint8). + * + * Counterpart to Solidity's `uint8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + */ + function toUint8(uint256 value) internal pure returns (uint8) { + require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); + return uint8(value); + } + + /** + * @dev Converts a signed int256 into an unsigned uint256. + * + * Requirements: + * + * - input must be greater than or equal to 0. + */ + function toUint256(int256 value) internal pure returns (uint256) { + require(value >= 0, "SafeCast: value must be positive"); + return uint256(value); + } + + /** + * @dev Returns the downcasted int224 from int256, reverting on + * overflow (when the input is less than smallest int224 or + * greater than largest int224). + * + * Counterpart to Solidity's `int224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + * + * _Available since v3.1._ + */ + function toInt224(int256 value) internal pure returns (int224) { + require(value >= type(int224).min && value <= type(int224).max, "SafeCast: value doesn't fit in 224 bits"); + return int224(value); + } + + /** + * @dev Returns the downcasted int216 from int256, reverting on + * overflow (when the input is less than smallest int216 or + * greater than largest int216). + * + * Counterpart to Solidity's `int216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + * + * _Available since v3.1._ + */ + function toInt216(int256 value) internal pure returns (int216) { + require(value >= type(int216).min && value <= type(int216).max, "SafeCast: value doesn't fit in 216 bits"); + return int216(value); + } + + /** + * @dev Returns the downcasted int208 from int256, reverting on + * overflow (when the input is less than smallest int208 or + * greater than largest int208). + * + * Counterpart to Solidity's `int208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + * + * _Available since v3.1._ + */ + function toInt208(int256 value) internal pure returns (int208) { + require(value >= type(int208).min && value <= type(int208).max, "SafeCast: value doesn't fit in 208 bits"); + return int208(value); + } + + /** + * @dev Returns the downcasted int200 from int256, reverting on + * overflow (when the input is less than smallest int200 or + * greater than largest int200). + * + * Counterpart to Solidity's `int200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + * + * _Available since v3.1._ + */ + function toInt200(int256 value) internal pure returns (int200) { + require(value >= type(int200).min && value <= type(int200).max, "SafeCast: value doesn't fit in 200 bits"); + return int200(value); + } + + /** + * @dev Returns the downcasted int192 from int256, reverting on + * overflow (when the input is less than smallest int192 or + * greater than largest int192). + * + * Counterpart to Solidity's `int192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + * + * _Available since v3.1._ + */ + function toInt192(int256 value) internal pure returns (int192) { + require(value >= type(int192).min && value <= type(int192).max, "SafeCast: value doesn't fit in 192 bits"); + return int192(value); + } + + /** + * @dev Returns the downcasted int184 from int256, reverting on + * overflow (when the input is less than smallest int184 or + * greater than largest int184). + * + * Counterpart to Solidity's `int184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + * + * _Available since v3.1._ + */ + function toInt184(int256 value) internal pure returns (int184) { + require(value >= type(int184).min && value <= type(int184).max, "SafeCast: value doesn't fit in 184 bits"); + return int184(value); + } + + /** + * @dev Returns the downcasted int176 from int256, reverting on + * overflow (when the input is less than smallest int176 or + * greater than largest int176). + * + * Counterpart to Solidity's `int176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + * + * _Available since v3.1._ + */ + function toInt176(int256 value) internal pure returns (int176) { + require(value >= type(int176).min && value <= type(int176).max, "SafeCast: value doesn't fit in 176 bits"); + return int176(value); + } + + /** + * @dev Returns the downcasted int168 from int256, reverting on + * overflow (when the input is less than smallest int168 or + * greater than largest int168). + * + * Counterpart to Solidity's `int168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + * + * _Available since v3.1._ + */ + function toInt168(int256 value) internal pure returns (int168) { + require(value >= type(int168).min && value <= type(int168).max, "SafeCast: value doesn't fit in 168 bits"); + return int168(value); + } + + /** + * @dev Returns the downcasted int160 from int256, reverting on + * overflow (when the input is less than smallest int160 or + * greater than largest int160). + * + * Counterpart to Solidity's `int160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + * + * _Available since v3.1._ + */ + function toInt160(int256 value) internal pure returns (int160) { + require(value >= type(int160).min && value <= type(int160).max, "SafeCast: value doesn't fit in 160 bits"); + return int160(value); + } + + /** + * @dev Returns the downcasted int152 from int256, reverting on + * overflow (when the input is less than smallest int152 or + * greater than largest int152). + * + * Counterpart to Solidity's `int152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + * + * _Available since v3.1._ + */ + function toInt152(int256 value) internal pure returns (int152) { + require(value >= type(int152).min && value <= type(int152).max, "SafeCast: value doesn't fit in 152 bits"); + return int152(value); + } + + /** + * @dev Returns the downcasted int144 from int256, reverting on + * overflow (when the input is less than smallest int144 or + * greater than largest int144). + * + * Counterpart to Solidity's `int144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + * + * _Available since v3.1._ + */ + function toInt144(int256 value) internal pure returns (int144) { + require(value >= type(int144).min && value <= type(int144).max, "SafeCast: value doesn't fit in 144 bits"); + return int144(value); + } + + /** + * @dev Returns the downcasted int136 from int256, reverting on + * overflow (when the input is less than smallest int136 or + * greater than largest int136). + * + * Counterpart to Solidity's `int136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + * + * _Available since v3.1._ + */ + function toInt136(int256 value) internal pure returns (int136) { + require(value >= type(int136).min && value <= type(int136).max, "SafeCast: value doesn't fit in 136 bits"); + return int136(value); + } + + /** + * @dev Returns the downcasted int128 from int256, reverting on + * overflow (when the input is less than smallest int128 or + * greater than largest int128). + * + * Counterpart to Solidity's `int128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + * + * _Available since v3.1._ + */ + function toInt128(int256 value) internal pure returns (int128) { + require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits"); + return int128(value); + } + + /** + * @dev Returns the downcasted int120 from int256, reverting on + * overflow (when the input is less than smallest int120 or + * greater than largest int120). + * + * Counterpart to Solidity's `int120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + * + * _Available since v3.1._ + */ + function toInt120(int256 value) internal pure returns (int120) { + require(value >= type(int120).min && value <= type(int120).max, "SafeCast: value doesn't fit in 120 bits"); + return int120(value); + } + + /** + * @dev Returns the downcasted int112 from int256, reverting on + * overflow (when the input is less than smallest int112 or + * greater than largest int112). + * + * Counterpart to Solidity's `int112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + * + * _Available since v3.1._ + */ + function toInt112(int256 value) internal pure returns (int112) { + require(value >= type(int112).min && value <= type(int112).max, "SafeCast: value doesn't fit in 112 bits"); + return int112(value); + } + + /** + * @dev Returns the downcasted int104 from int256, reverting on + * overflow (when the input is less than smallest int104 or + * greater than largest int104). + * + * Counterpart to Solidity's `int104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + * + * _Available since v3.1._ + */ + function toInt104(int256 value) internal pure returns (int104) { + require(value >= type(int104).min && value <= type(int104).max, "SafeCast: value doesn't fit in 104 bits"); + return int104(value); + } + + /** + * @dev Returns the downcasted int96 from int256, reverting on + * overflow (when the input is less than smallest int96 or + * greater than largest int96). + * + * Counterpart to Solidity's `int96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + * + * _Available since v3.1._ + */ + function toInt96(int256 value) internal pure returns (int96) { + require(value >= type(int96).min && value <= type(int96).max, "SafeCast: value doesn't fit in 96 bits"); + return int96(value); + } + + /** + * @dev Returns the downcasted int88 from int256, reverting on + * overflow (when the input is less than smallest int88 or + * greater than largest int88). + * + * Counterpart to Solidity's `int88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + * + * _Available since v3.1._ + */ + function toInt88(int256 value) internal pure returns (int88) { + require(value >= type(int88).min && value <= type(int88).max, "SafeCast: value doesn't fit in 88 bits"); + return int88(value); + } + + /** + * @dev Returns the downcasted int80 from int256, reverting on + * overflow (when the input is less than smallest int80 or + * greater than largest int80). + * + * Counterpart to Solidity's `int80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + * + * _Available since v3.1._ + */ + function toInt80(int256 value) internal pure returns (int80) { + require(value >= type(int80).min && value <= type(int80).max, "SafeCast: value doesn't fit in 80 bits"); + return int80(value); + } + + /** + * @dev Returns the downcasted int72 from int256, reverting on + * overflow (when the input is less than smallest int72 or + * greater than largest int72). + * + * Counterpart to Solidity's `int72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + * + * _Available since v3.1._ + */ + function toInt72(int256 value) internal pure returns (int72) { + require(value >= type(int72).min && value <= type(int72).max, "SafeCast: value doesn't fit in 72 bits"); + return int72(value); + } + + /** + * @dev Returns the downcasted int64 from int256, reverting on + * overflow (when the input is less than smallest int64 or + * greater than largest int64). + * + * Counterpart to Solidity's `int64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + * + * _Available since v3.1._ + */ + function toInt64(int256 value) internal pure returns (int64) { + require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits"); + return int64(value); + } + + /** + * @dev Returns the downcasted int56 from int256, reverting on + * overflow (when the input is less than smallest int56 or + * greater than largest int56). + * + * Counterpart to Solidity's `int56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + * + * _Available since v3.1._ + */ + function toInt56(int256 value) internal pure returns (int56) { + require(value >= type(int56).min && value <= type(int56).max, "SafeCast: value doesn't fit in 56 bits"); + return int56(value); + } + + /** + * @dev Returns the downcasted int48 from int256, reverting on + * overflow (when the input is less than smallest int48 or + * greater than largest int48). + * + * Counterpart to Solidity's `int48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + * + * _Available since v3.1._ + */ + function toInt48(int256 value) internal pure returns (int48) { + require(value >= type(int48).min && value <= type(int48).max, "SafeCast: value doesn't fit in 48 bits"); + return int48(value); + } + + /** + * @dev Returns the downcasted int40 from int256, reverting on + * overflow (when the input is less than smallest int40 or + * greater than largest int40). + * + * Counterpart to Solidity's `int40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + * + * _Available since v3.1._ + */ + function toInt40(int256 value) internal pure returns (int40) { + require(value >= type(int40).min && value <= type(int40).max, "SafeCast: value doesn't fit in 40 bits"); + return int40(value); + } + + /** + * @dev Returns the downcasted int32 from int256, reverting on + * overflow (when the input is less than smallest int32 or + * greater than largest int32). + * + * Counterpart to Solidity's `int32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + * + * _Available since v3.1._ + */ + function toInt32(int256 value) internal pure returns (int32) { + require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits"); + return int32(value); + } + + /** + * @dev Returns the downcasted int24 from int256, reverting on + * overflow (when the input is less than smallest int24 or + * greater than largest int24). + * + * Counterpart to Solidity's `int24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + * + * _Available since v3.1._ + */ + function toInt24(int256 value) internal pure returns (int24) { + require(value >= type(int24).min && value <= type(int24).max, "SafeCast: value doesn't fit in 24 bits"); + return int24(value); + } + + /** + * @dev Returns the downcasted int16 from int256, reverting on + * overflow (when the input is less than smallest int16 or + * greater than largest int16). + * + * Counterpart to Solidity's `int16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + * + * _Available since v3.1._ + */ + function toInt16(int256 value) internal pure returns (int16) { + require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits"); + return int16(value); + } + + /** + * @dev Returns the downcasted int8 from int256, reverting on + * overflow (when the input is less than smallest int8 or + * greater than largest int8). + * + * Counterpart to Solidity's `int8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + * + * _Available since v3.1._ + */ + function toInt8(int256 value) internal pure returns (int8) { + require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits"); + return int8(value); + } + + /** + * @dev Converts an unsigned uint256 into a signed int256. + * + * Requirements: + * + * - input must be less than or equal to maxInt256. + */ + function toInt256(uint256 value) internal pure returns (int256) { + // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive + require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); + return int256(value); + } +} \ No newline at end of file diff --git a/package.json b/package.json index f42ca47326f..a19fe1c9ec1 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "lint:sol": "solhint 'contracts/**/*.sol' && prettier -c 'contracts/**/*.sol'", "lint:sol:fix": "prettier --write \"contracts/**/*.sol\"", "clean": "hardhat clean && rimraf build contracts/build", - "prepare": "npm run clean && npm run generate && env COMPILE_MODE=production npm run compile", + "prepare": "npm run clean && env COMPILE_MODE=production npm run compile", "prepack": "scripts/prepack.sh", "generate": "scripts/generate/run.js", "release": "scripts/release/release.sh", From ea8e872eebacf01d2a1fcd1363ac7de9d5bbe4a1 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 7 Mar 2022 16:55:59 +0100 Subject: [PATCH 06/21] Revert "update workflows" This reverts commit 33032e603911e99d204e5e416e6da43887205f12. --- .github/workflows/test.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6040c87288c..d119119c0c7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,6 @@ jobs: restore-keys: npm-v2- - run: npm ci if: steps.cache.outputs.cache-hit != 'true' - - run: npm run prepare - run: npm run lint - run: npm run test env: @@ -51,7 +50,6 @@ jobs: restore-keys: npm-v2- - run: npm ci if: steps.cache.outputs.cache-hit != 'true' - - run: npm run prepare - run: npm run coverage env: NODE_OPTIONS: --max_old_space_size=4096 @@ -72,9 +70,9 @@ jobs: restore-keys: npm-v2- - run: npm ci if: steps.cache.outputs.cache-hit != 'true' - - run: npm run prepare - name: Set up Python uses: actions/setup-python@v2 + - name: Install dependencies run: pip3 install slither-analyzer - name: Summary of static analysis From d846b6ceae46a8d374a8a49e5c00d1501b7235cd Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Mon, 7 Mar 2022 18:41:31 +0100 Subject: [PATCH 07/21] lint --- contracts/utils/math/SafeCast.sol | 3 ++- scripts/generate/run.js | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/contracts/utils/math/SafeCast.sol b/contracts/utils/math/SafeCast.sol index 99812c66a5f..804883f5773 100644 --- a/contracts/utils/math/SafeCast.sol +++ b/contracts/utils/math/SafeCast.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: MIT + pragma solidity ^0.8.0; /** @@ -965,4 +966,4 @@ library SafeCast { require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } -} \ No newline at end of file +} diff --git a/scripts/generate/run.js b/scripts/generate/run.js index 5497921b34f..b83c81ef6e1 100755 --- a/scripts/generate/run.js +++ b/scripts/generate/run.js @@ -15,6 +15,7 @@ function genSafeCast (path) { // Header content.push([ '// SPDX-License-Identifier: MIT', + '', 'pragma solidity ^0.8.0;', '', '/**', @@ -48,6 +49,7 @@ function genSafeCast (path) { } content.push(toInt(256)); content.push('}'); + content.push(''); fs.writeFileSync(path, content.join('\n')); } From 519d0098911942e90ea7065e89bbbe2e40436485 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 12 May 2022 18:39:01 -0300 Subject: [PATCH 08/21] Refactor code generation --- contracts/utils/math/SafeCast.sol | 66 ++++++++++++ scripts/generate/format-lines.js | 17 +++ scripts/generate/run.js | 55 +--------- scripts/generate/templates/SafeCast.js | 103 +++++++++++++++++++ scripts/generate/templates/index.js | 6 -- scripts/generate/templates/toInt.js | 14 --- scripts/generate/templates/toIntDownCast.js | 20 ---- scripts/generate/templates/toUint.js | 13 --- scripts/generate/templates/toUintDownCast.js | 16 --- 9 files changed, 188 insertions(+), 122 deletions(-) create mode 100644 scripts/generate/format-lines.js mode change 100755 => 100644 scripts/generate/run.js create mode 100755 scripts/generate/templates/SafeCast.js delete mode 100644 scripts/generate/templates/index.js delete mode 100644 scripts/generate/templates/toInt.js delete mode 100644 scripts/generate/templates/toIntDownCast.js delete mode 100644 scripts/generate/templates/toUint.js delete mode 100644 scripts/generate/templates/toUintDownCast.js diff --git a/contracts/utils/math/SafeCast.sol b/contracts/utils/math/SafeCast.sol index 804883f5773..c941cf3d347 100644 --- a/contracts/utils/math/SafeCast.sol +++ b/contracts/utils/math/SafeCast.sol @@ -18,6 +18,36 @@ pragma solidity ^0.8.0; * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { + /** + * @dev Returns the downcasted uint240 from uint256, reverting on + * overflow (when the input is greater than largest uint240). + * + * Counterpart to Solidity's `uint240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + */ + function toUint240(uint256 value) internal pure returns (uint240) { + require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); + return uint240(value); + } + + /** + * @dev Returns the downcasted uint232 from uint256, reverting on + * overflow (when the input is greater than largest uint232). + * + * Counterpart to Solidity's `uint232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + */ + function toUint232(uint256 value) internal pure returns (uint232) { + require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); + return uint232(value); + } + /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). @@ -450,6 +480,42 @@ library SafeCast { return uint256(value); } + /** + * @dev Returns the downcasted int240 from int256, reverting on + * overflow (when the input is less than smallest int240 or + * greater than largest int240). + * + * Counterpart to Solidity's `int240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + * + * _Available since v3.1._ + */ + function toInt240(int256 value) internal pure returns (int240) { + require(value >= type(int240).min && value <= type(int240).max, "SafeCast: value doesn't fit in 240 bits"); + return int240(value); + } + + /** + * @dev Returns the downcasted int232 from int256, reverting on + * overflow (when the input is less than smallest int232 or + * greater than largest int232). + * + * Counterpart to Solidity's `int232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + * + * _Available since v3.1._ + */ + function toInt232(int256 value) internal pure returns (int232) { + require(value >= type(int232).min && value <= type(int232).max, "SafeCast: value doesn't fit in 232 bits"); + return int232(value); + } + /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or diff --git a/scripts/generate/format-lines.js b/scripts/generate/format-lines.js new file mode 100644 index 00000000000..e27004168f4 --- /dev/null +++ b/scripts/generate/format-lines.js @@ -0,0 +1,17 @@ +function formatLines (...lines) { + return [...indentEach(0, lines)].join('\n') + '\n'; +} + +function *indentEach (indent, lines) { + for (const line of lines) { + if (line === '') { + yield ''; + } else if (Array.isArray(line)) { + yield * indentEach(indent + 1, line); + } else { + yield ' '.repeat(indent) + line; + } + } +} + +module.exports = formatLines; diff --git a/scripts/generate/run.js b/scripts/generate/run.js old mode 100755 new mode 100644 index b83c81ef6e1..d04c730afc0 --- a/scripts/generate/run.js +++ b/scripts/generate/run.js @@ -2,56 +2,5 @@ const fs = require('fs'); -const { - toInt, - toUint, - toIntDownCast, - toUintDownCast, -} = require('./templates'); - -function genSafeCast (path) { - const content = []; - - // Header - content.push([ - '// SPDX-License-Identifier: MIT', - '', - 'pragma solidity ^0.8.0;', - '', - '/**', - ' * @dev Wrappers over Solidity\'s uintXX/intXX casting operators with added overflow', - ' * checks.', - ' *', - ' * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can', - ' * easily result in undesired exploitation or bugs, since developers usually', - ' * assume that overflows raise errors. `SafeCast` restores this intuition by', - ' * reverting the transaction when such an operation overflows.', - ' *', - ' * Using this library instead of the unchecked operations eliminates an entire', - ' * class of bugs, so it\'s recommended to use it always.', - ' *', - ' * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing', - ' * all math on `uint256` and `int256` and then downcasting.', - ' */', - ].join('\n')); - - // Library - content.push('library SafeCast {'); - for (let size = 224; size > 0; size -= 8) { - content.push(toUintDownCast(size)); - content.push(''); - } - content.push(toUint(256)); - content.push(''); - for (let size = 224; size > 0; size -= 8) { - content.push(toIntDownCast(size)); - content.push(''); - } - content.push(toInt(256)); - content.push('}'); - content.push(''); - - fs.writeFileSync(path, content.join('\n')); -} - -genSafeCast('./contracts/utils/math/SafeCast.sol'); +// SafeCast.sol +fs.writeFileSync('./contracts/utils/math/SafeCast.sol', require('./templates/SafeCast')); diff --git a/scripts/generate/templates/SafeCast.js b/scripts/generate/templates/SafeCast.js new file mode 100755 index 00000000000..efdf39f097b --- /dev/null +++ b/scripts/generate/templates/SafeCast.js @@ -0,0 +1,103 @@ +const format = require('../format-lines'); + +const header = `\ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow + * checks. + * + * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can + * easily result in undesired exploitation or bugs, since developers usually + * assume that overflows raise errors. \`SafeCast\` restores this intuition by + * reverting the transaction when such an operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + * + * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing + * all math on \`uint256\` and \`int256\` and then downcasting. + */`; + +const toInt = length => `\ +/** + * @dev Converts an unsigned uint${length} into a signed int${length}. + * + * Requirements: + * + * - input must be less than or equal to maxInt${length}. + */ +function toInt${length}(uint${length} value) internal pure returns (int${length}) { + // Note: Unsafe cast below is okay because \`type(int${length}).max\` is guaranteed to be positive + require(value <= uint${length}(type(int${length}).max), "SafeCast: value doesn't fit in an int${length}"); + return int${length}(value); +} +`; + +const toUint = length => `\ +/** + * @dev Converts a signed int${length} into an unsigned uint${length}. + * + * Requirements: + * + * - input must be greater than or equal to 0. + */ +function toUint${length}(int${length} value) internal pure returns (uint${length}) { + require(value >= 0, "SafeCast: value must be positive"); + return uint${length}(value); +} +`; + +const toIntDownCast = length => `\ +/** + * @dev Returns the downcasted int${length} from int256, reverting on + * overflow (when the input is less than smallest int${length} or + * greater than largest int${length}). + * + * Counterpart to Solidity's \`int${length}\` operator. + * + * Requirements: + * + * - input must fit into ${length} bits + * + * _Available since v3.1._ + */ +function toInt${length}(int256 value) internal pure returns (int${length}) { + require(value >= type(int${length}).min && value <= type(int${length}).max, "SafeCast: value doesn't fit in ${length} bits"); + return int${length}(value); +} +`; + +const toUintDownCast = length => `\ +/** + * @dev Returns the downcasted uint${length} from uint256, reverting on + * overflow (when the input is greater than largest uint${length}). + * + * Counterpart to Solidity's \`uint${length}\` operator. + * + * Requirements: + * + * - input must fit into ${length} bits + */ +function toUint${length}(uint256 value) internal pure returns (uint${length}) { + require(value <= type(uint${length}).max, "SafeCast: value doesn't fit in ${length} bits"); + return uint${length}(value); +} +`; + +// GENERATE +const LENGTHS = Array(30).fill().map((_, i) => (i + 1) * 8).reverse(); // 224 → 8 (in steps of 8) + +module.exports = format( + header, + 'library SafeCast {', + [ + ...LENGTHS.map(size => toUintDownCast(size)), + toUint(256), + ...LENGTHS.map(size => toIntDownCast(size)), + toInt(256).trimEnd(), + ].map(fn => fn.split('\n')), + '}', +); diff --git a/scripts/generate/templates/index.js b/scripts/generate/templates/index.js deleted file mode 100644 index e69b70d4f9b..00000000000 --- a/scripts/generate/templates/index.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - toInt: require('./toInt'), - toUint: require('./toUint'), - toIntDownCast: require('./toIntDownCast'), - toUintDownCast: require('./toUintDownCast'), -}; diff --git a/scripts/generate/templates/toInt.js b/scripts/generate/templates/toInt.js deleted file mode 100644 index 6125a4ba2fd..00000000000 --- a/scripts/generate/templates/toInt.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = length => [ - ' /**', - ` * @dev Converts an unsigned uint${length} into a signed int${length}.`, - ' *', - ' * Requirements:', - ' *', - ` * - input must be less than or equal to maxInt${length}.`, - ' */', - ` function toInt${length}(uint${length} value) internal pure returns (int${length}) {`, - ` // Note: Unsafe cast below is okay because \`type(int${length}).max\` is guaranteed to be positive`, - ` require(value <= uint${length}(type(int${length}).max), "SafeCast: value doesn't fit in an int${length}");`, - ` return int${length}(value);`, - ' }', -].join('\n'); diff --git a/scripts/generate/templates/toIntDownCast.js b/scripts/generate/templates/toIntDownCast.js deleted file mode 100644 index c6204859ae2..00000000000 --- a/scripts/generate/templates/toIntDownCast.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = length => [ - ' /**', - ` * @dev Returns the downcasted int${length} from int256, reverting on`, - ` * overflow (when the input is less than smallest int${length} or`, - ` * greater than largest int${length}).`, - ' *', - ` * Counterpart to Solidity's \`int${length}\` operator.`, - ' *', - ' * Requirements:', - ' *', - ` * - input must fit into ${length} bits`, - ' *', - ' * _Available since v3.1._', - ' */', - ` function toInt${length}(int256 value) internal pure returns (int${length}) {`, - // eslint-disable-next-line max-len - ` require(value >= type(int${length}).min && value <= type(int${length}).max, "SafeCast: value doesn't fit in ${length} bits");`, - ` return int${length}(value);`, - ' }', -].join('\n'); diff --git a/scripts/generate/templates/toUint.js b/scripts/generate/templates/toUint.js deleted file mode 100644 index 7e26543e94f..00000000000 --- a/scripts/generate/templates/toUint.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = length => [ - ' /**', - ` * @dev Converts a signed int${length} into an unsigned uint${length}.`, - ' *', - ' * Requirements:', - ' *', - ' * - input must be greater than or equal to 0.', - ' */', - ` function toUint${length}(int${length} value) internal pure returns (uint${length}) {`, - ' require(value >= 0, "SafeCast: value must be positive");', - ` return uint${length}(value);`, - ' }', -].join('\n'); diff --git a/scripts/generate/templates/toUintDownCast.js b/scripts/generate/templates/toUintDownCast.js deleted file mode 100644 index 00de90030cd..00000000000 --- a/scripts/generate/templates/toUintDownCast.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = length => [ - ' /**', - ` * @dev Returns the downcasted uint${length} from uint256, reverting on`, - ` * overflow (when the input is greater than largest uint${length}).`, - ' *', - ` * Counterpart to Solidity's \`uint${length}\` operator.`, - ' *', - ' * Requirements:', - ' *', - ` * - input must fit into ${length} bits`, - ' */', - ` function toUint${length}(uint256 value) internal pure returns (uint${length}) {`, - ` require(value <= type(uint${length}).max, "SafeCast: value doesn't fit in ${length} bits");`, - ` return uint${length}(value);`, - ' }', -].join('\n'); From 8f3453ceeab50a9c02775457c8f25f41a2d08453 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 12 May 2022 18:40:58 -0300 Subject: [PATCH 09/21] improve template consistency --- scripts/generate/templates/SafeCast.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/generate/templates/SafeCast.js b/scripts/generate/templates/SafeCast.js index efdf39f097b..3f962f68916 100755 --- a/scripts/generate/templates/SafeCast.js +++ b/scripts/generate/templates/SafeCast.js @@ -19,7 +19,8 @@ pragma solidity ^0.8.0; * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on \`uint256\` and \`int256\` and then downcasting. - */`; + */ +`; const toInt = length => `\ /** @@ -91,7 +92,7 @@ function toUint${length}(uint256 value) internal pure returns (uint${length}) { const LENGTHS = Array(30).fill().map((_, i) => (i + 1) * 8).reverse(); // 224 → 8 (in steps of 8) module.exports = format( - header, + header.trimEnd(), 'library SafeCast {', [ ...LENGTHS.map(size => toUintDownCast(size)), From 9cd95c44db1acccc8b881845cda59c56335b25b2 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 12 May 2022 18:47:25 -0300 Subject: [PATCH 10/21] fix lint --- scripts/generate/templates/SafeCast.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/generate/templates/SafeCast.js b/scripts/generate/templates/SafeCast.js index 3f962f68916..d1da3934483 100755 --- a/scripts/generate/templates/SafeCast.js +++ b/scripts/generate/templates/SafeCast.js @@ -51,6 +51,7 @@ function toUint${length}(int${length} value) internal pure returns (uint${length } `; +/* eslint-disable max-len */ const toIntDownCast = length => `\ /** * @dev Returns the downcasted int${length} from int256, reverting on @@ -70,6 +71,7 @@ function toInt${length}(int256 value) internal pure returns (int${length}) { return int${length}(value); } `; +/* eslint-enable max-len */ const toUintDownCast = length => `\ /** From debccb783efebd5bfbf68d9b341a172e85336cb3 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 12 May 2022 19:02:14 -0300 Subject: [PATCH 11/21] procedural generation of the safecast test --- contracts/mocks/SafeCastMock.sol | 193 +++++++++++++++++++++ scripts/generate/run.js | 1 + scripts/generate/templates/SafeCastMock.js | 51 ++++++ test/utils/math/SafeCast.test.js | 4 +- 4 files changed, 247 insertions(+), 2 deletions(-) create mode 100755 scripts/generate/templates/SafeCastMock.js diff --git a/contracts/mocks/SafeCastMock.sol b/contracts/mocks/SafeCastMock.sol index d1f1aaaba31..f326f53277a 100644 --- a/contracts/mocks/SafeCastMock.sol +++ b/contracts/mocks/SafeCastMock.sol @@ -12,26 +12,118 @@ contract SafeCastMock { return a.toUint256(); } + function toUint240(uint256 a) public pure returns (uint240) { + return a.toUint240(); + } + + function toUint232(uint256 a) public pure returns (uint232) { + return a.toUint232(); + } + function toUint224(uint256 a) public pure returns (uint224) { return a.toUint224(); } + function toUint216(uint256 a) public pure returns (uint216) { + return a.toUint216(); + } + + function toUint208(uint256 a) public pure returns (uint208) { + return a.toUint208(); + } + + function toUint200(uint256 a) public pure returns (uint200) { + return a.toUint200(); + } + + function toUint192(uint256 a) public pure returns (uint192) { + return a.toUint192(); + } + + function toUint184(uint256 a) public pure returns (uint184) { + return a.toUint184(); + } + + function toUint176(uint256 a) public pure returns (uint176) { + return a.toUint176(); + } + + function toUint168(uint256 a) public pure returns (uint168) { + return a.toUint168(); + } + + function toUint160(uint256 a) public pure returns (uint160) { + return a.toUint160(); + } + + function toUint152(uint256 a) public pure returns (uint152) { + return a.toUint152(); + } + + function toUint144(uint256 a) public pure returns (uint144) { + return a.toUint144(); + } + + function toUint136(uint256 a) public pure returns (uint136) { + return a.toUint136(); + } + function toUint128(uint256 a) public pure returns (uint128) { return a.toUint128(); } + function toUint120(uint256 a) public pure returns (uint120) { + return a.toUint120(); + } + + function toUint112(uint256 a) public pure returns (uint112) { + return a.toUint112(); + } + + function toUint104(uint256 a) public pure returns (uint104) { + return a.toUint104(); + } + function toUint96(uint256 a) public pure returns (uint96) { return a.toUint96(); } + function toUint88(uint256 a) public pure returns (uint88) { + return a.toUint88(); + } + + function toUint80(uint256 a) public pure returns (uint80) { + return a.toUint80(); + } + + function toUint72(uint256 a) public pure returns (uint72) { + return a.toUint72(); + } + function toUint64(uint256 a) public pure returns (uint64) { return a.toUint64(); } + function toUint56(uint256 a) public pure returns (uint56) { + return a.toUint56(); + } + + function toUint48(uint256 a) public pure returns (uint48) { + return a.toUint48(); + } + + function toUint40(uint256 a) public pure returns (uint40) { + return a.toUint40(); + } + function toUint32(uint256 a) public pure returns (uint32) { return a.toUint32(); } + function toUint24(uint256 a) public pure returns (uint24) { + return a.toUint24(); + } + function toUint16(uint256 a) public pure returns (uint16) { return a.toUint16(); } @@ -44,18 +136,118 @@ contract SafeCastMock { return a.toInt256(); } + function toInt240(int256 a) public pure returns (int240) { + return a.toInt240(); + } + + function toInt232(int256 a) public pure returns (int232) { + return a.toInt232(); + } + + function toInt224(int256 a) public pure returns (int224) { + return a.toInt224(); + } + + function toInt216(int256 a) public pure returns (int216) { + return a.toInt216(); + } + + function toInt208(int256 a) public pure returns (int208) { + return a.toInt208(); + } + + function toInt200(int256 a) public pure returns (int200) { + return a.toInt200(); + } + + function toInt192(int256 a) public pure returns (int192) { + return a.toInt192(); + } + + function toInt184(int256 a) public pure returns (int184) { + return a.toInt184(); + } + + function toInt176(int256 a) public pure returns (int176) { + return a.toInt176(); + } + + function toInt168(int256 a) public pure returns (int168) { + return a.toInt168(); + } + + function toInt160(int256 a) public pure returns (int160) { + return a.toInt160(); + } + + function toInt152(int256 a) public pure returns (int152) { + return a.toInt152(); + } + + function toInt144(int256 a) public pure returns (int144) { + return a.toInt144(); + } + + function toInt136(int256 a) public pure returns (int136) { + return a.toInt136(); + } + function toInt128(int256 a) public pure returns (int128) { return a.toInt128(); } + function toInt120(int256 a) public pure returns (int120) { + return a.toInt120(); + } + + function toInt112(int256 a) public pure returns (int112) { + return a.toInt112(); + } + + function toInt104(int256 a) public pure returns (int104) { + return a.toInt104(); + } + + function toInt96(int256 a) public pure returns (int96) { + return a.toInt96(); + } + + function toInt88(int256 a) public pure returns (int88) { + return a.toInt88(); + } + + function toInt80(int256 a) public pure returns (int80) { + return a.toInt80(); + } + + function toInt72(int256 a) public pure returns (int72) { + return a.toInt72(); + } + function toInt64(int256 a) public pure returns (int64) { return a.toInt64(); } + function toInt56(int256 a) public pure returns (int56) { + return a.toInt56(); + } + + function toInt48(int256 a) public pure returns (int48) { + return a.toInt48(); + } + + function toInt40(int256 a) public pure returns (int40) { + return a.toInt40(); + } + function toInt32(int256 a) public pure returns (int32) { return a.toInt32(); } + function toInt24(int256 a) public pure returns (int24) { + return a.toInt24(); + } + function toInt16(int256 a) public pure returns (int16) { return a.toInt16(); } @@ -63,4 +255,5 @@ contract SafeCastMock { function toInt8(int256 a) public pure returns (int8) { return a.toInt8(); } + } diff --git a/scripts/generate/run.js b/scripts/generate/run.js index d04c730afc0..aaa93799ca8 100644 --- a/scripts/generate/run.js +++ b/scripts/generate/run.js @@ -4,3 +4,4 @@ const fs = require('fs'); // SafeCast.sol fs.writeFileSync('./contracts/utils/math/SafeCast.sol', require('./templates/SafeCast')); +fs.writeFileSync('./contracts/mocks/SafeCastMock.sol', require('./templates/SafeCastMock')); diff --git a/scripts/generate/templates/SafeCastMock.js b/scripts/generate/templates/SafeCastMock.js new file mode 100755 index 00000000000..6f919b068d1 --- /dev/null +++ b/scripts/generate/templates/SafeCastMock.js @@ -0,0 +1,51 @@ +const format = require('../format-lines'); + +const header = `\ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../utils/math/SafeCast.sol"; +`; + +const toInt = length => `\ +function toInt${length}(uint${length} a) public pure returns (int${length}) { + return a.toInt${length}(); +} +`; + +const toUint = length => `\ +function toUint${length}(int${length} a) public pure returns (uint${length}) { + return a.toUint${length}(); +} +`; + +const toIntDownCast = length => `\ +function toInt${length}(int256 a) public pure returns (int${length}) { + return a.toInt${length}(); +} +`; + +const toUintDownCast = length => `\ +function toUint${length}(uint256 a) public pure returns (uint${length}) { + return a.toUint${length}(); +} +`; + +// GENERATE +const LENGTHS = Array(30).fill().map((_, i) => (i + 1) * 8).reverse(); // 224 → 8 (in steps of 8) + +module.exports = format( + header, + 'contract SafeCastMock {', + [ + 'using SafeCast for uint256;', + 'using SafeCast for int256;', + '', + toUint(256), + ...LENGTHS.map(size => toUintDownCast(size)), + toInt(256), + ...LENGTHS.map(size => toIntDownCast(size)), + ].map(fn => fn.split('\n')), + '}', +); diff --git a/test/utils/math/SafeCast.test.js b/test/utils/math/SafeCast.test.js index 09c7a3f1aed..215e361aaae 100644 --- a/test/utils/math/SafeCast.test.js +++ b/test/utils/math/SafeCast.test.js @@ -41,7 +41,7 @@ contract('SafeCast', async (accounts) => { }); } - [8, 16, 32, 64, 96, 128, 224].forEach(bits => testToUint(bits)); + Array(30).fill().map((_, i) => (i + 1) * 8).forEach(bits => testToUint(bits)); describe('toUint256', () => { const maxInt256 = new BN('2').pow(new BN(255)).subn(1); @@ -129,7 +129,7 @@ contract('SafeCast', async (accounts) => { }); } - [8, 16, 32, 64, 128].forEach(bits => testToInt(bits)); + Array(30).fill().map((_, i) => (i + 1) * 8).forEach(bits => testToInt(bits)); describe('toInt256', () => { const maxUint256 = new BN('2').pow(new BN(256)).subn(1); From 69c65e0ed856bb199851492f38d424cbe06d5101 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 13 May 2022 00:22:12 -0300 Subject: [PATCH 12/21] fix lint --- contracts/mocks/SafeCastMock.sol | 1 - scripts/generate/format-lines.js | 2 +- scripts/generate/templates/SafeCast.js | 2 +- scripts/generate/templates/SafeCastMock.js | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/contracts/mocks/SafeCastMock.sol b/contracts/mocks/SafeCastMock.sol index f326f53277a..09576571413 100644 --- a/contracts/mocks/SafeCastMock.sol +++ b/contracts/mocks/SafeCastMock.sol @@ -255,5 +255,4 @@ contract SafeCastMock { function toInt8(int256 a) public pure returns (int8) { return a.toInt8(); } - } diff --git a/scripts/generate/format-lines.js b/scripts/generate/format-lines.js index e27004168f4..2dae1748aec 100644 --- a/scripts/generate/format-lines.js +++ b/scripts/generate/format-lines.js @@ -9,7 +9,7 @@ function *indentEach (indent, lines) { } else if (Array.isArray(line)) { yield * indentEach(indent + 1, line); } else { - yield ' '.repeat(indent) + line; + yield ' '.repeat(indent) + line; } } } diff --git a/scripts/generate/templates/SafeCast.js b/scripts/generate/templates/SafeCast.js index d1da3934483..00b6c9c11c9 100755 --- a/scripts/generate/templates/SafeCast.js +++ b/scripts/generate/templates/SafeCast.js @@ -101,6 +101,6 @@ module.exports = format( toUint(256), ...LENGTHS.map(size => toIntDownCast(size)), toInt(256).trimEnd(), - ].map(fn => fn.split('\n')), + ].flatMap(fn => fn.split('\n')), '}', ); diff --git a/scripts/generate/templates/SafeCastMock.js b/scripts/generate/templates/SafeCastMock.js index 6f919b068d1..c408792f62b 100755 --- a/scripts/generate/templates/SafeCastMock.js +++ b/scripts/generate/templates/SafeCastMock.js @@ -46,6 +46,6 @@ module.exports = format( ...LENGTHS.map(size => toUintDownCast(size)), toInt(256), ...LENGTHS.map(size => toIntDownCast(size)), - ].map(fn => fn.split('\n')), + ].flatMap(fn => fn.split('\n')).slice(0, -1), '}', ); From 0684ee900e904be758c547b3f8620dbe17e3e385 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 13 May 2022 01:06:48 -0300 Subject: [PATCH 13/21] missed int248/uint248 --- contracts/mocks/SafeCastMock.sol | 8 ++++++ contracts/utils/math/SafeCast.sol | 33 ++++++++++++++++++++++ scripts/generate/templates/SafeCast.js | 2 +- scripts/generate/templates/SafeCastMock.js | 2 +- test/utils/math/SafeCast.test.js | 4 +-- 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/contracts/mocks/SafeCastMock.sol b/contracts/mocks/SafeCastMock.sol index 09576571413..806ce12740f 100644 --- a/contracts/mocks/SafeCastMock.sol +++ b/contracts/mocks/SafeCastMock.sol @@ -12,6 +12,10 @@ contract SafeCastMock { return a.toUint256(); } + function toUint248(uint256 a) public pure returns (uint248) { + return a.toUint248(); + } + function toUint240(uint256 a) public pure returns (uint240) { return a.toUint240(); } @@ -136,6 +140,10 @@ contract SafeCastMock { return a.toInt256(); } + function toInt248(int256 a) public pure returns (int248) { + return a.toInt248(); + } + function toInt240(int256 a) public pure returns (int240) { return a.toInt240(); } diff --git a/contracts/utils/math/SafeCast.sol b/contracts/utils/math/SafeCast.sol index c941cf3d347..7d1c1031836 100644 --- a/contracts/utils/math/SafeCast.sol +++ b/contracts/utils/math/SafeCast.sol @@ -18,6 +18,21 @@ pragma solidity ^0.8.0; * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { + /** + * @dev Returns the downcasted uint248 from uint256, reverting on + * overflow (when the input is greater than largest uint248). + * + * Counterpart to Solidity's `uint248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + */ + function toUint248(uint256 value) internal pure returns (uint248) { + require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); + return uint248(value); + } + /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). @@ -480,6 +495,24 @@ library SafeCast { return uint256(value); } + /** + * @dev Returns the downcasted int248 from int256, reverting on + * overflow (when the input is less than smallest int248 or + * greater than largest int248). + * + * Counterpart to Solidity's `int248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + * + * _Available since v3.1._ + */ + function toInt248(int256 value) internal pure returns (int248) { + require(value >= type(int248).min && value <= type(int248).max, "SafeCast: value doesn't fit in 248 bits"); + return int248(value); + } + /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or diff --git a/scripts/generate/templates/SafeCast.js b/scripts/generate/templates/SafeCast.js index 00b6c9c11c9..39083db6d22 100755 --- a/scripts/generate/templates/SafeCast.js +++ b/scripts/generate/templates/SafeCast.js @@ -91,7 +91,7 @@ function toUint${length}(uint256 value) internal pure returns (uint${length}) { `; // GENERATE -const LENGTHS = Array(30).fill().map((_, i) => (i + 1) * 8).reverse(); // 224 → 8 (in steps of 8) +const LENGTHS = Array(31).fill().map((_, i) => (i + 1) * 8).reverse(); // 248 → 8 (in steps of 8) module.exports = format( header.trimEnd(), diff --git a/scripts/generate/templates/SafeCastMock.js b/scripts/generate/templates/SafeCastMock.js index c408792f62b..eff1b897619 100755 --- a/scripts/generate/templates/SafeCastMock.js +++ b/scripts/generate/templates/SafeCastMock.js @@ -33,7 +33,7 @@ function toUint${length}(uint256 a) public pure returns (uint${length}) { `; // GENERATE -const LENGTHS = Array(30).fill().map((_, i) => (i + 1) * 8).reverse(); // 224 → 8 (in steps of 8) +const LENGTHS = Array(31).fill().map((_, i) => (i + 1) * 8).reverse(); // 248 → 8 (in steps of 8) module.exports = format( header, diff --git a/test/utils/math/SafeCast.test.js b/test/utils/math/SafeCast.test.js index 215e361aaae..0693a189944 100644 --- a/test/utils/math/SafeCast.test.js +++ b/test/utils/math/SafeCast.test.js @@ -41,7 +41,7 @@ contract('SafeCast', async (accounts) => { }); } - Array(30).fill().map((_, i) => (i + 1) * 8).forEach(bits => testToUint(bits)); + Array(31).fill().map((_, i) => (i + 1) * 8).forEach(bits => testToUint(bits)); describe('toUint256', () => { const maxInt256 = new BN('2').pow(new BN(255)).subn(1); @@ -129,7 +129,7 @@ contract('SafeCast', async (accounts) => { }); } - Array(30).fill().map((_, i) => (i + 1) * 8).forEach(bits => testToInt(bits)); + Array(31).fill().map((_, i) => (i + 1) * 8).forEach(bits => testToInt(bits)); describe('toInt256', () => { const maxUint256 = new BN('2').pow(new BN(256)).subn(1); From d4c789fdf894672b26c225b2d40d15e40602cee6 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 18 May 2022 20:15:34 +0200 Subject: [PATCH 14/21] accurate available since mentions for SafeCasts --- contracts/utils/math/SafeCast.sol | 118 ++++++++++++++++++------ scripts/generate/format-lines.js | 7 +- scripts/generate/run.js | 0 scripts/generate/templates/SafeCast.js | 121 +++++++++++++++++++------ 4 files changed, 186 insertions(+), 60 deletions(-) mode change 100644 => 100755 scripts/generate/run.js diff --git a/contracts/utils/math/SafeCast.sol b/contracts/utils/math/SafeCast.sol index 7d1c1031836..194edf6de5e 100644 --- a/contracts/utils/math/SafeCast.sol +++ b/contracts/utils/math/SafeCast.sol @@ -27,6 +27,8 @@ library SafeCast { * Requirements: * * - input must fit into 248 bits + * + * _Available since v4.7._ */ function toUint248(uint256 value) internal pure returns (uint248) { require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); @@ -42,6 +44,8 @@ library SafeCast { * Requirements: * * - input must fit into 240 bits + * + * _Available since v4.7._ */ function toUint240(uint256 value) internal pure returns (uint240) { require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); @@ -57,6 +61,8 @@ library SafeCast { * Requirements: * * - input must fit into 232 bits + * + * _Available since v4.7._ */ function toUint232(uint256 value) internal pure returns (uint232) { require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); @@ -72,6 +78,8 @@ library SafeCast { * Requirements: * * - input must fit into 224 bits + * + * _Available since v4.2._ */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); @@ -87,6 +95,8 @@ library SafeCast { * Requirements: * * - input must fit into 216 bits + * + * _Available since v4.7._ */ function toUint216(uint256 value) internal pure returns (uint216) { require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); @@ -102,6 +112,8 @@ library SafeCast { * Requirements: * * - input must fit into 208 bits + * + * _Available since v4.7._ */ function toUint208(uint256 value) internal pure returns (uint208) { require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); @@ -117,6 +129,8 @@ library SafeCast { * Requirements: * * - input must fit into 200 bits + * + * _Available since v4.7._ */ function toUint200(uint256 value) internal pure returns (uint200) { require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); @@ -132,6 +146,8 @@ library SafeCast { * Requirements: * * - input must fit into 192 bits + * + * _Available since v4.7._ */ function toUint192(uint256 value) internal pure returns (uint192) { require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); @@ -147,6 +163,8 @@ library SafeCast { * Requirements: * * - input must fit into 184 bits + * + * _Available since v4.7._ */ function toUint184(uint256 value) internal pure returns (uint184) { require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); @@ -162,6 +180,8 @@ library SafeCast { * Requirements: * * - input must fit into 176 bits + * + * _Available since v4.7._ */ function toUint176(uint256 value) internal pure returns (uint176) { require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); @@ -177,6 +197,8 @@ library SafeCast { * Requirements: * * - input must fit into 168 bits + * + * _Available since v4.7._ */ function toUint168(uint256 value) internal pure returns (uint168) { require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); @@ -192,6 +214,8 @@ library SafeCast { * Requirements: * * - input must fit into 160 bits + * + * _Available since v4.7._ */ function toUint160(uint256 value) internal pure returns (uint160) { require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); @@ -207,6 +231,8 @@ library SafeCast { * Requirements: * * - input must fit into 152 bits + * + * _Available since v4.7._ */ function toUint152(uint256 value) internal pure returns (uint152) { require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); @@ -222,6 +248,8 @@ library SafeCast { * Requirements: * * - input must fit into 144 bits + * + * _Available since v4.7._ */ function toUint144(uint256 value) internal pure returns (uint144) { require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); @@ -237,6 +265,8 @@ library SafeCast { * Requirements: * * - input must fit into 136 bits + * + * _Available since v4.7._ */ function toUint136(uint256 value) internal pure returns (uint136) { require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); @@ -252,6 +282,8 @@ library SafeCast { * Requirements: * * - input must fit into 128 bits + * + * _Available since v2.5._ */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); @@ -267,6 +299,8 @@ library SafeCast { * Requirements: * * - input must fit into 120 bits + * + * _Available since v4.7._ */ function toUint120(uint256 value) internal pure returns (uint120) { require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); @@ -282,6 +316,8 @@ library SafeCast { * Requirements: * * - input must fit into 112 bits + * + * _Available since v4.7._ */ function toUint112(uint256 value) internal pure returns (uint112) { require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); @@ -297,6 +333,8 @@ library SafeCast { * Requirements: * * - input must fit into 104 bits + * + * _Available since v4.7._ */ function toUint104(uint256 value) internal pure returns (uint104) { require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); @@ -312,6 +350,8 @@ library SafeCast { * Requirements: * * - input must fit into 96 bits + * + * _Available since v4.2._ */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); @@ -327,6 +367,8 @@ library SafeCast { * Requirements: * * - input must fit into 88 bits + * + * _Available since v4.7._ */ function toUint88(uint256 value) internal pure returns (uint88) { require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); @@ -342,6 +384,8 @@ library SafeCast { * Requirements: * * - input must fit into 80 bits + * + * _Available since v4.7._ */ function toUint80(uint256 value) internal pure returns (uint80) { require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); @@ -357,6 +401,8 @@ library SafeCast { * Requirements: * * - input must fit into 72 bits + * + * _Available since v4.7._ */ function toUint72(uint256 value) internal pure returns (uint72) { require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); @@ -372,6 +418,8 @@ library SafeCast { * Requirements: * * - input must fit into 64 bits + * + * _Available since v2.5._ */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); @@ -387,6 +435,8 @@ library SafeCast { * Requirements: * * - input must fit into 56 bits + * + * _Available since v4.7._ */ function toUint56(uint256 value) internal pure returns (uint56) { require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); @@ -402,6 +452,8 @@ library SafeCast { * Requirements: * * - input must fit into 48 bits + * + * _Available since v4.7._ */ function toUint48(uint256 value) internal pure returns (uint48) { require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); @@ -417,6 +469,8 @@ library SafeCast { * Requirements: * * - input must fit into 40 bits + * + * _Available since v4.7._ */ function toUint40(uint256 value) internal pure returns (uint40) { require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); @@ -432,6 +486,8 @@ library SafeCast { * Requirements: * * - input must fit into 32 bits + * + * _Available since v2.5._ */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); @@ -447,6 +503,8 @@ library SafeCast { * Requirements: * * - input must fit into 24 bits + * + * _Available since v4.7._ */ function toUint24(uint256 value) internal pure returns (uint24) { require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); @@ -462,6 +520,8 @@ library SafeCast { * Requirements: * * - input must fit into 16 bits + * + * _Available since v2.5._ */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); @@ -477,6 +537,8 @@ library SafeCast { * Requirements: * * - input must fit into 8 bits + * + * _Available since v2.5._ */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); @@ -489,6 +551,8 @@ library SafeCast { * Requirements: * * - input must be greater than or equal to 0. + * + * _Available since v3.0._ */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); @@ -506,7 +570,7 @@ library SafeCast { * * - input must fit into 248 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt248(int256 value) internal pure returns (int248) { require(value >= type(int248).min && value <= type(int248).max, "SafeCast: value doesn't fit in 248 bits"); @@ -524,7 +588,7 @@ library SafeCast { * * - input must fit into 240 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt240(int256 value) internal pure returns (int240) { require(value >= type(int240).min && value <= type(int240).max, "SafeCast: value doesn't fit in 240 bits"); @@ -542,7 +606,7 @@ library SafeCast { * * - input must fit into 232 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt232(int256 value) internal pure returns (int232) { require(value >= type(int232).min && value <= type(int232).max, "SafeCast: value doesn't fit in 232 bits"); @@ -560,7 +624,7 @@ library SafeCast { * * - input must fit into 224 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt224(int256 value) internal pure returns (int224) { require(value >= type(int224).min && value <= type(int224).max, "SafeCast: value doesn't fit in 224 bits"); @@ -578,7 +642,7 @@ library SafeCast { * * - input must fit into 216 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt216(int256 value) internal pure returns (int216) { require(value >= type(int216).min && value <= type(int216).max, "SafeCast: value doesn't fit in 216 bits"); @@ -596,7 +660,7 @@ library SafeCast { * * - input must fit into 208 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt208(int256 value) internal pure returns (int208) { require(value >= type(int208).min && value <= type(int208).max, "SafeCast: value doesn't fit in 208 bits"); @@ -614,7 +678,7 @@ library SafeCast { * * - input must fit into 200 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt200(int256 value) internal pure returns (int200) { require(value >= type(int200).min && value <= type(int200).max, "SafeCast: value doesn't fit in 200 bits"); @@ -632,7 +696,7 @@ library SafeCast { * * - input must fit into 192 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt192(int256 value) internal pure returns (int192) { require(value >= type(int192).min && value <= type(int192).max, "SafeCast: value doesn't fit in 192 bits"); @@ -650,7 +714,7 @@ library SafeCast { * * - input must fit into 184 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt184(int256 value) internal pure returns (int184) { require(value >= type(int184).min && value <= type(int184).max, "SafeCast: value doesn't fit in 184 bits"); @@ -668,7 +732,7 @@ library SafeCast { * * - input must fit into 176 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt176(int256 value) internal pure returns (int176) { require(value >= type(int176).min && value <= type(int176).max, "SafeCast: value doesn't fit in 176 bits"); @@ -686,7 +750,7 @@ library SafeCast { * * - input must fit into 168 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt168(int256 value) internal pure returns (int168) { require(value >= type(int168).min && value <= type(int168).max, "SafeCast: value doesn't fit in 168 bits"); @@ -704,7 +768,7 @@ library SafeCast { * * - input must fit into 160 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt160(int256 value) internal pure returns (int160) { require(value >= type(int160).min && value <= type(int160).max, "SafeCast: value doesn't fit in 160 bits"); @@ -722,7 +786,7 @@ library SafeCast { * * - input must fit into 152 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt152(int256 value) internal pure returns (int152) { require(value >= type(int152).min && value <= type(int152).max, "SafeCast: value doesn't fit in 152 bits"); @@ -740,7 +804,7 @@ library SafeCast { * * - input must fit into 144 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt144(int256 value) internal pure returns (int144) { require(value >= type(int144).min && value <= type(int144).max, "SafeCast: value doesn't fit in 144 bits"); @@ -758,7 +822,7 @@ library SafeCast { * * - input must fit into 136 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt136(int256 value) internal pure returns (int136) { require(value >= type(int136).min && value <= type(int136).max, "SafeCast: value doesn't fit in 136 bits"); @@ -794,7 +858,7 @@ library SafeCast { * * - input must fit into 120 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt120(int256 value) internal pure returns (int120) { require(value >= type(int120).min && value <= type(int120).max, "SafeCast: value doesn't fit in 120 bits"); @@ -812,7 +876,7 @@ library SafeCast { * * - input must fit into 112 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt112(int256 value) internal pure returns (int112) { require(value >= type(int112).min && value <= type(int112).max, "SafeCast: value doesn't fit in 112 bits"); @@ -830,7 +894,7 @@ library SafeCast { * * - input must fit into 104 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt104(int256 value) internal pure returns (int104) { require(value >= type(int104).min && value <= type(int104).max, "SafeCast: value doesn't fit in 104 bits"); @@ -848,7 +912,7 @@ library SafeCast { * * - input must fit into 96 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt96(int256 value) internal pure returns (int96) { require(value >= type(int96).min && value <= type(int96).max, "SafeCast: value doesn't fit in 96 bits"); @@ -866,7 +930,7 @@ library SafeCast { * * - input must fit into 88 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt88(int256 value) internal pure returns (int88) { require(value >= type(int88).min && value <= type(int88).max, "SafeCast: value doesn't fit in 88 bits"); @@ -884,7 +948,7 @@ library SafeCast { * * - input must fit into 80 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt80(int256 value) internal pure returns (int80) { require(value >= type(int80).min && value <= type(int80).max, "SafeCast: value doesn't fit in 80 bits"); @@ -902,7 +966,7 @@ library SafeCast { * * - input must fit into 72 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt72(int256 value) internal pure returns (int72) { require(value >= type(int72).min && value <= type(int72).max, "SafeCast: value doesn't fit in 72 bits"); @@ -938,7 +1002,7 @@ library SafeCast { * * - input must fit into 56 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt56(int256 value) internal pure returns (int56) { require(value >= type(int56).min && value <= type(int56).max, "SafeCast: value doesn't fit in 56 bits"); @@ -956,7 +1020,7 @@ library SafeCast { * * - input must fit into 48 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt48(int256 value) internal pure returns (int48) { require(value >= type(int48).min && value <= type(int48).max, "SafeCast: value doesn't fit in 48 bits"); @@ -974,7 +1038,7 @@ library SafeCast { * * - input must fit into 40 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt40(int256 value) internal pure returns (int40) { require(value >= type(int40).min && value <= type(int40).max, "SafeCast: value doesn't fit in 40 bits"); @@ -1010,7 +1074,7 @@ library SafeCast { * * - input must fit into 24 bits * - * _Available since v3.1._ + * _Available since v4.7._ */ function toInt24(int256 value) internal pure returns (int24) { require(value >= type(int24).min && value <= type(int24).max, "SafeCast: value doesn't fit in 24 bits"); @@ -1059,6 +1123,8 @@ library SafeCast { * Requirements: * * - input must be less than or equal to maxInt256. + * + * _Available since v3.0._ */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive diff --git a/scripts/generate/format-lines.js b/scripts/generate/format-lines.js index 2dae1748aec..e1f1823e834 100644 --- a/scripts/generate/format-lines.js +++ b/scripts/generate/format-lines.js @@ -4,12 +4,11 @@ function formatLines (...lines) { function *indentEach (indent, lines) { for (const line of lines) { - if (line === '') { - yield ''; - } else if (Array.isArray(line)) { + if (Array.isArray(line)) { yield * indentEach(indent + 1, line); } else { - yield ' '.repeat(indent) + line; + const padding = ' '.repeat(indent); + yield * line.split('\n').map(subline => subline === '' ? '' : padding + subline); } } } diff --git a/scripts/generate/run.js b/scripts/generate/run.js old mode 100644 new mode 100755 diff --git a/scripts/generate/templates/SafeCast.js b/scripts/generate/templates/SafeCast.js index 39083db6d22..b0236c93cfa 100755 --- a/scripts/generate/templates/SafeCast.js +++ b/scripts/generate/templates/SafeCast.js @@ -1,5 +1,62 @@ +const { assert } = require('chai'); const format = require('../format-lines'); +const LENGTHS = Array(31).fill().map((_, i) => (i + 1) * 8).reverse(); // 248 → 8 (in steps of 8) + +const version = (selector, length) => { + switch (selector) { + case 'toUint(uint)': { + switch (length) { + case 8: + case 16: + case 32: + case 64: + case 128: + return '2.5'; + case 96: + case 224: + return '4.2'; + default: + assert(LENGTHS.includes(length)); + return '4.7'; + } + } + case 'toInt(int)': { + switch (length) { + case 8: + case 16: + case 32: + case 64: + case 128: + return '3.1'; + default: + assert(LENGTHS.includes(length)); + return '4.7'; + } + } + case 'toUint(int)': { + switch (length) { + case 256: + return '3.0'; + default: + assert(false); + return; + } + } + case 'toInt(uint)': { + switch (length) { + case 256: + return '3.0'; + default: + assert(false); + return; + } + } + default: + assert(false); + } +}; + const header = `\ // SPDX-License-Identifier: MIT @@ -22,31 +79,21 @@ pragma solidity ^0.8.0; */ `; -const toInt = length => `\ +const toUintDownCast = length => `\ /** - * @dev Converts an unsigned uint${length} into a signed int${length}. - * - * Requirements: + * @dev Returns the downcasted uint${length} from uint256, reverting on + * overflow (when the input is greater than largest uint${length}). * - * - input must be less than or equal to maxInt${length}. - */ -function toInt${length}(uint${length} value) internal pure returns (int${length}) { - // Note: Unsafe cast below is okay because \`type(int${length}).max\` is guaranteed to be positive - require(value <= uint${length}(type(int${length}).max), "SafeCast: value doesn't fit in an int${length}"); - return int${length}(value); -} -`; - -const toUint = length => `\ -/** - * @dev Converts a signed int${length} into an unsigned uint${length}. + * Counterpart to Solidity's \`uint${length}\` operator. * * Requirements: * - * - input must be greater than or equal to 0. + * - input must fit into ${length} bits + * + * _Available since v${version('toUint(uint)', length)}._ */ -function toUint${length}(int${length} value) internal pure returns (uint${length}) { - require(value >= 0, "SafeCast: value must be positive"); +function toUint${length}(uint256 value) internal pure returns (uint${length}) { + require(value <= type(uint${length}).max, "SafeCast: value doesn't fit in ${length} bits"); return uint${length}(value); } `; @@ -64,7 +111,7 @@ const toIntDownCast = length => `\ * * - input must fit into ${length} bits * - * _Available since v3.1._ + * _Available since v${version('toInt(int)', length)}._ */ function toInt${length}(int256 value) internal pure returns (int${length}) { require(value >= type(int${length}).min && value <= type(int${length}).max, "SafeCast: value doesn't fit in ${length} bits"); @@ -73,26 +120,40 @@ function toInt${length}(int256 value) internal pure returns (int${length}) { `; /* eslint-enable max-len */ -const toUintDownCast = length => `\ +const toInt = length => `\ /** - * @dev Returns the downcasted uint${length} from uint256, reverting on - * overflow (when the input is greater than largest uint${length}). + * @dev Converts an unsigned uint${length} into a signed int${length}. * - * Counterpart to Solidity's \`uint${length}\` operator. + * Requirements: + * + * - input must be less than or equal to maxInt${length}. + * + * _Available since v${version('toInt(uint)', length)}._ + */ +function toInt${length}(uint${length} value) internal pure returns (int${length}) { + // Note: Unsafe cast below is okay because \`type(int${length}).max\` is guaranteed to be positive + require(value <= uint${length}(type(int${length}).max), "SafeCast: value doesn't fit in an int${length}"); + return int${length}(value); +} +`; + +const toUint = length => `\ +/** + * @dev Converts a signed int${length} into an unsigned uint${length}. * * Requirements: * - * - input must fit into ${length} bits + * - input must be greater than or equal to 0. + * + * _Available since v${version('toUint(int)', length)}._ */ -function toUint${length}(uint256 value) internal pure returns (uint${length}) { - require(value <= type(uint${length}).max, "SafeCast: value doesn't fit in ${length} bits"); +function toUint${length}(int${length} value) internal pure returns (uint${length}) { + require(value >= 0, "SafeCast: value must be positive"); return uint${length}(value); } `; // GENERATE -const LENGTHS = Array(31).fill().map((_, i) => (i + 1) * 8).reverse(); // 248 → 8 (in steps of 8) - module.exports = format( header.trimEnd(), 'library SafeCast {', @@ -101,6 +162,6 @@ module.exports = format( toUint(256), ...LENGTHS.map(size => toIntDownCast(size)), toInt(256).trimEnd(), - ].flatMap(fn => fn.split('\n')), + ], '}', ); From f827f2b4abbe8f707d085b21bbd8b54ac7d3b5db Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 18 May 2022 21:26:36 +0200 Subject: [PATCH 15/21] maintain constract version & test generation --- contracts/utils/math/SafeCast.sol | 1 + package.json | 3 ++- scripts/checks/generation.sh | 6 +++++ scripts/{ => checks}/inheritanceOrdering.js | 4 ++- scripts/generate/run.js | 29 ++++++++++++++++++--- scripts/generate/templates/SafeCast.js | 2 -- scripts/generate/templates/SafeCastMock.js | 2 -- 7 files changed, 38 insertions(+), 9 deletions(-) create mode 100755 scripts/checks/generation.sh rename scripts/{ => checks}/inheritanceOrdering.js (97%) mode change 100644 => 100755 diff --git a/contracts/utils/math/SafeCast.sol b/contracts/utils/math/SafeCast.sol index 194edf6de5e..2857638bdcf 100644 --- a/contracts/utils/math/SafeCast.sol +++ b/contracts/utils/math/SafeCast.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.4.1) (utils/math/SafeCast.sol) pragma solidity ^0.8.0; diff --git a/package.json b/package.json index a19fe1c9ec1..0aa95708bf0 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "release": "scripts/release/release.sh", "version": "scripts/release/version.sh", "test": "hardhat test", - "test:inheritance": "node scripts/inheritanceOrdering artifacts/build-info/*", + "test:inheritance": "scripts/checks/inheritanceOrdering.js artifacts/build-info/*", + "test:generation": "scripts/checks/generation.sh", "gas-report": "env ENABLE_GAS_REPORT=true npm run test", "slither": "npm run clean && slither . --detect reentrancy-eth,reentrancy-no-eth,reentrancy-unlimited-gas" }, diff --git a/scripts/checks/generation.sh b/scripts/checks/generation.sh new file mode 100755 index 00000000000..574a645d60d --- /dev/null +++ b/scripts/checks/generation.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -euo pipefail + +npm run generate +git diff --quiet --exit-code \ No newline at end of file diff --git a/scripts/inheritanceOrdering.js b/scripts/checks/inheritanceOrdering.js old mode 100644 new mode 100755 similarity index 97% rename from scripts/inheritanceOrdering.js rename to scripts/checks/inheritanceOrdering.js index 2d285895c72..9d332cba330 --- a/scripts/inheritanceOrdering.js +++ b/scripts/checks/inheritanceOrdering.js @@ -1,10 +1,12 @@ +#!/usr/bin/env node + const path = require('path'); const graphlib = require('graphlib'); const { findAll } = require('solidity-ast/utils'); const { _: artifacts } = require('yargs').argv; for (const artifact of artifacts) { - const { output: solcOutput } = require(path.resolve(__dirname, '..', artifact)); + const { output: solcOutput } = require(path.resolve(__dirname, '../..', artifact)); const graph = new graphlib.Graph({ directed: true }); const names = {}; diff --git a/scripts/generate/run.js b/scripts/generate/run.js index aaa93799ca8..533421303f6 100755 --- a/scripts/generate/run.js +++ b/scripts/generate/run.js @@ -1,7 +1,30 @@ #!/usr/bin/env node const fs = require('fs'); +const format = require('./format-lines'); -// SafeCast.sol -fs.writeFileSync('./contracts/utils/math/SafeCast.sol', require('./templates/SafeCast')); -fs.writeFileSync('./contracts/mocks/SafeCastMock.sol', require('./templates/SafeCastMock')); +function getVersion (path) { + try { + return fs + .readFileSync(path) + .toString() + .match(/\/\/ OpenZeppelin Contracts \(last updated v\d+\.\d+\.\d+\)/)[0]; + } catch (err) { + return null; + } +} + +for (const [ file, template ] of Object.entries({ + 'utils/math/SafeCast.sol': './templates/SafeCast', + 'mocks/SafeCastMock.sol': './templates/SafeCastMock', +})) { + const path = `./contracts/${file}`; + const version = getVersion(path); + const content = format( + '// SPDX-License-Identifier: MIT', + (version ? version + ` (${file})\n` : ''), + require(template).trimEnd(), + ); + + fs.writeFileSync(path, content); +} diff --git a/scripts/generate/templates/SafeCast.js b/scripts/generate/templates/SafeCast.js index b0236c93cfa..67e042c7ec6 100755 --- a/scripts/generate/templates/SafeCast.js +++ b/scripts/generate/templates/SafeCast.js @@ -58,8 +58,6 @@ const version = (selector, length) => { }; const header = `\ -// SPDX-License-Identifier: MIT - pragma solidity ^0.8.0; /** diff --git a/scripts/generate/templates/SafeCastMock.js b/scripts/generate/templates/SafeCastMock.js index eff1b897619..bda69c1a326 100755 --- a/scripts/generate/templates/SafeCastMock.js +++ b/scripts/generate/templates/SafeCastMock.js @@ -1,8 +1,6 @@ const format = require('../format-lines'); const header = `\ -// SPDX-License-Identifier: MIT - pragma solidity ^0.8.0; import "../utils/math/SafeCast.sol"; From 6a1c6d46afb0adef3d945b72c868aae176ff52c2 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 18 May 2022 21:30:10 +0200 Subject: [PATCH 16/21] enable generation check in CI --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d119119c0c7..c4828994f1d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,6 +30,7 @@ jobs: FORCE_COLOR: 1 ENABLE_GAS_REPORT: true - run: npm run test:inheritance + - run: npm run test:generation - name: Print gas report run: cat gas-report.txt From 7a4c2ad076d2df2a2f8f661e99485f79e48a7d17 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 19 May 2022 10:12:22 +0200 Subject: [PATCH 17/21] Apply suggestions from code review Co-authored-by: Francisco Giordano --- scripts/generate/run.js | 3 +-- scripts/generate/templates/SafeCast.js | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/generate/run.js b/scripts/generate/run.js index 533421303f6..5ebb1b37a33 100755 --- a/scripts/generate/run.js +++ b/scripts/generate/run.js @@ -6,8 +6,7 @@ const format = require('./format-lines'); function getVersion (path) { try { return fs - .readFileSync(path) - .toString() + .readFileSync(path, 'utf8') .match(/\/\/ OpenZeppelin Contracts \(last updated v\d+\.\d+\.\d+\)/)[0]; } catch (err) { return null; diff --git a/scripts/generate/templates/SafeCast.js b/scripts/generate/templates/SafeCast.js index 67e042c7ec6..3c5897ca434 100755 --- a/scripts/generate/templates/SafeCast.js +++ b/scripts/generate/templates/SafeCast.js @@ -1,8 +1,10 @@ -const { assert } = require('chai'); +const assert = require('assert'); const format = require('../format-lines'); const LENGTHS = Array(31).fill().map((_, i) => (i + 1) * 8).reverse(); // 248 → 8 (in steps of 8) +// Returns the version of OpenZeppelin Contracts in which a particular function was introduced. +// This is used in the docs for each function. const version = (selector, length) => { switch (selector) { case 'toUint(uint)': { From 772efdab59638b14d857bcdc1e412c08090d3073 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 19 May 2022 10:20:46 +0200 Subject: [PATCH 18/21] add range js helpers --- scripts/generate/templates/SafeCast.js | 3 ++- scripts/generate/templates/SafeCastMock.js | 5 +++-- scripts/helpers.js | 23 ++++++++++++++++++++++ test/utils/math/SafeCast.test.js | 6 +++--- 4 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 scripts/helpers.js diff --git a/scripts/generate/templates/SafeCast.js b/scripts/generate/templates/SafeCast.js index 3c5897ca434..ce36c26c4e4 100755 --- a/scripts/generate/templates/SafeCast.js +++ b/scripts/generate/templates/SafeCast.js @@ -1,7 +1,8 @@ const assert = require('assert'); const format = require('../format-lines'); +const { range } = require('../../helpers'); -const LENGTHS = Array(31).fill().map((_, i) => (i + 1) * 8).reverse(); // 248 → 8 (in steps of 8) +const LENGTHS = range(8, 256, 8).reverse(); // 248 → 8 (in steps of 8) // Returns the version of OpenZeppelin Contracts in which a particular function was introduced. // This is used in the docs for each function. diff --git a/scripts/generate/templates/SafeCastMock.js b/scripts/generate/templates/SafeCastMock.js index bda69c1a326..9bb64d2c766 100755 --- a/scripts/generate/templates/SafeCastMock.js +++ b/scripts/generate/templates/SafeCastMock.js @@ -1,4 +1,7 @@ const format = require('../format-lines'); +const { range } = require('../../helpers'); + +const LENGTHS = range(8, 256, 8).reverse(); // 248 → 8 (in steps of 8) const header = `\ pragma solidity ^0.8.0; @@ -31,8 +34,6 @@ function toUint${length}(uint256 a) public pure returns (uint${length}) { `; // GENERATE -const LENGTHS = Array(31).fill().map((_, i) => (i + 1) * 8).reverse(); // 248 → 8 (in steps of 8) - module.exports = format( header, 'contract SafeCastMock {', diff --git a/scripts/helpers.js b/scripts/helpers.js new file mode 100644 index 00000000000..3a08272da9b --- /dev/null +++ b/scripts/helpers.js @@ -0,0 +1,23 @@ +function chunk (array, size = 1) { + return Array.range(Math.ceil(array.length / size)).map(i => array.slice(i * size, i * size + size)) +} + +function range (start, stop = undefined, step = 1) { + if (!stop) { stop = start; start = 0; } + return start < stop ? Array(Math.ceil((stop - start) / step)).fill().map((_, i) => start + i * step) : []; +} + +function unique (array, op = x => x) { + return array.filter((obj, i) => array.findIndex(entry => op(obj) === op(entry)) === i); +} + +function zip (...args) { + return Array(Math.max(...args.map(arg => arg.length))).fill(null).map((_, i) => args.map(arg => arg[i])); +} + +module.exports = { + chunk, + range, + unique, + zip, +} \ No newline at end of file diff --git a/test/utils/math/SafeCast.test.js b/test/utils/math/SafeCast.test.js index 0693a189944..97fc22e9258 100644 --- a/test/utils/math/SafeCast.test.js +++ b/test/utils/math/SafeCast.test.js @@ -1,6 +1,6 @@ const { BN, expectRevert } = require('@openzeppelin/test-helpers'); - const { expect } = require('chai'); +const { range } = require('../../../scripts/helpers'); const SafeCastMock = artifacts.require('SafeCastMock'); @@ -41,7 +41,7 @@ contract('SafeCast', async (accounts) => { }); } - Array(31).fill().map((_, i) => (i + 1) * 8).forEach(bits => testToUint(bits)); + range(8, 256, 8).forEach(bits => testToUint(bits)); describe('toUint256', () => { const maxInt256 = new BN('2').pow(new BN(255)).subn(1); @@ -129,7 +129,7 @@ contract('SafeCast', async (accounts) => { }); } - Array(31).fill().map((_, i) => (i + 1) * 8).forEach(bits => testToInt(bits)); + range(8, 256, 8).forEach(bits => testToInt(bits)); describe('toInt256', () => { const maxUint256 = new BN('2').pow(new BN(256)).subn(1); From 7461e2ab121e1fae49e746a24d777685d4701441 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 20 May 2022 09:48:07 +0200 Subject: [PATCH 19/21] fix lint --- scripts/helpers.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/helpers.js b/scripts/helpers.js index 3a08272da9b..cbd0e01e3b5 100644 --- a/scripts/helpers.js +++ b/scripts/helpers.js @@ -1,23 +1,23 @@ function chunk (array, size = 1) { - return Array.range(Math.ceil(array.length / size)).map(i => array.slice(i * size, i * size + size)) + return Array.range(Math.ceil(array.length / size)).map(i => array.slice(i * size, i * size + size)); } function range (start, stop = undefined, step = 1) { - if (!stop) { stop = start; start = 0; } - return start < stop ? Array(Math.ceil((stop - start) / step)).fill().map((_, i) => start + i * step) : []; + if (!stop) { stop = start; start = 0; } + return start < stop ? Array(Math.ceil((stop - start) / step)).fill().map((_, i) => start + i * step) : []; } function unique (array, op = x => x) { - return array.filter((obj, i) => array.findIndex(entry => op(obj) === op(entry)) === i); + return array.filter((obj, i) => array.findIndex(entry => op(obj) === op(entry)) === i); } function zip (...args) { - return Array(Math.max(...args.map(arg => arg.length))).fill(null).map((_, i) => args.map(arg => arg[i])); + return Array(Math.max(...args.map(arg => arg.length))).fill(null).map((_, i) => args.map(arg => arg[i])); } module.exports = { - chunk, - range, - unique, - zip, -} \ No newline at end of file + chunk, + range, + unique, + zip, +}; From d675c0d27dafa05266a49a1e34484efba0e0943b Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 20 May 2022 15:26:00 +0200 Subject: [PATCH 20/21] add Changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 007688e535c..1f272d827d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * `CrossChainEnabledPolygonChild`: replace the `require` statement with the custom error `NotCrossChainCall`. ([#3380](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3380)) * `ERC20FlashMint`: Add customizable flash fee receiver. ([#3327](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3327)) * `Strings`: add a new overloaded function `toHexString` that converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. ([#3403](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3403)) + * `SafeCast`: add support for many more types, using procedural code generation. ([#3245](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3245)) ## 4.6.0 (2022-04-26) From 550d20aeadc0f004a828cd509c47baa81f5efa1d Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 20 May 2022 15:26:33 +0200 Subject: [PATCH 21/21] lint --- scripts/checks/generation.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/checks/generation.sh b/scripts/checks/generation.sh index 574a645d60d..165c35a06b0 100755 --- a/scripts/checks/generation.sh +++ b/scripts/checks/generation.sh @@ -3,4 +3,4 @@ set -euo pipefail npm run generate -git diff --quiet --exit-code \ No newline at end of file +git diff --quiet --exit-code