StorageSlot.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. const format = require('../format-lines');
  2. const { capitalize, unique } = require('../../helpers');
  3. const TYPES = [
  4. { type: 'address', isValueType: true, version: '4.1' },
  5. { type: 'bool', isValueType: true, name: 'Boolean', version: '4.1' },
  6. { type: 'bytes32', isValueType: true, version: '4.1' },
  7. { type: 'uint256', isValueType: true, version: '4.1' },
  8. { type: 'string', isValueType: false, version: '4.9' },
  9. { type: 'bytes', isValueType: false, version: '4.9' },
  10. ].map(type => Object.assign(type, { struct: (type.name ?? capitalize(type.type)) + 'Slot' }));
  11. const VERSIONS = unique(TYPES.map(t => t.version)).map(
  12. version =>
  13. `_Available since v${version} for ${TYPES.filter(t => t.version == version)
  14. .map(t => `\`${t.type}\``)
  15. .join(', ')}._`,
  16. );
  17. const header = `\
  18. pragma solidity ^0.8.19;
  19. /**
  20. * @dev Library for reading and writing primitive types to specific storage slots.
  21. *
  22. * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
  23. * This library helps with reading and writing to such slots without the need for inline assembly.
  24. *
  25. * The functions in this library return Slot structs that contain a \`value\` member that can be used to read or write.
  26. *
  27. * Example usage to set ERC1967 implementation slot:
  28. * \`\`\`solidity
  29. * contract ERC1967 {
  30. * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
  31. *
  32. * function _getImplementation() internal view returns (address) {
  33. * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
  34. * }
  35. *
  36. * function _setImplementation(address newImplementation) internal {
  37. * require(newImplementation.code.length > 0);
  38. * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
  39. * }
  40. * }
  41. * \`\`\`
  42. *
  43. ${VERSIONS.map(s => ` * ${s}`).join('\n')}
  44. */
  45. `;
  46. const struct = type => `\
  47. struct ${type.struct} {
  48. ${type.type} value;
  49. }
  50. `;
  51. const get = type => `\
  52. /**
  53. * @dev Returns an \`${type.struct}\` with member \`value\` located at \`slot\`.
  54. */
  55. function get${type.struct}(bytes32 slot) internal pure returns (${type.struct} storage r) {
  56. /// @solidity memory-safe-assembly
  57. assembly {
  58. r.slot := slot
  59. }
  60. }
  61. `;
  62. const getStorage = type => `\
  63. /**
  64. * @dev Returns an \`${type.struct}\` representation of the ${type.type} storage pointer \`store\`.
  65. */
  66. function get${type.struct}(${type.type} storage store) internal pure returns (${type.struct} storage r) {
  67. /// @solidity memory-safe-assembly
  68. assembly {
  69. r.slot := store.slot
  70. }
  71. }
  72. `;
  73. // GENERATE
  74. module.exports = format(
  75. header.trimEnd(),
  76. 'library StorageSlot {',
  77. [...TYPES.map(struct), ...TYPES.flatMap(type => [get(type), type.isValueType ? '' : getStorage(type)])],
  78. '}',
  79. );