SafeCast.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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. /* eslint-disable max-len */
  56. const toIntDownCast = length => `\
  57. /**
  58. * @dev Returns the downcasted int${length} from int256, reverting on
  59. * overflow (when the input is less than smallest int${length} or
  60. * greater than largest int${length}).
  61. *
  62. * Counterpart to Solidity's \`int${length}\` operator.
  63. *
  64. * Requirements:
  65. *
  66. * - input must fit into ${length} bits
  67. */
  68. function toInt${length}(int256 value) internal pure returns (int${length} downcasted) {
  69. downcasted = int${length}(value);
  70. if (downcasted != value) {
  71. revert SafeCastOverflowedIntDowncast(${length}, value);
  72. }
  73. }
  74. `;
  75. /* eslint-enable max-len */
  76. const toInt = length => `\
  77. /**
  78. * @dev Converts an unsigned uint${length} into a signed int${length}.
  79. *
  80. * Requirements:
  81. *
  82. * - input must be less than or equal to maxInt${length}.
  83. */
  84. function toInt${length}(uint${length} value) internal pure returns (int${length}) {
  85. // Note: Unsafe cast below is okay because \`type(int${length}).max\` is guaranteed to be positive
  86. if (value > uint${length}(type(int${length}).max)) {
  87. revert SafeCastOverflowedUintToInt(value);
  88. }
  89. return int${length}(value);
  90. }
  91. `;
  92. const toUint = length => `\
  93. /**
  94. * @dev Converts a signed int${length} into an unsigned uint${length}.
  95. *
  96. * Requirements:
  97. *
  98. * - input must be greater than or equal to 0.
  99. */
  100. function toUint${length}(int${length} value) internal pure returns (uint${length}) {
  101. if (value < 0) {
  102. revert SafeCastOverflowedIntToUint(value);
  103. }
  104. return uint${length}(value);
  105. }
  106. `;
  107. const boolToUint = `
  108. /**
  109. * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
  110. */
  111. function toUint(bool b) internal pure returns (uint256 u) {
  112. /// @solidity memory-safe-assembly
  113. assembly {
  114. u := iszero(iszero(b))
  115. }
  116. }
  117. `;
  118. // GENERATE
  119. module.exports = format(
  120. header.trimEnd(),
  121. 'library SafeCast {',
  122. errors,
  123. [...LENGTHS.map(toUintDownCast), toUint(256), ...LENGTHS.map(toIntDownCast), toInt(256), boolToUint],
  124. '}',
  125. );