SafeCast.js 4.38 KB
Newer Older
vicotor's avatar
vicotor committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
const assert = require('assert');
const format = require('../format-lines');
const { range } = require('../../helpers');

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.
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 = `\
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 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
 *
 * _Available since v${version('toUint(uint)', 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");
    return uint${length}(value);
}
`;

/* eslint-disable max-len */
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 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");
    return int${length}(value);
}
`;
/* eslint-enable max-len */

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}.
 *
 * _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 be greater than or equal to 0.
 *
 * _Available since v${version('toUint(int)', length)}._
 */
function toUint${length}(int${length} value) internal pure returns (uint${length}) {
    require(value >= 0, "SafeCast: value must be positive");
    return uint${length}(value);
}
`;

// GENERATE
module.exports = format(
  header.trimEnd(),
  'library SafeCast {',
  [
    ...LENGTHS.map(size => toUintDownCast(size)),
    toUint(256),
    ...LENGTHS.map(size => toIntDownCast(size)),
    toInt(256).trimEnd(),
  ],
  '}',
);