Browse Source

Generate already lint code from procedural generation (#5060)

Hadrien Croubois 1 year ago
parent
commit
dd1e8988ab

+ 3 - 3
scripts/generate/run.js

@@ -1,6 +1,6 @@
 #!/usr/bin/env node
 
-const cp = require('child_process');
+// const cp = require('child_process');
 const fs = require('fs');
 const path = require('path');
 const format = require('./format-lines');
@@ -23,11 +23,11 @@ function generateFromTemplate(file, template, outputPrefix = '') {
     ...(version ? [version + ` (${file})`] : []),
     `// This file was procedurally generated from ${input}.`,
     '',
-    require(template),
+    require(template).trimEnd(),
   );
 
   fs.writeFileSync(output, content);
-  cp.execFileSync('prettier', ['--write', output]);
+  // cp.execFileSync('prettier', ['--write', output]);
 }
 
 // Contracts

+ 78 - 71
scripts/generate/templates/Arrays.js

@@ -15,39 +15,39 @@ import {Math} from "./math/Math.sol";
 `;
 
 const sort = type => `\
-    /**
-     * @dev Sort an array of ${type} (in memory) following the provided comparator function.
-     *
-     * This function does the sorting "in place", meaning that it overrides the input. The object is returned for
-     * convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
-     *
-     * NOTE: this function's cost is \`O(n · log(n))\` in average and \`O(n²)\` in the worst case, with n the length of the
-     * array. Using it in view functions that are executed through \`eth_call\` is safe, but one should be very careful
-     * when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
-     * consume more gas than is available in a block, leading to potential DoS.
-     */
-    function sort(
-        ${type}[] memory array,
-        function(${type}, ${type}) pure returns (bool) comp
-    ) internal pure returns (${type}[] memory) {
-        ${
-          type === 'bytes32'
-            ? '_quickSort(_begin(array), _end(array), comp);'
-            : 'sort(_castToBytes32Array(array), _castToBytes32Comp(comp));'
-        }
-        return array;
+/**
+ * @dev Sort an array of ${type} (in memory) following the provided comparator function.
+ *
+ * This function does the sorting "in place", meaning that it overrides the input. The object is returned for
+ * convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
+ *
+ * NOTE: this function's cost is \`O(n · log(n))\` in average and \`O(n²)\` in the worst case, with n the length of the
+ * array. Using it in view functions that are executed through \`eth_call\` is safe, but one should be very careful
+ * when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
+ * consume more gas than is available in a block, leading to potential DoS.
+ */
+function sort(
+    ${type}[] memory array,
+    function(${type}, ${type}) pure returns (bool) comp
+) internal pure returns (${type}[] memory) {
+    ${
+      type === 'bytes32'
+        ? '_quickSort(_begin(array), _end(array), comp);'
+        : 'sort(_castToBytes32Array(array), _castToBytes32Comp(comp));'
     }
+    return array;
+}
 
-    /**
-     * @dev Variant of {sort} that sorts an array of ${type} in increasing order.
-     */
-    function sort(${type}[] memory array) internal pure returns (${type}[] memory) {
-        ${type === 'bytes32' ? 'sort(array, _defaultComp);' : 'sort(_castToBytes32Array(array), _defaultComp);'}
-        return array;
-    }
+/**
+ * @dev Variant of {sort} that sorts an array of ${type} in increasing order.
+ */
+function sort(${type}[] memory array) internal pure returns (${type}[] memory) {
+    ${type === 'bytes32' ? 'sort(array, _defaultComp);' : 'sort(_castToBytes32Array(array), _defaultComp);'}
+    return array;
+}
 `;
 
-const quickSort = `
+const quickSort = `\
 /**
  * @dev Performs a quick sort of a segment of memory. The segment sorted starts at \`begin\` (inclusive), and stops
  * at end (exclusive). Sorting follows the \`comp\` comparator.
@@ -123,34 +123,34 @@ function _swap(uint256 ptr1, uint256 ptr2) private pure {
 }
 `;
 
-const defaultComparator = `
-    /// @dev Comparator for sorting arrays in increasing order.
-    function _defaultComp(bytes32 a, bytes32 b) private pure returns (bool) {
-        return a < b;
-    }
+const defaultComparator = `\
+/// @dev Comparator for sorting arrays in increasing order.
+function _defaultComp(bytes32 a, bytes32 b) private pure returns (bool) {
+    return a < b;
+}
 `;
 
 const castArray = type => `\
-    /// @dev Helper: low level cast ${type} memory array to uint256 memory array
-    function _castToBytes32Array(${type}[] memory input) private pure returns (bytes32[] memory output) {
-        assembly {
-            output := input
-        }
+/// @dev Helper: low level cast ${type} memory array to uint256 memory array
+function _castToBytes32Array(${type}[] memory input) private pure returns (bytes32[] memory output) {
+    assembly {
+        output := input
     }
+}
 `;
 
 const castComparator = type => `\
-    /// @dev Helper: low level cast ${type} comp function to bytes32 comp function
-    function _castToBytes32Comp(
-        function(${type}, ${type}) pure returns (bool) input
-    ) private pure returns (function(bytes32, bytes32) pure returns (bool) output) {
-        assembly {
-            output := input
-        }
+/// @dev Helper: low level cast ${type} comp function to bytes32 comp function
+function _castToBytes32Comp(
+    function(${type}, ${type}) pure returns (bool) input
+) private pure returns (function(bytes32, bytes32) pure returns (bool) output) {
+    assembly {
+        output := input
     }
+}
 `;
 
-const search = `
+const search = `\
 /**
  * @dev Searches a sorted \`array\` and returns the first index that contains
  * a value greater or equal to \`element\`. If no such index exists (i.e. all
@@ -319,12 +319,12 @@ function upperBoundMemory(uint256[] memory array, uint256 element) internal pure
 }
 `;
 
-const unsafeAccessStorage = type => `
+const unsafeAccessStorage = type => `\
 /**
-* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
-*
-* WARNING: Only use if you are certain \`pos\` is lower than the array length.
-*/
+ * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
+ *
+ * WARNING: Only use if you are certain \`pos\` is lower than the array length.
+ */
 function unsafeAccess(${type}[] storage arr, uint256 pos) internal pure returns (StorageSlot.${capitalize(
   type,
 )}Slot storage) {
@@ -334,9 +334,10 @@ function unsafeAccess(${type}[] storage arr, uint256 pos) internal pure returns
         slot := arr.slot
     }
     return slot.deriveArray().offset(pos).get${capitalize(type)}Slot();
-}`;
+}
+`;
 
-const unsafeAccessMemory = type => `
+const unsafeAccessMemory = type => `\
 /**
  * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
  *
@@ -349,7 +350,7 @@ function unsafeMemoryAccess(${type}[] memory arr, uint256 pos) internal pure ret
 }
 `;
 
-const unsafeSetLength = type => `
+const unsafeSetLength = type => `\
 /**
  * @dev Helper to set the length of an dynamic array. Directly writing to \`.length\` is forbidden.
  *
@@ -360,26 +361,32 @@ function unsafeSetLength(${type}[] storage array, uint256 len) internal {
     assembly {
         sstore(array.slot, len)
     }
-}`;
+}
+`;
 
 // GENERATE
 module.exports = format(
   header.trimEnd(),
   'library Arrays {',
-  'using SlotDerivation for bytes32;',
-  'using StorageSlot for bytes32;',
-  // sorting, comparator, helpers and internal
-  sort('bytes32'),
-  TYPES.filter(type => type !== 'bytes32').map(sort),
-  quickSort,
-  defaultComparator,
-  TYPES.filter(type => type !== 'bytes32').map(castArray),
-  TYPES.filter(type => type !== 'bytes32').map(castComparator),
-  // lookup
-  search,
-  // unsafe (direct) storage and memory access
-  TYPES.map(unsafeAccessStorage),
-  TYPES.map(unsafeAccessMemory),
-  TYPES.map(unsafeSetLength),
+  format(
+    [].concat(
+      'using SlotDerivation for bytes32;',
+      'using StorageSlot for bytes32;',
+      '',
+      // sorting, comparator, helpers and internal
+      sort('bytes32'),
+      TYPES.filter(type => type !== 'bytes32').map(sort),
+      quickSort,
+      defaultComparator,
+      TYPES.filter(type => type !== 'bytes32').map(castArray),
+      TYPES.filter(type => type !== 'bytes32').map(castComparator),
+      // lookup
+      search,
+      // unsafe (direct) storage and memory access
+      TYPES.map(unsafeAccessStorage),
+      TYPES.map(unsafeAccessMemory),
+      TYPES.map(unsafeSetLength),
+    ),
+  ).trimEnd(),
   '}',
 );

+ 18 - 31
scripts/generate/templates/Checkpoints.js

@@ -17,10 +17,10 @@ import {Math} from "../math/Math.sol";
 `;
 
 const errors = `\
-    /**
-     * @dev A value was attempted to be inserted on a past checkpoint.
-     */
-    error CheckpointUnorderedInsertion();
+/**
+ * @dev A value was attempted to be inserted on a past checkpoint.
+ */
+error CheckpointUnorderedInsertion();
 `;
 
 const template = opts => `\
@@ -37,15 +37,11 @@ struct ${opts.checkpointTypeName} {
  * @dev Pushes a (\`key\`, \`value\`) pair into a ${opts.historyTypeName} so that it is stored as the checkpoint.
  *
  * Returns previous value and new value.
- * 
+ *
  * IMPORTANT: Never accept \`key\` as a user input, since an arbitrary \`type(${opts.keyTypeName}).max\` key set will disable the
  * library.
  */
-function push(
-    ${opts.historyTypeName} storage self,
-    ${opts.keyTypeName} key,
-    ${opts.valueTypeName} value
-) internal returns (${opts.valueTypeName}, ${opts.valueTypeName}) {
+function push(${opts.historyTypeName} storage self, ${opts.keyTypeName} key, ${opts.valueTypeName} value) internal returns (${opts.valueTypeName}, ${opts.valueTypeName}) {
     return _insert(self.${opts.checkpointFieldName}, key, value);
 }
 
@@ -108,15 +104,7 @@ function latest(${opts.historyTypeName} storage self) internal view returns (${o
  * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
  * in the most recent checkpoint.
  */
-function latestCheckpoint(${opts.historyTypeName} storage self)
-    internal
-    view
-    returns (
-        bool exists,
-        ${opts.keyTypeName} ${opts.keyFieldName},
-        ${opts.valueTypeName} ${opts.valueFieldName}
-    )
-{
+function latestCheckpoint(${opts.historyTypeName} storage self) internal view returns (bool exists, ${opts.keyTypeName} ${opts.keyFieldName}, ${opts.valueTypeName} ${opts.valueFieldName}) {
     uint256 pos = self.${opts.checkpointFieldName}.length;
     if (pos == 0) {
         return (false, 0, 0);
@@ -144,11 +132,7 @@ function at(${opts.historyTypeName} storage self, uint32 pos) internal view retu
  * @dev Pushes a (\`key\`, \`value\`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
  * or by updating the last one.
  */
-function _insert(
-    ${opts.checkpointTypeName}[] storage self,
-    ${opts.keyTypeName} key,
-    ${opts.valueTypeName} value
-) private returns (${opts.valueTypeName}, ${opts.valueTypeName}) {
+function _insert(${opts.checkpointTypeName}[] storage self, ${opts.keyTypeName} key, ${opts.valueTypeName} value) private returns (${opts.valueTypeName}, ${opts.valueTypeName}) {
     uint256 pos = self.length;
 
     if (pos > 0) {
@@ -225,11 +209,10 @@ function _lowerBinaryLookup(
 /**
  * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
  */
-function _unsafeAccess(${opts.checkpointTypeName}[] storage self, uint256 pos)
-    private
-    pure
-    returns (${opts.checkpointTypeName} storage result)
-{
+function _unsafeAccess(
+    ${opts.checkpointTypeName}[] storage self,
+    uint256 pos
+) private pure returns (${opts.checkpointTypeName} storage result) {
     assembly {
         mstore(0, self.slot)
         result.slot := add(keccak256(0, 0x20), pos)
@@ -242,7 +225,11 @@ function _unsafeAccess(${opts.checkpointTypeName}[] storage self, uint256 pos)
 module.exports = format(
   header.trimEnd(),
   'library Checkpoints {',
-  errors,
-  OPTS.flatMap(opts => template(opts)),
+  format(
+    [].concat(
+      errors,
+      OPTS.map(opts => template(opts)),
+    ),
+  ).trimEnd(),
   '}',
 );

+ 20 - 28
scripts/generate/templates/Checkpoints.t.js

@@ -22,18 +22,13 @@ uint8 internal constant _KEY_MAX_GAP = 64;
 Checkpoints.${opts.historyTypeName} internal _ckpts;
 
 // helpers
-function _bound${capitalize(opts.keyTypeName)}(
-    ${opts.keyTypeName} x,
-    ${opts.keyTypeName} min,
-    ${opts.keyTypeName} max
-) internal pure returns (${opts.keyTypeName}) {
+function _bound${capitalize(opts.keyTypeName)}(${opts.keyTypeName} x, ${opts.keyTypeName} min, ${
+  opts.keyTypeName
+} max) internal pure returns (${opts.keyTypeName}) {
     return SafeCast.to${capitalize(opts.keyTypeName)}(bound(uint256(x), uint256(min), uint256(max)));
 }
 
-function _prepareKeys(
-    ${opts.keyTypeName}[] memory keys,
-    ${opts.keyTypeName} maxSpread
-) internal pure {
+function _prepareKeys(${opts.keyTypeName}[] memory keys, ${opts.keyTypeName} maxSpread) internal pure {
     ${opts.keyTypeName} lastKey = 0;
     for (uint256 i = 0; i < keys.length; ++i) {
         ${opts.keyTypeName} key = _bound${capitalize(opts.keyTypeName)}(keys[i], lastKey, lastKey + maxSpread);
@@ -42,11 +37,7 @@ function _prepareKeys(
     }
 }
 
-function _assertLatestCheckpoint(
-    bool exist,
-    ${opts.keyTypeName} key,
-    ${opts.valueTypeName} value
-) internal {
+function _assertLatestCheckpoint(bool exist, ${opts.keyTypeName} key, ${opts.valueTypeName} value) internal {
     (bool _exist, ${opts.keyTypeName} _key, ${opts.valueTypeName} _value) = _ckpts.latestCheckpoint();
     assertEq(_exist, exist);
     assertEq(_key, key);
@@ -54,11 +45,9 @@ function _assertLatestCheckpoint(
 }
 
 // tests
-function testPush(
-    ${opts.keyTypeName}[] memory keys,
-    ${opts.valueTypeName}[] memory values,
-    ${opts.keyTypeName} pastKey
-) public {
+function testPush(${opts.keyTypeName}[] memory keys, ${opts.valueTypeName}[] memory values, ${
+  opts.keyTypeName
+} pastKey) public {
     vm.assume(values.length > 0 && values.length <= keys.length);
     _prepareKeys(keys, _KEY_MAX_GAP);
 
@@ -71,7 +60,7 @@ function testPush(
     for (uint256 i = 0; i < keys.length; ++i) {
         ${opts.keyTypeName} key = keys[i];
         ${opts.valueTypeName} value = values[i % values.length];
-        if (i > 0 && key == keys[i-1]) ++duplicates;
+        if (i > 0 && key == keys[i - 1]) ++duplicates;
 
         // push
         _ckpts.push(key, value);
@@ -95,14 +84,12 @@ function testPush(
 
 // used to test reverts
 function push(${opts.keyTypeName} key, ${opts.valueTypeName} value) external {
-  _ckpts.push(key, value);
+    _ckpts.push(key, value);
 }
 
-function testLookup(
-    ${opts.keyTypeName}[] memory keys,
-    ${opts.valueTypeName}[] memory values,
-    ${opts.keyTypeName} lookup
-) public {
+function testLookup(${opts.keyTypeName}[] memory keys, ${opts.valueTypeName}[] memory values, ${
+  opts.keyTypeName
+} lookup) public {
     vm.assume(values.length > 0 && values.length <= keys.length);
     _prepareKeys(keys, _KEY_MAX_GAP);
 
@@ -124,7 +111,7 @@ function testLookup(
             upper = value;
         }
         // find the first key that is not smaller than the lookup key
-        if (key >= lookup && (i == 0 || keys[i-1] < lookup)) {
+        if (key >= lookup && (i == 0 || keys[i - 1] < lookup)) {
             lowerKey = key;
         }
         if (key == lowerKey) {
@@ -142,5 +129,10 @@ function testLookup(
 // GENERATE
 module.exports = format(
   header,
-  ...OPTS.flatMap(opts => [`contract Checkpoints${opts.historyTypeName}Test is Test {`, [template(opts)], '}']),
+  ...OPTS.flatMap(opts => [
+    `contract Checkpoints${opts.historyTypeName}Test is Test {`,
+    [template(opts).trimEnd()],
+    '}',
+    '',
+  ]),
 );

+ 12 - 18
scripts/generate/templates/EnumerableMap.js

@@ -54,7 +54,7 @@ import {EnumerableSet} from "./EnumerableSet.sol";
 `;
 /* eslint-enable max-len */
 
-const defaultMap = () => `\
+const defaultMap = `\
 // To implement this library for multiple types with as little code repetition as possible, we write it in
 // terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions,
 // and user-facing implementations such as \`UintToAddressMap\` are just wrappers around the underlying Map.
@@ -78,11 +78,7 @@ struct Bytes32ToBytes32Map {
  * Returns true if the key was added to the map, that is if it was not
  * already present.
  */
-function set(
-    Bytes32ToBytes32Map storage map,
-    bytes32 key,
-    bytes32 value
-) internal returns (bool) {
+function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
     map._values[key] = value;
     return map._keys.add(key);
 }
@@ -148,7 +144,7 @@ function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view retu
  */
 function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
     bytes32 value = map._values[key];
-    if(value == 0 && !contains(map, key)) {
+    if (value == 0 && !contains(map, key)) {
         revert EnumerableMapNonexistentKey(key);
     }
     return value;
@@ -181,11 +177,7 @@ struct ${name} {
  * Returns true if the key was added to the map, that is if it was not
  * already present.
  */
-function set(
-    ${name} storage map,
-    ${keyType} key,
-    ${valueType} value
-) internal returns (bool) {
+function set(${name} storage map, ${keyType} key, ${valueType} value) internal returns (bool) {
     return set(map._inner, ${toBytes32(keyType, 'key')}, ${toBytes32(valueType, 'value')});
 }
 
@@ -271,11 +263,13 @@ function keys(${name} storage map) internal view returns (${keyType}[] memory) {
 module.exports = format(
   header.trimEnd(),
   'library EnumerableMap {',
-  [
-    'using EnumerableSet for EnumerableSet.Bytes32Set;',
-    '',
-    defaultMap(),
-    TYPES.map(details => customMap(details).trimEnd()).join('\n\n'),
-  ],
+  format(
+    [].concat(
+      'using EnumerableSet for EnumerableSet.Bytes32Set;',
+      '',
+      defaultMap,
+      TYPES.map(details => customMap(details)),
+    ),
+  ).trimEnd(),
   '}',
 );

+ 7 - 2
scripts/generate/templates/EnumerableSet.js

@@ -43,7 +43,7 @@ pragma solidity ^0.8.20;
 `;
 /* eslint-enable max-len */
 
-const defaultSet = () => `\
+const defaultSet = `\
 // To implement this library for multiple types with as little code
 // repetition as possible, we write it in terms of a generic Set type with
 // bytes32 values.
@@ -240,6 +240,11 @@ function values(${name} storage set) internal view returns (${type}[] memory) {
 module.exports = format(
   header.trimEnd(),
   'library EnumerableSet {',
-  [defaultSet(), TYPES.map(details => customSet(details).trimEnd()).join('\n\n')],
+  format(
+    [].concat(
+      defaultSet,
+      TYPES.map(details => customSet(details)),
+    ),
+  ).trimEnd(),
   '}',
 );

+ 36 - 35
scripts/generate/templates/SafeCast.js

@@ -21,25 +21,25 @@ pragma solidity ^0.8.20;
 `;
 
 const errors = `\
-  /**
-   * @dev Value doesn't fit in an uint of \`bits\` size.
-   */
-  error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
-  
-  /**
-   * @dev An int value doesn't fit in an uint of \`bits\` size.
-   */
-  error SafeCastOverflowedIntToUint(int256 value);
-  
-  /**
-   * @dev Value doesn't fit in an int of \`bits\` size.
-   */
-  error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
-  
-  /**
-   * @dev An uint value doesn't fit in an int of \`bits\` size.
-   */
-  error SafeCastOverflowedUintToInt(uint256 value);
+/**
+ * @dev Value doesn't fit in an uint of \`bits\` size.
+ */
+error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
+
+/**
+ * @dev An int value doesn't fit in an uint of \`bits\` size.
+ */
+error SafeCastOverflowedIntToUint(int256 value);
+
+/**
+ * @dev Value doesn't fit in an int of \`bits\` size.
+ */
+error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
+
+/**
+ * @dev An uint value doesn't fit in an int of \`bits\` size.
+ */
+error SafeCastOverflowedUintToInt(uint256 value);
 `;
 
 const toUintDownCast = length => `\
@@ -55,7 +55,7 @@ const toUintDownCast = length => `\
  */
 function toUint${length}(uint256 value) internal pure returns (uint${length}) {
     if (value > type(uint${length}).max) {
-      revert SafeCastOverflowedUintDowncast(${length}, value);
+        revert SafeCastOverflowedUintDowncast(${length}, value);
     }
     return uint${length}(value);
 }
@@ -77,7 +77,7 @@ const toIntDownCast = length => `\
 function toInt${length}(int256 value) internal pure returns (int${length} downcasted) {
     downcasted = int${length}(value);
     if (downcasted != value) {
-      revert SafeCastOverflowedIntDowncast(${length}, value);
+        revert SafeCastOverflowedIntDowncast(${length}, value);
     }
 }
 `;
@@ -94,7 +94,7 @@ const toInt = 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
     if (value > uint${length}(type(int${length}).max)) {
-      revert SafeCastOverflowedUintToInt(value);
+        revert SafeCastOverflowedUintToInt(value);
     }
     return int${length}(value);
 }
@@ -110,29 +110,30 @@ const toUint = length => `\
  */
 function toUint${length}(int${length} value) internal pure returns (uint${length}) {
     if (value < 0) {
-      revert SafeCastOverflowedIntToUint(value);
+        revert SafeCastOverflowedIntToUint(value);
     }
     return uint${length}(value);
 }
 `;
 
-const boolToUint = `
-  /**
-   * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
-   */
-  function toUint(bool b) internal pure returns (uint256 u) {
-      /// @solidity memory-safe-assembly
-      assembly {
-          u := iszero(iszero(b))
-      }
-  }
+const boolToUint = `\
+/**
+ * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
+ */
+function toUint(bool b) internal pure returns (uint256 u) {
+    /// @solidity memory-safe-assembly
+    assembly {
+        u := iszero(iszero(b))
+    }
+}
 `;
 
 // GENERATE
 module.exports = format(
   header.trimEnd(),
   'library SafeCast {',
-  errors,
-  [...LENGTHS.map(toUintDownCast), toUint(256), ...LENGTHS.map(toIntDownCast), toInt(256), boolToUint],
+  format(
+    [].concat(errors, LENGTHS.map(toUintDownCast), toUint(256), LENGTHS.map(toIntDownCast), toInt(256), boolToUint),
+  ).trimEnd(),
   '}',
 );

+ 39 - 35
scripts/generate/templates/SlotDerivation.js

@@ -10,7 +10,7 @@ pragma solidity ^0.8.20;
  * the solidity language / compiler.
  *
  * See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.].
- * 
+ *
  * Example usage:
  * \`\`\`solidity
  * contract Example {
@@ -30,9 +30,9 @@ pragma solidity ^0.8.20;
  *     }
  * }
  * \`\`\`
- * 
+ *
  * TIP: Consider using this library along with {StorageSlot}.
- * 
+ *
  * NOTE: This library provides a way to manipulate storage locations in a non-standard way. Tooling for checking
  * upgrade safety will ignore the slots accessed through this library.
  */
@@ -43,11 +43,11 @@ const namespace = `\
  * @dev Derive an ERC-7201 slot from a string (namespace).
  */
 function erc7201Slot(string memory namespace) internal pure returns (bytes32 slot) {
-  /// @solidity memory-safe-assembly
-  assembly {
-      mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1))
-      slot := and(keccak256(0x00, 0x20), not(0xff))
-  }
+    /// @solidity memory-safe-assembly
+    assembly {
+        mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1))
+        slot := and(keccak256(0x00, 0x20), not(0xff))
+    }
 }
 `;
 
@@ -56,20 +56,20 @@ const array = `\
  * @dev Add an offset to a slot to get the n-th element of a structure or an array.
  */
 function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) {
-  unchecked {
-      return bytes32(uint256(slot) + pos);
-  }
+    unchecked {
+        return bytes32(uint256(slot) + pos);
+    }
 }
 
 /**
  * @dev Derive the location of the first element in an array from the slot where the length is stored.
  */
 function deriveArray(bytes32 slot) internal pure returns (bytes32 result) {
-  /// @solidity memory-safe-assembly
-  assembly {
-      mstore(0x00, slot)
-      result := keccak256(0x00, 0x20)
-  }
+    /// @solidity memory-safe-assembly
+    assembly {
+        mstore(0x00, slot)
+        result := keccak256(0x00, 0x20)
+    }
 }
 `;
 
@@ -78,12 +78,12 @@ const mapping = ({ type }) => `\
  * @dev Derive the location of a mapping element from the key.
  */
 function deriveMapping(bytes32 slot, ${type} key) internal pure returns (bytes32 result) {
-  /// @solidity memory-safe-assembly
-  assembly {
-      mstore(0x00, key)
-      mstore(0x20, slot)
-      result := keccak256(0x00, 0x40)
-  }
+    /// @solidity memory-safe-assembly
+    assembly {
+        mstore(0x00, key)
+        mstore(0x20, slot)
+        result := keccak256(0x00, 0x40)
+    }
 }
 `;
 
@@ -92,16 +92,16 @@ const mapping2 = ({ type }) => `\
  * @dev Derive the location of a mapping element from the key.
  */
 function deriveMapping(bytes32 slot, ${type} memory key) internal pure returns (bytes32 result) {
-  /// @solidity memory-safe-assembly
-  assembly {
-    let length := mload(key)
-    let begin :=  add(key, 0x20)
-    let end := add(begin, length)
-    let cache := mload(end)
-    mstore(end, slot)
-    result := keccak256(begin, add(length, 0x20))
-    mstore(end, cache)
-  }
+    /// @solidity memory-safe-assembly
+    assembly {
+        let length := mload(key)
+        let begin := add(key, 0x20)
+        let end := add(begin, length)
+        let cache := mload(end)
+        mstore(end, slot)
+        result := keccak256(begin, add(length, 0x20))
+        mstore(end, cache)
+    }
 }
 `;
 
@@ -109,8 +109,12 @@ function deriveMapping(bytes32 slot, ${type} memory key) internal pure returns (
 module.exports = format(
   header.trimEnd(),
   'library SlotDerivation {',
-  namespace,
-  array,
-  TYPES.map(type => (type.isValueType ? mapping(type) : mapping2(type))),
+  format(
+    [].concat(
+      namespace,
+      array,
+      TYPES.map(type => (type.isValueType ? mapping(type) : mapping2(type))),
+    ),
+  ).trimEnd(),
   '}',
 );

+ 61 - 57
scripts/generate/templates/SlotDerivation.t.js

@@ -14,31 +14,31 @@ const array = `\
 bytes[] private _array;
 
 function symbolicDeriveArray(uint256 length, uint256 offset) public {
-  vm.assume(length > 0);
-  vm.assume(offset < length);
-  _assertDeriveArray(length, offset);
+    vm.assume(length > 0);
+    vm.assume(offset < length);
+    _assertDeriveArray(length, offset);
 }
 
 function testDeriveArray(uint256 length, uint256 offset) public {
-  length = bound(length, 1, type(uint256).max);
-  offset = bound(offset, 0, length - 1);
-  _assertDeriveArray(length, offset);
+    length = bound(length, 1, type(uint256).max);
+    offset = bound(offset, 0, length - 1);
+    _assertDeriveArray(length, offset);
 }
 
 function _assertDeriveArray(uint256 length, uint256 offset) public {
-  bytes32 baseSlot;
-  assembly {
-    baseSlot := _array.slot
-    sstore(baseSlot, length) // store length so solidity access does not revert
-  }
-
-  bytes storage derived = _array[offset];
-  bytes32 derivedSlot;
-  assembly {
-    derivedSlot := derived.slot
-  }
-
-  assertEq(baseSlot.deriveArray().offset(offset), derivedSlot);
+    bytes32 baseSlot;
+    assembly {
+        baseSlot := _array.slot
+        sstore(baseSlot, length) // store length so solidity access does not revert
+    }
+
+    bytes storage derived = _array[offset];
+    bytes32 derivedSlot;
+    assembly {
+        derivedSlot := derived.slot
+    }
+
+    assertEq(baseSlot.deriveArray().offset(offset), derivedSlot);
 }
 `;
 
@@ -46,18 +46,18 @@ const mapping = ({ type, name }) => `\
 mapping(${type} => bytes) private _${type}Mapping;
 
 function testSymbolicDeriveMapping${name}(${type} key) public {
-  bytes32 baseSlot;
-  assembly {
-    baseSlot := _${type}Mapping.slot
-  }
-
-  bytes storage derived = _${type}Mapping[key];
-  bytes32 derivedSlot;
-  assembly {
-    derivedSlot := derived.slot
-  }
-
-  assertEq(baseSlot.deriveMapping(key), derivedSlot);
+    bytes32 baseSlot;
+    assembly {
+        baseSlot := _${type}Mapping.slot
+    }
+
+    bytes storage derived = _${type}Mapping[key];
+    bytes32 derivedSlot;
+    assembly {
+        derivedSlot := derived.slot
+    }
+
+    assertEq(baseSlot.deriveMapping(key), derivedSlot);
 }
 `;
 
@@ -65,45 +65,49 @@ const boundedMapping = ({ type, name }) => `\
 mapping(${type} => bytes) private _${type}Mapping;
 
 function testDeriveMapping${name}(${type} memory key) public {
-  _assertDeriveMapping${name}(key);
+    _assertDeriveMapping${name}(key);
 }
 
 function symbolicDeriveMapping${name}() public {
-  _assertDeriveMapping${name}(svm.create${name}(256, "DeriveMapping${name}Input"));
+    _assertDeriveMapping${name}(svm.create${name}(256, "DeriveMapping${name}Input"));
 }
 
 function _assertDeriveMapping${name}(${type} memory key) internal {
-  bytes32 baseSlot;
-  assembly {
-      baseSlot := _${type}Mapping.slot
-  }
-
-  bytes storage derived = _${type}Mapping[key];
-  bytes32 derivedSlot;
-  assembly {
-      derivedSlot := derived.slot
-  }
-
-  assertEq(baseSlot.deriveMapping(key), derivedSlot);
+    bytes32 baseSlot;
+    assembly {
+        baseSlot := _${type}Mapping.slot
+    }
+
+    bytes storage derived = _${type}Mapping[key];
+    bytes32 derivedSlot;
+    assembly {
+        derivedSlot := derived.slot
+    }
+
+    assertEq(baseSlot.deriveMapping(key), derivedSlot);
 }
 `;
 
 // GENERATE
 module.exports = format(
-  header.trimEnd(),
+  header,
   'contract SlotDerivationTest is Test, SymTest {',
-  'using SlotDerivation for bytes32;',
-  '',
-  array,
-  TYPES.flatMap(type =>
+  format(
     [].concat(
-      type,
-      (type.variants ?? []).map(variant => ({
-        type: variant,
-        name: capitalize(variant),
-        isValueType: type.isValueType,
-      })),
+      'using SlotDerivation for bytes32;',
+      '',
+      array,
+      TYPES.flatMap(type =>
+        [].concat(
+          type,
+          (type.variants ?? []).map(variant => ({
+            type: variant,
+            name: capitalize(variant),
+            isValueType: type.isValueType,
+          })),
+        ),
+      ).map(type => (type.isValueType ? mapping(type) : boundedMapping(type))),
     ),
-  ).map(type => (type.isValueType ? mapping(type) : boundedMapping(type))),
+  ).trimEnd(),
   '}',
 );

+ 28 - 22
scripts/generate/templates/StorageSlot.js

@@ -53,7 +53,7 @@ pragma solidity ^0.8.24;
 
 const struct = ({ type, name }) => `\
 struct ${name}Slot {
-  ${type} value;
+    ${type} value;
 }
 `;
 
@@ -62,10 +62,10 @@ const get = ({ name }) => `\
  * @dev Returns an \`${name}Slot\` with member \`value\` located at \`slot\`.
  */
 function get${name}Slot(bytes32 slot) internal pure returns (${name}Slot storage r) {
-  /// @solidity memory-safe-assembly
-  assembly {
-      r.slot := slot
-  }
+    /// @solidity memory-safe-assembly
+    assembly {
+        r.slot := slot
+    }
 }
 `;
 
@@ -74,10 +74,10 @@ const getStorage = ({ type, name }) => `\
  * @dev Returns an \`${name}Slot\` representation of the ${type} storage pointer \`store\`.
  */
 function get${name}Slot(${type} storage store) internal pure returns (${name}Slot storage r) {
-  /// @solidity memory-safe-assembly
-  assembly {
-      r.slot := store.slot
-  }
+    /// @solidity memory-safe-assembly
+    assembly {
+        r.slot := store.slot
+    }
 }
 `;
 
@@ -86,11 +86,12 @@ const udvt = ({ type, name }) => `\
  * @dev UDVT that represent a slot holding a ${type}.
  */
 type ${name}SlotType is bytes32;
+
 /**
  * @dev Cast an arbitrary slot to a ${name}SlotType.
  */
 function as${name}(bytes32 slot) internal pure returns (${name}SlotType) {
-  return ${name}SlotType.wrap(slot);
+    return ${name}SlotType.wrap(slot);
 }
 `;
 
@@ -99,19 +100,20 @@ const transient = ({ type, name }) => `\
  * @dev Load the value held at location \`slot\` in transient storage.
  */
 function tload(${name}SlotType slot) internal view returns (${type} value) {
-  /// @solidity memory-safe-assembly
-  assembly {
-    value := tload(slot)
-  }
+    /// @solidity memory-safe-assembly
+    assembly {
+        value := tload(slot)
+    }
 }
+
 /**
  * @dev Store \`value\` at location \`slot\` in transient storage.
  */
 function tstore(${name}SlotType slot, ${type} value) internal {
-  /// @solidity memory-safe-assembly
-  assembly {
-    tstore(slot, value)
-  }
+    /// @solidity memory-safe-assembly
+    assembly {
+        tstore(slot, value)
+    }
 }
 `;
 
@@ -119,9 +121,13 @@ function tstore(${name}SlotType slot, ${type} value) internal {
 module.exports = format(
   header.trimEnd(),
   'library StorageSlot {',
-  TYPES.map(type => struct(type)),
-  TYPES.flatMap(type => [get(type), type.isValueType ? '' : getStorage(type)]),
-  TYPES.filter(type => type.isValueType).map(type => udvt(type)),
-  TYPES.filter(type => type.isValueType).map(type => transient(type)),
+  format(
+    [].concat(
+      TYPES.map(type => struct(type)),
+      TYPES.flatMap(type => [get(type), !type.isValueType && getStorage(type)].filter(Boolean)),
+      TYPES.filter(type => type.isValueType).map(type => udvt(type)),
+      TYPES.filter(type => type.isValueType).map(type => transient(type)),
+    ),
+  ).trimEnd(),
   '}',
 );

+ 13 - 8
scripts/generate/templates/StorageSlotMock.js

@@ -44,22 +44,27 @@ const transient = ({ type, name }) => `\
 event ${name}Value(bytes32 slot, ${type} value);
 
 function tload${name}(bytes32 slot) public {
-  emit ${name}Value(slot, slot.as${name}().tload());
+    emit ${name}Value(slot, slot.as${name}().tload());
 }
 
 function tstore(bytes32 slot, ${type} value) public {
-  slot.as${name}().tstore(value);
+    slot.as${name}().tstore(value);
 }
 `;
 
 // GENERATE
 module.exports = format(
-  header.trimEnd(),
+  header,
   'contract StorageSlotMock is Multicall {',
-  'using StorageSlot for *;',
-  TYPES.filter(type => type.isValueType).map(type => storageSetValueType(type)),
-  TYPES.filter(type => type.isValueType).map(type => storageGetValueType(type)),
-  TYPES.filter(type => !type.isValueType).map(type => storageSetNonValueType(type)),
-  TYPES.filter(type => type.isValueType).map(type => transient(type)),
+  format(
+    [].concat(
+      'using StorageSlot for *;',
+      '',
+      TYPES.filter(type => type.isValueType).map(type => storageSetValueType(type)),
+      TYPES.filter(type => type.isValueType).map(type => storageGetValueType(type)),
+      TYPES.filter(type => !type.isValueType).map(type => storageSetNonValueType(type)),
+      TYPES.filter(type => type.isValueType).map(type => transient(type)),
+    ),
+  ).trimEnd(),
   '}',
 );