StorageSlot.js 2.4 KB

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