SafeCast.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. const format = require('../format-lines');
  2. const { range } = require('../../helpers');
  3. const LENGTHS = range(8, 256, 8).reverse(); // 248 → 8 (in steps of 8)
  4. const header = `\
  5. pragma solidity ^0.8.20;
  6. /**
  7. * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
  8. * checks.
  9. *
  10. * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
  11. * easily result in undesired exploitation or bugs, since developers usually
  12. * assume that overflows raise errors. \`SafeCast\` restores this intuition by
  13. * reverting the transaction when such an operation overflows.
  14. *
  15. * Using this library instead of the unchecked operations eliminates an entire
  16. * class of bugs, so it's recommended to use it always.
  17. */
  18. `;
  19. const errors = `\
  20. /**
  21. * @dev Value doesn't fit in an uint of \`bits\` size.
  22. */
  23. error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
  24. /**
  25. * @dev An int value doesn't fit in an uint of \`bits\` size.
  26. */
  27. error SafeCastOverflowedIntToUint(int256 value);
  28. /**
  29. * @dev Value doesn't fit in an int of \`bits\` size.
  30. */
  31. error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
  32. /**
  33. * @dev An uint value doesn't fit in an int of \`bits\` size.
  34. */
  35. error SafeCastOverflowedUintToInt(uint256 value);
  36. `;
  37. const toUintDownCast = length => `\
  38. /**
  39. * @dev Returns the downcasted uint${length} from uint256, reverting on
  40. * overflow (when the input is greater than largest uint${length}).
  41. *
  42. * Counterpart to Solidity's \`uint${length}\` operator.
  43. *
  44. * Requirements:
  45. *
  46. * - input must fit into ${length} bits
  47. */
  48. function toUint${length}(uint256 value) internal pure returns (uint${length}) {
  49. if (value > type(uint${length}).max) {
  50. revert SafeCastOverflowedUintDowncast(${length}, value);
  51. }
  52. return uint${length}(value);
  53. }
  54. `;
  55. const toIntDownCast = length => `\
  56. /**
  57. * @dev Returns the downcasted int${length} from int256, reverting on
  58. * overflow (when the input is less than smallest int${length} or
  59. * greater than largest int${length}).
  60. *
  61. * Counterpart to Solidity's \`int${length}\` operator.
  62. *
  63. * Requirements:
  64. *
  65. * - input must fit into ${length} bits
  66. */
  67. function toInt${length}(int256 value) internal pure returns (int${length} downcasted) {
  68. downcasted = int${length}(value);
  69. if (downcasted != value) {
  70. revert SafeCastOverflowedIntDowncast(${length}, value);
  71. }
  72. }
  73. `;
  74. const toInt = length => `\
  75. /**
  76. * @dev Converts an unsigned uint${length} into a signed int${length}.
  77. *
  78. * Requirements:
  79. *
  80. * - input must be less than or equal to maxInt${length}.
  81. */
  82. function toInt${length}(uint${length} value) internal pure returns (int${length}) {
  83. // Note: Unsafe cast below is okay because \`type(int${length}).max\` is guaranteed to be positive
  84. if (value > uint${length}(type(int${length}).max)) {
  85. revert SafeCastOverflowedUintToInt(value);
  86. }
  87. return int${length}(value);
  88. }
  89. `;
  90. const toUint = length => `\
  91. /**
  92. * @dev Converts a signed int${length} into an unsigned uint${length}.
  93. *
  94. * Requirements:
  95. *
  96. * - input must be greater than or equal to 0.
  97. */
  98. function toUint${length}(int${length} value) internal pure returns (uint${length}) {
  99. if (value < 0) {
  100. revert SafeCastOverflowedIntToUint(value);
  101. }
  102. return uint${length}(value);
  103. }
  104. `;
  105. const boolToUint = `\
  106. /**
  107. * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
  108. */
  109. function toUint(bool b) internal pure returns (uint256 u) {
  110. assembly ("memory-safe") {
  111. u := iszero(iszero(b))
  112. }
  113. }
  114. `;
  115. // GENERATE
  116. module.exports = format(
  117. header.trimEnd(),
  118. 'library SafeCast {',
  119. format(
  120. [].concat(errors, LENGTHS.map(toUintDownCast), toUint(256), LENGTHS.map(toIntDownCast), toInt(256), boolToUint),
  121. ).trimEnd(),
  122. '}',
  123. );